diff options
author | delta <darkussdelta@gmail.com> | 2025-09-13 19:55:00 +0200 |
---|---|---|
committer | delta <darkussdelta@gmail.com> | 2025-09-13 19:55:00 +0200 |
commit | 031bf426ca73146d9253548e77f90ec112ebcedb (patch) | |
tree | b3dcd2de915ba1c31ad7bec2e30fc44df9226251 /lua/prismite |
initial commit
Diffstat (limited to 'lua/prismite')
-rw-r--r-- | lua/prismite/groups/colors.lua | 20 | ||||
-rw-r--r-- | lua/prismite/groups/diagnostics.lua | 22 | ||||
-rw-r--r-- | lua/prismite/groups/init.lua | 7 | ||||
-rw-r--r-- | lua/prismite/groups/kinds.lua | 44 | ||||
-rw-r--r-- | lua/prismite/groups/plugins/blink.lua | 10 | ||||
-rw-r--r-- | lua/prismite/groups/plugins/indentmini.lua | 5 | ||||
-rw-r--r-- | lua/prismite/groups/plugins/init.lua | 11 | ||||
-rw-r--r-- | lua/prismite/groups/plugins/marks.lua | 6 | ||||
-rw-r--r-- | lua/prismite/groups/plugins/noice.lua | 33 | ||||
-rw-r--r-- | lua/prismite/groups/plugins/nvim-tree.lua | 7 | ||||
-rw-r--r-- | lua/prismite/groups/plugins/treesitter.lua | 164 | ||||
-rw-r--r-- | lua/prismite/groups/plugins/trouble.lua | 13 | ||||
-rw-r--r-- | lua/prismite/groups/syntax.lua | 92 | ||||
-rw-r--r-- | lua/prismite/groups/ui.lua | 74 | ||||
-rw-r--r-- | lua/prismite/init.lua | 53 | ||||
-rw-r--r-- | lua/prismite/oklab.lua | 623 | ||||
-rw-r--r-- | lua/prismite/palette.lua | 29 | ||||
-rw-r--r-- | lua/prismite/utils.lua | 73 |
18 files changed, 1286 insertions, 0 deletions
diff --git a/lua/prismite/groups/colors.lua b/lua/prismite/groups/colors.lua new file mode 100644 index 0000000..0502baf --- /dev/null +++ b/lua/prismite/groups/colors.lua @@ -0,0 +1,20 @@ +local colors = require "prismite.palette" + +return { + PrismiteRed = { fg = colors.red }, + PrismiteRedBright = { fg = colors.red_bright }, + PrismiteOrange = { fg = colors.orange }, + PrismiteOrangeBright = { fg = colors.orange }, + PrismiteYellow = { fg = colors.yellow }, + PrismiteYellowBright = { fg = colors.yellow_bright }, + PrismiteGreen = { fg = colors.green }, + PrismiteGreenBright = { fg = colors.green_bright }, + PrismiteCyan = { fg = colors.cyan }, + PrismiteCyanBright = { fg = colors.cyan_bright }, + PrismiteBlue = { fg = colors.blue }, + PrismiteBlueBright = { fg = colors.blue_bright }, + PrismitePurple = { fg = colors.purple }, + PrismitePuprleBright = { fg = colors.purple_bright }, + PrismitePink = { fg = colors.pink }, + PrismitePinkBright = { fg = colors.pink_bright }, +} diff --git a/lua/prismite/groups/diagnostics.lua b/lua/prismite/groups/diagnostics.lua new file mode 100644 index 0000000..11e1de9 --- /dev/null +++ b/lua/prismite/groups/diagnostics.lua @@ -0,0 +1,22 @@ +local colors = require "prismite.palette" +local utils = require "prismite.utils" + +local opacity = 0.1 + +return { + DiagnosticError = { fg = colors.red }, + DiagnosticUnderlineError = { sp = colors.red, undercurl = true }, + DiagnosticVirtualTextError = { bg = utils.overlay_oklch(colors.red, colors.bg, opacity), fg = colors.red }, + DiagnosticWarn = { fg = colors.orange }, + DiagnosticUnderlineWarn = { sp = colors.orange, undercurl = true }, + DiagnosticVirtualTextWarn = { bg = utils.overlay_oklch(colors.orange, colors.bg, opacity), fg = colors.orange }, + DiagnosticInfo = { fg = colors.blue }, + DiagnosticUnderlineInfo = { sp = colors.blue, underline = true }, + DiagnosticVirtualTextInfo = { bg = utils.overlay_oklch(colors.blue, colors.bg, opacity), fg = colors.blue }, + DiagnosticHint = { fg = colors.purple }, + DiagnosticUnderlineHint = { sp = colors.purple, underline = true }, + DiagnosticVirtualTextHint = { bg = utils.overlay_oklch(colors.purple, colors.bg, opacity), fg = colors.purple }, + DiagnosticOk = { fg = colors.green }, + DiagnosticUnderlineOk = { sp = colors.green, underline = true }, + DiagnosticVirtualTextOk = { bg = utils.overlay_oklch(colors.green, colors.bg, opacity), fg = colors.green }, +} diff --git a/lua/prismite/groups/init.lua b/lua/prismite/groups/init.lua new file mode 100644 index 0000000..c203ebe --- /dev/null +++ b/lua/prismite/groups/init.lua @@ -0,0 +1,7 @@ +return { + require "prismite.groups.syntax", + require "prismite.groups.ui", + require "prismite.groups.plugins", + require "prismite.groups.diagnostics", + require "prismite.groups.colors", +} diff --git a/lua/prismite/groups/kinds.lua b/lua/prismite/groups/kinds.lua new file mode 100644 index 0000000..69a25c2 --- /dev/null +++ b/lua/prismite/groups/kinds.lua @@ -0,0 +1,44 @@ +local colors = require "prismite.palette" + +local M = {} + +M.base = { + [""] = { fg = colors.blue }, + Text = { link = "__base__" }, + Method = { link = "@function.method" }, + Function = { link = "@function" }, + Constructor = { link = "@constructor" }, + Field = { link = "@variable.member" }, + Variable = { link = "@variable" }, + Class = { link = "@type" }, + Interface = { link = "@type" }, + Module = { link = "@module" }, + Property = { link = "@property" }, + Unit = { link = "@constant" }, + Value = { link = "__base__" }, + Enum = { link = "@type" }, + Keyword = { link = "@keyword" }, + Snippet = { link = "__base__" }, + Color = { link = "@string.special" }, + File = { link = "@string.special.path" }, + Reference = { link = "__base__" }, + Folder = { link = "@string.special.path" }, + EnumMember = { link = "@constant" }, + Constant = { link = "@constant" }, + Struct = { link = "@type" }, + Event = { link = "@constant" }, + Operator = { link = "@operator" }, + TypeParameter = { link = "@parameter" }, +} + +function M.generate(base) + local ret = {} + for kind, hl in pairs(M.base) do + ret[base .. kind] = + vim.tbl_deep_extend("force", hl, hl.link and { link = hl.link:gsub("__base__", base) } or {}) + end + + return ret +end + +return M diff --git a/lua/prismite/groups/plugins/blink.lua b/lua/prismite/groups/plugins/blink.lua new file mode 100644 index 0000000..b343c9f --- /dev/null +++ b/lua/prismite/groups/plugins/blink.lua @@ -0,0 +1,10 @@ +local colors = require "prismite.palette" +local kinds = require "prismite.groups.kinds" + +return vim.tbl_deep_extend("error", { + BlinkCmpMenuBorder = { fg = colors.border }, + BlinkCmpGhostText = { fg = colors.fg_low }, + BlinkCmpDocBorder = { fg = colors.border }, + BlinkCmpLabelDescription = { fg = colors.fg_low }, + BlinkCmpScrollBarThumb = { bg = colors.yellow }, +}, kinds.generate "BlinkCmpKind") diff --git a/lua/prismite/groups/plugins/indentmini.lua b/lua/prismite/groups/plugins/indentmini.lua new file mode 100644 index 0000000..5c2debc --- /dev/null +++ b/lua/prismite/groups/plugins/indentmini.lua @@ -0,0 +1,5 @@ +local colors = require "prismite.palette" +return { + IndentLine = { fg = colors.border_variant }, + IndentLineCurrent = { fg = colors.border }, +} diff --git a/lua/prismite/groups/plugins/init.lua b/lua/prismite/groups/plugins/init.lua new file mode 100644 index 0000000..21bd8e6 --- /dev/null +++ b/lua/prismite/groups/plugins/init.lua @@ -0,0 +1,11 @@ +return vim.tbl_deep_extend( + "error", + {}, + require "prismite.groups.plugins.indentmini", + require "prismite.groups.plugins.treesitter", + require "prismite.groups.plugins.noice", + require "prismite.groups.plugins.marks", + require "prismite.groups.plugins.trouble", + require "prismite.groups.plugins.nvim-tree", + require "prismite.groups.plugins.blink" +) diff --git a/lua/prismite/groups/plugins/marks.lua b/lua/prismite/groups/plugins/marks.lua new file mode 100644 index 0000000..0bc2ef1 --- /dev/null +++ b/lua/prismite/groups/plugins/marks.lua @@ -0,0 +1,6 @@ +local colors = require "prismite.palette" + +return { + MarkSignHL = { fg = colors.green }, + MarkSignNumHL = {}, +} diff --git a/lua/prismite/groups/plugins/noice.lua b/lua/prismite/groups/plugins/noice.lua new file mode 100644 index 0000000..5982db2 --- /dev/null +++ b/lua/prismite/groups/plugins/noice.lua @@ -0,0 +1,33 @@ +local colors = require "prismite.palette" + +return { + NoiceCmdline = { link = "Normal" }, + NoiceCmdlineIcon = { bg = colors.bg, fg = colors.yellow }, + NoiceCmdlinePopup = { bg = colors.bg }, + NoicePopupBorder = { link = "WinSeparator" }, + NoiceCmdlinePopupBorder = { link = "NoicePopupBorder" }, + NoiceCmdlinePopupBorderSearch = { link = "NoicePopupBorder" }, + + NoiceCompletionItemKindClass = { link = "@type" }, + NoiceCompletionItemKindColor = { link = "@type" }, + NoiceCompletionItemKindConstant = { link = "@constant" }, + NoiceCompletionItemKindConstructor = { link = "@constructor" }, + NoiceCompletionItemKindDefault = { bold = true }, + NoiceCompletionItemKindEnum = { link = "@type" }, + NoiceCompletionItemKindEnumMember = { link = "@constant" }, + NoiceCompletionItemKindField = { link = "@variable.member" }, + NoiceCompletionItemKindFile = { link = "@constant" }, + NoiceCompletionItemKindFolder = { link = "@constant" }, + NoiceCompletionItemKindFunction = { link = "@function" }, + NoiceCompletionItemKindInterface = { link = "@type" }, + NoiceCompletionItemKindKeyword = { link = "@keyword" }, + NoiceCompletionItemKindMethod = { link = "@function.method" }, + NoiceCompletionItemKindModule = { link = "@module" }, + NoiceCompletionItemKindProperty = { link = "@property" }, + NoiceCompletionItemKindSnippet = { fg = colors.cyan }, + NoiceCompletionItemKindStruct = { link = "@type" }, + -- NoiceCompletionItemKindText = {}, + NoiceCompletionItemKindUnit = { link = "@constant" }, + NoiceCompletionItemKindValue = { link = "@constant" }, + NoiceCompletionItemKindVariable = { link = "@variable" }, +} diff --git a/lua/prismite/groups/plugins/nvim-tree.lua b/lua/prismite/groups/plugins/nvim-tree.lua new file mode 100644 index 0000000..a358d19 --- /dev/null +++ b/lua/prismite/groups/plugins/nvim-tree.lua @@ -0,0 +1,7 @@ +local colors = require "prismite.palette" + +return { + NvimTreeSpecialFile = { fg = colors.green }, + NvimTreeRootFolder = { link = "Directory" }, + NvimTreeIndentMarker = { fg = colors.border }, +} diff --git a/lua/prismite/groups/plugins/treesitter.lua b/lua/prismite/groups/plugins/treesitter.lua new file mode 100644 index 0000000..0302c53 --- /dev/null +++ b/lua/prismite/groups/plugins/treesitter.lua @@ -0,0 +1,164 @@ +local colors = require "prismite.palette" + +return { + -- various variable names + ["@variable"] = { link = "Identifier" }, + -- built-in variable names (e.g. `this`) + ["@variable.builtin"] = { fg = colors.pink, bold = true }, + -- parameters of a function + ["@variable.parameter"] = { link = "@variable" }, + -- special parameters (e.g. `_`, `it`) + ["@variable.parameter.builtin"] = { link = "@variable.builtin" }, + -- object and struct fields + ["@variable.member"] = { fg = colors.fg }, + + -- constant identifiers + ["@constant"] = { link = "Constant" }, + -- built-in constant values + ["@constant.builtin"] = { link = "@constant" }, + -- constants defined by the preprocessor + ["@constant.macro"] = { link = "@constant" }, + + -- modules or namespaces + ["@module"] = { italic = true }, + -- built-in modules or namespaces + ["@module.builtin"] = { link = "@module" }, + -- GOTO and other labels (e.g. `label:` in C), including heredoc labels + ["@label"] = { link = "Todo" }, + + -- string literals + ["@string"] = { link = "String" }, + -- string documenting code (e.g. Python docstrings + ["@string.documentation"] = { link = "@comment" }, + -- regular expressions + ["@string.regexp"] = { link = "@constant" }, + -- escape sequences + ["@string.escape"] = { fg = colors.cyan }, + -- other special strings (e.g. dates) + ["@string.special"] = { link = "@string" }, + -- symbols or atoms + ["@string.special.symbol"] = { link = "@string" }, + -- URIs (e.g. hyperlinks) + ["@string.special.url"] = { underline = true }, + -- filenames + ["@string.special.path"] = { link = "@string" }, + + -- character literals + ["@character"] = { link = "@constant" }, + -- special characters (e.g. wildcards) + ["@character.special"] = { link = "String" }, + + -- boolean literals + ["@boolean"] = { link = "@constant" }, + -- numeric literals + ["@number"] = { link = "@constant" }, + -- floating-point number literals + ["@number.float"] = { link = "@number" }, + + -- type or class definitions and annotations + ["@type"] = { link = "Type" }, + -- built-in types + ["@type.builtin"] = { link = "@type" }, + -- identifiers in type definitions (e.g. `typedef <type> <identifier>` in C) + ["@type.definition"] = { link = "@type" }, + + -- attribute annotations (e.g. Python decorators, Rust lifetimes) + ["@attribute"] = { link = "Function" }, + -- builtin annotations (e.g. `@property` in Python) + ["@attribute.builtin"] = { link = "@attribute" }, + -- the key in key/value pairs + ["@property"] = { fg = colors.fg }, + + -- function definitions + ["@function"] = { link = "Function" }, + -- built-in functions + ["@function.builtin"] = { link = "@function" }, + -- function calls + ["@function.call"] = { link = "@function" }, + -- preprocessor macros + ["@function.macro"] = { link = "@constant.macro" }, + + -- method definitions + ["@function.method"] = { link = "@function" }, + -- method calls + ["@function.method.call"] = { link = "@function.method" }, + + -- constructor calls and definitions + ["@constructor"] = { link = "@function" }, + -- this is overriden because for some reason the {} in table inits + -- are parsed as constructors, which ig makes sense + ["@constructor.lua"] = { fg = colors.fg }, + -- symbolic operators (e.g. `+` / `*`) + ["@operator"] = { fg = colors.fg }, + + -- keywords not fitting into specific categories + ["@keyword"] = { link = "Keyword" }, + -- keywords related to coroutines (e.g. `go` in Go, `async/await` in Python) + ["@keyword.coroutine"] = { link = "@keyword" }, + -- keywords that define a function (e.g. `func` in Go, `def` in Python) + ["@keyword.function"] = { link = "@keyword" }, + -- operators that are English words (e.g. `and` / `or`) + ["@keyword.operator"] = { link = "@keyword" }, + -- keywords for including or exporting modules (e.g. `import` / `from` in Python) + ["@keyword.import"] = { link = "@keyword" }, + -- keywords describing namespaces and composite types (e.g. `struct`, `enum`) + ["@keyword.type"] = { link = "@keyword" }, + -- keywords modifying other constructs (e.g. `const`, `static`, `public`) + ["@keyword.modifier"] = { link = "@keyword" }, + -- keywords related to loops (e.g. `for` / `while`) + ["@keyword.repeat"] = { link = "@keyword" }, + -- keywords like `return` and `yield` + ["@keyword.return"] = { link = "@keyword" }, + -- keywords related to debugging + ["@keyword.debug"] = { link = "@keyword" }, + -- keywords related to exceptions (e.g. `throw` / `catch`) + ["@keyword.exception"] = { link = "@keyword" }, + + -- keywords related to conditionals (e.g. `if` / `else`) + ["@keyword.conditional"] = { link = "@keyword" }, + -- ternary operator (e.g. `?` / `:`) + ["@keyword.conditional.ternary"] = { link = "@operator" }, + + -- various preprocessor directives & shebangs + ["@keyword.directive"] = { link = "@keyword" }, + -- preprocessor definition directives + ["@keyword.directive.define"] = { link = "@keyword" }, + + -- delimiters (e.g. `;` / `.` / `,`) + ["@punctuation"] = { fg = colors.fg }, + -- brackets (e.g. `()` / `{}` / `[]`) + ["@punctuation.special"] = { fg = colors.cyan }, + -- special symbols (e.g. `{}` in string interpolation) + + -- line and block comments + ["@comment"] = { link = "Comment" }, + -- comments documenting code + ["@comment.documentation"] = { link = "@comment" }, + + ["@comment.error"] = { sp = colors.red, underline = true }, + ["@comment.warning"] = { sp = colors.orange, underline = true }, + ["@comment.todo"] = { link = "Todo" }, + ["@comment.note"] = { sp = colors.blue, underline = true }, + + ["@markup.strong"] = { bold = true }, + ["@markup.italic"] = { italic = true }, + ["@markup.strikethrough"] = { strikethrough = true }, + ["@markup.underline"] = { underline = true }, + + ["@markup.heading"] = { fg = colors.blue }, + + ["@markup.link"] = { fg = colors.cyan }, + ["@markup.link.label"] = { bold = true }, + ["@markup.link.url"] = { underline = true }, + + ["@diff.plus"] = { link = "Added" }, + ["@diff.delta"] = { link = "Changed" }, + ["@diff.minus"] = { link = "Removed" }, + + ["@tag"] = { link = "htmlTag" }, + ["@tag.builtin"] = { link = "@tag" }, + ["@tag.attribute"] = { link = "@property" }, + ["@tag.delimeter"] = { link = "@tag" }, + + ["@none"] = { link = "Normal" }, +} diff --git a/lua/prismite/groups/plugins/trouble.lua b/lua/prismite/groups/plugins/trouble.lua new file mode 100644 index 0000000..0b6fdf9 --- /dev/null +++ b/lua/prismite/groups/plugins/trouble.lua @@ -0,0 +1,13 @@ +local colors = require "prismite.palette" + +return { + TroubleIndent = { fg = colors.border }, + TroubleIndentFoldOpen = { fg = colors.fg }, + TroubleIndentFoldClose = { fg = colors.fg }, + TroubleIconDirectory = { fg = colors.fg }, + TroubleFsCount = { fg = colors.fg }, + TroubleDiagnosticsPost = { fg = colors.fg }, + TroubleCode = { fg = colors.cyan }, + TroubleLspCount = { fg = colors.fg }, + TroubleLspPos = { fg = colors.fg }, +} diff --git a/lua/prismite/groups/syntax.lua b/lua/prismite/groups/syntax.lua new file mode 100644 index 0000000..3efde03 --- /dev/null +++ b/lua/prismite/groups/syntax.lua @@ -0,0 +1,92 @@ +local colors = require "prismite.palette" + +return { + Comment = { fg = colors.fg_low }, + + Constant = { fg = colors.pink }, + String = { fg = colors.yellow }, + Character = { link = "Constant" }, + Number = { link = "Constant" }, + Float = { link = "Number" }, + Boolean = { link = "Constant" }, + + Identifier = { fg = colors.fg }, + Function = { fg = colors.purple }, + + Statement = { fg = colors.blue }, + Conditional = { link = "Statement" }, + Repeat = { link = "Statement" }, + Label = { link = "Statement" }, + Operator = { fg = colors.fg }, + Keyword = { link = "Statement" }, + Exception = { link = "Statement" }, + + PreProc = { link = "Statement" }, + Include = { link = "PreProc" }, + Define = { link = "PreProc" }, + Macro = { link = "PreProc" }, + PreCondit = { link = "PreProc" }, + + Type = { fg = colors.orange }, + StorageClass = { link = "Statement" }, + Structure = { link = "Statement" }, + Typedef = { link = "Statement" }, + + Special = { link = "Constant" }, + SpecialChar = { link = "Special" }, + Tag = { fg = colors.blue }, + Delimiter = { fg = colors.fg }, + SpecialComment = { fg = "fg", bg = "bg", underline = true }, + Debug = { link = "Constant" }, + + Underlined = { underline = true }, + + Ignore = { fg = "bg" }, + + Error = { fg = colors.red }, + + Todo = { fg = colors.purple, bg = colors.bg }, + + Added = { link = "DiffAdd" }, + Changed = { link = "DiffChange" }, + Removed = { link = "DiffDelete" }, + + -- HTML specific highlights + htmlTag = { fg = colors.pink }, + htmlEndTag = { fg = colors.pink }, + + -- Semantic highlights, see :h lsp-semantic-highlight + ["@lsp.type.class"] = { link = "@type" }, + ["@lsp.type.comment"] = { link = "@comment" }, + ["@lsp.type.decorator"] = { link = "@attribute" }, + ["@lsp.type.enum"] = { link = "@type" }, + ["@lsp.type.enumMember"] = { link = "@constant" }, + -- ["@lsp.type.event"] = { link = "" }, -- TODO: figure out which hl this should link to + ["@lsp.type.function"] = { link = "@function" }, + ["@lsp.type.interface"] = { link = "@type" }, + ["@lsp.type.keyword"] = { link = "@keyword" }, + ["@lsp.type.macro"] = { link = "@constant.macro" }, + ["@lsp.type.method"] = { link = "@function.method" }, + ["@lsp.type.modifier"] = { link = "@keyword.modifer" }, + ["@lsp.type.namespace"] = { link = "@module" }, + ["@lsp.type.number"] = { link = "@number" }, + ["@lsp.type.operator"] = { link = "@operator" }, + ["@lsp.type.parameter"] = { link = "@variable.parameter" }, + ["@lsp.type.property"] = { link = "@property" }, + ["@lsp.type.regexp"] = { link = "@string.regexp" }, + ["@lsp.type.string"] = { link = "@string" }, + ["@lsp.type.struct"] = { link = "@type" }, + ["@lsp.type.type"] = { link = "@type" }, + ["@lsp.type.typeParameter"] = { link = "@variable.parameter" }, + ["@lsp.type.variable"] = { link = "@variable" }, + + ["@lsp.mod.abstract"] = {}, + ["@lsp.mod.async"] = {}, + ["@lsp.mod.declaration"] = {}, + ["@lsp.mod.defaultLibrary"] = {}, + ["@lsp.mod.definition"] = {}, + ["@lsp.mod.deprecated"] = { strikethrough = true }, + ["@lsp.mod.documentation"] = { underline = true }, + ["@lsp.mod.modification"] = {}, + ["@lsp.mod.static"] = {}, +} diff --git a/lua/prismite/groups/ui.lua b/lua/prismite/groups/ui.lua new file mode 100644 index 0000000..0941fd8 --- /dev/null +++ b/lua/prismite/groups/ui.lua @@ -0,0 +1,74 @@ +local colors = require "prismite.palette" +local utils = require "prismite.utils" + +return { + Conceal = { fg = colors.bg_high }, + CurSearch = { link = "Search" }, + Cursor = { fg = colors.bg, bg = colors.fg }, + CursorIM = { link = "Cursor" }, + CursorColumn = { bg = colors.bg_high }, + CursorLine = { link = "CursorColumn" }, + Directory = { fg = colors.blue }, + DiffAdd = { bg = utils.overlay_oklch(colors.green, colors.bg, 0.1) }, + DiffChange = { bg = utils.overlay_oklch(colors.cyan, colors.bg, 0.1) }, + DiffDelete = { bg = utils.overlay_oklch(colors.red, colors.bg, 0.1) }, + DiffText = { link = "Normal" }, + EndOfBuffer = { link = "Comment" }, + -- TermCursor = { link = "Cursor" }, + -- TermCursorNC = { link = "Cursor" }, + ErrorMsg = { fg = colors.red }, + WinSeparator = { fg = colors.border }, + Folded = { fg = colors.bg, bg = colors.cyan }, + FoldColumn = {}, + SignColumn = {}, + IncSearch = { link = "Search" }, + Substitute = { underline = true }, + LineNr = { fg = colors.fg, bg = colors.bg }, + LineNrAbove = { fg = colors.fg_low }, + LineNrBelow = { fg = colors.fg_low }, + CursorLineNr = { fg = colors.fg, bg = colors.bg_high }, + CursorLineFold = { fg = colors.fg, bg = colors.bg_high }, + CursorLineSign = { bg = colors.bg_high }, + MatchParen = { underline = true }, + ModeMsg = { bg = colors.bg_high }, + MsgArea = { bg = colors.bg_high }, + MsgSeparator = {}, + MoreMsg = { bg = colors.bg_high }, + NonText = { fg = colors.fg_darjer }, + Normal = { fg = colors.fg, bg = colors.bg }, + NormalFloat = { link = "Normal" }, + FloatBorder = { link = "WinSeparator" }, + FloatTitle = {}, + FloatFooter = {}, + NormalNC = { link = "Normal" }, + Pmenu = { link = "Normal" }, + PmenuSel = { link = "CursorLine" }, + PmenuKind = { link = "Pmenu" }, + PmenuKindSel = { link = "PmenuSel" }, + PmenuExtra = { link = "Pmenu" }, + PmenuExtraSel = { link = "PmenuSel" }, + PmenuSbar = { fg = colors.fg_low }, + PmenuThumb = { fg = colors.yellow }, + Question = {}, + QuickFixLine = { link = "CursorLine" }, + Search = { fg = colors.bg, bg = colors.yellow }, + SpecialKey = { link = "NonText" }, + SpellBad = { undercurl = true }, + SpellCap = { undercurl = true }, + SpellLocal = { undercurl = true }, + SpellRare = { undercurl = true, italic = true }, + StatusLine = { fg = colors.fg }, + StatusLineNC = { fg = colors.fg_low }, + TabLine = { bg = colors.bg_low, fg = colors.fg_low }, + TabLineFill = { bg = colors.bg_lowest }, + TabLineSel = { bg = colors.bg }, + Title = {}, + Visual = { bg = colors.bg_highest }, + VisualNOS = { fg = colors.bg, bg = colors.orange }, + WarningMsg = { fg = colors.orange }, + Whitespace = { fg = colors.border }, + WildMenu = { link = "CursorLine" }, + WinBar = { link = "Normal" }, + WinBarNC = { link = "WinBar" }, + LspInlayHint = { link = "Comment" }, +} diff --git a/lua/prismite/init.lua b/lua/prismite/init.lua new file mode 100644 index 0000000..8fd4d02 --- /dev/null +++ b/lua/prismite/init.lua @@ -0,0 +1,53 @@ +local utils = require "prismite.utils" + +local M = {} + +local function term_color(idx, color) + vim.g["terminal_color_" .. idx] = color +end + +function M.load() + if vim.g.colors_name then + vim.cmd.hi "clear" + end + + vim.g.colors_name = "prismite" + vim.o.termguicolors = true + + local colors = require "prismite.palette" + term_color(0, colors.bg_lowest) + term_color(8, colors.bg_low) + term_color(1, colors.red) + term_color(9, colors.red_bright) + term_color(2, colors.green) + term_color(10, colors.green_bright) + term_color(3, colors.yellow) + term_color(11, colors.yellow_bright) + term_color(4, colors.blue) + term_color(12, colors.blue_bright) + term_color(5, colors.pink) + term_color(13, colors.pink_bright) + term_color(6, colors.cyan) + term_color(14, colors.cyan_bright) + term_color(7, colors.fg) + term_color(15, colors.fg_high) + + local groups = vim.tbl_deep_extend("error", {}, unpack(require "prismite.groups")) + for group, hl in pairs(groups) do + if type(hl.fg) == "table" and utils.is_oklch(hl.fg) then + hl.fg = utils.oklch2hex(hl.fg) + end + + if type(hl.bg) == "table" and utils.is_oklch(hl.bg) then + hl.bg = utils.oklch2hex(hl.bg) + end + + if type(hl.sp) == "table" and utils.is_oklch(hl.sp) then + hl.sp = utils.oklch2hex(hl.sp) + end + + vim.api.nvim_set_hl(0, group, hl) + end +end + +return M diff --git a/lua/prismite/oklab.lua b/lua/prismite/oklab.lua new file mode 100644 index 0000000..0a5c8ed --- /dev/null +++ b/lua/prismite/oklab.lua @@ -0,0 +1,623 @@ +-- Source: https://github.com/ramojus/mellifluous.nvim/blob/9add9975a067e448da077dab92b70473aab41a76/lua/mellifluous/utils/oklab.lua +-- modifications: +-- - lightness calculation adjustments: https://github.com/echasnovski/mini.nvim/issues/1628#issuecomment-2692645938 +-- - chroma normalization + +-- MIT License +-- +-- Copyright (c) 2024 Ramojus Lapinskas +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in all +-- copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. + +-- Oklab color space: https://bottosson.github.io/posts/oklab/ + +-- Original code taken from: +-- https://github.com/echasnovski/mini.colors/blob/main/lua/mini/colors.lua, +-- then slightly refactored and restyled to fit project coding style. +-- Parts of code unrelated to conversion of colors were removed. + +local M = {} + +local tau = 2 * math.pi + +-- Cusps for Oklch color space. These represent (c, l) points of Oklch space +-- (with **not corrected lightness**) inside a hue leaf (points with +-- `math.floor(h) = <index>`) with the highest value of chroma (`c`). +-- They are used to model the whole RGB gamut (region inside which sRGB colors +-- are converted in Oklch space). It is modelled as triangle with vertices: +-- (0, 0), (0, 100) and cusp. NOTE: this is an approximation, i.e. not all RGB +-- colors lie inside this triangle **AND** not all points inside triangle are +-- RGB colors. But both proportions are small: around 0.5% with similar modeled +-- RGB color for first one and around 2.16% for second one. +--stylua: ignore start +---@diagnostic disable start +---@private +local cusps = { + [0] = { 26.23, 64.74 }, + { 26.14, 64.65 }, + { 26.06, 64.56 }, + { 25.98, 64.48 }, + { 25.91, 64.39 }, + { 25.82, 64.29 }, + { 25.76, 64.21 }, + { 25.70, 64.13 }, + { 25.65, 64.06 }, + { 25.59, 63.97 }, + { 25.55, 63.90 }, + { 25.52, 63.83 }, + { 25.48, 63.77 }, + { 25.45, 63.69 }, + { 25.43, 63.63 }, + { 25.41, 63.55 }, + { 25.40, 63.50 }, + { 25.39, 63.43 }, + { 25.40, 63.33 }, + { 25.40, 63.27 }, + { 25.42, 63.22 }, + { 25.44, 63.15 }, + { 25.46, 63.11 }, + { 25.50, 63.05 }, + { 25.53, 63.00 }, + { 25.58, 62.95 }, + { 25.63, 62.90 }, + { 25.69, 62.85 }, + { 25.75, 62.81 }, + { 25.77, 62.80 }, + { 25.34, 63.25 }, + { 24.84, 63.79 }, + { 24.37, 64.32 }, + { 23.92, 64.83 }, + { 23.48, 65.35 }, + { 23.08, 65.85 }, + { 22.65, 66.38 }, + { 22.28, 66.86 }, + { 21.98, 67.27 }, + { 21.67, 67.70 }, + { 21.36, 68.14 }, + { 21.05, 68.60 }, + { 20.74, 69.08 }, + { 20.50, 69.45 }, + { 20.27, 69.83 }, + { 20.04, 70.22 }, + { 19.82, 70.62 }, + { 19.60, 71.03 }, + { 19.38, 71.44 }, + { 19.17, 71.87 }, + { 19.03, 72.16 }, + { 18.83, 72.59 }, + { 18.71, 72.89 }, + { 18.52, 73.34 }, + { 18.40, 73.64 }, + { 18.28, 73.95 }, + { 18.17, 74.26 }, + { 18.01, 74.74 }, + { 17.91, 75.05 }, + { 17.82, 75.38 }, + { 17.72, 75.70 }, + { 17.64, 76.03 }, + { 17.56, 76.36 }, + { 17.48, 76.69 }, + { 17.41, 77.03 }, + { 17.35, 77.36 }, + { 17.29, 77.71 }, + { 17.24, 78.05 }, + { 17.19, 78.39 }, + { 17.15, 78.74 }, + { 17.12, 79.09 }, + { 17.09, 79.45 }, + { 17.07, 79.80 }, + { 17.05, 80.16 }, + { 17.04, 80.52 }, + { 17.04, 81.06 }, + { 17.04, 81.42 }, + { 17.05, 81.79 }, + { 17.07, 82.16 }, + { 17.08, 82.53 }, + { 17.11, 82.72 }, + { 17.14, 83.09 }, + { 17.18, 83.46 }, + { 17.22, 83.84 }, + { 17.27, 84.22 }, + { 17.33, 84.60 }, + { 17.39, 84.98 }, + { 17.48, 85.56 }, + { 17.56, 85.94 }, + { 17.64, 86.33 }, + { 17.73, 86.72 }, + { 17.81, 87.10 }, + { 17.91, 87.50 }, + { 18.04, 88.09 }, + { 18.16, 88.48 }, + { 18.27, 88.88 }, + { 18.40, 89.48 }, + { 18.57, 89.87 }, + { 18.69, 90.27 }, + { 18.88, 90.87 }, + { 19.03, 91.48 }, + { 19.22, 91.88 }, + { 19.44, 92.49 }, + { 19.66, 93.10 }, + { 19.85, 93.71 }, + { 20.04, 94.33 }, + { 20.33, 94.94 }, + { 20.60, 95.56 }, + { 20.85, 96.18 }, + { 21.10, 96.80 }, + { 21.19, 96.48 }, + { 21.27, 96.24 }, + { 21.38, 95.93 }, + { 21.47, 95.70 }, + { 21.59, 95.40 }, + { 21.72, 95.10 }, + { 21.86, 94.80 }, + { 21.97, 94.58 }, + { 22.12, 94.30 }, + { 22.27, 94.02 }, + { 22.43, 93.74 }, + { 22.64, 93.40 }, + { 22.81, 93.14 }, + { 23.04, 92.81 }, + { 23.22, 92.56 }, + { 23.45, 92.25 }, + { 23.68, 91.95 }, + { 23.92, 91.65 }, + { 24.21, 91.31 }, + { 24.45, 91.04 }, + { 24.74, 90.72 }, + { 25.08, 90.36 }, + { 25.37, 90.07 }, + { 25.70, 89.74 }, + { 26.08, 89.39 }, + { 26.44, 89.07 }, + { 26.87, 88.69 }, + { 27.27, 88.34 }, + { 27.72, 87.98 }, + { 28.19, 87.61 }, + { 28.68, 87.23 }, + { 29.21, 86.84 }, + { 29.48, 86.64 }, + { 28.99, 86.70 }, + { 28.13, 86.81 }, + { 27.28, 86.92 }, + { 26.56, 87.02 }, + { 25.83, 87.12 }, + { 25.18, 87.22 }, + { 24.57, 87.32 }, + { 24.01, 87.41 }, + { 23.53, 87.49 }, + { 23.03, 87.58 }, + { 22.53, 87.68 }, + { 22.10, 87.76 }, + { 21.68, 87.84 }, + { 21.26, 87.93 }, + { 20.92, 88.01 }, + { 20.58, 88.08 }, + { 20.25, 88.16 }, + { 19.92, 88.24 }, + { 19.59, 88.33 }, + { 19.35, 88.39 }, + { 19.12, 88.46 }, + { 18.81, 88.55 }, + { 18.58, 88.61 }, + { 18.36, 88.68 }, + { 18.14, 88.76 }, + { 17.93, 88.83 }, + { 17.79, 88.88 }, + { 17.59, 88.95 }, + { 17.39, 89.03 }, + { 17.26, 89.08 }, + { 17.08, 89.16 }, + { 16.96, 89.21 }, + { 16.79, 89.29 }, + { 16.68, 89.35 }, + { 16.58, 89.41 }, + { 16.43, 89.49 }, + { 16.33, 89.55 }, + { 16.24, 89.60 }, + { 16.16, 89.66 }, + { 16.04, 89.75 }, + { 15.96, 89.81 }, + { 15.89, 89.87 }, + { 15.83, 89.93 }, + { 15.77, 89.99 }, + { 15.71, 90.05 }, + { 15.66, 90.12 }, + { 15.61, 90.18 }, + { 15.57, 90.24 }, + { 15.54, 90.31 }, + { 15.51, 90.37 }, + { 15.48, 90.44 }, + { 15.46, 90.51 }, + { 15.40, 90.30 }, + { 15.30, 89.83 }, + { 15.21, 89.36 }, + { 15.12, 88.89 }, + { 15.03, 88.67 }, + { 14.99, 88.18 }, + { 14.92, 87.71 }, + { 14.85, 87.24 }, + { 14.78, 86.77 }, + { 14.75, 86.53 }, + { 14.70, 86.06 }, + { 14.65, 85.59 }, + { 14.61, 85.12 }, + { 14.60, 84.89 }, + { 14.57, 84.42 }, + { 14.54, 83.94 }, + { 14.53, 83.71 }, + { 14.52, 83.24 }, + { 14.51, 82.77 }, + { 14.52, 82.30 }, + { 14.52, 81.83 }, + { 14.53, 81.60 }, + { 14.55, 81.13 }, + { 14.58, 80.66 }, + { 14.59, 80.43 }, + { 14.63, 79.96 }, + { 14.68, 79.49 }, + { 14.70, 79.26 }, + { 14.76, 78.79 }, + { 14.82, 78.32 }, + { 14.85, 78.09 }, + { 14.93, 77.62 }, + { 15.01, 77.16 }, + { 15.10, 76.69 }, + { 15.19, 76.23 }, + { 15.24, 76.00 }, + { 15.34, 75.54 }, + { 15.45, 75.07 }, + { 15.57, 74.61 }, + { 15.69, 74.15 }, + { 15.82, 73.69 }, + { 15.96, 73.23 }, + { 16.10, 72.77 }, + { 16.24, 72.31 }, + { 16.39, 71.86 }, + { 16.55, 71.40 }, + { 16.71, 70.95 }, + { 16.96, 70.26 }, + { 17.14, 69.81 }, + { 17.32, 69.36 }, + { 17.59, 68.69 }, + { 17.88, 68.02 }, + { 18.07, 67.57 }, + { 18.37, 66.90 }, + { 18.67, 66.24 }, + { 18.99, 65.58 }, + { 19.30, 64.93 }, + { 19.74, 64.06 }, + { 20.07, 63.42 }, + { 20.51, 62.57 }, + { 20.97, 61.73 }, + { 21.54, 60.69 }, + { 22.00, 59.87 }, + { 22.70, 58.66 }, + { 23.39, 57.49 }, + { 24.19, 56.16 }, + { 25.20, 54.52 }, + { 26.38, 52.66 }, + { 28.55, 49.32 }, + { 31.32, 45.20 }, + { 31.15, 45.42 }, + { 30.99, 45.64 }, + { 30.85, 45.85 }, + { 30.72, 46.06 }, + { 30.57, 46.31 }, + { 30.47, 46.50 }, + { 30.34, 46.75 }, + { 30.23, 46.97 }, + { 30.13, 47.20 }, + { 30.03, 47.45 }, + { 29.93, 47.71 }, + { 29.86, 47.91 }, + { 29.77, 48.20 }, + { 29.71, 48.43 }, + { 29.65, 48.66 }, + { 29.58, 48.98 }, + { 29.53, 49.23 }, + { 29.48, 49.48 }, + { 29.44, 49.74 }, + { 29.41, 50.01 }, + { 29.37, 50.29 }, + { 29.35, 50.57 }, + { 29.33, 50.86 }, + { 29.31, 51.16 }, + { 29.30, 51.56 }, + { 29.29, 51.87 }, + { 29.29, 52.39 }, + { 29.30, 52.72 }, + { 29.31, 53.05 }, + { 29.33, 53.38 }, + { 29.35, 53.72 }, + { 29.37, 54.06 }, + { 29.40, 54.41 }, + { 29.43, 54.76 }, + { 29.47, 55.12 }, + { 29.52, 55.60 }, + { 29.56, 55.97 }, + { 29.61, 56.34 }, + { 29.66, 56.72 }, + { 29.73, 57.22 }, + { 29.79, 57.61 }, + { 29.84, 57.99 }, + { 29.93, 58.52 }, + { 29.99, 58.91 }, + { 30.08, 59.44 }, + { 30.15, 59.84 }, + { 30.24, 60.38 }, + { 30.34, 60.93 }, + { 30.42, 61.34 }, + { 30.52, 61.90 }, + { 30.63, 62.45 }, + { 30.73, 63.02 }, + { 30.85, 63.58 }, + { 30.96, 64.15 }, + { 31.08, 64.72 }, + { 31.19, 65.30 }, + { 31.31, 65.88 }, + { 31.44, 66.46 }, + { 31.59, 67.20 }, + { 31.72, 67.79 }, + { 31.88, 68.53 }, + { 32.01, 69.12 }, + { 32.18, 69.87 }, + { 32.25, 70.17 }, + { 32.06, 69.99 }, + { 31.76, 69.70 }, + { 31.45, 69.42 }, + { 31.21, 69.20 }, + { 30.97, 68.98 }, + { 30.68, 68.71 }, + { 30.44, 68.50 }, + { 30.21, 68.29 }, + { 29.98, 68.09 }, + { 29.75, 67.89 }, + { 29.53, 67.69 }, + { 29.31, 67.50 }, + { 29.09, 67.31 }, + { 28.88, 67.12 }, + { 28.72, 66.98 }, + { 28.52, 66.80 }, + { 28.31, 66.63 }, + { 28.16, 66.50 }, + { 27.97, 66.33 }, + { 27.78, 66.17 }, + { 27.64, 66.05 }, + { 27.49, 65.94 }, + { 27.33, 65.77 }, + { 27.20, 65.66 }, + { 27.04, 65.51 }, + { 26.92, 65.40 }, + { 26.81, 65.30 }, + { 26.66, 65.16 }, + { 26.55, 65.06 }, + { 26.45, 64.96 }, + { 26.35, 64.87 }, +} + +local function cuberoot(x) + return math.pow(x, 1 / 3) +end + +local function correct_channel(x) + return 0.04045 < x and math.pow((x + 0.055) / 1.055, 2.4) or (x / 12.92) +end + +local function correct_channel_inv(x) + return (0.0031308 >= x) and (12.92 * x) or (1.055 * math.pow(x, 0.416666667) - 0.055) +end + +local function clip(x, from, to) + return math.min(math.max(x, from), to) +end + +local function round(x) + return math.floor(x + 0.5) +end + +local function degree2rad(x) + return (x % 360) * tau / 360 +end + +local function rad2degree(x) + return (x % tau) * 360 / tau +end + +local function correct_lightness(x) + x = 0.01 * x + local k1, k2 = 0.206, 0.03 + local k3 = (1 + k1) / (1 + k2) + + local res = 0.5 * (k3 * x - k1 + math.sqrt((k3 * x - k1) ^ 2 + 4 * k2 * k3 * x)) + return 100 * res +end + +local function correct_lightness_inv(x) + x = 0.01 * x + local k1, k2 = 0.206, 0.03 + local k3 = (1 + k1) / (1 + k2) + local res = (x / k3) * (x + k1) / (x + k2) + return 100 * res +end + +-- Get gamut ranges for Lch point. They are computed for its hue leaf as +-- segments of triangle in (c, l) coordinates ((0, 0), (0, 100), cusp). +-- Equations for triangle parts: +-- - Lower segment ((0; 0) to cusp): y * c_cusp = x * L_cusp +-- - Upper segment ((0; 100) to cusp): (100 - y) * c_cusp = x * (100 - L_cusp) +-- NOTEs: +-- - It is **very important** that this triangle is computed for **not +-- corrected** lightness. But it is assumed **corrected lightness** in input. +-- - This approach is not entirely accurate and can results in ranges outside +-- of input `lch` for in-gamut point. Put it should be pretty rare: ~0.5% +-- cases for most saturated colors. +local function get_gamut_points(lch) + local c, l = lch.c, clip(lch.l, 0, 100) + l = correct_lightness_inv(l) + local cusp = cusps[math.floor(lch.h % 360)] + local c_cusp, l_cusp = cusp[1], cusp[2] + + -- Maximum allowed chroma is computed based on current lightness and depends + -- on whether `l` is below or above cusp's `l`: + -- - If below, then it is from lower triangle segment. + -- - If above - from upper segment. + local c_upper = l <= l_cusp and (c_cusp * l / l_cusp) or (c_cusp * (100 - l) / (100 - l_cusp)) + -- - Don't allow negative chroma (can happen if `l` is out of [0; 100]) + c_upper = clip(c_upper, 0, math.huge) + + -- Other points can be computed only in presence of actual chroma + if c == nil then return { c_upper = c_upper } end + + -- Range of allowed lightness is computed based on current chroma: + -- - Lower is from segment between (0, 0) and cusp. + -- - Upper is from segment between (0, 100) and cusp. + local l_lower, l_upper + if c < 0 then + l_lower, l_upper = 0, 100 + elseif c_cusp < c then + l_lower, l_upper = l_cusp, l_cusp + else + local saturation = c / c_cusp + l_lower = saturation * l_cusp + l_upper = saturation * (l_cusp - 100) + 100 + end + + -- Intersection of segment between (c, l) and (0, l_cusp) with gamut boundary + local c_cusp_clip, l_cusp_clip + if c <= 0 then + c_cusp_clip, l_cusp_clip = c, l + elseif l <= l_cusp then + -- Intersection with lower segment + local prop = 1 - l / l_cusp + c_cusp_clip = c_cusp * c / (c_cusp * prop + c) + l_cusp_clip = l_cusp * c_cusp_clip / c_cusp + else + -- Intersection with upper segment + local prop = 1 - (l - 100) / (l_cusp - 100) + c_cusp_clip = c_cusp * c / (c_cusp * prop + c) + l_cusp_clip = 100 + c_cusp_clip * (l_cusp - 100) / c_cusp + end + + return { + l_lower = correct_lightness(l_lower), + l_upper = correct_lightness(l_upper), + c_upper = c_upper, + l_cusp_clip = correct_lightness(l_cusp_clip), + c_cusp_clip = c_cusp_clip, + } +end + +function M.oklch2okhsl(lch) + if lch.c <= 0 or lch.h == nil then return { l = lch.l, s = 0 } end + + local gamut_points = get_gamut_points(lch) + local percent = 100 * lch.c / gamut_points.c_upper + + return { l = lch.l, s = clip(percent, 0, 100), h = lch.h } +end + +function M.okhsl2oklch(lsh) + if lsh.s <= 0 or lsh.h == nil then return { l = lsh.l, c = 0 } end + + local gamut_points = get_gamut_points(lsh) + local c = 0.01 * lsh.s * gamut_points.c_upper + + return { l = lsh.l, c = clip(c, 0, math.huge), h = lsh.h } +end + +function M.oklab2oklch(lab) + local c = math.sqrt(lab.a ^ 2 + lab.b ^ 2) + -- Treat grays specially + local h = nil + if c > 0 then h = rad2degree(math.atan2(lab.b, lab.a)) end + return { l = lab.l, c = c, h = h } +end + +function M.oklch2oklab(lch) + -- Treat grays specially + if lch.c <= 0 or lch.h == nil then return { l = lch.l, a = 0, b = 0 } end + + local a = lch.c * math.cos(degree2rad(lch.h)) + local b = lch.c * math.sin(degree2rad(lch.h)) + return { l = lch.l, a = a, b = b } +end + +function M.rgb2oklab(rgb) + -- Convert to linear RGB + local r, g, b = correct_channel(rgb.r / 255), correct_channel(rgb.g / 255), correct_channel(rgb.b / 255) + + -- Convert to Oklab + local l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b + local m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b + local s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b + + local l_, m_, s_ = cuberoot(l), cuberoot(m), cuberoot(s) + + local L = 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_ + local A = 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_ + local B = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_ + + -- Explicitly convert for nearly achromatic colors + if math.abs(A) < 1e-4 then A = 0 end + if math.abs(B) < 1e-4 then B = 0 end + + -- Normalize to appropriate range + return { l = 100 * L, a = A, b = B } +end + +function M.oklab2rgb(oklab) + local L, A, B = 0.01 * oklab.l, oklab.a, oklab.b + + local l_ = L + 0.3963377774 * A + 0.2158037573 * B + local m_ = L - 0.1055613458 * A - 0.0638541728 * B + local s_ = L - 0.0894841775 * A - 1.2914855480 * B + + local l = l_ * l_ * l_ + local m = m_ * m_ * m_ + local s = s_ * s_ * s_ + + --stylua: ignore + local r = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s + local g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s + local b = -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s + + return { r = 255 * correct_channel_inv(r), g = 255 * correct_channel_inv(g), b = 255 * correct_channel_inv(b) } +end + +function M.hex2rgb(hex) + local dec = tonumber(hex:sub(2), 16) + + local b = math.fmod(dec, 256) + local g = math.fmod((dec - b) / 256, 256) + local r = math.floor(dec / 65536) + + return { r = r, g = g, b = b } +end + +function M.rgb2hex(rgb) + -- Use straightforward clipping to [0; 255] here to ensure correctness. + -- Modify `rgb` prior to this to ensure only a small distortion. + local r = clip(round(rgb.r), 0, 255) + local g = clip(round(rgb.g), 0, 255) + local b = clip(round(rgb.b), 0, 255) + + return string.format('#%02x%02x%02x', r, g, b) +end + +return M diff --git a/lua/prismite/palette.lua b/lua/prismite/palette.lua new file mode 100644 index 0000000..fbc3f46 --- /dev/null +++ b/lua/prismite/palette.lua @@ -0,0 +1,29 @@ +-- autogenerated from palette.json +return { + bg_high = { l = 22.5, c = 0.01368, h = 253.09 }, + bg_highest = { l = 25, c = 0.01368, h = 253.09 }, + bg_low = { l = 17.5, c = 0.01368, h = 253.09 }, + bg_lowest = { l = 15, c = 0.01368, h = 253.09 }, + bg = { l = 20, c = 0.01368, h = 253.09 }, + blue_bright = { l = 90, c = 0.09994, h = 249.58 }, + blue = { l = 80, c = 0.09994, h = 249.58 }, + border = { l = 35, c = 0.023, h = 253.09 }, + border_variant = { l = 30, c = 0.023, h = 253.09 }, + cyan_bright = { l = 90, c = 0.09919, h = 181.49 }, + cyan = { l = 80, c = 0.09919, h = 181.49 }, + fg_high = { l = 95, c = 0.01368, h = 253.09 }, + fg_low = { l = 75, c = 0.01368, h = 253.09 }, + fg = { l = 85, c = 0.01368, h = 253.09 }, + green_bright = { l = 90, c = 0.11211, h = 147.63 }, + green = { l = 80, c = 0.11211, h = 147.63 }, + orange_bright = { l = 90, c = 0.139, h = 45.216 }, + orange = { l = 80, c = 0.139, h = 45.216 }, + pink_bright = { l = 90, c = 0.06662, h = 9.2146 }, + pink = { l = 80, c = 0.06662, h = 9.2146 }, + purple_bright = { l = 90, c = 0.09923, h = 296.61 }, + purple = { l = 80, c = 0.09923, h = 296.61 }, + red_bright = { l = 90, c = 0.15765, h = 24.728 }, + red = { l = 80, c = 0.15765, h = 24.728 }, + yellow_bright = { l = 90, c = 0.12758, h = 76.402 }, + yellow = { l = 80, c = 0.12758, h = 76.402 }, +} diff --git a/lua/prismite/utils.lua b/lua/prismite/utils.lua new file mode 100644 index 0000000..8a0dbca --- /dev/null +++ b/lua/prismite/utils.lua @@ -0,0 +1,73 @@ +local oklab = require "prismite.oklab" + +local M = {} + +function M.lerp(a, b, t) + return a + t * (b - a) +end + +function M.mix_oklch(a, b, t) + if not (a.l and a.c and a.h) then + error("`a` must be a valid OKLCH color", 2) + end + + if not (b.l and b.c and b.h) then + error("`b` must be a valid OKLCH color", 2) + end + + if type(t) ~= "number" then + error("'t' must be a number", 2) + end + + local a_h = a.h + local b_h = b.h + + -- https://drafts.csswg.org/css-color-4/#hue-shorter + if b_h - a_h > 180 then + print "adjusted +" + a_h = a_h + 360 + end + if b_h - a_h < -180 then + print "adjusted -" + b_h = b_h + 360 + end + + return { l = M.lerp(a.l, b.l, t), c = M.lerp(a.c, b.c, t), h = M.lerp(a_h, b_h, t) } +end + +local function over(a, b, alpha) + return a * alpha + b * (1 - alpha) +end + +function M.overlay_oklch(a, b, alpha) + if not (a.l and a.c and a.h) then + error("`a` must be a valid OKLCH color", 2) + end + + if not (b.l and b.c and b.h) then + error("`b` must be a valid OKLCH color", 2) + end + + if type(alpha) ~= "number" then + error("'alpha' must be a number", 2) + end + + local a_oklab = oklab.oklch2oklab(a) + local b_oklab = oklab.oklch2oklab(b) + + local _l = over(a_oklab.l, b_oklab.l, alpha) + local _a = over(a_oklab.a, b_oklab.a, alpha) + local _b = over(a_oklab.b, b_oklab.b, alpha) + + return oklab.oklab2oklch { l = _l, a = _a, b = _b } +end + +function M.is_oklch(oklch) + return (oklch.l and oklch.c and oklch.h) +end + +function M.oklch2hex(oklch) + return oklab.rgb2hex(oklab.oklab2rgb(oklab.oklch2oklab(oklch))) +end + +return M |