diff --git a/.gitignore b/.gitignore
index 9619102..6891fd5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,15 @@ Thumbs.db
# Backups
*.bak
*.backup
+
+# Local AI sessions / usage cache
+.gemini/
+
+# Local migration/editor backups
+.local-backups/
+# Python cache
+__pycache__/
+*.py[cod]
+
+# Local AI session metadata
+.atl/
diff --git a/.opencode/themes/dreamcoder.json b/.opencode/themes/dreamcoder.json
index bcb96a5..050abe6 100644
--- a/.opencode/themes/dreamcoder.json
+++ b/.opencode/themes/dreamcoder.json
@@ -1,129 +1,129 @@
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
- "dreamBackground": "#f8f2ea",
- "dreamPanel": "#fbf6ee",
- "dreamElement": "#ece4d8",
- "dreamText": "#15130f",
- "dreamMuted": "#4a463f",
- "dreamCocoa": "#78522d",
- "dreamLucuma": "#925b25",
- "dreamDiagnostic": "#36665a",
- "dreamSage": "#496f45",
- "dreamViolet": "#665593",
- "dreamMauve": "#844c71",
- "dreamCoral": "#9b4b40",
- "dreamWarning": "#765019"
+ "dreamBackground": "#14110e",
+ "dreamPanel": "#231c18",
+ "dreamElement": "#1d1613",
+ "dreamText": "#f0e7dc",
+ "dreamMuted": "#c7b9aa",
+ "dreamCocoa": "#ce836c",
+ "dreamLucuma": "#cdae7d",
+ "dreamDiagnostic": "#c7b2a2",
+ "dreamSage": "#b8bf84",
+ "dreamViolet": "#c9a8dc",
+ "dreamMauve": "#d98aa9",
+ "dreamCoral": "#e98272",
+ "dreamWarning": "#e8b866"
},
"theme": {
- "background": "#f8f2ea",
- "backgroundPanel": "#fbf6ee",
- "backgroundElement": "#ece4d8",
- "backgroundHover": "#ece3d7",
- "backgroundSelected": "#e2c8a2",
- "backgroundCode": "#faf5ed",
- "backgroundSearch": "#d4c5af",
- "backgroundLine": "#f9f4ec",
- "backgroundAssistant": "#d9dcd3",
- "backgroundUser": "#e8daca",
- "backgroundTool": "#e4dcde",
- "text": "#15130f",
- "textMuted": "#4a463f",
- "textSubtle": "#6b6458",
- "textPlaceholder": "#6a6258",
- "textAssistant": "#30574c",
- "textUser": "#7f5022",
- "textTool": "#665593",
- "primary": "#925b25",
- "secondary": "#78522d",
- "accent": "#925b25",
- "accentMuted": "#c3a384",
- "error": "#9b4b40",
- "warning": "#765019",
- "success": "#496f45",
- "info": "#36665a",
- "border": "#75644f",
- "borderActive": "#925b25",
- "borderSubtle": "#aa9a85",
- "borderFocus": "#36665a",
- "shadow": "#bab6b0",
- "diffAdded": "#496f45",
- "diffRemoved": "#9b4b40",
- "diffContext": "#4a463f",
- "diffHunkHeader": "#665593",
- "diffHighlightAdded": "#496f45",
- "diffHighlightRemoved": "#9b4b40",
- "diffAddedBg": "#d8dacc",
- "diffRemovedBg": "#e9d7cf",
- "diffContextBg": "#f8f2ea",
- "diffLineNumber": "#6b6458",
- "diffAddedLineNumberBg": "#d8dacc",
- "diffRemovedLineNumberBg": "#e9d7cf",
- "diffHunkHeaderBg": "#e1d9dc",
- "diffFold": "#6a6258",
- "diffFoldBg": "#f4ede4",
- "markdownText": "#15130f",
- "markdownHeading": "#925b25",
- "markdownLink": "#36665a",
- "markdownLinkText": "#925b25",
- "markdownCode": "#496f45",
- "markdownBlockQuote": "#78522d",
- "markdownEmph": "#36665a",
- "markdownStrong": "#925b25",
- "markdownHorizontalRule": "#aa9a85",
- "markdownListItem": "#925b25",
- "markdownListEnumeration": "#665593",
- "markdownTableBorder": "#75644f",
- "markdownTableHeader": "#78522d",
- "markdownImage": "#844c71",
- "markdownImageText": "#15130f",
- "markdownCodeBlock": "#15130f",
- "markdownCodeBlockBg": "#faf5ed",
- "markdownInlineCodeBg": "#dcddd0",
- "syntaxComment": "#6a6258",
- "syntaxKeyword": "#8c5922",
- "syntaxFunction": "#36665a",
- "syntaxMethod": "#335e52",
- "syntaxVariable": "#15130f",
- "syntaxParameter": "#271e14",
- "syntaxProperty": "#202f28",
- "syntaxField": "#20271b",
- "syntaxString": "#496f45",
- "syntaxNumber": "#78522d",
- "syntaxBoolean": "#7b513d",
- "syntaxConstant": "#7b513d",
- "syntaxType": "#665593",
- "syntaxClass": "#6e567f",
- "syntaxInterface": "#5b5986",
- "syntaxEnum": "#615a85",
- "syntaxOperator": "#844c71",
- "syntaxPunctuation": "#4a463f",
- "syntaxTag": "#8c5922",
- "syntaxAttribute": "#202f28",
- "syntaxRegexp": "#8a4c63",
- "syntaxEscape": "#765019",
- "syntaxNamespace": "#665593",
- "syntaxModule": "#665593",
- "syntaxDecorator": "#844c71",
- "syntaxBuiltin": "#7b513d",
- "syntaxSpecial": "#765019",
- "syntaxTodo": "#6a4918",
- "syntaxDeprecated": "#844a40",
- "terminalBlack": "#fbf6ee",
- "terminalRed": "#9b4b40",
- "terminalGreen": "#496f45",
- "terminalYellow": "#765019",
- "terminalBlue": "#36665a",
- "terminalMagenta": "#844c71",
- "terminalCyan": "#665593",
- "terminalWhite": "#15130f",
- "terminalBrightBlack": "#6b6458",
- "terminalBrightRed": "#834137",
- "terminalBrightGreen": "#405e3b",
- "terminalBrightYellow": "#664617",
- "terminalBrightBlue": "#30574c",
- "terminalBrightMagenta": "#70425f",
- "terminalBrightCyan": "#57497b",
- "terminalBrightWhite": "#15130f"
+ "background": "none",
+ "backgroundPanel": "#231c18",
+ "backgroundElement": "#1d1613",
+ "backgroundHover": "#221b17",
+ "backgroundSelected": "#373027",
+ "backgroundCode": "#231c18",
+ "backgroundSearch": "#4f4027",
+ "backgroundLine": "#231c18",
+ "backgroundAssistant": "#312b26",
+ "backgroundUser": "#322a20",
+ "backgroundTool": "#2d262b",
+ "text": "#f0e7dc",
+ "textMuted": "#c7b9aa",
+ "textSubtle": "#aa927c",
+ "textPlaceholder": "#9c826d",
+ "textAssistant": "#cebcac",
+ "textUser": "#d2b78b",
+ "textTool": "#c9a8dc",
+ "primary": "#cdae7d",
+ "secondary": "#ce836c",
+ "accent": "#cdae7d",
+ "accentMuted": "#746348",
+ "error": "#e98272",
+ "warning": "#e8b866",
+ "success": "#b8bf84",
+ "info": "#c7b2a2",
+ "border": "#806754",
+ "borderActive": "#cdae7d",
+ "borderSubtle": "#594d46",
+ "borderFocus": "#c7b2a2",
+ "shadow": "#0f0d0a",
+ "diffAdded": "#b8bf84",
+ "diffRemoved": "#e98272",
+ "diffContext": "#c7b9aa",
+ "diffHunkHeader": "#c9a8dc",
+ "diffHighlightAdded": "#b8bf84",
+ "diffHighlightRemoved": "#e98272",
+ "diffAddedBg": "#323023",
+ "diffRemovedBg": "#36231e",
+ "diffContextBg": "none",
+ "diffLineNumber": "#aa927c",
+ "diffAddedLineNumberBg": "#323023",
+ "diffRemovedLineNumberBg": "#36231e",
+ "diffHunkHeaderBg": "#31292f",
+ "diffFold": "#9c826d",
+ "diffFoldBg": "#191411",
+ "markdownText": "#f0e7dc",
+ "markdownHeading": "#cdae7d",
+ "markdownLink": "#c7b2a2",
+ "markdownLinkText": "#cdae7d",
+ "markdownCode": "#b8bf84",
+ "markdownBlockQuote": "#ce836c",
+ "markdownEmph": "#c7b2a2",
+ "markdownStrong": "#cdae7d",
+ "markdownHorizontalRule": "#806754",
+ "markdownListItem": "#cdae7d",
+ "markdownListEnumeration": "#c9a8dc",
+ "markdownTableBorder": "#806754",
+ "markdownTableHeader": "#ce836c",
+ "markdownImage": "#d98aa9",
+ "markdownImageText": "#f0e7dc",
+ "markdownCodeBlock": "#f0e7dc",
+ "markdownCodeBlockBg": "#231c18",
+ "markdownInlineCodeBg": "#2e2d21",
+ "syntaxComment": "#9c826d",
+ "syntaxKeyword": "#d3b078",
+ "syntaxFunction": "#c7b2a2",
+ "syntaxMethod": "#c7b0ab",
+ "syntaxVariable": "#f0e7dc",
+ "syntaxParameter": "#d08973",
+ "syntaxProperty": "#c9b4a4",
+ "syntaxField": "#bbc188",
+ "syntaxString": "#b8bf84",
+ "syntaxNumber": "#ce836c",
+ "syntaxBoolean": "#d1857b",
+ "syntaxConstant": "#d1857b",
+ "syntaxType": "#c9a8dc",
+ "syntaxClass": "#caa9cb",
+ "syntaxInterface": "#c9aacf",
+ "syntaxEnum": "#c6accc",
+ "syntaxOperator": "#d98aa9",
+ "syntaxPunctuation": "#c7b9aa",
+ "syntaxTag": "#d3b078",
+ "syntaxAttribute": "#c9b4a4",
+ "syntaxRegexp": "#dd889a",
+ "syntaxEscape": "#e8b866",
+ "syntaxNamespace": "#c9a8dc",
+ "syntaxModule": "#c9a8dc",
+ "syntaxDecorator": "#d98aa9",
+ "syntaxBuiltin": "#d1857b",
+ "syntaxSpecial": "#e8b866",
+ "syntaxTodo": "#e9be74",
+ "syntaxDeprecated": "#df9182",
+ "terminalBlack": "#868280",
+ "terminalRed": "#e98272",
+ "terminalGreen": "#b8bf84",
+ "terminalYellow": "#e8b866",
+ "terminalBlue": "#c7b2a2",
+ "terminalMagenta": "#d98aa9",
+ "terminalCyan": "#cebcac",
+ "terminalWhite": "#f0e7dc",
+ "terminalBrightBlack": "#aa927c",
+ "terminalBrightRed": "#ea9485",
+ "terminalBrightGreen": "#c2c694",
+ "terminalBrightYellow": "#e9c079",
+ "terminalBrightBlue": "#cebcac",
+ "terminalBrightMagenta": "#dd9bb2",
+ "terminalBrightCyan": "#d0b3dc",
+ "terminalBrightWhite": "#f0e7dc"
}
}
diff --git a/Antigravity/Dreamcoder-Dark.json b/Antigravity/Dreamcoder-Dark.json
new file mode 100644
index 0000000..b42c240
--- /dev/null
+++ b/Antigravity/Dreamcoder-Dark.json
@@ -0,0 +1,119 @@
+{
+ "name": "Dreamcoder Ember Noir",
+ "type": "light",
+ "colors": {
+ "editor.background": "#15100d",
+ "editor.foreground": "#f0e7dc",
+ "activityBar.background": "#241b16",
+ "activityBar.foreground": "#e6a15c",
+ "activityBar.inactiveForeground": "#9c826d",
+ "activityBar.border": "#806754",
+ "sideBar.background": "#241b16",
+ "sideBar.foreground": "#f0e7dc",
+ "sideBar.border": "#806754",
+ "statusBar.background": "#15100d",
+ "statusBar.foreground": "#f0e7dc",
+ "statusBar.border": "#806754",
+ "editorGroupHeader.tabsBackground": "#241b16",
+ "tab.activeBackground": "#15100d",
+ "tab.activeForeground": "#e6a15c",
+ "tab.inactiveBackground": "#241b16",
+ "tab.inactiveForeground": "#9c826d",
+ "tab.border": "#806754",
+ "editor.lineHighlightBackground": "#241b16",
+ "editorLineNumber.foreground": "#9c826d",
+ "editorLineNumber.activeForeground": "#e6a15c",
+ "editorWidget.background": "#30231c",
+ "editorWidget.border": "#806754",
+ "input.background": "#30231c",
+ "input.foreground": "#f0e7dc",
+ "input.border": "#e6a15c",
+ "button.background": "#d66f50",
+ "button.foreground": "#f0e7dc",
+ "list.activeSelectionBackground": "#43291d",
+ "list.activeSelectionForeground": "#f0e7dc",
+ "list.hoverBackground": "#241b16",
+ "editor.selectionBackground": "#43291d",
+ "terminal.background": "#15100d",
+ "terminal.foreground": "#f0e7dc",
+ "terminal.ansiBlack": "#241b16",
+ "terminal.ansiRed": "#e98272",
+ "terminal.ansiGreen": "#b8bf84",
+ "terminal.ansiYellow": "#e8b866",
+ "terminal.ansiBlue": "#e6a15c",
+ "terminal.ansiMagenta": "#d98aa9",
+ "terminal.ansiCyan": "#d2a268",
+ "terminal.ansiWhite": "#f0e7dc"
+ },
+ "tokenColors": [
+ {
+ "scope": [
+ "comment",
+ "punctuation.definition.comment"
+ ],
+ "settings": {
+ "foreground": "#9c826d",
+ "fontStyle": "italic"
+ }
+ },
+ {
+ "scope": [
+ "keyword",
+ "storage.type",
+ "storage.modifier",
+ "keyword.operator"
+ ],
+ "settings": {
+ "foreground": "#e6a15c",
+ "fontStyle": "bold"
+ }
+ },
+ {
+ "scope": [
+ "entity.name.function",
+ "support.function",
+ "entity.name.method"
+ ],
+ "settings": {
+ "foreground": "#d2a268"
+ }
+ },
+ {
+ "scope": [
+ "string",
+ "punctuation.definition.string"
+ ],
+ "settings": {
+ "foreground": "#b8bf84"
+ }
+ },
+ {
+ "scope": [
+ "constant.numeric",
+ "constant.language"
+ ],
+ "settings": {
+ "foreground": "#d66f50"
+ }
+ },
+ {
+ "scope": [
+ "support.type",
+ "entity.name.type",
+ "entity.name.class"
+ ],
+ "settings": {
+ "foreground": "#c9a8dc"
+ }
+ },
+ {
+ "scope": [
+ "variable",
+ "meta.definition.variable"
+ ],
+ "settings": {
+ "foreground": "#f0e7dc"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Antigravity/Dreamcoder-Dusk.json b/Antigravity/Dreamcoder-Dusk.json
new file mode 100644
index 0000000..a2a1b28
--- /dev/null
+++ b/Antigravity/Dreamcoder-Dusk.json
@@ -0,0 +1,119 @@
+{
+ "name": "Dreamcoder Dusk",
+ "type": "light",
+ "colors": {
+ "editor.background": "#ebe4d6",
+ "editor.foreground": "#1a1713",
+ "activityBar.background": "#f1eadf",
+ "activityBar.foreground": "#8a5520",
+ "activityBar.inactiveForeground": "#615548",
+ "activityBar.border": "#665845",
+ "sideBar.background": "#f1eadf",
+ "sideBar.foreground": "#1a1713",
+ "sideBar.border": "#665845",
+ "statusBar.background": "#ebe4d6",
+ "statusBar.foreground": "#1a1713",
+ "statusBar.border": "#665845",
+ "editorGroupHeader.tabsBackground": "#f1eadf",
+ "tab.activeBackground": "#ebe4d6",
+ "tab.activeForeground": "#8a5520",
+ "tab.inactiveBackground": "#f1eadf",
+ "tab.inactiveForeground": "#615548",
+ "tab.border": "#665845",
+ "editor.lineHighlightBackground": "#f1eadf",
+ "editorLineNumber.foreground": "#615548",
+ "editorLineNumber.activeForeground": "#8a5520",
+ "editorWidget.background": "#d8cbb8",
+ "editorWidget.border": "#665845",
+ "input.background": "#d8cbb8",
+ "input.foreground": "#1a1713",
+ "input.border": "#216a73",
+ "button.background": "#96411e",
+ "button.foreground": "#1a1713",
+ "list.activeSelectionBackground": "#1a1713",
+ "list.activeSelectionForeground": "#1a1713",
+ "list.hoverBackground": "#f1eadf",
+ "editor.selectionBackground": "#1a1713",
+ "terminal.background": "#ebe4d6",
+ "terminal.foreground": "#1a1713",
+ "terminal.ansiBlack": "#f1eadf",
+ "terminal.ansiRed": "#773126",
+ "terminal.ansiGreen": "#466b41",
+ "terminal.ansiYellow": "#604000",
+ "terminal.ansiBlue": "#8a5520",
+ "terminal.ansiMagenta": "#784762",
+ "terminal.ansiCyan": "#104b67",
+ "terminal.ansiWhite": "#1a1713"
+ },
+ "tokenColors": [
+ {
+ "scope": [
+ "comment",
+ "punctuation.definition.comment"
+ ],
+ "settings": {
+ "foreground": "#615548",
+ "fontStyle": "italic"
+ }
+ },
+ {
+ "scope": [
+ "keyword",
+ "storage.type",
+ "storage.modifier",
+ "keyword.operator"
+ ],
+ "settings": {
+ "foreground": "#8a5520",
+ "fontStyle": "bold"
+ }
+ },
+ {
+ "scope": [
+ "entity.name.function",
+ "support.function",
+ "entity.name.method"
+ ],
+ "settings": {
+ "foreground": "#104b67"
+ }
+ },
+ {
+ "scope": [
+ "string",
+ "punctuation.definition.string"
+ ],
+ "settings": {
+ "foreground": "#466b41"
+ }
+ },
+ {
+ "scope": [
+ "constant.numeric",
+ "constant.language"
+ ],
+ "settings": {
+ "foreground": "#96411e"
+ }
+ },
+ {
+ "scope": [
+ "support.type",
+ "entity.name.type",
+ "entity.name.class"
+ ],
+ "settings": {
+ "foreground": "#5b4e86"
+ }
+ },
+ {
+ "scope": [
+ "variable",
+ "meta.definition.variable"
+ ],
+ "settings": {
+ "foreground": "#1a1713"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Antigravity/Dreamcoder-Light.json b/Antigravity/Dreamcoder-Light.json
new file mode 100644
index 0000000..35de37e
--- /dev/null
+++ b/Antigravity/Dreamcoder-Light.json
@@ -0,0 +1,119 @@
+{
+ "name": "Dreamcoder Light",
+ "type": "light",
+ "colors": {
+ "editor.background": "#f3eadc",
+ "editor.foreground": "#17120d",
+ "activityBar.background": "#fff7ea",
+ "activityBar.foreground": "#824f16",
+ "activityBar.inactiveForeground": "#66523f",
+ "activityBar.border": "#66513b",
+ "sideBar.background": "#fff7ea",
+ "sideBar.foreground": "#17120d",
+ "sideBar.border": "#66513b",
+ "statusBar.background": "#f3eadc",
+ "statusBar.foreground": "#17120d",
+ "statusBar.border": "#66513b",
+ "editorGroupHeader.tabsBackground": "#fff7ea",
+ "tab.activeBackground": "#f3eadc",
+ "tab.activeForeground": "#824f16",
+ "tab.inactiveBackground": "#fff7ea",
+ "tab.inactiveForeground": "#66523f",
+ "tab.border": "#66513b",
+ "editor.lineHighlightBackground": "#fff7ea",
+ "editorLineNumber.foreground": "#66523f",
+ "editorLineNumber.activeForeground": "#824f16",
+ "editorWidget.background": "#decbb1",
+ "editorWidget.border": "#66513b",
+ "input.background": "#decbb1",
+ "input.foreground": "#17120d",
+ "input.border": "#0f6570",
+ "button.background": "#a7471c",
+ "button.foreground": "#17120d",
+ "list.activeSelectionBackground": "#17120d",
+ "list.activeSelectionForeground": "#17120d",
+ "list.hoverBackground": "#fff7ea",
+ "editor.selectionBackground": "#17120d",
+ "terminal.background": "#f3eadc",
+ "terminal.foreground": "#17120d",
+ "terminal.ansiBlack": "#fff7ea",
+ "terminal.ansiRed": "#842f24",
+ "terminal.ansiGreen": "#3f6b35",
+ "terminal.ansiYellow": "#654300",
+ "terminal.ansiBlue": "#824f16",
+ "terminal.ansiMagenta": "#7d3e64",
+ "terminal.ansiCyan": "#15516e",
+ "terminal.ansiWhite": "#17120d"
+ },
+ "tokenColors": [
+ {
+ "scope": [
+ "comment",
+ "punctuation.definition.comment"
+ ],
+ "settings": {
+ "foreground": "#66523f",
+ "fontStyle": "italic"
+ }
+ },
+ {
+ "scope": [
+ "keyword",
+ "storage.type",
+ "storage.modifier",
+ "keyword.operator"
+ ],
+ "settings": {
+ "foreground": "#824f16",
+ "fontStyle": "bold"
+ }
+ },
+ {
+ "scope": [
+ "entity.name.function",
+ "support.function",
+ "entity.name.method"
+ ],
+ "settings": {
+ "foreground": "#15516e"
+ }
+ },
+ {
+ "scope": [
+ "string",
+ "punctuation.definition.string"
+ ],
+ "settings": {
+ "foreground": "#3f6b35"
+ }
+ },
+ {
+ "scope": [
+ "constant.numeric",
+ "constant.language"
+ ],
+ "settings": {
+ "foreground": "#a7471c"
+ }
+ },
+ {
+ "scope": [
+ "support.type",
+ "entity.name.type",
+ "entity.name.class"
+ ],
+ "settings": {
+ "foreground": "#57478b"
+ }
+ },
+ {
+ "scope": [
+ "variable",
+ "meta.definition.variable"
+ ],
+ "settings": {
+ "foreground": "#17120d"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Codex-App/Dreamcoder-Dark.codex-theme.json b/Codex-App/Dreamcoder-Dark.codex-theme.json
index 5423edf..b464f2f 100644
--- a/Codex-App/Dreamcoder-Dark.codex-theme.json
+++ b/Codex-App/Dreamcoder-Dark.codex-theme.json
@@ -1,129 +1,129 @@
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
- "dreamBackground": "#101216",
- "dreamPanel": "#1c222b",
- "dreamElement": "#161922",
- "dreamText": "#e8e1d7",
- "dreamMuted": "#b8b0a5",
- "dreamCocoa": "#b87a48",
- "dreamLucuma": "#d9ad67",
- "dreamDiagnostic": "#92c7cd",
- "dreamSage": "#a5b89c",
- "dreamViolet": "#c4b3df",
- "dreamMauve": "#d2a3c3",
- "dreamCoral": "#d78373",
- "dreamWarning": "#ddb36b"
+ "dreamBackground": "#15100d",
+ "dreamPanel": "#241b16",
+ "dreamElement": "#1d1613",
+ "dreamText": "#f0e7dc",
+ "dreamMuted": "#c7b9aa",
+ "dreamCocoa": "#d66f50",
+ "dreamLucuma": "#e6a15c",
+ "dreamDiagnostic": "#d2a268",
+ "dreamSage": "#b8bf84",
+ "dreamViolet": "#c9a8dc",
+ "dreamMauve": "#d98aa9",
+ "dreamCoral": "#e98272",
+ "dreamWarning": "#e8b866"
},
"theme": {
- "background": "#101216",
- "backgroundPanel": "#1c222b",
- "backgroundElement": "#161922",
- "backgroundHover": "#1d2129",
- "backgroundSelected": "#32291f",
- "backgroundCode": "#191e25",
- "backgroundSearch": "#493f2e",
- "backgroundLine": "#15181e",
- "backgroundAssistant": "#252f33",
- "backgroundUser": "#302b23",
- "backgroundTool": "#292932",
- "text": "#e8e1d7",
- "textMuted": "#b8b0a5",
- "textSubtle": "#928a80",
- "textPlaceholder": "#8c857c",
- "textAssistant": "#a1cccf",
- "textUser": "#dbb578",
- "textTool": "#c4b3df",
- "primary": "#d9ad67",
- "secondary": "#b87a48",
- "accent": "#d9ad67",
- "accentMuted": "#796340",
- "error": "#d78373",
- "warning": "#ddb36b",
- "success": "#a5b89c",
- "info": "#92c7cd",
- "border": "#c8bda9",
- "borderActive": "#d9ad67",
- "borderSubtle": "#3d4350",
- "borderFocus": "#92c7cd",
- "shadow": "#0c0e10",
- "diffAdded": "#a5b89c",
- "diffRemoved": "#d78373",
- "diffContext": "#b8b0a5",
- "diffHunkHeader": "#c4b3df",
- "diffHighlightAdded": "#a5b89c",
- "diffHighlightRemoved": "#d78373",
- "diffAddedBg": "#2b302e",
- "diffRemovedBg": "#302425",
- "diffContextBg": "#101216",
- "diffLineNumber": "#928a80",
- "diffAddedLineNumberBg": "#2b302e",
- "diffRemovedLineNumberBg": "#302425",
- "diffHunkHeaderBg": "#2d2c36",
- "diffFold": "#8c857c",
- "diffFoldBg": "#14171c",
- "markdownText": "#e8e1d7",
- "markdownHeading": "#d9ad67",
- "markdownLink": "#92c7cd",
- "markdownLinkText": "#d9ad67",
- "markdownCode": "#a5b89c",
- "markdownBlockQuote": "#b87a48",
- "markdownEmph": "#92c7cd",
- "markdownStrong": "#d9ad67",
- "markdownHorizontalRule": "#3d4350",
- "markdownListItem": "#d9ad67",
- "markdownListEnumeration": "#c4b3df",
- "markdownTableBorder": "#c8bda9",
- "markdownTableHeader": "#b87a48",
- "markdownImage": "#d2a3c3",
- "markdownImageText": "#e8e1d7",
- "markdownCodeBlock": "#e8e1d7",
- "markdownCodeBlockBg": "#191e25",
- "markdownInlineCodeBg": "#282d2b",
- "syntaxComment": "#8c857c",
- "syntaxKeyword": "#daae68",
- "syntaxFunction": "#92c7cd",
- "syntaxMethod": "#9bcace",
- "syntaxVariable": "#e8e1d7",
- "syntaxParameter": "#dfcebd",
- "syntaxProperty": "#cbd8d4",
- "syntaxField": "#d9d8ca",
- "syntaxString": "#a5b89c",
- "syntaxNumber": "#b87a48",
- "syntaxBoolean": "#be8466",
- "syntaxConstant": "#be8466",
- "syntaxType": "#c4b3df",
- "syntaxClass": "#c8b2c9",
- "syntaxInterface": "#b9b7db",
- "syntaxEnum": "#beb4d3",
- "syntaxOperator": "#d2a3c3",
- "syntaxPunctuation": "#b8b0a5",
- "syntaxTag": "#daae68",
- "syntaxAttribute": "#cbd8d4",
- "syntaxRegexp": "#d39aad",
- "syntaxEscape": "#ddb36b",
- "syntaxNamespace": "#c4b3df",
- "syntaxModule": "#c4b3df",
- "syntaxDecorator": "#d2a3c3",
- "syntaxBuiltin": "#be8466",
- "syntaxSpecial": "#ddb36b",
- "syntaxTodo": "#deb978",
- "syntaxDeprecated": "#ce9081",
- "terminalBlack": "#1c222b",
- "terminalRed": "#d78373",
- "terminalGreen": "#a5b89c",
- "terminalYellow": "#ddb36b",
- "terminalBlue": "#92c7cd",
- "terminalMagenta": "#d2a3c3",
- "terminalCyan": "#c4b3df",
- "terminalWhite": "#e8e1d7",
- "terminalBrightBlack": "#928a80",
- "terminalBrightRed": "#da9485",
- "terminalBrightGreen": "#b1bfa7",
- "terminalBrightYellow": "#dfba7c",
- "terminalBrightBlue": "#a1cccf",
- "terminalBrightMagenta": "#d6aec7",
- "terminalBrightCyan": "#cabbde",
- "terminalBrightWhite": "#e8e1d7"
+ "background": "#15100d",
+ "backgroundPanel": "#241b16",
+ "backgroundElement": "#1d1613",
+ "backgroundHover": "#241a15",
+ "backgroundSelected": "#43291d",
+ "backgroundCode": "#241b16",
+ "backgroundSearch": "#503f26",
+ "backgroundLine": "#241b16",
+ "backgroundAssistant": "#33271c",
+ "backgroundUser": "#36271a",
+ "backgroundTool": "#2e252a",
+ "text": "#f0e7dc",
+ "textMuted": "#c7b9aa",
+ "textSubtle": "#aa927c",
+ "textPlaceholder": "#9c826d",
+ "textAssistant": "#d7ae7d",
+ "textUser": "#e8ac6f",
+ "textTool": "#c9a8dc",
+ "primary": "#e6a15c",
+ "secondary": "#d66f50",
+ "accent": "#e6a15c",
+ "accentMuted": "#825b36",
+ "error": "#e98272",
+ "warning": "#e8b866",
+ "success": "#b8bf84",
+ "info": "#d2a268",
+ "border": "#806754",
+ "borderActive": "#e6a15c",
+ "borderSubtle": "#49362c",
+ "borderFocus": "#d2a268",
+ "shadow": "#100c0a",
+ "diffAdded": "#b8bf84",
+ "diffRemoved": "#e98272",
+ "diffContext": "#c7b9aa",
+ "diffHunkHeader": "#c9a8dc",
+ "diffHighlightAdded": "#b8bf84",
+ "diffHighlightRemoved": "#e98272",
+ "diffAddedBg": "#323022",
+ "diffRemovedBg": "#37221d",
+ "diffContextBg": "#15100d",
+ "diffLineNumber": "#aa927c",
+ "diffAddedLineNumberBg": "#323022",
+ "diffRemovedLineNumberBg": "#37221d",
+ "diffHunkHeaderBg": "#32282e",
+ "diffFold": "#9c826d",
+ "diffFoldBg": "#1a1310",
+ "markdownText": "#f0e7dc",
+ "markdownHeading": "#e6a15c",
+ "markdownLink": "#d2a268",
+ "markdownLinkText": "#e6a15c",
+ "markdownCode": "#b8bf84",
+ "markdownBlockQuote": "#d66f50",
+ "markdownEmph": "#d2a268",
+ "markdownStrong": "#e6a15c",
+ "markdownHorizontalRule": "#806754",
+ "markdownListItem": "#e6a15c",
+ "markdownListEnumeration": "#c9a8dc",
+ "markdownTableBorder": "#806754",
+ "markdownTableHeader": "#d66f50",
+ "markdownImage": "#d98aa9",
+ "markdownImageText": "#f0e7dc",
+ "markdownCodeBlock": "#f0e7dc",
+ "markdownCodeBlockBg": "#241b16",
+ "markdownInlineCodeBg": "#2f2c20",
+ "syntaxComment": "#9c826d",
+ "syntaxKeyword": "#e6a65e",
+ "syntaxFunction": "#d2a268",
+ "syntaxMethod": "#d1a37b",
+ "syntaxVariable": "#f0e7dc",
+ "syntaxParameter": "#d87658",
+ "syntaxProperty": "#d3a56d",
+ "syntaxField": "#bbc188",
+ "syntaxString": "#b8bf84",
+ "syntaxNumber": "#d66f50",
+ "syntaxBoolean": "#d77565",
+ "syntaxConstant": "#d77565",
+ "syntaxType": "#c9a8dc",
+ "syntaxClass": "#cea7c5",
+ "syntaxInterface": "#cba7c2",
+ "syntaxEnum": "#c6accc",
+ "syntaxOperator": "#d98aa9",
+ "syntaxPunctuation": "#c7b9aa",
+ "syntaxTag": "#e6a65e",
+ "syntaxAttribute": "#d3a56d",
+ "syntaxRegexp": "#dd889a",
+ "syntaxEscape": "#e8b866",
+ "syntaxNamespace": "#c9a8dc",
+ "syntaxModule": "#c9a8dc",
+ "syntaxDecorator": "#d98aa9",
+ "syntaxBuiltin": "#d77565",
+ "syntaxSpecial": "#e8b866",
+ "syntaxTodo": "#e9be74",
+ "syntaxDeprecated": "#df9182",
+ "terminalBlack": "#86827e",
+ "terminalRed": "#e98272",
+ "terminalGreen": "#b8bf84",
+ "terminalYellow": "#e8b866",
+ "terminalBlue": "#d2a268",
+ "terminalMagenta": "#d98aa9",
+ "terminalCyan": "#d7ae7d",
+ "terminalWhite": "#f0e7dc",
+ "terminalBrightBlack": "#aa927c",
+ "terminalBrightRed": "#ea9485",
+ "terminalBrightGreen": "#c2c694",
+ "terminalBrightYellow": "#e9c079",
+ "terminalBrightBlue": "#d7ae7d",
+ "terminalBrightMagenta": "#dd9bb2",
+ "terminalBrightCyan": "#d0b3dc",
+ "terminalBrightWhite": "#f0e7dc"
}
}
diff --git a/Codex-App/Dreamcoder-Dusk.codex-theme.json b/Codex-App/Dreamcoder-Dusk.codex-theme.json
new file mode 100644
index 0000000..285c47f
--- /dev/null
+++ b/Codex-App/Dreamcoder-Dusk.codex-theme.json
@@ -0,0 +1,129 @@
+{
+ "$schema": "https://opencode.ai/theme.json",
+ "defs": {
+ "dreamBackground": "#ebe4d6",
+ "dreamPanel": "#f1eadf",
+ "dreamElement": "#dfd5c4",
+ "dreamText": "#1a1713",
+ "dreamMuted": "#4c443a",
+ "dreamCocoa": "#96411e",
+ "dreamLucuma": "#8a5520",
+ "dreamDiagnostic": "#104b67",
+ "dreamSage": "#466b41",
+ "dreamViolet": "#5b4e86",
+ "dreamMauve": "#784762",
+ "dreamCoral": "#773126",
+ "dreamWarning": "#604000"
+ },
+ "theme": {
+ "background": "#ebe4d6",
+ "backgroundPanel": "#f1eadf",
+ "backgroundElement": "#dfd5c4",
+ "backgroundHover": "#e1d6c6",
+ "backgroundSelected": "#1a1713",
+ "backgroundCode": "#f1eadf",
+ "backgroundSearch": "#c4b69a",
+ "backgroundLine": "#f1eadf",
+ "backgroundAssistant": "#c8ccc4",
+ "backgroundUser": "#dbcdb9",
+ "backgroundTool": "#d7cfcb",
+ "text": "#1a1713",
+ "textMuted": "#4c443a",
+ "textSubtle": "#5a4f43",
+ "textPlaceholder": "#615548",
+ "textAssistant": "#124258",
+ "textUser": "#794c1e",
+ "textTool": "#5b4e86",
+ "primary": "#8a5520",
+ "secondary": "#96411e",
+ "accent": "#8a5520",
+ "accentMuted": "#b99a77",
+ "error": "#773126",
+ "warning": "#604000",
+ "success": "#466b41",
+ "info": "#104b67",
+ "border": "#665845",
+ "borderActive": "#8a5520",
+ "borderSubtle": "#a7947a",
+ "borderFocus": "#104b67",
+ "shadow": "#b0aba0",
+ "diffAdded": "#466b41",
+ "diffRemoved": "#773126",
+ "diffContext": "#4c443a",
+ "diffHunkHeader": "#5b4e86",
+ "diffHighlightAdded": "#466b41",
+ "diffHighlightRemoved": "#773126",
+ "diffAddedBg": "#cdcebb",
+ "diffRemovedBg": "#d8c7ba",
+ "diffContextBg": "#ebe4d6",
+ "diffLineNumber": "#5a4f43",
+ "diffAddedLineNumberBg": "#cdcebb",
+ "diffRemovedLineNumberBg": "#d8c7ba",
+ "diffHunkHeaderBg": "#d4ccc9",
+ "diffFold": "#615548",
+ "diffFoldBg": "#e8e0d1",
+ "markdownText": "#1a1713",
+ "markdownHeading": "#8a5520",
+ "markdownLink": "#104b67",
+ "markdownLinkText": "#8a5520",
+ "markdownCode": "#466b41",
+ "markdownBlockQuote": "#96411e",
+ "markdownEmph": "#104b67",
+ "markdownStrong": "#8a5520",
+ "markdownHorizontalRule": "#665845",
+ "markdownListItem": "#8a5520",
+ "markdownListEnumeration": "#5b4e86",
+ "markdownTableBorder": "#665845",
+ "markdownTableHeader": "#96411e",
+ "markdownImage": "#784762",
+ "markdownImageText": "#1a1713",
+ "markdownCodeBlock": "#1a1713",
+ "markdownCodeBlockBg": "#f1eadf",
+ "markdownInlineCodeBg": "#d1d1be",
+ "syntaxComment": "#615548",
+ "syntaxKeyword": "#815019",
+ "syntaxFunction": "#104b67",
+ "syntaxMethod": "#1c4b6c",
+ "syntaxVariable": "#1a1713",
+ "syntaxParameter": "#8f3e1d",
+ "syntaxProperty": "#104964",
+ "syntaxField": "#44673f",
+ "syntaxString": "#466b41",
+ "syntaxNumber": "#96411e",
+ "syntaxBoolean": "#8f422e",
+ "syntaxConstant": "#8f422e",
+ "syntaxType": "#5b4e86",
+ "syntaxClass": "#634f74",
+ "syntaxInterface": "#4a4d7f",
+ "syntaxEnum": "#57537a",
+ "syntaxOperator": "#784762",
+ "syntaxPunctuation": "#4c443a",
+ "syntaxTag": "#815019",
+ "syntaxAttribute": "#104964",
+ "syntaxRegexp": "#784151",
+ "syntaxEscape": "#604000",
+ "syntaxNamespace": "#5b4e86",
+ "syntaxModule": "#5b4e86",
+ "syntaxDecorator": "#784762",
+ "syntaxBuiltin": "#8f422e",
+ "syntaxSpecial": "#604000",
+ "syntaxTodo": "#583b02",
+ "syntaxDeprecated": "#6b362c",
+ "terminalBlack": "#595753",
+ "terminalRed": "#773126",
+ "terminalGreen": "#466b41",
+ "terminalYellow": "#604000",
+ "terminalBlue": "#104b67",
+ "terminalMagenta": "#784762",
+ "terminalCyan": "#124258",
+ "terminalWhite": "#1a1713",
+ "terminalBrightBlack": "#5a4f43",
+ "terminalBrightRed": "#662c23",
+ "terminalBrightGreen": "#3e5c39",
+ "terminalBrightYellow": "#553903",
+ "terminalBrightBlue": "#124258",
+ "terminalBrightMagenta": "#673e54",
+ "terminalBrightCyan": "#4f4471",
+ "terminalBrightWhite": "#1a1713"
+ }
+}
diff --git a/Codex-App/Dreamcoder-Light.codex-theme.json b/Codex-App/Dreamcoder-Light.codex-theme.json
index 767355b..126e895 100644
--- a/Codex-App/Dreamcoder-Light.codex-theme.json
+++ b/Codex-App/Dreamcoder-Light.codex-theme.json
@@ -1,129 +1,129 @@
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
- "dreamBackground": "#f6f1e8",
- "dreamPanel": "#fbf8f1",
- "dreamElement": "#ece4d8",
- "dreamText": "#15130f",
- "dreamMuted": "#4a463f",
- "dreamCocoa": "#7f4a27",
- "dreamLucuma": "#855719",
- "dreamDiagnostic": "#1e6871",
- "dreamSage": "#496f45",
- "dreamViolet": "#665593",
- "dreamMauve": "#844c71",
- "dreamCoral": "#9b4b40",
- "dreamWarning": "#765019"
+ "dreamBackground": "#f3eadc",
+ "dreamPanel": "#fff7ea",
+ "dreamElement": "#e6d7c4",
+ "dreamText": "#17120d",
+ "dreamMuted": "#3d3228",
+ "dreamCocoa": "#a7471c",
+ "dreamLucuma": "#824f16",
+ "dreamDiagnostic": "#15516e",
+ "dreamSage": "#3f6b35",
+ "dreamViolet": "#57478b",
+ "dreamMauve": "#7d3e64",
+ "dreamCoral": "#842f24",
+ "dreamWarning": "#654300"
},
"theme": {
- "background": "#f6f1e8",
- "backgroundPanel": "#fbf8f1",
- "backgroundElement": "#ece4d8",
- "backgroundHover": "#e9e0d4",
- "backgroundSelected": "#dcc49e",
- "backgroundCode": "#faf6ee",
- "backgroundSearch": "#d2c4ae",
- "backgroundLine": "#f8f4eb",
- "backgroundAssistant": "#d3dbd5",
- "backgroundUser": "#e4d8c7",
- "backgroundTool": "#e2dbdc",
- "text": "#15130f",
- "textMuted": "#4a463f",
- "textSubtle": "#6b6458",
- "textPlaceholder": "#6a6258",
- "textAssistant": "#1c595f",
- "textUser": "#744d18",
- "textTool": "#665593",
- "primary": "#855719",
- "secondary": "#7f4a27",
- "accent": "#855719",
- "accentMuted": "#bba17c",
- "error": "#9b4b40",
- "warning": "#765019",
- "success": "#496f45",
- "info": "#1e6871",
- "border": "#75644f",
- "borderActive": "#855719",
- "borderSubtle": "#b7a78f",
- "borderFocus": "#1e6871",
- "shadow": "#b8b5ae",
- "diffAdded": "#496f45",
- "diffRemoved": "#9b4b40",
- "diffContext": "#4a463f",
- "diffHunkHeader": "#665593",
- "diffHighlightAdded": "#496f45",
- "diffHighlightRemoved": "#9b4b40",
- "diffAddedBg": "#d7dacb",
- "diffRemovedBg": "#e7d6cd",
- "diffContextBg": "#f6f1e8",
- "diffLineNumber": "#6b6458",
- "diffAddedLineNumberBg": "#d7dacb",
- "diffRemovedLineNumberBg": "#e7d6cd",
- "diffHunkHeaderBg": "#dfd8da",
- "diffFold": "#6a6258",
- "diffFoldBg": "#f2ece2",
- "markdownText": "#15130f",
- "markdownHeading": "#855719",
- "markdownLink": "#1e6871",
- "markdownLinkText": "#855719",
- "markdownCode": "#496f45",
- "markdownBlockQuote": "#7f4a27",
- "markdownEmph": "#1e6871",
- "markdownStrong": "#855719",
- "markdownHorizontalRule": "#b7a78f",
- "markdownListItem": "#855719",
- "markdownListEnumeration": "#665593",
- "markdownTableBorder": "#75644f",
- "markdownTableHeader": "#7f4a27",
- "markdownImage": "#844c71",
- "markdownImageText": "#15130f",
- "markdownCodeBlock": "#15130f",
- "markdownCodeBlockBg": "#faf6ee",
- "markdownInlineCodeBg": "#dadcce",
- "syntaxComment": "#6a6258",
- "syntaxKeyword": "#825519",
- "syntaxFunction": "#1e6871",
- "syntaxMethod": "#1d6067",
- "syntaxVariable": "#15130f",
- "syntaxParameter": "#281d13",
- "syntaxProperty": "#183030",
- "syntaxField": "#20271b",
- "syntaxString": "#496f45",
- "syntaxNumber": "#7f4a27",
- "syntaxBoolean": "#804a39",
- "syntaxConstant": "#804a39",
- "syntaxType": "#665593",
- "syntaxClass": "#6c557d",
- "syntaxInterface": "#56598c",
- "syntaxEnum": "#615a85",
- "syntaxOperator": "#844c71",
- "syntaxPunctuation": "#4a463f",
- "syntaxTag": "#825519",
- "syntaxAttribute": "#183030",
- "syntaxRegexp": "#8a4c63",
- "syntaxEscape": "#765019",
- "syntaxNamespace": "#665593",
- "syntaxModule": "#665593",
- "syntaxDecorator": "#844c71",
- "syntaxBuiltin": "#804a39",
- "syntaxSpecial": "#765019",
- "syntaxTodo": "#6a4918",
- "syntaxDeprecated": "#844a40",
- "terminalBlack": "#fbf8f1",
- "terminalRed": "#9b4b40",
- "terminalGreen": "#496f45",
- "terminalYellow": "#765019",
- "terminalBlue": "#1e6871",
- "terminalMagenta": "#844c71",
- "terminalCyan": "#665593",
- "terminalWhite": "#15130f",
- "terminalBrightBlack": "#6b6458",
- "terminalBrightRed": "#834137",
- "terminalBrightGreen": "#405e3b",
- "terminalBrightYellow": "#664617",
- "terminalBrightBlue": "#1c595f",
- "terminalBrightMagenta": "#70425f",
- "terminalBrightCyan": "#57497b",
- "terminalBrightWhite": "#15130f"
+ "background": "#f3eadc",
+ "backgroundPanel": "#fff7ea",
+ "backgroundElement": "#e6d7c4",
+ "backgroundHover": "#e7d9c4",
+ "backgroundSelected": "#17120d",
+ "backgroundCode": "#fff7ea",
+ "backgroundSearch": "#cbbb9e",
+ "backgroundLine": "#fff7ea",
+ "backgroundAssistant": "#cfd2ca",
+ "backgroundUser": "#e1d1bc",
+ "backgroundTool": "#ddd3d1",
+ "text": "#17120d",
+ "textMuted": "#3d3228",
+ "textSubtle": "#554635",
+ "textPlaceholder": "#66523f",
+ "textAssistant": "#15465d",
+ "textUser": "#724615",
+ "textTool": "#57478b",
+ "primary": "#824f16",
+ "secondary": "#a7471c",
+ "accent": "#824f16",
+ "accentMuted": "#b89975",
+ "error": "#842f24",
+ "warning": "#654300",
+ "success": "#3f6b35",
+ "info": "#15516e",
+ "border": "#66513b",
+ "borderActive": "#824f16",
+ "borderSubtle": "#8a7358",
+ "borderFocus": "#15516e",
+ "shadow": "#b6b0a5",
+ "diffAdded": "#3f6b35",
+ "diffRemoved": "#842f24",
+ "diffContext": "#3d3228",
+ "diffHunkHeader": "#57478b",
+ "diffHighlightAdded": "#3f6b35",
+ "diffHighlightRemoved": "#842f24",
+ "diffAddedBg": "#d3d3be",
+ "diffRemovedBg": "#e1ccbf",
+ "diffContextBg": "#f3eadc",
+ "diffLineNumber": "#554635",
+ "diffAddedLineNumberBg": "#d3d3be",
+ "diffRemovedLineNumberBg": "#e1ccbf",
+ "diffHunkHeaderBg": "#dad0cf",
+ "diffFold": "#66523f",
+ "diffFoldBg": "#efe4d4",
+ "markdownText": "#17120d",
+ "markdownHeading": "#824f16",
+ "markdownLink": "#15516e",
+ "markdownLinkText": "#824f16",
+ "markdownCode": "#3f6b35",
+ "markdownBlockQuote": "#a7471c",
+ "markdownEmph": "#15516e",
+ "markdownStrong": "#824f16",
+ "markdownHorizontalRule": "#66513b",
+ "markdownListItem": "#824f16",
+ "markdownListEnumeration": "#57478b",
+ "markdownTableBorder": "#66513b",
+ "markdownTableHeader": "#a7471c",
+ "markdownImage": "#7d3e64",
+ "markdownImageText": "#17120d",
+ "markdownCodeBlock": "#17120d",
+ "markdownCodeBlockBg": "#fff7ea",
+ "markdownInlineCodeBg": "#d6d6c1",
+ "syntaxComment": "#66523f",
+ "syntaxKeyword": "#7c4c11",
+ "syntaxFunction": "#15516e",
+ "syntaxMethod": "#204f73",
+ "syntaxVariable": "#17120d",
+ "syntaxParameter": "#9e441b",
+ "syntaxProperty": "#154e6a",
+ "syntaxField": "#3d6733",
+ "syntaxString": "#3f6b35",
+ "syntaxNumber": "#a7471c",
+ "syntaxBoolean": "#9d452d",
+ "syntaxConstant": "#9d452d",
+ "syntaxType": "#57478b",
+ "syntaxClass": "#5f4876",
+ "syntaxInterface": "#484985",
+ "syntaxEnum": "#534d7c",
+ "syntaxOperator": "#7d3e64",
+ "syntaxPunctuation": "#3d3228",
+ "syntaxTag": "#7c4c11",
+ "syntaxAttribute": "#154e6a",
+ "syntaxRegexp": "#7f3a52",
+ "syntaxEscape": "#654300",
+ "syntaxNamespace": "#57478b",
+ "syntaxModule": "#57478b",
+ "syntaxDecorator": "#7d3e64",
+ "syntaxBuiltin": "#9d452d",
+ "syntaxSpecial": "#654300",
+ "syntaxTodo": "#5c3d02",
+ "syntaxDeprecated": "#703025",
+ "terminalBlack": "#5e5c57",
+ "terminalRed": "#842f24",
+ "terminalGreen": "#3f6b35",
+ "terminalYellow": "#654300",
+ "terminalBlue": "#15516e",
+ "terminalMagenta": "#7d3e64",
+ "terminalCyan": "#15465d",
+ "terminalWhite": "#17120d",
+ "terminalBrightBlack": "#554635",
+ "terminalBrightRed": "#702a20",
+ "terminalBrightGreen": "#385b2e",
+ "terminalBrightYellow": "#593b02",
+ "terminalBrightBlue": "#15465d",
+ "terminalBrightMagenta": "#6b3654",
+ "terminalBrightCyan": "#4b3d74",
+ "terminalBrightWhite": "#17120d"
}
}
diff --git a/Codex-App/Dreamcoder.codex-theme.json b/Codex-App/Dreamcoder.codex-theme.json
index bcb96a5..283fb61 100644
--- a/Codex-App/Dreamcoder.codex-theme.json
+++ b/Codex-App/Dreamcoder.codex-theme.json
@@ -1,129 +1,129 @@
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
- "dreamBackground": "#f8f2ea",
- "dreamPanel": "#fbf6ee",
- "dreamElement": "#ece4d8",
- "dreamText": "#15130f",
- "dreamMuted": "#4a463f",
- "dreamCocoa": "#78522d",
- "dreamLucuma": "#925b25",
- "dreamDiagnostic": "#36665a",
- "dreamSage": "#496f45",
- "dreamViolet": "#665593",
- "dreamMauve": "#844c71",
- "dreamCoral": "#9b4b40",
- "dreamWarning": "#765019"
+ "dreamBackground": "#14110e",
+ "dreamPanel": "#231c18",
+ "dreamElement": "#1d1613",
+ "dreamText": "#f0e7dc",
+ "dreamMuted": "#c7b9aa",
+ "dreamCocoa": "#ce836c",
+ "dreamLucuma": "#cdae7d",
+ "dreamDiagnostic": "#c7b2a2",
+ "dreamSage": "#b8bf84",
+ "dreamViolet": "#c9a8dc",
+ "dreamMauve": "#d98aa9",
+ "dreamCoral": "#e98272",
+ "dreamWarning": "#e8b866"
},
"theme": {
- "background": "#f8f2ea",
- "backgroundPanel": "#fbf6ee",
- "backgroundElement": "#ece4d8",
- "backgroundHover": "#ece3d7",
- "backgroundSelected": "#e2c8a2",
- "backgroundCode": "#faf5ed",
- "backgroundSearch": "#d4c5af",
- "backgroundLine": "#f9f4ec",
- "backgroundAssistant": "#d9dcd3",
- "backgroundUser": "#e8daca",
- "backgroundTool": "#e4dcde",
- "text": "#15130f",
- "textMuted": "#4a463f",
- "textSubtle": "#6b6458",
- "textPlaceholder": "#6a6258",
- "textAssistant": "#30574c",
- "textUser": "#7f5022",
- "textTool": "#665593",
- "primary": "#925b25",
- "secondary": "#78522d",
- "accent": "#925b25",
- "accentMuted": "#c3a384",
- "error": "#9b4b40",
- "warning": "#765019",
- "success": "#496f45",
- "info": "#36665a",
- "border": "#75644f",
- "borderActive": "#925b25",
- "borderSubtle": "#aa9a85",
- "borderFocus": "#36665a",
- "shadow": "#bab6b0",
- "diffAdded": "#496f45",
- "diffRemoved": "#9b4b40",
- "diffContext": "#4a463f",
- "diffHunkHeader": "#665593",
- "diffHighlightAdded": "#496f45",
- "diffHighlightRemoved": "#9b4b40",
- "diffAddedBg": "#d8dacc",
- "diffRemovedBg": "#e9d7cf",
- "diffContextBg": "#f8f2ea",
- "diffLineNumber": "#6b6458",
- "diffAddedLineNumberBg": "#d8dacc",
- "diffRemovedLineNumberBg": "#e9d7cf",
- "diffHunkHeaderBg": "#e1d9dc",
- "diffFold": "#6a6258",
- "diffFoldBg": "#f4ede4",
- "markdownText": "#15130f",
- "markdownHeading": "#925b25",
- "markdownLink": "#36665a",
- "markdownLinkText": "#925b25",
- "markdownCode": "#496f45",
- "markdownBlockQuote": "#78522d",
- "markdownEmph": "#36665a",
- "markdownStrong": "#925b25",
- "markdownHorizontalRule": "#aa9a85",
- "markdownListItem": "#925b25",
- "markdownListEnumeration": "#665593",
- "markdownTableBorder": "#75644f",
- "markdownTableHeader": "#78522d",
- "markdownImage": "#844c71",
- "markdownImageText": "#15130f",
- "markdownCodeBlock": "#15130f",
- "markdownCodeBlockBg": "#faf5ed",
- "markdownInlineCodeBg": "#dcddd0",
- "syntaxComment": "#6a6258",
- "syntaxKeyword": "#8c5922",
- "syntaxFunction": "#36665a",
- "syntaxMethod": "#335e52",
- "syntaxVariable": "#15130f",
- "syntaxParameter": "#271e14",
- "syntaxProperty": "#202f28",
- "syntaxField": "#20271b",
- "syntaxString": "#496f45",
- "syntaxNumber": "#78522d",
- "syntaxBoolean": "#7b513d",
- "syntaxConstant": "#7b513d",
- "syntaxType": "#665593",
- "syntaxClass": "#6e567f",
- "syntaxInterface": "#5b5986",
- "syntaxEnum": "#615a85",
- "syntaxOperator": "#844c71",
- "syntaxPunctuation": "#4a463f",
- "syntaxTag": "#8c5922",
- "syntaxAttribute": "#202f28",
- "syntaxRegexp": "#8a4c63",
- "syntaxEscape": "#765019",
- "syntaxNamespace": "#665593",
- "syntaxModule": "#665593",
- "syntaxDecorator": "#844c71",
- "syntaxBuiltin": "#7b513d",
- "syntaxSpecial": "#765019",
- "syntaxTodo": "#6a4918",
- "syntaxDeprecated": "#844a40",
- "terminalBlack": "#fbf6ee",
- "terminalRed": "#9b4b40",
- "terminalGreen": "#496f45",
- "terminalYellow": "#765019",
- "terminalBlue": "#36665a",
- "terminalMagenta": "#844c71",
- "terminalCyan": "#665593",
- "terminalWhite": "#15130f",
- "terminalBrightBlack": "#6b6458",
- "terminalBrightRed": "#834137",
- "terminalBrightGreen": "#405e3b",
- "terminalBrightYellow": "#664617",
- "terminalBrightBlue": "#30574c",
- "terminalBrightMagenta": "#70425f",
- "terminalBrightCyan": "#57497b",
- "terminalBrightWhite": "#15130f"
+ "background": "#14110e",
+ "backgroundPanel": "#231c18",
+ "backgroundElement": "#1d1613",
+ "backgroundHover": "#221b17",
+ "backgroundSelected": "#373027",
+ "backgroundCode": "#231c18",
+ "backgroundSearch": "#4f4027",
+ "backgroundLine": "#231c18",
+ "backgroundAssistant": "#312b26",
+ "backgroundUser": "#322a20",
+ "backgroundTool": "#2d262b",
+ "text": "#f0e7dc",
+ "textMuted": "#c7b9aa",
+ "textSubtle": "#aa927c",
+ "textPlaceholder": "#9c826d",
+ "textAssistant": "#cebcac",
+ "textUser": "#d2b78b",
+ "textTool": "#c9a8dc",
+ "primary": "#cdae7d",
+ "secondary": "#ce836c",
+ "accent": "#cdae7d",
+ "accentMuted": "#746348",
+ "error": "#e98272",
+ "warning": "#e8b866",
+ "success": "#b8bf84",
+ "info": "#c7b2a2",
+ "border": "#806754",
+ "borderActive": "#cdae7d",
+ "borderSubtle": "#594d46",
+ "borderFocus": "#c7b2a2",
+ "shadow": "#0f0d0a",
+ "diffAdded": "#b8bf84",
+ "diffRemoved": "#e98272",
+ "diffContext": "#c7b9aa",
+ "diffHunkHeader": "#c9a8dc",
+ "diffHighlightAdded": "#b8bf84",
+ "diffHighlightRemoved": "#e98272",
+ "diffAddedBg": "#323023",
+ "diffRemovedBg": "#36231e",
+ "diffContextBg": "#14110e",
+ "diffLineNumber": "#aa927c",
+ "diffAddedLineNumberBg": "#323023",
+ "diffRemovedLineNumberBg": "#36231e",
+ "diffHunkHeaderBg": "#31292f",
+ "diffFold": "#9c826d",
+ "diffFoldBg": "#191411",
+ "markdownText": "#f0e7dc",
+ "markdownHeading": "#cdae7d",
+ "markdownLink": "#c7b2a2",
+ "markdownLinkText": "#cdae7d",
+ "markdownCode": "#b8bf84",
+ "markdownBlockQuote": "#ce836c",
+ "markdownEmph": "#c7b2a2",
+ "markdownStrong": "#cdae7d",
+ "markdownHorizontalRule": "#806754",
+ "markdownListItem": "#cdae7d",
+ "markdownListEnumeration": "#c9a8dc",
+ "markdownTableBorder": "#806754",
+ "markdownTableHeader": "#ce836c",
+ "markdownImage": "#d98aa9",
+ "markdownImageText": "#f0e7dc",
+ "markdownCodeBlock": "#f0e7dc",
+ "markdownCodeBlockBg": "#231c18",
+ "markdownInlineCodeBg": "#2e2d21",
+ "syntaxComment": "#9c826d",
+ "syntaxKeyword": "#d3b078",
+ "syntaxFunction": "#c7b2a2",
+ "syntaxMethod": "#c7b0ab",
+ "syntaxVariable": "#f0e7dc",
+ "syntaxParameter": "#d08973",
+ "syntaxProperty": "#c9b4a4",
+ "syntaxField": "#bbc188",
+ "syntaxString": "#b8bf84",
+ "syntaxNumber": "#ce836c",
+ "syntaxBoolean": "#d1857b",
+ "syntaxConstant": "#d1857b",
+ "syntaxType": "#c9a8dc",
+ "syntaxClass": "#caa9cb",
+ "syntaxInterface": "#c9aacf",
+ "syntaxEnum": "#c6accc",
+ "syntaxOperator": "#d98aa9",
+ "syntaxPunctuation": "#c7b9aa",
+ "syntaxTag": "#d3b078",
+ "syntaxAttribute": "#c9b4a4",
+ "syntaxRegexp": "#dd889a",
+ "syntaxEscape": "#e8b866",
+ "syntaxNamespace": "#c9a8dc",
+ "syntaxModule": "#c9a8dc",
+ "syntaxDecorator": "#d98aa9",
+ "syntaxBuiltin": "#d1857b",
+ "syntaxSpecial": "#e8b866",
+ "syntaxTodo": "#e9be74",
+ "syntaxDeprecated": "#df9182",
+ "terminalBlack": "#868280",
+ "terminalRed": "#e98272",
+ "terminalGreen": "#b8bf84",
+ "terminalYellow": "#e8b866",
+ "terminalBlue": "#c7b2a2",
+ "terminalMagenta": "#d98aa9",
+ "terminalCyan": "#cebcac",
+ "terminalWhite": "#f0e7dc",
+ "terminalBrightBlack": "#aa927c",
+ "terminalBrightRed": "#ea9485",
+ "terminalBrightGreen": "#c2c694",
+ "terminalBrightYellow": "#e9c079",
+ "terminalBrightBlue": "#cebcac",
+ "terminalBrightMagenta": "#dd9bb2",
+ "terminalBrightCyan": "#d0b3dc",
+ "terminalBrightWhite": "#f0e7dc"
}
}
diff --git a/Codex-CLI/Dreamcoder-Dark.tmTheme b/Codex-CLI/Dreamcoder-Dark.tmTheme
new file mode 100644
index 0000000..f339fec
--- /dev/null
+++ b/Codex-CLI/Dreamcoder-Dark.tmTheme
@@ -0,0 +1,29 @@
+
+
+
+
+ nameDreamcoder
+ settings
+
+ settings
+ background#15100d
+ foreground#f0e7dc
+ caret#e6a15c
+ selection#43291d
+ lineHighlight#241b16
+ gutter#30231c
+ gutterForeground#c7b9aa
+ invisibles#9c826d
+
+ scopecommentsettingsforeground#9c826dfontStyleitalic
+ scopekeyword, storagesettingsforeground#e6a65efontStylebold
+ scopeentity.name.function, support.functionsettingsforeground#d2a268
+ scopevariable, meta.definition.variablesettingsforeground#f0e7dc
+ scopestringsettingsforeground#b8bf84
+ scopeconstant.numeric, constant.languagesettingsforeground#d77565
+ scopeentity.name.type, support.typesettingsforeground#c9a8dc
+ scopepunctuation, keyword.operatorsettingsforeground#d98aa9
+
+
+
diff --git a/Codex-CLI/Dreamcoder-Dusk.tmTheme b/Codex-CLI/Dreamcoder-Dusk.tmTheme
new file mode 100644
index 0000000..b6918ed
--- /dev/null
+++ b/Codex-CLI/Dreamcoder-Dusk.tmTheme
@@ -0,0 +1,29 @@
+
+
+
+
+ nameDreamcoder
+ settings
+
+ settings
+ background#ebe4d6
+ foreground#1a1713
+ caret#8a5520
+ selection#1a1713
+ lineHighlight#d8cbb8
+ gutter#c6b6a0
+ gutterForeground#4c443a
+ invisibles#615548
+
+ scopecommentsettingsforeground#615548fontStyleitalic
+ scopekeyword, storagesettingsforeground#815019fontStylebold
+ scopeentity.name.function, support.functionsettingsforeground#104b67
+ scopevariable, meta.definition.variablesettingsforeground#1a1713
+ scopestringsettingsforeground#466b41
+ scopeconstant.numeric, constant.languagesettingsforeground#8f422e
+ scopeentity.name.type, support.typesettingsforeground#5b4e86
+ scopepunctuation, keyword.operatorsettingsforeground#784762
+
+
+
diff --git a/Codex-CLI/Dreamcoder-Light.tmTheme b/Codex-CLI/Dreamcoder-Light.tmTheme
new file mode 100644
index 0000000..523f171
--- /dev/null
+++ b/Codex-CLI/Dreamcoder-Light.tmTheme
@@ -0,0 +1,29 @@
+
+
+
+
+ nameDreamcoder
+ settings
+
+ settings
+ background#f3eadc
+ foreground#17120d
+ caret#824f16
+ selection#17120d
+ lineHighlight#decbb1
+ gutter#c8ad89
+ gutterForeground#3d3228
+ invisibles#66523f
+
+ scopecommentsettingsforeground#66523ffontStyleitalic
+ scopekeyword, storagesettingsforeground#7c4c11fontStylebold
+ scopeentity.name.function, support.functionsettingsforeground#15516e
+ scopevariable, meta.definition.variablesettingsforeground#17120d
+ scopestringsettingsforeground#3f6b35
+ scopeconstant.numeric, constant.languagesettingsforeground#9d452d
+ scopeentity.name.type, support.typesettingsforeground#57478b
+ scopepunctuation, keyword.operatorsettingsforeground#7d3e64
+
+
+
diff --git a/Codex-CLI/Dreamcoder.tmTheme b/Codex-CLI/Dreamcoder.tmTheme
new file mode 100644
index 0000000..3f9c481
--- /dev/null
+++ b/Codex-CLI/Dreamcoder.tmTheme
@@ -0,0 +1,29 @@
+
+
+
+
+ nameDreamcoder
+ settings
+
+ settings
+ background#14110e
+ foreground#f0e7dc
+ caret#cdae7d
+ selection#373027
+ lineHighlight#231c18
+ gutter#2e241f
+ gutterForeground#c7b9aa
+ invisibles#9c826d
+
+ scopecommentsettingsforeground#9c826dfontStyleitalic
+ scopekeyword, storagesettingsforeground#d3b078fontStylebold
+ scopeentity.name.function, support.functionsettingsforeground#c7b2a2
+ scopevariable, meta.definition.variablesettingsforeground#f0e7dc
+ scopestringsettingsforeground#b8bf84
+ scopeconstant.numeric, constant.languagesettingsforeground#d1857b
+ scopeentity.name.type, support.typesettingsforeground#c9a8dc
+ scopepunctuation, keyword.operatorsettingsforeground#d98aa9
+
+
+
diff --git a/Ghostty/.config/ghostty/config b/Ghostty/.config/ghostty/config
index 106c16a..f12dd69 100644
--- a/Ghostty/.config/ghostty/config
+++ b/Ghostty/.config/ghostty/config
@@ -66,7 +66,7 @@ keybind = ctrl+shift+r=reload_config
# Note: ctrl+shift+p show_scrollback not supported (use toggle_command_palette), f1 open_url, f2 inspect not supported, f3 hints line not directly supported
# Background opacity
-background-opacity = 0.72
+background-opacity = 0.98
background-opacity-cells = true
background-blur = false
minimum-contrast = 4.5
diff --git a/Ghostty/.config/ghostty/themes/dreamcoder b/Ghostty/.config/ghostty/themes/dreamcoder
index a295dab..c90397f 100644
--- a/Ghostty/.config/ghostty/themes/dreamcoder
+++ b/Ghostty/.config/ghostty/themes/dreamcoder
@@ -1,26 +1,26 @@
-# Dreamcoder Light
-background = #f8f2ea
-foreground = #15130f
-cursor-color = #925b25
-cursor-text = #f8f2ea
-selection-background = #e2c8a2
-selection-foreground = #15130f
+# Dreamcoder Ember Noir
+background = #14110e
+foreground = #f0e7dc
+cursor-color = #cdae7d
+cursor-text = #14110e
+selection-background = #373027
+selection-foreground = #f0e7dc
minimum-contrast = 4.5
background-opacity-cells = true
-palette = 0=#fbf6ee
-palette = 1=#9b4b40
-palette = 2=#496f45
-palette = 3=#925b25
-palette = 4=#36665a
-palette = 5=#844c71
-palette = 6=#665593
-palette = 7=#4a463f
-palette = 8=#6b6458
+palette = 0=#868280
+palette = 1=#e98272
+palette = 2=#b8bf84
+palette = 3=#cdae7d
+palette = 4=#c7b2a2
+palette = 5=#d98aa9
+palette = 6=#c9a8dc
+palette = 7=#c7b9aa
+palette = 8=#aa927c
palette = 9=#e9a092
palette = 10=#75a579
-palette = 11=#765019
+palette = 11=#e8b866
palette = 12=#579ba4
palette = 13=#846fc5
palette = 14=#72b6bd
-palette = 15=#15130f
+palette = 15=#f0e7dc
diff --git a/Ghostty/.config/ghostty/themes/dreamcoder-dark b/Ghostty/.config/ghostty/themes/dreamcoder-dark
index 9736764..00e02de 100644
--- a/Ghostty/.config/ghostty/themes/dreamcoder-dark
+++ b/Ghostty/.config/ghostty/themes/dreamcoder-dark
@@ -1,26 +1,26 @@
-# Dreamcoder Dark
-background = #101216
-foreground = #e8e1d7
-cursor-color = #d9ad67
-cursor-text = #101216
-selection-background = #32291f
-selection-foreground = #e8e1d7
+# Dreamcoder Ember Noir
+background = #15100d
+foreground = #f0e7dc
+cursor-color = #e6a15c
+cursor-text = #15100d
+selection-background = #43291d
+selection-foreground = #f0e7dc
minimum-contrast = 4.5
background-opacity-cells = true
-palette = 0=#1c222b
-palette = 1=#d78373
-palette = 2=#a5b89c
-palette = 3=#d9ad67
-palette = 4=#92c7cd
-palette = 5=#d2a3c3
-palette = 6=#c4b3df
-palette = 7=#b8b0a5
-palette = 8=#928a80
+palette = 0=#86827e
+palette = 1=#e98272
+palette = 2=#b8bf84
+palette = 3=#e6a15c
+palette = 4=#d2a268
+palette = 5=#d98aa9
+palette = 6=#c9a8dc
+palette = 7=#c7b9aa
+palette = 8=#aa927c
palette = 9=#e9a092
palette = 10=#75a579
-palette = 11=#ddb36b
+palette = 11=#e8b866
palette = 12=#579ba4
palette = 13=#846fc5
palette = 14=#72b6bd
-palette = 15=#e8e1d7
+palette = 15=#f0e7dc
diff --git a/Ghostty/.config/ghostty/themes/dreamcoder-dusk b/Ghostty/.config/ghostty/themes/dreamcoder-dusk
new file mode 100644
index 0000000..a14f764
--- /dev/null
+++ b/Ghostty/.config/ghostty/themes/dreamcoder-dusk
@@ -0,0 +1,26 @@
+# Dreamcoder Dusk
+background = #ebe4d6
+foreground = #1a1713
+cursor-color = #8a5520
+cursor-text = #ebe4d6
+selection-background = #1a1713
+selection-foreground = #ebe4d6
+minimum-contrast = 4.5
+background-opacity-cells = true
+
+palette = 0=#595753
+palette = 1=#773126
+palette = 2=#466b41
+palette = 3=#8a5520
+palette = 4=#104b67
+palette = 5=#784762
+palette = 6=#5b4e86
+palette = 7=#4c443a
+palette = 8=#5a4f43
+palette = 9=#815850
+palette = 10=#415b42
+palette = 11=#604000
+palette = 12=#3a686e
+palette = 13=#6c5ba2
+palette = 14=#3e6468
+palette = 15=#1a1713
diff --git a/Ghostty/.config/ghostty/themes/dreamcoder-light b/Ghostty/.config/ghostty/themes/dreamcoder-light
index 6551e78..5cbcd6e 100644
--- a/Ghostty/.config/ghostty/themes/dreamcoder-light
+++ b/Ghostty/.config/ghostty/themes/dreamcoder-light
@@ -1,26 +1,26 @@
# Dreamcoder Light
-background = #f6f1e8
-foreground = #15130f
-cursor-color = #855719
-cursor-text = #f6f1e8
-selection-background = #dcc49e
-selection-foreground = #15130f
+background = #f3eadc
+foreground = #17120d
+cursor-color = #824f16
+cursor-text = #f3eadc
+selection-background = #17120d
+selection-foreground = #f3eadc
minimum-contrast = 4.5
background-opacity-cells = true
-palette = 0=#fbf8f1
-palette = 1=#9b4b40
-palette = 2=#496f45
-palette = 3=#855719
-palette = 4=#1e6871
-palette = 5=#844c71
-palette = 6=#665593
-palette = 7=#4a463f
-palette = 8=#6b6458
-palette = 9=#e9a092
-palette = 10=#75a579
-palette = 11=#765019
-palette = 12=#579ba4
-palette = 13=#846fc5
-palette = 14=#72b6bd
-palette = 15=#15130f
+palette = 0=#5e5c57
+palette = 1=#842f24
+palette = 2=#3f6b35
+palette = 3=#824f16
+palette = 4=#15516e
+palette = 5=#7d3e64
+palette = 6=#57478b
+palette = 7=#3d3228
+palette = 8=#554635
+palette = 9=#815850
+palette = 10=#4f6f51
+palette = 11=#654300
+palette = 12=#3a686e
+palette = 13=#6c5ba2
+palette = 14=#3e6468
+palette = 15=#17120d
diff --git a/Kitty/.config/kitty/colors-dreamcoder-dark.conf b/Kitty/.config/kitty/colors-dreamcoder-dark.conf
index 38679e2..1575fed 100644
--- a/Kitty/.config/kitty/colors-dreamcoder-dark.conf
+++ b/Kitty/.config/kitty/colors-dreamcoder-dark.conf
@@ -1,52 +1,52 @@
# ==========================================================
-# Dreamcoder Dark
+# Dreamcoder Ember Noir OLED
# ==========================================================
# Color-only theme layer. Keep ML4W/Gentleman behavior elsewhere.
-foreground #e8e1d7
-background #101216
-selection_foreground #e8e1d7
-selection_background #32291f
-url_color #92c7cd
+foreground #e8dfd0
+background #12100e
+selection_foreground #e8dfd0
+selection_background #373027
+url_color #c8b2a2
-cursor #d9ad67
-cursor_text_color #101216
+cursor #d99555
+cursor_text_color #12100e
cursor_shape block
cursor_blink_interval 0.5
cursor_stop_blinking_after 15.0
-active_tab_foreground #101216
-active_tab_background #d9ad67
-inactive_tab_foreground #b8b0a5
-inactive_tab_background #101216
-tab_bar_background #101216
+active_tab_foreground #12100e
+active_tab_background #d99555
+inactive_tab_foreground #b8a99a
+inactive_tab_background #12100e
+tab_bar_background #12100e
-active_border_color #d9ad67
-inactive_border_color #3d4350
-bell_border_color #d78373
+active_border_color #d99555
+inactive_border_color #594d46
+bell_border_color #e98272
-color0 #1c222b
-color1 #d78373
-color2 #a5b89c
-color3 #d9ad67
-color4 #92c7cd
-color5 #d2a3c3
-color6 #c4b3df
-color7 #b8b0a5
-color8 #928a80
+color0 #86827e
+color1 #e98272
+color2 #b8bf84
+color3 #d99555
+color4 #c8b2a2
+color5 #d98aa9
+color6 #c9a8dc
+color7 #b8a99a
+color8 #9a8b7c
color9 #e9a092
color10 #75a579
-color11 #ddb36b
+color11 #e8b866
color12 #579ba4
color13 #846fc5
color14 #72b6bd
-color15 #e8e1d7
-color16 #b87a48
-color17 #d78373
+color15 #e8dfd0
+color16 #c96a45
+color17 #e98272
-mark1_foreground #101216
-mark1_background #d9ad67
-mark2_foreground #101216
-mark2_background #92c7cd
-mark3_foreground #101216
-mark3_background #d2a3c3
+mark1_foreground #12100e
+mark1_background #d99555
+mark2_foreground #12100e
+mark2_background #c8b2a2
+mark3_foreground #12100e
+mark3_background #d98aa9
\ No newline at end of file
diff --git a/Kitty/.config/kitty/colors-dreamcoder-dusk.conf b/Kitty/.config/kitty/colors-dreamcoder-dusk.conf
new file mode 100644
index 0000000..a65c73f
--- /dev/null
+++ b/Kitty/.config/kitty/colors-dreamcoder-dusk.conf
@@ -0,0 +1,52 @@
+# ==========================================================
+# Dreamcoder Dusk
+# ==========================================================
+# Color-only theme layer. Keep ML4W/Gentleman behavior elsewhere.
+
+foreground #1a1713
+background #ebe4d6
+selection_foreground #ebe4d6
+selection_background #1a1713
+url_color #104b67
+
+cursor #8a5520
+cursor_text_color #ebe4d6
+cursor_shape block
+cursor_blink_interval 0.5
+cursor_stop_blinking_after 15.0
+
+active_tab_foreground #ebe4d6
+active_tab_background #8a5520
+inactive_tab_foreground #4c443a
+inactive_tab_background #ebe4d6
+tab_bar_background #ebe4d6
+
+active_border_color #8a5520
+inactive_border_color #a7947a
+bell_border_color #773126
+
+color0 #595753
+color1 #773126
+color2 #466b41
+color3 #8a5520
+color4 #104b67
+color5 #784762
+color6 #5b4e86
+color7 #4c443a
+color8 #5a4f43
+color9 #815850
+color10 #415b42
+color11 #604000
+color12 #3a686e
+color13 #6c5ba2
+color14 #3e6468
+color15 #1a1713
+color16 #96411e
+color17 #773126
+
+mark1_foreground #ebe4d6
+mark1_background #8a5520
+mark2_foreground #ebe4d6
+mark2_background #104b67
+mark3_foreground #ebe4d6
+mark3_background #784762
diff --git a/Kitty/.config/kitty/colors-dreamcoder-light.conf b/Kitty/.config/kitty/colors-dreamcoder-light.conf
index 637fdc1..df887ee 100644
--- a/Kitty/.config/kitty/colors-dreamcoder-light.conf
+++ b/Kitty/.config/kitty/colors-dreamcoder-light.conf
@@ -3,50 +3,50 @@
# ==========================================================
# Color-only theme layer. Keep ML4W/Gentleman behavior elsewhere.
-foreground #15130f
-background #f6f1e8
-selection_foreground #15130f
-selection_background #dcc49e
-url_color #1e6871
+foreground #17120d
+background #f3eadc
+selection_foreground #f3eadc
+selection_background #17120d
+url_color #15516e
-cursor #855719
-cursor_text_color #f6f1e8
+cursor #824f16
+cursor_text_color #f3eadc
cursor_shape block
cursor_blink_interval 0.5
cursor_stop_blinking_after 15.0
-active_tab_foreground #f6f1e8
-active_tab_background #855719
-inactive_tab_foreground #4a463f
-inactive_tab_background #f6f1e8
-tab_bar_background #f6f1e8
+active_tab_foreground #f3eadc
+active_tab_background #824f16
+inactive_tab_foreground #3d3228
+inactive_tab_background #f3eadc
+tab_bar_background #f3eadc
-active_border_color #855719
-inactive_border_color #b7a78f
-bell_border_color #9b4b40
+active_border_color #824f16
+inactive_border_color #8a7358
+bell_border_color #842f24
-color0 #fbf8f1
-color1 #9b4b40
-color2 #496f45
-color3 #855719
-color4 #1e6871
-color5 #844c71
-color6 #665593
-color7 #4a463f
-color8 #6b6458
-color9 #e9a092
-color10 #75a579
-color11 #765019
-color12 #579ba4
-color13 #846fc5
-color14 #72b6bd
-color15 #15130f
-color16 #7f4a27
-color17 #9b4b40
+color0 #5e5c57
+color1 #842f24
+color2 #3f6b35
+color3 #824f16
+color4 #15516e
+color5 #7d3e64
+color6 #57478b
+color7 #3d3228
+color8 #554635
+color9 #815850
+color10 #4f6f51
+color11 #654300
+color12 #3a686e
+color13 #6c5ba2
+color14 #3e6468
+color15 #17120d
+color16 #a7471c
+color17 #842f24
-mark1_foreground #f6f1e8
-mark1_background #855719
-mark2_foreground #f6f1e8
-mark2_background #1e6871
-mark3_foreground #f6f1e8
-mark3_background #844c71
+mark1_foreground #f3eadc
+mark1_background #824f16
+mark2_foreground #f3eadc
+mark2_background #15516e
+mark3_foreground #f3eadc
+mark3_background #7d3e64
diff --git a/Kitty/.config/kitty/dreamcoder-ui.conf b/Kitty/.config/kitty/dreamcoder-ui.conf
index ae4479f..dae5d85 100644
--- a/Kitty/.config/kitty/dreamcoder-ui.conf
+++ b/Kitty/.config/kitty/dreamcoder-ui.conf
@@ -17,7 +17,7 @@ single_window_padding_width -1
placement_strategy center
initial_window_width 1180
initial_window_height 780
-background_opacity 0.72
+background_opacity 0.76
dynamic_background_opacity no
background_blur 0
diff --git a/Pi/.pi/agent/themes/dreamcoder-dark.json b/Pi/.pi/agent/themes/dreamcoder-dark.json
new file mode 100644
index 0000000..ac8eaad
--- /dev/null
+++ b/Pi/.pi/agent/themes/dreamcoder-dark.json
@@ -0,0 +1,71 @@
+{
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
+ "name": "dreamcoder",
+ "vars": {
+ "cocoa": "#d66f50",
+ "lucuma": "#e6a15c",
+ "diagnostic": "#d2a268",
+ "sage": "#b8bf84",
+ "mauve": "#d98aa9",
+ "coral": "#e98272",
+ "warning": "#e8b866",
+ "muted": "#c7b9aa",
+ "subtle": "#aa927c",
+ "comment": "#9c826d",
+ "borderUi": "#806754",
+ "borderMuted": "#49362c"
+ },
+ "colors": {
+ "accent": "lucuma",
+ "border": "borderUi",
+ "borderAccent": "diagnostic",
+ "borderMuted": "borderMuted",
+ "success": "sage",
+ "error": "coral",
+ "warning": "warning",
+ "muted": "muted",
+ "dim": "subtle",
+ "text": "",
+ "thinkingText": "muted",
+ "selectedBg": "#43291d",
+ "userMessageBg": "#36271a",
+ "userMessageText": "",
+ "customMessageBg": "#241b16",
+ "customMessageText": "",
+ "customMessageLabel": "lucuma",
+ "toolPendingBg": "#1c1511",
+ "toolSuccessBg": "#323022",
+ "toolErrorBg": "#37221d",
+ "toolTitle": "lucuma",
+ "toolOutput": "",
+ "mdHeading": "lucuma",
+ "mdLink": "diagnostic",
+ "mdLinkUrl": "muted",
+ "mdCode": "sage",
+ "mdCodeBlock": "",
+ "mdCodeBlockBorder": "borderMuted",
+ "mdQuote": "cocoa",
+ "mdQuoteBorder": "borderMuted",
+ "mdHr": "borderMuted",
+ "mdListBullet": "lucuma",
+ "toolDiffAdded": "sage",
+ "toolDiffRemoved": "coral",
+ "toolDiffContext": "muted",
+ "syntaxComment": "comment",
+ "syntaxKeyword": "#e6a65e",
+ "syntaxFunction": "#d2a268",
+ "syntaxVariable": "#f0e7dc",
+ "syntaxString": "#b8bf84",
+ "syntaxNumber": "#d66f50",
+ "syntaxType": "#c9a8dc",
+ "syntaxOperator": "#d98aa9",
+ "syntaxPunctuation": "#c7b9aa",
+ "thinkingOff": "comment",
+ "thinkingMinimal": "borderUi",
+ "thinkingLow": "#d6aa76",
+ "thinkingMedium": "diagnostic",
+ "thinkingHigh": "mauve",
+ "thinkingXhigh": "coral",
+ "bashMode": "lucuma"
+ }
+}
diff --git a/Pi/.pi/agent/themes/dreamcoder-dusk.json b/Pi/.pi/agent/themes/dreamcoder-dusk.json
new file mode 100644
index 0000000..d9bc905
--- /dev/null
+++ b/Pi/.pi/agent/themes/dreamcoder-dusk.json
@@ -0,0 +1,71 @@
+{
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
+ "name": "dreamcoder",
+ "vars": {
+ "cocoa": "#96411e",
+ "lucuma": "#8a5520",
+ "diagnostic": "#104b67",
+ "sage": "#466b41",
+ "mauve": "#784762",
+ "coral": "#773126",
+ "warning": "#604000",
+ "muted": "#4c443a",
+ "subtle": "#5a4f43",
+ "comment": "#615548",
+ "borderUi": "#665845",
+ "borderMuted": "#a7947a"
+ },
+ "colors": {
+ "accent": "lucuma",
+ "border": "borderUi",
+ "borderAccent": "diagnostic",
+ "borderMuted": "borderMuted",
+ "success": "sage",
+ "error": "coral",
+ "warning": "warning",
+ "muted": "muted",
+ "dim": "subtle",
+ "text": "",
+ "thinkingText": "muted",
+ "selectedBg": "#1a1713",
+ "userMessageBg": "#dbcdb9",
+ "userMessageText": "",
+ "customMessageBg": "#f1eadf",
+ "customMessageText": "",
+ "customMessageLabel": "lucuma",
+ "toolPendingBg": "#eee7da",
+ "toolSuccessBg": "#cdcebb",
+ "toolErrorBg": "#d8c7ba",
+ "toolTitle": "lucuma",
+ "toolOutput": "",
+ "mdHeading": "lucuma",
+ "mdLink": "diagnostic",
+ "mdLinkUrl": "muted",
+ "mdCode": "sage",
+ "mdCodeBlock": "",
+ "mdCodeBlockBorder": "borderMuted",
+ "mdQuote": "cocoa",
+ "mdQuoteBorder": "borderMuted",
+ "mdHr": "borderMuted",
+ "mdListBullet": "lucuma",
+ "toolDiffAdded": "sage",
+ "toolDiffRemoved": "coral",
+ "toolDiffContext": "muted",
+ "syntaxComment": "comment",
+ "syntaxKeyword": "#815019",
+ "syntaxFunction": "#104b67",
+ "syntaxVariable": "#1a1713",
+ "syntaxString": "#466b41",
+ "syntaxNumber": "#96411e",
+ "syntaxType": "#5b4e86",
+ "syntaxOperator": "#784762",
+ "syntaxPunctuation": "#4c443a",
+ "thinkingOff": "comment",
+ "thinkingMinimal": "borderUi",
+ "thinkingLow": "#11455d",
+ "thinkingMedium": "diagnostic",
+ "thinkingHigh": "mauve",
+ "thinkingXhigh": "coral",
+ "bashMode": "lucuma"
+ }
+}
diff --git a/Pi/.pi/agent/themes/dreamcoder-light.json b/Pi/.pi/agent/themes/dreamcoder-light.json
new file mode 100644
index 0000000..cdb8bbf
--- /dev/null
+++ b/Pi/.pi/agent/themes/dreamcoder-light.json
@@ -0,0 +1,71 @@
+{
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
+ "name": "dreamcoder",
+ "vars": {
+ "cocoa": "#a7471c",
+ "lucuma": "#824f16",
+ "diagnostic": "#15516e",
+ "sage": "#3f6b35",
+ "mauve": "#7d3e64",
+ "coral": "#842f24",
+ "warning": "#654300",
+ "muted": "#3d3228",
+ "subtle": "#554635",
+ "comment": "#66523f",
+ "borderUi": "#66513b",
+ "borderMuted": "#8a7358"
+ },
+ "colors": {
+ "accent": "lucuma",
+ "border": "borderUi",
+ "borderAccent": "diagnostic",
+ "borderMuted": "borderMuted",
+ "success": "sage",
+ "error": "coral",
+ "warning": "warning",
+ "muted": "muted",
+ "dim": "subtle",
+ "text": "",
+ "thinkingText": "muted",
+ "selectedBg": "#17120d",
+ "userMessageBg": "#e1d1bc",
+ "userMessageText": "",
+ "customMessageBg": "#fff7ea",
+ "customMessageText": "",
+ "customMessageLabel": "lucuma",
+ "toolPendingBg": "#f8f0e2",
+ "toolSuccessBg": "#d3d3be",
+ "toolErrorBg": "#e1ccbf",
+ "toolTitle": "lucuma",
+ "toolOutput": "",
+ "mdHeading": "lucuma",
+ "mdLink": "diagnostic",
+ "mdLinkUrl": "muted",
+ "mdCode": "sage",
+ "mdCodeBlock": "",
+ "mdCodeBlockBorder": "borderMuted",
+ "mdQuote": "cocoa",
+ "mdQuoteBorder": "borderMuted",
+ "mdHr": "borderMuted",
+ "mdListBullet": "lucuma",
+ "toolDiffAdded": "sage",
+ "toolDiffRemoved": "coral",
+ "toolDiffContext": "muted",
+ "syntaxComment": "comment",
+ "syntaxKeyword": "#7c4c11",
+ "syntaxFunction": "#15516e",
+ "syntaxVariable": "#17120d",
+ "syntaxString": "#3f6b35",
+ "syntaxNumber": "#a7471c",
+ "syntaxType": "#57478b",
+ "syntaxOperator": "#7d3e64",
+ "syntaxPunctuation": "#3d3228",
+ "thinkingOff": "comment",
+ "thinkingMinimal": "borderUi",
+ "thinkingLow": "#154962",
+ "thinkingMedium": "diagnostic",
+ "thinkingHigh": "mauve",
+ "thinkingXhigh": "coral",
+ "bashMode": "lucuma"
+ }
+}
diff --git a/Pi/.pi/agent/themes/dreamcoder.json b/Pi/.pi/agent/themes/dreamcoder.json
new file mode 100644
index 0000000..175a1c6
--- /dev/null
+++ b/Pi/.pi/agent/themes/dreamcoder.json
@@ -0,0 +1,71 @@
+{
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
+ "name": "dreamcoder",
+ "vars": {
+ "cocoa": "#ce836c",
+ "lucuma": "#cdae7d",
+ "diagnostic": "#c7b2a2",
+ "sage": "#b8bf84",
+ "mauve": "#d98aa9",
+ "coral": "#e98272",
+ "warning": "#e8b866",
+ "muted": "#c7b9aa",
+ "subtle": "#aa927c",
+ "comment": "#9c826d",
+ "borderUi": "#806754",
+ "borderMuted": "#594d46"
+ },
+ "colors": {
+ "accent": "lucuma",
+ "border": "borderUi",
+ "borderAccent": "diagnostic",
+ "borderMuted": "borderMuted",
+ "success": "sage",
+ "error": "coral",
+ "warning": "warning",
+ "muted": "muted",
+ "dim": "subtle",
+ "text": "",
+ "thinkingText": "muted",
+ "selectedBg": "#373027",
+ "userMessageBg": "#322a20",
+ "userMessageText": "",
+ "customMessageBg": "#231c18",
+ "customMessageText": "",
+ "customMessageLabel": "lucuma",
+ "toolPendingBg": "#1b1612",
+ "toolSuccessBg": "#323023",
+ "toolErrorBg": "#36231e",
+ "toolTitle": "lucuma",
+ "toolOutput": "",
+ "mdHeading": "lucuma",
+ "mdLink": "diagnostic",
+ "mdLinkUrl": "muted",
+ "mdCode": "sage",
+ "mdCodeBlock": "",
+ "mdCodeBlockBorder": "borderMuted",
+ "mdQuote": "cocoa",
+ "mdQuoteBorder": "borderMuted",
+ "mdHr": "borderMuted",
+ "mdListBullet": "lucuma",
+ "toolDiffAdded": "sage",
+ "toolDiffRemoved": "coral",
+ "toolDiffContext": "muted",
+ "syntaxComment": "comment",
+ "syntaxKeyword": "#d3b078",
+ "syntaxFunction": "#c7b2a2",
+ "syntaxVariable": "#f0e7dc",
+ "syntaxString": "#b8bf84",
+ "syntaxNumber": "#ce836c",
+ "syntaxType": "#c9a8dc",
+ "syntaxOperator": "#d98aa9",
+ "syntaxPunctuation": "#c7b9aa",
+ "thinkingOff": "comment",
+ "thinkingMinimal": "borderUi",
+ "thinkingLow": "#ccb8a9",
+ "thinkingMedium": "diagnostic",
+ "thinkingHigh": "mauve",
+ "thinkingXhigh": "coral",
+ "bashMode": "lucuma"
+ }
+}
diff --git a/README.md b/README.md
index fac3d3e..bd0b85d 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@ Dreamcoder is not a neon rice. It is a workbench:
./scripts/dreamcoder preview # regenerate docs/dreamcoder-theme-preview.md
./scripts/dreamcoder auto # apply light/dark for current time
./scripts/dreamcoder light # force light mode
+./scripts/dreamcoder dusk # force dusk transitional mode
./scripts/dreamcoder dark # force dark mode
./scripts/set-wallpaper.sh # set wallpaper and refresh Dreamcoder
```
@@ -48,30 +49,83 @@ This reapplies hooks, restows modules, restarts the timer, refreshes the current
## Theme system
-- **Day**: warm light mode, paper-like surfaces, dark graphite text.
-- **Night**: softened graphite mode, warm text, lower glare than pure black.
+Dreamcoder themes are **generated from one canonical token set** (`themes/dreamcoder/tokens.json`) and rendered into **22 targets** — spanning terminals, editors, shell tooling, desktop UI, and even browser/note apps.
+
+- **Day** (`light`): warm paper surfaces, flat surface ladder, distinct semantic tokens.
+- **Dusk** (`dusk`): transitional warmth (default 16:00–18:00) before night mode.
+- **Night** (`dark`): Ember Noir — espresso/cacao glass with semi-transparent backgrounds, warm silver text, refined orange and maple red protagonists, gold support accent.
- **Wallpaper adaptive**: wallpaper colors can tint accents, but contrast guardrails stay mandatory.
-- **opencode/Codex**: only `dreamcoder.json` is generated and selected; stale opencode theme JSONs are removed by the sync script.
+- **UI affordances**: focus and meaningful borders use dedicated 3:1+ tokens; decorative borders stay subtle.
+- **Neovim glass blur**: the Neovim theme uses `none` backgrounds for main groups so terminal transparency and blur show through; panels and selections carry the autumn glass color.
+- **One source of truth**: `sync-dreamcoder-theme.py` reads `tokens.json`, renders all targets, and the `dreamcoder` CLI applies modes or regenerates selectively.
+
+### 22 theme targets
+
+| Target | Files | How it wires up |
+|--------|-------|-----------------|
+| **Kitty** | `kitty-dreamcoder-{mode}.conf` | `include` in kitty.conf |
+| **Ghostty** | `ghostty-dreamcoder-{mode}` | `theme = dreamcoder-{mode}` |
+| **Warp** | `Warp/.../Dreamcoder-{Mode}.yaml` | Warp theme picker |
+| **Hyprland** | `hyprland-{mode}.conf` | `source` from hyprland.conf |
+| **Waybar** | `waybar-{mode}.css` | `@import` in waybar style.css |
+| **Rofi** | `rofi-{mode}.rasi` | `@import` or `-theme` in rofi launch |
+| **Starship** | `starship-{mode}.toml` | `STARSHIP_CONFIG` env var |
+| **Antigravity** | `Antigravity/Dreamcoder-{Mode}.json` | Antigravity theme selector |
+| **opencode** | `opencode/dreamcoder.json` | `theme: "dreamcoder"` in opencode config |
+| **Codex CLI** | `Codex-CLI/Dreamcoder-{Mode}.tmTheme` | `theme = "Dreamcoder"` in codex config |
+| **PI CLI** | `Pi/.pi/agent/themes/dreamcoder-{mode}.json` | `theme: "dreamcoder"` in pi settings |
+| **Neovim** | `nvim-dreamcoder-{mode}.lua` | `require('dreamcoder')` in neovim config |
+| **Zsh-syntax-highlighting** | `zsh-syntax-highlighting-dreamcoder-{mode}.zsh` | `source` after zsh-syntax-highlighting plugin |
+| **LS_COLORS / eza** | `ls-colors-dreamcoder-{mode}.sh` | `source` in .zshrc or .bashrc |
+| **Bat** | `bat-dreamcoder-{mode}.sh` | Sets `BAT_THEME` env var; pairs with Codex CLI tmTheme |
+| **Delta (git diff)** | `delta-dreamcoder-{mode}.gitconfig` | `[include]` in `~/.config/git/config` |
+| **Fzf** | `fzf-dreamcoder-{mode}.sh` | `source` in .zshrc or .bashrc |
+| **Btop** | `btop-dreamcoder-{mode}.theme` | Place in `~/.config/btop/themes/` |
+| **Dunst** | `dunst-dreamcoder-{mode}.conf` | `[include]` in dunstrc |
+| **Firefox** | `firefox-dreamcoder-{mode}.css` | userChrome.css for Firefox customization |
+| **Obsidian** | `obsidian-dreamcoder-{mode}.css` | CSS snippet in Obsidian vault |
+| **Cava** | `cava-dreamcoder-{mode}.config` | `include` in `~/.config/cava/config` |
Important files:
```txt
-themes/dreamcoder/tokens.json # canonical design tokens: colors + guardrails
-themes/dreamcoder/tokens.schema.json # schema for the token contract
-scripts/dreamcoder # unified CLI entrypoint
-scripts/sync-dreamcoder-theme.py # generator for terminals/opencode/overlays
-scripts/theme-auto.sh # time-based light/dark orchestrator
-scripts/wallpaper-hook.sh # robust wallpaper + Dreamcoder refresh hook
-scripts/verify-theme-health.py # contrast and eye-comfort guardrails
-scripts/generate-theme-preview.py # Markdown palette/contrast preview
-Systemd/.config/systemd/user/* # day/night user timer
+themes/dreamcoder/tokens.json # canonical design tokens: colors + guardrails
+themes/dreamcoder/tokens.schema.json # schema for the token contract
+scripts/dreamcoder # unified CLI entrypoint
+scripts/sync-dreamcoder-theme.py # generator for core targets (terminals/opencode)
+scripts/dreamcoder_theme/renderers.py # shared rendering helpers
+scripts/dreamcoder_theme/renderers_core.py # core target renderers
+scripts/dreamcoder_theme/renderers_extra.py # extra target renderers (neovim, fzf, bat, delta, etc.)
+scripts/dreamcoder_theme/renderers_desktop.py # desktop-UI renderers
+scripts/dreamcoder_theme/settings.py # schema-based token settings
+scripts/dreamcoder_theme/sync.py # stale-file cleanup & install orchestration
+scripts/apply-theme-mode.sh # shared mode applier for all targets
+scripts/install-dreamcoder-hooks.sh # one-time hook installer (symlinks, includes)
+scripts/theme-auto.sh # time-based light/dusk/dark selector
+scripts/wallpaper-hook.sh # robust wallpaper + Dreamcoder refresh hook
+scripts/verify-theme-health.py # contrast and eye-comfort guardrails
+scripts/generate-theme-preview.py # Markdown palette/contrast preview
+Systemd/.config/systemd/user/* # day/night user timer
```
## Design-token architecture
Dreamcoder has one canonical palette contract: `themes/dreamcoder/tokens.json`. The generator reads these tokens first, then emits terminal, Codex/opencode, Waybar, Rofi, Hyprland, and prompt outputs. This keeps the system coherent after ML4W/Gentleman updates and prevents each app from drifting into a different palette.
-The token file includes hard guardrails: canonical opencode theme `dreamcoder`, no harsh pure black/white primary backgrounds, AA minimum token contrast, and AAA target contrast for main text.
+The token file includes hard guardrails: canonical opencode theme `dreamcoder`, no harsh pure black/white primary backgrounds, WCAG AA minimum token contrast, AAA target for main text, and APCA Lc checks for body and UI tokens.
+
+See the auditable palette gallery: [docs/dreamcoder-theme-preview.md](docs/dreamcoder-theme-preview.md).
+
+## Design rationale (light)
+
+Dreamcoder light themes are **identity-first**, not wallpaper-derived:
+
+- **Cocoa/lúcuma warmth**: parchment backgrounds (`#f6f1e8`), graphite-brown text, gold accent and terracotta secondary (hue-separated, not just darker gold).
+- **Flat surface ladder**: `surface0` → `surface2` moves in small luminance steps so panels layer without muddy mid-tones.
+- **Distinct semantics**: `comment` ≠ `subtle`, `focus` (teal) ≠ `diagnostic` (ink blue), `border_ui` ≠ `border_hi`.
+- **Dusk bridge**: warm intermediate palette for late afternoon before dark mode.
+
+Top light themes (Catppuccin Latte, Rosé Pine Dawn, etc.) share these patterns; Dreamcoder adds a named philosophy and token-level enforcement in `verify-theme-health.py`.
## Visual health policy
@@ -80,8 +134,10 @@ Dreamcoder prioritizes long-session comfort over trendy contrast extremes:
- no pure black or pure white as primary backgrounds;
- warm off-white light mode to reduce glare;
- softened dark mode instead of harsh black/white inversion;
-- AAA-level main text contrast where practical;
+- AAA-level main text contrast where practical (WCAG 2);
+- APCA Lc ≥ 75 for body tokens and ≥ 60 for UI affordances on light/dusk backgrounds;
- AA-or-better semantic token contrast for code, markdown, and diffs;
+- 3:1+ contrast for meaningful focus rings and UI boundaries;
- typography and spacing tuned for fewer micro-adjustments.
## Troubleshooting
@@ -97,6 +153,7 @@ Common fixes:
- **ML4W update overwrote hooks**: run `./scripts/dreamcoder repair`.
- **opencode theme looks old**: ensure `~/.config/opencode/tui.json` uses `dreamcoder`, then run `./scripts/dreamcoder auto`.
- **Theme feels too intense**: run `DREAMCODER_ADAPTIVE=0 ./scripts/dreamcoder auto` to disable wallpaper tinting for that run.
+- **Shell startup feels noisy**: set `DREAMCODER_FASTFETCH_ON_START=1` only when you want Fastfetch on new Zsh shells.
## Structure
@@ -107,8 +164,9 @@ Ghostty/ Ghostty config and Dreamcoder theme
Warp/ Warp Terminal themes
Fastfetch/ Fastfetch config
Codex-App/ Codex/opencode theme exports
-themes/ ML4W/Gentleman portable color overlays
-scripts/ install, repair, doctor, theme, wallpaper, verification
+themes/ Canonical theme tokens + generated snippets for all 22 targets
+themes/dreamcoder/ tokens.json, tokens.schema.json, nvim, fzf, delta, dunst, etc.
+scripts/ install, repair, doctor, theme sync, wallpaper, verification, preview
Systemd/ user timer/service for automatic day/night mode
```
diff --git a/Shell/.bash_profile b/Shell/.bash_profile
index ab1f844..4bb24fc 100644
--- a/Shell/.bash_profile
+++ b/Shell/.bash_profile
@@ -2,6 +2,10 @@ set -euo pipefail
export TERM="${TERM:-xterm-256color}"
BASHRC="${HOME}/.bashrc"
CARGO_ENV="${HOME}/.cargo/env"
+LOCAL_BIN="${HOME}/.local/bin"
+if [[ ":${PATH}:" != *":${LOCAL_BIN}:"* ]]; then
+ export PATH="${LOCAL_BIN}:${PATH}"
+fi
if [[ "${-}" == *i* ]]; then
[[ -f "${BASHRC}" ]] && source "${BASHRC}"
fi
diff --git a/Shell/.bashrc b/Shell/.bashrc
index 4998d02..6dfb674 100644
--- a/Shell/.bashrc
+++ b/Shell/.bashrc
@@ -1,5 +1,5 @@
-# shellcheck shell=bash
-# shellcheck disable=SC1090,SC1091
+# shellcheck shell=bash disable=SC1090,SC1091
+set -euo pipefail
[[ -z "${TERM:-}" || "${TERM}" == "dumb" ]] && export TERM="xterm-256color"
export COLORTERM="${COLORTERM:-truecolor}"
[[ "${-}" != *i* ]] && return
@@ -21,7 +21,10 @@ for group in core aliases functions; do
for file in "${SHELL_DIR}/${group}"/*.sh; do [[ -f "${file}" ]] && source "${file}"; done
done
command -v fzf >/dev/null && eval "$(fzf --bash)"
-command -v starship >/dev/null && eval "$(starship init bash)"
+if command -v starship >/dev/null; then
+ eval "$(starship init bash)"
+ enable_transience
+fi
command -v zoxide >/dev/null && eval "$(zoxide init bash)"
[[ -f "${HOME}/.cargo/env" ]] && source "${HOME}/.cargo/env"
unset PATH_DIRS dir group file BUN_COMPLETION SHELL_DIR
diff --git a/Shell/.config/starship-dark.toml b/Shell/.config/starship-dark.toml
index 5478fe4..4b06fcf 100644
--- a/Shell/.config/starship-dark.toml
+++ b/Shell/.config/starship-dark.toml
@@ -16,22 +16,22 @@ $cmd_duration
$character"""
[palettes.dreamcoder]
-bg = "#101216"
-text = "#e8e1d7"
-muted = "#b8b0a5"
-prompt_bg = "#19120c"
-prompt_surface0 = "#2a1d13"
-prompt_surface1 = "#402c18"
-prompt_surface2 = "#5a3a1f"
-prompt_text = "#f4e9dd"
-prompt_muted = "#d5c3b5"
-prompt_accent = "#e8aa67"
-prompt_accent_2 = "#be7d42"
-sage = "#a5b89c"
-diagnostic = "#92c7cd"
-lavender = "#c4b3df"
-mauve = "#d2a3c3"
-error = "#d78373"
+bg = "#15100d"
+text = "#f0e7dc"
+muted = "#c7b9aa"
+prompt_bg = "#19110c"
+prompt_surface0 = "#2c1c14"
+prompt_surface1 = "#52301c"
+prompt_surface2 = "#744524"
+prompt_text = "#fff0df"
+prompt_muted = "#dcc3aa"
+prompt_accent = "#e6a15c"
+prompt_accent_2 = "#d66f50"
+sage = "#b8bf84"
+diagnostic = "#d2a268"
+lavender = "#c9a8dc"
+mauve = "#d98aa9"
+error = "#e98272"
[username]
show_always = true
diff --git a/Shell/.config/starship-dusk.toml b/Shell/.config/starship-dusk.toml
new file mode 100644
index 0000000..e1c45df
--- /dev/null
+++ b/Shell/.config/starship-dusk.toml
@@ -0,0 +1,117 @@
+add_newline = true
+palette = "dreamcoder"
+
+format = """
+[](fg:prompt_surface0)\
+$username\
+[](bg:prompt_surface1 fg:prompt_surface0)\
+$directory\
+[](bg:prompt_accent fg:prompt_surface1)\
+$git_branch\
+$git_status\
+[](fg:prompt_accent)\
+$fill\
+$hostname\
+$cmd_duration
+$character"""
+
+[palettes.dreamcoder]
+bg = "#ebe4d6"
+text = "#1a1713"
+muted = "#4c443a"
+prompt_bg = "#ebe4d6"
+prompt_surface0 = "#f2e6d4"
+prompt_surface1 = "#d3bc98"
+prompt_surface2 = "#b88958"
+prompt_text = "#261c14"
+prompt_muted = "#574939"
+prompt_accent = "#965f25"
+prompt_accent_2 = "#96411e"
+sage = "#466b41"
+diagnostic = "#104b67"
+lavender = "#5b4e86"
+mauve = "#784762"
+error = "#773126"
+
+[username]
+show_always = true
+style_user = "bg:prompt_surface0 fg:prompt_text bold"
+style_root = "bg:prompt_surface0 fg:error bold"
+format = "[ $user ]($style)"
+
+[hostname]
+ssh_only = true
+style = "fg:prompt_muted bold"
+format = "[ $hostname ]($style) "
+
+[directory]
+style = "bg:prompt_surface1 fg:prompt_text bold"
+format = "[ $path ]($style)"
+truncation_length = 2
+truncate_to_repo = true
+
+[git_branch]
+symbol = ""
+style = "bg:prompt_accent fg:prompt_bg bold"
+format = "[ $symbol $branch ]($style)"
+
+[git_status]
+style = "bg:prompt_accent fg:prompt_bg bold"
+format = "[$all_status$ahead_behind ]($style)"
+conflicted = "${count} "
+ahead = "⇡${count} "
+behind = "⇣${count} "
+diverged = "⇕⇡${ahead_count}⇣${behind_count} "
+untracked = "?${count} "
+stashed = "${count} "
+modified = "~${count} "
+staged = "+${count} "
+renamed = "»${count} "
+deleted = "✘${count} "
+
+[fill]
+symbol = " "
+
+[bun]
+symbol = ""
+style = "fg:prompt_accent bold"
+format = "[ $symbol $version]($style)"
+
+[nodejs]
+symbol = ""
+style = "fg:sage bold"
+format = "[ $symbol $version]($style)"
+
+[python]
+symbol = ""
+style = "fg:diagnostic bold"
+format = "[ $symbol $version]($style)"
+
+[golang]
+symbol = ""
+style = "fg:lavender bold"
+format = "[ $symbol $version]($style)"
+
+[rust]
+symbol = ""
+style = "fg:mauve bold"
+format = "[ $symbol $version]($style)"
+
+[docker_context]
+symbol = ""
+style = "fg:diagnostic bold"
+format = "[ $symbol $context]($style)"
+only_with_files = true
+
+[cmd_duration]
+min_time = 2500
+style = "fg:prompt_muted"
+format = "[ $duration ]($style) "
+
+[time]
+disabled = true
+
+[character]
+success_symbol = "[❯](bold fg:prompt_accent)"
+error_symbol = "[❯](bold fg:error)"
+vimcmd_symbol = "[❮](bold fg:sage)"
diff --git a/Shell/.config/starship-light.toml b/Shell/.config/starship-light.toml
index e6ac181..da437af 100644
--- a/Shell/.config/starship-light.toml
+++ b/Shell/.config/starship-light.toml
@@ -16,22 +16,22 @@ $cmd_duration
$character"""
[palettes.dreamcoder]
-bg = "#f6f1e8"
-text = "#15130f"
-muted = "#4a463f"
-prompt_bg = "#f6f1e8"
-prompt_surface0 = "#fbefd9"
-prompt_surface1 = "#dcc094"
-prompt_surface2 = "#bd905a"
-prompt_text = "#241911"
-prompt_muted = "#63503e"
-prompt_accent = "#985d2b"
-prompt_accent_2 = "#7a5028"
-sage = "#496f45"
-diagnostic = "#1e6871"
-lavender = "#665593"
-mauve = "#844c71"
-error = "#9b4b40"
+bg = "#f3eadc"
+text = "#17120d"
+muted = "#3d3228"
+prompt_bg = "#f3eadc"
+prompt_surface0 = "#fff0d8"
+prompt_surface1 = "#d6ac72"
+prompt_surface2 = "#a96d31"
+prompt_text = "#20150c"
+prompt_muted = "#53402e"
+prompt_accent = "#8a5520"
+prompt_accent_2 = "#a7471c"
+sage = "#3f6b35"
+diagnostic = "#15516e"
+lavender = "#57478b"
+mauve = "#7d3e64"
+error = "#842f24"
[username]
show_always = true
diff --git a/Shell/.config/starship.toml b/Shell/.config/starship.toml
index d6bf517..746a78f 100644
--- a/Shell/.config/starship.toml
+++ b/Shell/.config/starship.toml
@@ -16,22 +16,22 @@ $cmd_duration
$character"""
[palettes.dreamcoder]
-bg = "#f8f2ea"
-text = "#15130f"
-muted = "#4a463f"
-prompt_bg = "#f6f1e8"
-prompt_surface0 = "#fbefd9"
-prompt_surface1 = "#dcc094"
-prompt_surface2 = "#bd905a"
-prompt_text = "#241911"
-prompt_muted = "#63503e"
-prompt_accent = "#925b25"
-prompt_accent_2 = "#78522d"
-sage = "#496f45"
-diagnostic = "#36665a"
-lavender = "#665593"
-mauve = "#844c71"
-error = "#9b4b40"
+bg = "#14110e"
+text = "#f0e7dc"
+muted = "#c7b9aa"
+prompt_bg = "#19110c"
+prompt_surface0 = "#2c1c14"
+prompt_surface1 = "#52301c"
+prompt_surface2 = "#744524"
+prompt_text = "#fff0df"
+prompt_muted = "#dcc3aa"
+prompt_accent = "#cdae7d"
+prompt_accent_2 = "#ce836c"
+sage = "#b8bf84"
+diagnostic = "#c7b2a2"
+lavender = "#c9a8dc"
+mauve = "#d98aa9"
+error = "#e98272"
[username]
show_always = true
diff --git a/Shell/.zshenv b/Shell/.zshenv
index 31fc611..217e1fa 100644
--- a/Shell/.zshenv
+++ b/Shell/.zshenv
@@ -90,9 +90,15 @@ _add_to_path "$HOME/.npm-global/bin"
export PNPM_HOME="$HOME/.local/share/pnpm"
_add_to_path "$PNPM_HOME"
-# NVM (lazy-loaded via shell config, but path here as fallback)
+# NVM (resolved statically to avoid NVM startup subshell overhead)
export NVM_DIR="$HOME/.nvm"
-[[ -d "$NVM_DIR" ]] && _add_to_path "$NVM_DIR/versions/node/$(nvm version default)/bin"
+if [[ -d "$NVM_DIR/versions/node" ]]; then
+ local node_bins
+ node_bins=("$NVM_DIR"/versions/node/*/bin)
+ if [[ -d "${node_bins[-1]:-}" ]]; then
+ _add_to_path "${node_bins[-1]}"
+ fi
+fi
# Cleanup function
unset -f _add_to_path
diff --git a/Shell/.zshrc b/Shell/.zshrc
index d53c59b..4a62237 100644
--- a/Shell/.zshrc
+++ b/Shell/.zshrc
@@ -1,68 +1,42 @@
-# Enable Powerlevel10k instant prompt only when Starship is unavailable
-if ! command -v starship >/dev/null 2>&1 && [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
- source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
-fi
-
-export ZSH="$HOME/.oh-my-zsh"
-export EDITOR="nvim"
-export VISUAL="nvim"
-
-# PATH - include all important bins
-export PATH="$HOME/.local/bin:$HOME/.opencode/bin:$HOME/.cargo/bin:$HOME/.volta/bin:$HOME/.bun/bin:$HOME/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/usr/local/bin:$HOME/.config:$PATH"
-
-# LS_COLORS - gruvbox inspired
-export LS_COLORS="di=38;5;67:ow=48;5;60:ex=38;5;132:ln=38;5;144:*.tar=38;5;180:*.zip=38;5;180:*.jpg=38;5;175:*.png=38;5;175:*.mp3=38;5;175:*.wav=38;5;175:*.txt=38;5;223:*.sh=38;5;132"
-
-# bun
-[[ -s "$HOME/.bun/_bun" ]] && source "$HOME/.bun/_bun"
-export BUN_INSTALL="$HOME/.bun"
-export PATH="$BUN_INSTALL/bin:$PATH"
-
-# Zsh plugins
-plugins=(
- git
- zsh-autosuggestions
- zsh-syntax-highlighting
- archlinux
- sudo
- web-search
-)
-
-source "${ZSH}/oh-my-zsh.sh"
-
-# History
-HISTSIZE=50000
-SAVEHIST=50000
-setopt HIST_IGNORE_DUPS SHARE_HISTORY AUTO_CD
-
-# Load custom shell config
-_shell_dir="${XDG_CONFIG_HOME:-$HOME/.config}/shell"
-for d in core aliases functions; do
- for f in "$_shell_dir/$d"/*.sh(N); do source "$f"; done
+# Dreamcoder interactive Zsh ergonomics.
+set -euo pipefail
+[[ -o interactive ]] || return
+
+export EDITOR="nvim" VISUAL="nvim" COLORTERM="${COLORTERM:-truecolor}"
+typeset -U path PATH
+path=("${HOME}/.local/bin" "${HOME}/.opencode/bin" "${HOME}/.cargo/bin" "${HOME}/.volta/bin" "${HOME}/.bun/bin" "${HOME}/.nix-profile/bin" "${HOME}/.config" "${path[@]}")
+BUN_INSTALL="${HOME}/.bun"
+[[ -s "${BUN_INSTALL}/_bun" ]] && source "${BUN_INSTALL}/_bun"
+[[ -d "${BUN_INSTALL}/bin" ]] && path=("${BUN_INSTALL}/bin" "${path[@]}")
+export BUN_INSTALL
+
+# ── Dreamcoder Theme Hooks ─────────────────────────────────────
+# Source shell-level theme snippets based on current mode.
+_dc_mode="${DREAMCODER_THEME_MODE:-dark}"
+_dc_theme_dir="${DREAMCODER_DOTS_DIR:-${HOME}/Documents/PROYECTOS/dreamcoder-dots}/themes/dreamcoder"
+
+[[ -f "${_dc_theme_dir}/ls-colors-dreamcoder-${_dc_mode}.sh" ]] && source "${_dc_theme_dir}/ls-colors-dreamcoder-${_dc_mode}.sh"
+[[ -f "${_dc_theme_dir}/bat-dreamcoder-${_dc_mode}.sh" ]] && source "${_dc_theme_dir}/bat-dreamcoder-${_dc_mode}.sh"
+[[ -f "${_dc_theme_dir}/fzf-dreamcoder-${_dc_mode}.sh" ]] && source "${_dc_theme_dir}/fzf-dreamcoder-${_dc_mode}.sh"
+# zsh-syntax-highlighting is sourced AFTER the plugin loads (below)
+
+ZSH="${ZSH:-${HOME}/.oh-my-zsh}"
+plugins=(git sudo web-search)
+for plugin in zsh-autosuggestions zsh-syntax-highlighting archlinux; do
+ [[ -d "${ZSH_CUSTOM:-${ZSH}/custom}/plugins/${plugin}" || -d "${ZSH}/plugins/${plugin}" ]] && plugins+=("${plugin}")
done
-
-# Integrations
-eval "$(fzf --zsh)"
-command -v zoxide &>/dev/null && eval "$(zoxide init zsh)"
-
-# Prompt: Dreamcoder with Powerlevel10k fallback
-if command -v starship >/dev/null 2>&1; then
- eval "$(starship init zsh)"
-elif [[ -f "${HOME}/.p10k.zsh" ]]; then
- source "${HOME}/.p10k.zsh"
-fi
-
-# Cargo
-[[ -f "$HOME/.cargo/env" ]] && source "$HOME/.cargo/env"
-
-# Fastfetch with random logo on shell start
-command -v fastfetch &>/dev/null && fastfetch
-
-# Start tmux/zellij if needed
-start_if_needed() {
- WM_VAR="/$TMUX"
- WM_CMD="tmux"
- if [[ $- == *i* ]] && [[ -z "${WM_VAR#/}" ]] && [[ -t 1 ]]; then
- exec $WM_CMD
- fi
-}
+[[ -f "${ZSH}/oh-my-zsh.sh" ]] && source "${ZSH}/oh-my-zsh.sh"
+
+# Source zsh-syntax-highlighting theme (must be AFTER plugin loads)
+[[ -f "${_dc_theme_dir}/zsh-syntax-highlighting-dreamcoder-${_dc_mode}.zsh" ]] && source "${_dc_theme_dir}/zsh-syntax-highlighting-dreamcoder-${_dc_mode}.zsh"
+
+HISTSIZE=50000; SAVEHIST=50000; setopt HIST_IGNORE_DUPS SHARE_HISTORY AUTO_CD
+shell_dir="${XDG_CONFIG_HOME:-${HOME}/.config}/shell"
+for group in core aliases functions; do for file in "${shell_dir}/${group}"/*.sh(N); do [[ -f "${file}" ]] && source "${file}"; done; done
+command -v fzf >/dev/null 2>&1 && eval "$(fzf --zsh)"
+command -v zoxide >/dev/null 2>&1 && eval "$(zoxide init zsh)"
+command -v starship >/dev/null 2>&1 && eval "$(starship init zsh)" && functions -q enable_transience && enable_transience
+command -v starship >/dev/null 2>&1 || { [[ -f "${HOME}/.p10k.zsh" ]] && source "${HOME}/.p10k.zsh"; }
+[[ -f "${HOME}/.cargo/env" ]] && source "${HOME}/.cargo/env"
+[[ "${DREAMCODER_FASTFETCH_ON_START:-0}" == "1" ]] && command -v fastfetch >/dev/null 2>&1 && fastfetch
+unset _dc_mode _dc_theme_dir shell_dir group file plugin
diff --git a/Systemd/.config/systemd/user/dreamcoder-theme-auto.timer b/Systemd/.config/systemd/user/dreamcoder-theme-auto.timer
index d2c5f0a..eabf8b4 100644
--- a/Systemd/.config/systemd/user/dreamcoder-theme-auto.timer
+++ b/Systemd/.config/systemd/user/dreamcoder-theme-auto.timer
@@ -4,6 +4,7 @@ Description=Run Dreamcoder day/night theme automation
[Timer]
OnBootSec=2min
OnCalendar=*-*-* 07:00:00
+OnCalendar=*-*-* 16:00:00
OnCalendar=*-*-* 18:00:00
Persistent=true
AccuracySec=1min
diff --git a/Warp/.local/share/warp-terminal/themes/Dreamcoder-Dark.yaml b/Warp/.local/share/warp-terminal/themes/Dreamcoder-Dark.yaml
index 0132bb4..1f9e1dd 100644
--- a/Warp/.local/share/warp-terminal/themes/Dreamcoder-Dark.yaml
+++ b/Warp/.local/share/warp-terminal/themes/Dreamcoder-Dark.yaml
@@ -1,25 +1,25 @@
-name: Dreamcoder Dark
-accent: '#d9ad67'
-cursor: '#d9ad67'
-background: '#101216'
-foreground: '#e8e1d7'
+name: Dreamcoder Ember Noir
+accent: '#e6a15c'
+cursor: '#e6a15c'
+background: '#15100d'
+foreground: '#f0e7dc'
details: darker
terminal_colors:
normal:
- black: '#1c222b'
- red: '#d78373'
- green: '#a5b89c'
- yellow: '#d9ad67'
- blue: '#92c7cd'
- magenta: '#d2a3c3'
- cyan: '#c4b3df'
- white: '#b8b0a5'
+ black: '#86827e'
+ red: '#e98272'
+ green: '#b8bf84'
+ yellow: '#e6a15c'
+ blue: '#d2a268'
+ magenta: '#d98aa9'
+ cyan: '#c9a8dc'
+ white: '#c7b9aa'
bright:
- black: '#928a80'
+ black: '#aa927c'
red: '#e9a092'
green: '#75a579'
- yellow: '#ddb36b'
+ yellow: '#e8b866'
blue: '#579ba4'
magenta: '#846fc5'
cyan: '#72b6bd'
- white: '#e8e1d7'
+ white: '#f0e7dc'
diff --git a/Warp/.local/share/warp-terminal/themes/Dreamcoder-Dusk.yaml b/Warp/.local/share/warp-terminal/themes/Dreamcoder-Dusk.yaml
new file mode 100644
index 0000000..555774a
--- /dev/null
+++ b/Warp/.local/share/warp-terminal/themes/Dreamcoder-Dusk.yaml
@@ -0,0 +1,25 @@
+name: Dreamcoder Dusk
+accent: '#8a5520'
+cursor: '#8a5520'
+background: '#ebe4d6'
+foreground: '#1a1713'
+details: lighter
+terminal_colors:
+ normal:
+ black: '#595753'
+ red: '#773126'
+ green: '#466b41'
+ yellow: '#8a5520'
+ blue: '#104b67'
+ magenta: '#784762'
+ cyan: '#5b4e86'
+ white: '#4c443a'
+ bright:
+ black: '#5a4f43'
+ red: '#815850'
+ green: '#415b42'
+ yellow: '#604000'
+ blue: '#3a686e'
+ magenta: '#6c5ba2'
+ cyan: '#3e6468'
+ white: '#1a1713'
diff --git a/Warp/.local/share/warp-terminal/themes/Dreamcoder-Light.yaml b/Warp/.local/share/warp-terminal/themes/Dreamcoder-Light.yaml
index b80346b..59a3c9f 100644
--- a/Warp/.local/share/warp-terminal/themes/Dreamcoder-Light.yaml
+++ b/Warp/.local/share/warp-terminal/themes/Dreamcoder-Light.yaml
@@ -1,25 +1,25 @@
name: Dreamcoder Light
-accent: '#855719'
-cursor: '#855719'
-background: '#f6f1e8'
-foreground: '#15130f'
+accent: '#824f16'
+cursor: '#824f16'
+background: '#f3eadc'
+foreground: '#17120d'
details: lighter
terminal_colors:
normal:
- black: '#fbf8f1'
- red: '#9b4b40'
- green: '#496f45'
- yellow: '#855719'
- blue: '#1e6871'
- magenta: '#844c71'
- cyan: '#665593'
- white: '#4a463f'
+ black: '#5e5c57'
+ red: '#842f24'
+ green: '#3f6b35'
+ yellow: '#824f16'
+ blue: '#15516e'
+ magenta: '#7d3e64'
+ cyan: '#57478b'
+ white: '#3d3228'
bright:
- black: '#6b6458'
- red: '#e9a092'
- green: '#75a579'
- yellow: '#765019'
- blue: '#579ba4'
- magenta: '#846fc5'
- cyan: '#72b6bd'
- white: '#15130f'
+ black: '#554635'
+ red: '#815850'
+ green: '#4f6f51'
+ yellow: '#654300'
+ blue: '#3a686e'
+ magenta: '#6c5ba2'
+ cyan: '#3e6468'
+ white: '#17120d'
diff --git a/docs/dreamcoder-theme-preview.md b/docs/dreamcoder-theme-preview.md
index f859e41..d08ad7e 100644
--- a/docs/dreamcoder-theme-preview.md
+++ b/docs/dreamcoder-theme-preview.md
@@ -2,88 +2,230 @@
Generated from `themes/dreamcoder/tokens.json`.
+## Design rationale
+
+Dreamcoder light themes follow a **cocoa/lúcuma** identity: warm parchment backgrounds, graphite-brown text, and restrained accents. Unlike generic light themes that jump from white to mid-gray surfaces, Dreamcoder uses a **flat surface ladder** (~10 luminance points between steps) so panels feel layered without looking muddy.
+
+Dreamcoder dark uses an **Ember Noir** identity: espresso/cacao glass surfaces, warm silver text, refined orange and maple red protagonists, and gold as the support accent. The opencode theme keeps the main background as `none` so the terminal's semi-transparent background remains visible while panels and selections carry the autumn glass color.
+
+Semantic tokens are intentionally distinct:
+
+- `comment` is softer and lower-chroma than `subtle` (syntax vs UI chrome).
+- Dark `accent` (refined ember orange), `accent_2` (maple red), `error` (soft coral red), and `warning` (lúcuma gold) form the orange/red/gold signature.
+- `focus` follows the orange protagonist instead of a separate cyan ring; `diagnostic` stays warm amber so the palette remains autumnal.
+- **Dusk** bridges daytime light and night dark for late-afternoon sessions on Arch.
+
## Palette
-### Dreamcoder Dark
+### Dreamcoder Ember Noir
| Role | Color |
| --- | --- |
-| `bg` | `#101216` |
-| `bg_soft` | `#161922` |
-| `surface0` | `#1c222b` |
-| `surface1` | `#282e38` |
-| `text` | `#e8e1d7` |
-| `muted` | `#b8b0a5` |
-| `accent` | `#d9ad67` |
-| `accent_2` | `#b87a48` |
-| `diagnostic` | `#92c7cd` |
-| `sage` | `#a5b89c` |
-| `lavender` | `#c4b3df` |
-| `mauve` | `#d2a3c3` |
-| `error` | `#d78373` |
-| `warning` | `#ddb36b` |
+| `bg` | `#15100d` |
+| `bg_soft` | `#1d1613` |
+| `surface0` | `#241b16` |
+| `surface1` | `#30231c` |
+| `surface2` | `#3e2c22` |
+| `text` | `#f0e7dc` |
+| `muted` | `#c7b9aa` |
+| `subtle` | `#aa927c` |
+| `comment` | `#9c826d` |
+| `accent` | `#e6a15c` |
+| `accent_2` | `#d66f50` |
+| `diagnostic` | `#d2a268` |
+| `sage` | `#b8bf84` |
+| `lavender` | `#c9a8dc` |
+| `mauve` | `#d98aa9` |
+| `error` | `#e98272` |
+| `warning` | `#e8b866` |
+| `border` | `#49362c` |
+| `border_ui` | `#806754` |
+| `border_hi` | `#d8c1a5` |
+| `focus` | `#e6a15c` |
### Dreamcoder Light
| Role | Color |
| --- | --- |
-| `bg` | `#f6f1e8` |
-| `bg_soft` | `#ece4d8` |
-| `surface0` | `#fbf8f1` |
-| `surface1` | `#ded3c4` |
-| `text` | `#15130f` |
-| `muted` | `#4a463f` |
-| `accent` | `#855719` |
-| `accent_2` | `#7f4a27` |
-| `diagnostic` | `#1e6871` |
-| `sage` | `#496f45` |
-| `lavender` | `#665593` |
-| `mauve` | `#844c71` |
-| `error` | `#9b4b40` |
-| `warning` | `#765019` |
+| `bg` | `#f3eadc` |
+| `bg_soft` | `#e6d7c4` |
+| `surface0` | `#fff7ea` |
+| `surface1` | `#decbb1` |
+| `surface2` | `#c8ad89` |
+| `text` | `#17120d` |
+| `muted` | `#3d3228` |
+| `subtle` | `#554635` |
+| `comment` | `#66523f` |
+| `accent` | `#824f16` |
+| `accent_2` | `#a7471c` |
+| `diagnostic` | `#15516e` |
+| `sage` | `#3f6b35` |
+| `lavender` | `#57478b` |
+| `mauve` | `#7d3e64` |
+| `error` | `#842f24` |
+| `warning` | `#654300` |
+| `border` | `#8a7358` |
+| `border_ui` | `#66513b` |
+| `border_hi` | `#3e2f20` |
+| `focus` | `#0f6570` |
+
+### Dreamcoder Dusk
+
+| Role | Color |
+| --- | --- |
+| `bg` | `#ebe4d6` |
+| `bg_soft` | `#dfd5c4` |
+| `surface0` | `#f1eadf` |
+| `surface1` | `#d8cbb8` |
+| `surface2` | `#c6b6a0` |
+| `text` | `#1a1713` |
+| `muted` | `#4c443a` |
+| `subtle` | `#5a4f43` |
+| `comment` | `#615548` |
+| `accent` | `#8a5520` |
+| `accent_2` | `#96411e` |
+| `diagnostic` | `#104b67` |
+| `sage` | `#466b41` |
+| `lavender` | `#5b4e86` |
+| `mauve` | `#784762` |
+| `error` | `#773126` |
+| `warning` | `#604000` |
+| `border` | `#a7947a` |
+| `border_ui` | `#665845` |
+| `border_hi` | `#4a3f32` |
+| `focus` | `#216a73` |
## Contrast audit
-### Dreamcoder Dark contrast
+### Dreamcoder Ember Noir contrast (WCAG 2)
| Token | Ratio vs bg | Target |
| --- | ---: | --- |
-| `text` | 14.45:1 | AAA |
-| `muted` | 8.74:1 | AA |
-| `comment` | 5.14:1 | AA |
-| `accent` | 9.04:1 | AA |
-| `accent_2` | 5.28:1 | AA |
-| `diagnostic` | 10.06:1 | AA |
-| `sage` | 8.87:1 | AA |
-| `error` | 6.59:1 | AA |
-| `warning` | 9.58:1 | AA |
-
-### Dreamcoder Light contrast
+| `text` | 15.44:1 | AAA |
+| `muted` | 9.85:1 | AA |
+| `comment` | 5.24:1 | AA |
+| `accent` | 8.65:1 | AA |
+| `accent_2` | 5.62:1 | AA |
+| `diagnostic` | 8.19:1 | AA |
+| `sage` | 9.74:1 | AA |
+| `error` | 7.11:1 | AA |
+| `warning` | 10.32:1 | AA |
+
+### Dreamcoder Ember Noir APCA
+
+| Token | Lc vs bg | Target |
+| --- | ---: | --- |
+| `text` | 95.0 | ≥75 (body) |
+| `muted` | 70.9 | ≥75 (FAIL) |
+| `comment` | 45.2 | ≥75 (FAIL) |
+| `accent` | 64.9 | ≥75 (FAIL) |
+| `accent_2` | 47.7 | ≥75 (FAIL) |
+| `diagnostic` | 62.5 | ≥75 (FAIL) |
+| `sage` | 70.3 | ≥75 (FAIL) |
+| `error` | 56.6 | ≥75 (FAIL) |
+| `warning` | 73.1 | ≥75 (FAIL) |
+| `border_ui` | 33.1 | ≥60 (FAIL) |
+| `focus` | 64.9 | ≥60 (UI) |
+
+### Dreamcoder Ember Noir UI affordance contrast
+
+| Token | Ratio vs bg | Target |
+| --- | ---: | --- |
+| `border_ui` | 3.59:1 | PASS |
+| `border_hi` | 10.88:1 | PASS |
+| `focus` | 8.65:1 | PASS |
+
+### Dreamcoder Light contrast (WCAG 2)
+
+| Token | Ratio vs bg | Target |
+| --- | ---: | --- |
+| `text` | 15.60:1 | AAA |
+| `muted` | 10.45:1 | AA |
+| `comment` | 6.19:1 | AA |
+| `accent` | 5.72:1 | AA |
+| `accent_2` | 4.95:1 | AA |
+| `diagnostic` | 7.24:1 | AA |
+| `sage` | 5.23:1 | AA |
+| `error` | 7.30:1 | AA |
+| `warning` | 7.48:1 | AA |
+
+### Dreamcoder Light APCA
+
+| Token | Lc vs bg | Target |
+| --- | ---: | --- |
+| `text` | 96.0 | ≥75 (body) |
+| `muted` | 85.5 | ≥75 (body) |
+| `comment` | 72.8 | ≥75 (FAIL) |
+| `accent` | 70.7 | ≥75 (FAIL) |
+| `accent_2` | 66.7 | ≥75 (FAIL) |
+| `diagnostic` | 76.7 | ≥75 (body) |
+| `sage` | 68.3 | ≥75 (FAIL) |
+| `error` | 76.9 | ≥75 (body) |
+| `warning` | 77.5 | ≥75 (body) |
+| `border_ui` | 73.1 | ≥60 (UI) |
+| `focus` | 70.4 | ≥60 (UI) |
+
+### Dreamcoder Light UI affordance contrast
+
+| Token | Ratio vs bg | Target |
+| --- | ---: | --- |
+| `border_ui` | 6.28:1 | PASS |
+| `border_hi` | 10.79:1 | PASS |
+| `focus` | 5.65:1 | PASS |
+
+### Dreamcoder Dusk contrast (WCAG 2)
+
+| Token | Ratio vs bg | Target |
+| --- | ---: | --- |
+| `text` | 14.12:1 | AAA |
+| `muted` | 7.56:1 | AA |
+| `comment` | 5.72:1 | AA |
+| `accent` | 4.88:1 | AA |
+| `accent_2` | 5.40:1 | AA |
+| `diagnostic` | 7.46:1 | AA |
+| `sage` | 4.83:1 | AA |
+| `error` | 7.37:1 | AA |
+| `warning` | 7.44:1 | AA |
+
+### Dreamcoder Dusk APCA
+
+| Token | Lc vs bg | Target |
+| --- | ---: | --- |
+| `text` | 91.1 | ≥75 (body) |
+| `muted` | 75.7 | ≥75 (body) |
+| `comment` | 68.7 | ≥75 (FAIL) |
+| `accent` | 64.4 | ≥75 (FAIL) |
+| `accent_2` | 67.2 | ≥75 (FAIL) |
+| `diagnostic` | 75.3 | ≥75 (body) |
+| `sage` | 64.1 | ≥75 (FAIL) |
+| `error` | 75.0 | ≥75 (body) |
+| `warning` | 75.3 | ≥75 (body) |
+| `border_ui` | 67.4 | ≥60 (UI) |
+| `focus` | 64.6 | ≥60 (UI) |
+
+### Dreamcoder Dusk UI affordance contrast
| Token | Ratio vs bg | Target |
| --- | ---: | --- |
-| `text` | 16.49:1 | AAA |
-| `muted` | 8.34:1 | AA |
-| `comment` | 5.33:1 | AA |
-| `accent` | 5.53:1 | AA |
-| `accent_2` | 6.41:1 | AA |
-| `diagnostic` | 5.70:1 | AA |
-| `sage` | 5.12:1 | AA |
-| `error` | 5.37:1 | AA |
-| `warning` | 6.37:1 | AA |
+| `border_ui` | 5.45:1 | PASS |
+| `border_hi` | 8.10:1 | PASS |
+| `focus` | 4.92:1 | PASS |
## Usage
```bash
./scripts/dreamcoder auto
./scripts/dreamcoder light
+./scripts/dreamcoder dusk
./scripts/dreamcoder dark
./scripts/dreamcoder verify
+./scripts/dreamcoder preview
```
## Design notes
- Main backgrounds avoid pure black and pure white.
-- Main text targets AAA contrast for long coding sessions.
-- Cocoa/Lúcuma accents are identity colors; cyan is diagnostic, not decoration.
-- opencode uses one canonical theme: `dreamcoder`.
+- Main text targets AAA (WCAG 2) and APCA Lc ≥ 75 for long coding sessions.
+- Cocoa/Lúcuma accents are identity colors in light/dusk; Ember Noir uses refined orange, maple red, soft coral, and gold for dark-mode personality.
+- UI affordance tokens (`border_ui`, `border_hi`, `focus`) target at least 3:1 against the main background.
+- opencode uses one canonical theme: `dreamcoder`; its main `background` is generated as `none` for terminal transparency.
diff --git a/docs/superpowers/plans/2026-05-24-pi-cli-dreamcoder-theme.md b/docs/superpowers/plans/2026-05-24-pi-cli-dreamcoder-theme.md
new file mode 100644
index 0000000..fca0c1a
--- /dev/null
+++ b/docs/superpowers/plans/2026-05-24-pi-cli-dreamcoder-theme.md
@@ -0,0 +1,40 @@
+# PI CLI Dreamcoder Theme Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Generate and select the Dreamcoder theme globally for PI CLI.
+
+**Architecture:** Extend `scripts/sync-dreamcoder-theme.py` with a PI theme writer that maps canonical Dreamcoder tokens to PI CLI's 51-token JSON schema. The existing mode pipeline remains the orchestrator, so `./scripts/dreamcoder light/dusk/dark/auto` regenerates the active PI theme.
+
+**Tech Stack:** Bash, Python 3 standard library, PI CLI theme JSON schema.
+
+---
+
+### Task 1: Add tests for PI theme generation
+
+**Files:**
+- Create: `tests/test_pi_theme_generation.py`
+
+- [x] Write a test that runs the sync script in a temporary home/config environment and asserts `dreamcoder.json` exists with 51 PI tokens.
+- [x] Write a test that asserts `settings.json` preserves existing keys and sets `theme` to `dreamcoder`.
+- [x] Run the tests and verify they fail before implementation.
+
+### Task 2: Implement PI theme generation
+
+**Files:**
+- Modify: `scripts/sync-dreamcoder-theme.py`
+
+- [x] Add PI output path variables.
+- [x] Add `ensure_pi_theme_settings()`.
+- [x] Add `pi_theme_content()` mapping Dreamcoder tokens to PI CLI's required schema.
+- [x] Wire PI output into the sync `changed` map and repo variants.
+
+### Task 3: Update verification and docs
+
+**Files:**
+- Modify: `scripts/verify.sh`
+- Modify: `README.md`
+
+- [x] Add global PI theme/settings validation to `verify.sh`.
+- [x] Document PI CLI as a generated/selected theme target.
+- [x] Run tests, compile, theme sync, and verification.
diff --git a/docs/superpowers/specs/2026-05-24-pi-cli-dreamcoder-theme-design.md b/docs/superpowers/specs/2026-05-24-pi-cli-dreamcoder-theme-design.md
new file mode 100644
index 0000000..2e232b9
--- /dev/null
+++ b/docs/superpowers/specs/2026-05-24-pi-cli-dreamcoder-theme-design.md
@@ -0,0 +1,19 @@
+# PI CLI Dreamcoder Theme Design
+
+## Goal
+Apply the Dreamcoder visual identity to the PI CLI globally.
+
+## Approved Approach
+Use the existing token-driven generator (`scripts/sync-dreamcoder-theme.py`) as the single source of truth. The generator writes the active mode to `~/.pi/agent/themes/dreamcoder.json` and safely updates `~/.pi/agent/settings.json` with `"theme": "dreamcoder"`.
+
+## Runtime Architecture
+`themes/dreamcoder/tokens.json` defines dark, light, and dusk palettes. `apply-theme-mode.sh` sets `DREAMCODER_THEME_MODE` and calls `sync-dreamcoder-theme.py`. The sync script generates all existing terminal/editor outputs plus the PI CLI theme. PI reads the theme globally from `~/.pi/agent/themes/dreamcoder.json`.
+
+## Theme Mapping
+PI requires 51 color tokens. Dreamcoder maps core UI tokens from canonical colors (`accent`, `accent_2`, `diagnostic`, `sage`, `error`, `warning`, `muted`, `subtle`, `border_ui`) and syntax tokens from the guarded mapping already used for opencode/Codex. Text tokens that PI examples leave as terminal defaults remain `""` where appropriate.
+
+## Settings Safety
+The settings merge only adds or updates the `theme` field. Existing provider, model, packages, MCP, and user settings remain untouched. Invalid JSON is treated as an empty settings file, matching the existing opencode settings behavior.
+
+## Verification
+Automated tests validate generation without touching the real home directory by using temporary `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, `PI_AGENT_DIR`, and output path overrides. `scripts/verify.sh` validates the global PI theme file and settings.
diff --git a/gitconfig/.config/git/config b/gitconfig/.config/git/config
index 72abd96..1e626e3 100644
--- a/gitconfig/.config/git/config
+++ b/gitconfig/.config/git/config
@@ -13,7 +13,6 @@
navigate = true
side-by-side = true
line-numbers = true
- syntax-theme = Catppuccin Mocha
features = decorations
[delta "decorations"]
@@ -21,6 +20,9 @@
file-style = bold yellow ul
hunk-header-decoration-style = yellow box
+[include]
+ path = ~/.config/git/delta-dreamcoder.gitconfig
+
[merge]
conflictstyle = zdiff3
diff --git a/scripts/apply-cli-env-hooks.sh b/scripts/apply-cli-env-hooks.sh
new file mode 100755
index 0000000..3167b81
--- /dev/null
+++ b/scripts/apply-cli-env-hooks.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+CACHE_HOME="${XDG_CACHE_HOME:-${HOME}/.cache}"
+HOOK_NAME="90-dreamcoder-ai-cli.sh"
+ZSH_DIR="${ZSHRC_DIR:-${HOME}/.config/zshrc}"
+BASH_DIR="${BASHRC_DIR:-${HOME}/.config/bashrc}"
+ENV_FILE="${CACHE_HOME}/dreamcoder/cursor-cli.env"
+
+write_hook() {
+ local dir="$1"
+ local file="${dir}/${HOOK_NAME}"
+ mkdir -p "${dir}"
+ cat >"${file}" <<'HOOK'
+# Dreamcoder AI CLI contrast environment.
+env_file="${XDG_CACHE_HOME:-${HOME}/.cache}/dreamcoder/cursor-cli.env"
+[[ -f "${env_file}" ]] && source "${env_file}"
+unset env_file
+HOOK
+}
+
+mkdir -p "$(dirname "${ENV_FILE}")"
+[[ -f "${ENV_FILE}" ]] || printf 'export COLORTERM="truecolor"\n' >"${ENV_FILE}"
+write_hook "${ZSH_DIR}"
+write_hook "${BASH_DIR}"
+printf '✓ Dreamcoder AI CLI shell hooks applied\n'
diff --git a/scripts/apply-fastfetch-assets.sh b/scripts/apply-fastfetch-assets.sh
new file mode 100755
index 0000000..04b2f9a
--- /dev/null
+++ b/scripts/apply-fastfetch-assets.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ENV_FILE="${DREAMCODER_DOTS_ENV:-${0%/*}/dreamcoder-env.sh}"
+# shellcheck source=/dev/null
+[[ -f "${ENV_FILE}" ]] && source "${ENV_FILE}"
+
+SOURCE_DIR="${DREAMCODER_DOTS_DIR}/Fastfetch/.config/dreamcoder"
+TARGET_DIR="${CONFIG_HOME}/dreamcoder"
+BACKUP_DIR="${CONFIG_HOME}/dreamcoder-backup-$(date +%Y%m%d-%H%M%S)"
+
+[[ -f "${SOURCE_DIR}/Dreamcoder01.jpg" ]] || {
+ printf '✗ Missing Fastfetch logo: %s\n' "${SOURCE_DIR}/Dreamcoder01.jpg" >&2
+ exit 1
+}
+
+mkdir -p "${CONFIG_HOME}"
+if [[ -e "${TARGET_DIR}" && ! -L "${TARGET_DIR}" ]]; then
+ mkdir -p "${BACKUP_DIR}"
+ mv "${TARGET_DIR}" "${BACKUP_DIR}/"
+fi
+
+ln -sfn "${SOURCE_DIR}" "${TARGET_DIR}"
+printf '✓ Fastfetch assets linked: %s\n' "${TARGET_DIR}"
diff --git a/scripts/apply-system-mode.sh b/scripts/apply-system-mode.sh
index eba2132..4a3bb42 100755
--- a/scripts/apply-system-mode.sh
+++ b/scripts/apply-system-mode.sh
@@ -4,9 +4,11 @@ set -euo pipefail
MODE="${1:-light}"
GTK3="${XDG_CONFIG_HOME:-${HOME}/.config}/gtk-3.0/settings.ini"
GTK4="${XDG_CONFIG_HOME:-${HOME}/.config}/gtk-4.0/settings.ini"
-[[ "${MODE}" == "dark" ]] && GTK3_VALUE="1" || GTK3_VALUE="0"
-[[ "${MODE}" == "dark" ]] && GTK4_VALUE="true" || GTK4_VALUE="false"
-[[ "${MODE}" == "dark" ]] && SCHEME="prefer-dark" || SCHEME="prefer-light"
+if [[ "${MODE}" == "dark" ]]; then
+ GTK3_VALUE="1"; GTK4_VALUE="true"; SCHEME="prefer-dark"
+else
+ GTK3_VALUE="0"; GTK4_VALUE="false"; SCHEME="prefer-light"
+fi
set_gtk_key() {
local file="${1}" key="${2}" value="${3}"
diff --git a/scripts/apply-theme-mode.sh b/scripts/apply-theme-mode.sh
new file mode 100755
index 0000000..9623b6f
--- /dev/null
+++ b/scripts/apply-theme-mode.sh
@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ENV_FILE="${DREAMCODER_DOTS_ENV:-${0%/*}/dreamcoder-env.sh}"
+# shellcheck source=/dev/null
+[[ -f "${ENV_FILE}" ]] && source "${ENV_FILE}"
+MODE="${1:-light}"
+WALLPAPER="${2:-${DREAMCODER_WALLPAPER:-${WALLPAPER:-}}}"
+ML4W_WALLPAPER="${ML4W_CACHE_DIR}/current_wallpaper"
+[[ "${MODE}" == "light" || "${MODE}" == "dark" || "${MODE}" == "dusk" ]] || { printf 'Invalid mode: %s\n' "${MODE}" >&2; exit 1; }
+if [[ -z "${WALLPAPER}" && -f "${ML4W_WALLPAPER}" ]]; then WALLPAPER="$(cat "${ML4W_WALLPAPER}")"; fi
+
+CURSOR_CLI_ENV="${CACHE_HOME:-${HOME}/.cache}/dreamcoder/cursor-cli.env"
+case "${MODE}" in
+ light|dusk) CLI_COLORFGBG="0;15" ;;
+ dark) CLI_COLORFGBG="15;0" ;;
+esac
+mkdir -p "$(dirname "${CURSOR_CLI_ENV}")"
+printf 'export COLORFGBG="%s"\nexport DREAMCODER_THEME_MODE="%s"\nexport COLORTERM="truecolor"\nexport FORCE_COLOR="3"\nexport CLICOLOR_FORCE="1"\nunset NO_COLOR\n' "${CLI_COLORFGBG}" "${MODE}" >"${CURSOR_CLI_ENV}"
+
+"${DREAMCODER_DOTS_DIR}/scripts/apply-system-mode.sh" "${MODE}"
+if [[ -n "${WALLPAPER}" && -f "${WALLPAPER}" ]] && command -v matugen >/dev/null; then
+ matugen image "${WALLPAPER}" -m "${MODE}" >/dev/null 2>&1 || true
+fi
+DREAMCODER_THEME_MODE="${MODE}" DREAMCODER_WALLPAPER="${WALLPAPER}" \
+ "${DREAMCODER_DOTS_DIR}/scripts/sync-dreamcoder-theme.py"
+command -v pkill >/dev/null && pkill -SIGUSR1 kitty 2>/dev/null || true
+
+# --- tmux integration: propagate theme to running sessions ---
+KANAGAWA_DIR="${HOME}/.tmux/plugins/tmux-kanagawa"
+if command -v tmux >/dev/null 2>&1 && tmux list-sessions &>/dev/null 2>&1; then
+ # Update global environment so NEW panes/windows inherit the right vars
+ tmux set-environment -g DREAMCODER_THEME_MODE "${MODE}" 2>/dev/null || true
+ tmux set-environment -g COLORFGBG "${CLI_COLORFGBG}" 2>/dev/null || true
+
+ # Switch tmux-kanagawa theme variant to match Dreamcoder mode
+ if [[ -d "${KANAGAWA_DIR}" ]]; then
+ case "${MODE}" in
+ light)
+ KANAGAWA_VARIANT="lotus"
+ # Override default lotus colors with Dreamcoder Light palette
+ tmux set-option -g @ukiyo-color-text "#17120d" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-bg-bar "#e6d7c4" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-bg-pane "#f3eadc" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-info "#15516e" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-notice "#a7471c" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-accent "#824f16" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-muted "#3d3228" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-error "#842f24" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-alert "#654300" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-highlight "#0f6570" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-selection "#c8ad89" 2>/dev/null || true
+ # Restore default plugin color order (fg=bg_pane claro sobre bg=color)
+ tmux set-option -gu "@ukiyo-git-colors" 2>/dev/null || true
+ tmux set-option -gu "@ukiyo-cpu-usage-colors" 2>/dev/null || true
+ tmux set-option -gu "@ukiyo-ram-usage-colors" 2>/dev/null || true
+ ;;
+ dusk)
+ KANAGAWA_VARIANT="lotus"
+ # Override with Dreamcoder Dusk palette (warmer light)
+ tmux set-option -g @ukiyo-color-text "#1a1713" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-bg-bar "#dfd5c4" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-bg-pane "#ebe4d6" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-info "#104b67" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-notice "#96411e" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-accent "#8a5520" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-muted "#4c443a" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-error "#773126" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-alert "#604000" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-highlight "#216a73" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-selection "#c6b6a0" 2>/dev/null || true
+ # Restore default plugin color order (fg=bg_pane claro sobre bg=color)
+ tmux set-option -gu "@ukiyo-git-colors" 2>/dev/null || true
+ tmux set-option -gu "@ukiyo-cpu-usage-colors" 2>/dev/null || true
+ tmux set-option -gu "@ukiyo-ram-usage-colors" 2>/dev/null || true
+ ;;
+ dark)
+ KANAGAWA_VARIANT="dragon"
+ # Dreamcoder Ember Noir OLED palette: fondos profundos, colores vibrantes
+ tmux set-option -g @ukiyo-color-text "#f0e7dc" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-bg-bar "#1d1613" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-bg-pane "#15100d" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-accent "#e6a15c" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-info "#b8bf84" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-notice "#d66f50" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-muted "#c7b9aa" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-error "#e98272" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-alert "#e8b866" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-highlight "#e6a15c" 2>/dev/null || true
+ tmux set-option -g @ukiyo-color-selection "#3e2c22" 2>/dev/null || true
+ # Swap plugin color order: fg=bg_bar (oscuro) sobre bg=color vibrante
+ tmux set-option -g @ukiyo-git-colors "accent bg_bar" 2>/dev/null || true
+ tmux set-option -g @ukiyo-cpu-usage-colors "notice bg_bar" 2>/dev/null || true
+ tmux set-option -g @ukiyo-ram-usage-colors "info bg_bar" 2>/dev/null || true
+ ;;
+ esac
+ tmux set-option -g @ukiyo-theme "kanagawa/${KANAGAWA_VARIANT}" 2>/dev/null || true
+ # Reload plugin to apply new theme colors immediately
+ bash "${KANAGAWA_DIR}/ukiyo.tmux" 2>/dev/null || true
+ fi
+
+ printf ' tmux environment and theme updated for %s mode\n' "${MODE}"
+fi
+# --- /tmux integration ---
+
+printf '✓ Dreamcoder %s mode applied\n' "${MODE}"
diff --git a/scripts/auto-colors.sh b/scripts/auto-colors.sh
index ba9bd51..dfacb2c 100755
--- a/scripts/auto-colors.sh
+++ b/scripts/auto-colors.sh
@@ -14,6 +14,7 @@ fi
if [[ -z "${WALLPAPER}" ]] && command -v swww >/dev/null; then
WALLPAPER="$(swww query 2>/dev/null | sed 's/.*image: //' || true)"
fi
+WALLPAPER="$("${DREAMCODER_DOTS_DIR}/scripts/normalize-wallpaper-path.sh" "${WALLPAPER}")"
if [[ -z "${WALLPAPER}" || ! -f "${WALLPAPER}" ]]; then
printf '✗ Wallpaper not found: %s\n' "${WALLPAPER:-none}" >&2
diff --git a/scripts/dreamcoder b/scripts/dreamcoder
index aaf3666..17403f2 100755
--- a/scripts/dreamcoder
+++ b/scripts/dreamcoder
@@ -11,14 +11,14 @@ case "${CMD}" in
install) exec "${DREAMCODER_DOTS_DIR}/scripts/install.sh" "$@" ;;
repair) exec "${DREAMCODER_DOTS_DIR}/scripts/repair.sh" "$@" ;;
doctor) exec "${DREAMCODER_DOTS_DIR}/scripts/doctor.sh" "$@" ;;
+ status) exec "${DREAMCODER_DOTS_DIR}/scripts/status.sh" "$@" ;;
verify) exec "${DREAMCODER_DOTS_DIR}/scripts/verify.sh" "$@" ;;
preview) exec "${DREAMCODER_DOTS_DIR}/scripts/generate-theme-preview.py" "$@" ;;
auto) exec "${DREAMCODER_DOTS_DIR}/scripts/theme-auto.sh" "$@" ;;
- light|dark)
- DREAMCODER_THEME_MODE="${CMD}" exec "${DREAMCODER_DOTS_DIR}/scripts/sync-dreamcoder-theme.py" "$@"
- ;;
+ light|dark|dusk) exec "${DREAMCODER_DOTS_DIR}/scripts/apply-theme-mode.sh" "${CMD}" "$@" ;;
sync) exec "${DREAMCODER_DOTS_DIR}/scripts/sync-dreamcoder-theme.py" "$@" ;;
*)
- printf 'Usage: dreamcoder {install|repair|doctor|verify|preview|auto|light|dark|sync}\n'
+ printf 'Usage: dreamcoder {install|repair|doctor|status|verify|preview|auto|light|dusk|dark|sync}\n' >&2
+ exit 2
;;
esac
diff --git a/scripts/dreamcoder_theme/__init__.py b/scripts/dreamcoder_theme/__init__.py
new file mode 100644
index 0000000..92a9189
--- /dev/null
+++ b/scripts/dreamcoder_theme/__init__.py
@@ -0,0 +1 @@
+"""Dreamcoder theme generation package."""
diff --git a/scripts/dreamcoder_theme/palette.py b/scripts/dreamcoder_theme/palette.py
new file mode 100644
index 0000000..9f8bc9d
--- /dev/null
+++ b/scripts/dreamcoder_theme/palette.py
@@ -0,0 +1,270 @@
+"""Palette tokens and contrast helpers."""
+
+from __future__ import annotations
+
+import json
+import re
+import subprocess
+from pathlib import Path
+
+VARIANTS = {
+ "dark": {
+ "name": "Dreamcoder Ember Noir",
+ "bg": "#15100d",
+ "bg_soft": "#1d1613",
+ "surface0": "#241b16",
+ "surface1": "#30231c",
+ "surface2": "#3e2c22",
+ "text": "#f0e7dc",
+ "muted": "#c7b9aa",
+ "subtle": "#aa927c",
+ "comment": "#9c826d",
+ "border": "#49362c",
+ "border_ui": "#806754",
+ "border_hi": "#d8c1a5",
+ "focus": "#e6a15c",
+ "accent": "#e6a15c",
+ "accent_2": "#d66f50",
+ "diagnostic": "#d2a268",
+ "sage": "#b8bf84",
+ "lavender": "#c9a8dc",
+ "mauve": "#d98aa9",
+ "error": "#e98272",
+ "warning": "#e8b866",
+ "selection": "#43291d",
+ "panel_rgba": "rgba(21, 16, 13, 0.76)",
+ "module_rgba": "rgba(240, 231, 220, 0.08)",
+ "active_rgba": "rgba(230, 161, 92, 0.24)",
+ "inactive_border": "rgba(49362cc8)",
+ "details": "darker",
+ "prompt_bg": "#19110c",
+ "prompt_surface0": "#2c1c14",
+ "prompt_surface1": "#52301c",
+ "prompt_surface2": "#744524",
+ "prompt_text": "#fff0df",
+ "prompt_muted": "#dcc3aa",
+ "prompt_accent": "#e6a15c",
+ "prompt_accent_2": "#d66f50",
+ },
+ "light": {
+ "name": "Dreamcoder Light",
+ "bg": "#f3eadc",
+ "bg_soft": "#e6d7c4",
+ "surface0": "#fff7ea",
+ "surface1": "#decbb1",
+ "surface2": "#c8ad89",
+ "text": "#17120d",
+ "muted": "#3d3228",
+ "subtle": "#554635",
+ "comment": "#66523f",
+ "border": "#8a7358",
+ "border_ui": "#66513b",
+ "border_hi": "#3e2f20",
+ "focus": "#0f6570",
+ "accent": "#824f16",
+ "accent_2": "#a7471c",
+ "diagnostic": "#15516e",
+ "sage": "#3f6b35",
+ "lavender": "#57478b",
+ "mauve": "#7d3e64",
+ "error": "#842f24",
+ "warning": "#654300",
+ "selection": "#17120d",
+ "panel_rgba": "rgba(243, 234, 220, 0.96)",
+ "module_rgba": "rgba(222, 203, 177, 0.80)",
+ "active_rgba": "rgba(130, 79, 22, 0.34)",
+ "inactive_border": "rgba(8a7358ee)",
+ "details": "lighter",
+ "prompt_bg": "#f3eadc",
+ "prompt_surface0": "#fff0d8",
+ "prompt_surface1": "#d6ac72",
+ "prompt_surface2": "#a96d31",
+ "prompt_text": "#20150c",
+ "prompt_muted": "#53402e",
+ "prompt_accent": "#8a5520",
+ "prompt_accent_2": "#a7471c",
+ },
+ "dusk": {
+ "name": "Dreamcoder Dusk",
+ "bg": "#ebe4d6",
+ "bg_soft": "#dfd5c4",
+ "surface0": "#f1eadf",
+ "surface1": "#d8cbb8",
+ "surface2": "#c6b6a0",
+ "text": "#1a1713",
+ "muted": "#4c443a",
+ "subtle": "#5a4f43",
+ "comment": "#615548",
+ "border": "#a7947a",
+ "border_ui": "#665845",
+ "border_hi": "#4a3f32",
+ "focus": "#216a73",
+ "accent": "#8a5520",
+ "accent_2": "#96411e",
+ "diagnostic": "#104b67",
+ "sage": "#466b41",
+ "lavender": "#5b4e86",
+ "mauve": "#784762",
+ "error": "#773126",
+ "warning": "#604000",
+ "selection": "#1a1713",
+ "panel_rgba": "rgba(235, 228, 214, 0.88)",
+ "module_rgba": "rgba(26, 23, 19, 0.08)",
+ "active_rgba": "rgba(138, 85, 32, 0.22)",
+ "inactive_border": "rgba(a7947adf)",
+ "details": "lighter",
+ "prompt_bg": "#ebe4d6",
+ "prompt_surface0": "#f2e6d4",
+ "prompt_surface1": "#d3bc98",
+ "prompt_surface2": "#b88958",
+ "prompt_text": "#261c14",
+ "prompt_muted": "#574939",
+ "prompt_accent": "#965f25",
+ "prompt_accent_2": "#96411e",
+ },
+}
+
+ANSI_KEYS = [
+ "surface0",
+ "error",
+ "sage",
+ "accent",
+ "diagnostic",
+ "mauve",
+ "lavender",
+ "muted",
+ "subtle",
+ "#e9a092",
+ "#75a579",
+ "warning",
+ "#579ba4",
+ "#846fc5",
+ "#72b6bd",
+ "text",
+]
+
+
+def load_variants(defaults: dict[str, dict[str, str]], tokens_file: Path) -> dict[str, dict[str, str]]:
+ if not tokens_file.exists():
+ return defaults
+ tokens = json.loads(tokens_file.read_text())
+ modes = tokens.get("modes", {})
+ merged = {key: value.copy() for key, value in defaults.items()}
+ for key in ("dark", "light", "dusk"):
+ if key in modes:
+ merged[key].update(modes[key])
+ return merged
+
+
+def matugen_mode_name(mode_name: str) -> str:
+ return "light" if mode_name in {"light", "dusk"} else "dark"
+
+
+def resolve_color(palette: dict[str, str], value: str) -> str:
+ return palette.get(value, value)
+
+
+def hex_to_rgb(value: str) -> tuple[int, int, int]:
+ value = value.lstrip("#")
+ return tuple(int(value[i : i + 2], 16) for i in (0, 2, 4))
+
+
+def rgb_to_hex(rgb: tuple[int, int, int]) -> str:
+ return "#" + "".join(f"{max(0, min(255, part)):02x}" for part in rgb)
+
+
+def mix(left: str, right: str, amount: float) -> str:
+ a = hex_to_rgb(left)
+ b = hex_to_rgb(right)
+ return rgb_to_hex(tuple(round(x + (y - x) * amount) for x, y in zip(a, b)))
+
+
+def rel_luminance(value: str) -> float:
+ def channel(part: int) -> float:
+ scaled = part / 255
+ return scaled / 12.92 if scaled <= 0.03928 else ((scaled + 0.055) / 1.055) ** 2.4
+
+ r, g, b = (channel(part) for part in hex_to_rgb(value))
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b
+
+
+def contrast(left: str, right: str) -> float:
+ a, b = sorted((rel_luminance(left), rel_luminance(right)), reverse=True)
+ return (a + 0.05) / (b + 0.05)
+
+
+def guard(color: str, background: str, mode_name: str, minimum: float = 4.5) -> str:
+ target = "#ffffff" if mode_name == "dark" else "#000000"
+ safe = color
+ for _ in range(12):
+ if contrast(safe, background) >= minimum:
+ return safe
+ safe = mix(safe, target, 0.18)
+ return safe
+
+
+def surface_guard(color: str, background: str, mode_name: str, minimum: float = 1.05, maximum: float = 2.4) -> str:
+ if contrast(color, background) < minimum:
+ target = "#ffffff" if mode_name == "dark" else "#000000"
+ safe = color
+ for _ in range(12):
+ safe = mix(safe, target, 0.08)
+ if contrast(safe, background) >= minimum:
+ return safe
+ return safe
+ if contrast(color, background) > maximum:
+ safe = color
+ for _ in range(12):
+ safe = mix(safe, background, 0.12)
+ if contrast(safe, background) <= maximum:
+ return safe
+ return safe
+ return color
+
+
+def matugen_scheme(path: Path, mode_name: str, adaptive: bool) -> dict[str, str]:
+ if not adaptive or not path.is_file():
+ return {}
+ result = subprocess.run(
+ ["matugen", "image", str(path), "--json", "hex", "-m", matugen_mode_name(mode_name)],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL,
+ text=True,
+ check=False,
+ )
+ match = re.search(r"\{.*\}", result.stdout, flags=re.S)
+ if not match:
+ return {}
+ return json.loads(match.group(0)).get("colors", {}).get(matugen_mode_name(mode_name), {})
+
+
+def adaptive_palette(base: dict[str, str], mode_name: str, wallpaper: Path, adaptive: bool) -> dict[str, str]:
+ scheme = matugen_scheme(wallpaper, mode_name, adaptive)
+ if not scheme:
+ return base
+
+ c = dict(base)
+ bg = mix(c["bg"], scheme.get("background", c["bg"]), 0.18)
+ if contrast(bg, c["text"]) >= 7:
+ c["bg"] = bg
+ c["surface0"] = surface_guard(mix(c["surface0"], scheme.get("surface_container", c["surface0"]), 0.16), c["bg"], mode_name)
+ c["surface1"] = surface_guard(mix(c["surface1"], scheme.get("surface_container_high", c["surface1"]), 0.18), c["bg"], mode_name)
+ c["surface2"] = surface_guard(mix(c["surface2"], scheme.get("surface_variant", c["surface2"]), 0.18), c["bg"], mode_name)
+ c["bg_soft"] = surface_guard(c["bg_soft"], c["bg"], mode_name)
+ c["accent"] = guard(mix(c["prompt_accent"], scheme.get("primary", c["accent"]), 0.25), c["bg"], mode_name)
+ c["accent_2"] = guard(mix(c["prompt_accent_2"], scheme.get("secondary", c["accent_2"]), 0.22), c["bg"], mode_name)
+ c["diagnostic"] = guard(mix(c["diagnostic"], scheme.get("tertiary", c["diagnostic"]), 0.45), c["bg"], mode_name)
+ c["border"] = mix(c["border"], scheme.get("outline", c["border"]), 0.25)
+ c["selection"] = mix(c["selection"], scheme.get("primary_container", c["selection"]), 0.18)
+ c["prompt_accent"] = c["accent"]
+ c["prompt_accent_2"] = c["accent_2"]
+ return c
+
+
+def ansi(palette: dict[str, str]) -> list[str]:
+ mode_name = "dark" if palette["details"] == "darker" else "light"
+ safe = []
+ for key in ANSI_KEYS:
+ color = resolve_color(palette, key)
+ safe.append(guard(color, palette["bg"], mode_name))
+ return safe
diff --git a/scripts/dreamcoder_theme/renderers.py b/scripts/dreamcoder_theme/renderers.py
new file mode 100644
index 0000000..4c5b471
--- /dev/null
+++ b/scripts/dreamcoder_theme/renderers.py
@@ -0,0 +1,63 @@
+"""Public renderer exports grouped by application family."""
+
+from .renderers_cli import (
+ codex_tmtheme_content,
+ opencode_content,
+ opencode_tokens,
+ pi_theme_content,
+)
+from .renderers_desktop import (
+ antigravity_content,
+ hypr_content,
+ readme_content,
+ rofi_content,
+ waybar_content,
+)
+from .renderers_extra import (
+ bat_content,
+ btop_content,
+ cava_content,
+ delta_content,
+ dunst_content,
+ firefox_content,
+ fzf_content,
+ ls_colors_content,
+ nvim_content,
+ obsidian_content,
+ zsh_syntax_content,
+)
+from .renderers_terminal import (
+ ghostty_content,
+ kitty_content,
+ kitty_ui_content,
+ starship_content,
+ warp_content,
+)
+
+__all__ = [
+ "antigravity_content",
+ "bat_content",
+ "btop_content",
+ "cava_content",
+ "codex_tmtheme_content",
+ "delta_content",
+ "dunst_content",
+ "firefox_content",
+ "fzf_content",
+ "ghostty_content",
+ "hypr_content",
+ "kitty_content",
+ "kitty_ui_content",
+ "ls_colors_content",
+ "nvim_content",
+ "obsidian_content",
+ "opencode_content",
+ "opencode_tokens",
+ "pi_theme_content",
+ "readme_content",
+ "rofi_content",
+ "starship_content",
+ "warp_content",
+ "waybar_content",
+ "zsh_syntax_content",
+]
diff --git a/scripts/dreamcoder_theme/renderers_cli.py b/scripts/dreamcoder_theme/renderers_cli.py
new file mode 100644
index 0000000..995f23b
--- /dev/null
+++ b/scripts/dreamcoder_theme/renderers_cli.py
@@ -0,0 +1,296 @@
+"""CLI/editor theme renderers."""
+
+from __future__ import annotations
+
+import json
+
+from .palette import guard, mix
+from .settings import PI_THEME_SCHEMA
+
+def pi_theme_content(c: dict[str, str]) -> str:
+ t = opencode_tokens(c)
+ mode_name = "dark" if c["details"] == "darker" else "light"
+ user_bg = mix(c["accent"], c["bg"], 0.84)
+ pending_bg = mix(c["surface0"], c["bg"], 0.55)
+ success_bg = mix(c["sage"], c["bg"], 0.82)
+ error_bg = mix(c["error"], c["bg"], 0.84)
+ theme = {
+ "$schema": PI_THEME_SCHEMA,
+ "name": "dreamcoder",
+ "vars": {
+ "cocoa": c["accent_2"],
+ "lucuma": c["accent"],
+ "diagnostic": c["diagnostic"],
+ "sage": c["sage"],
+ "mauve": c["mauve"],
+ "coral": c["error"],
+ "warning": c["warning"],
+ "muted": c["muted"],
+ "subtle": c["subtle"],
+ "comment": c["comment"],
+ "borderUi": c["border_ui"],
+ "borderMuted": c["border"],
+ },
+ "colors": {
+ "accent": "lucuma",
+ "border": "borderUi",
+ "borderAccent": "diagnostic",
+ "borderMuted": "borderMuted",
+ "success": "sage",
+ "error": "coral",
+ "warning": "warning",
+ "muted": "muted",
+ "dim": "subtle",
+ "text": "",
+ "thinkingText": "muted",
+ "selectedBg": c["selection"],
+ "userMessageBg": user_bg,
+ "userMessageText": "",
+ "customMessageBg": c["surface0"],
+ "customMessageText": "",
+ "customMessageLabel": "lucuma",
+ "toolPendingBg": pending_bg,
+ "toolSuccessBg": success_bg,
+ "toolErrorBg": error_bg,
+ "toolTitle": "lucuma",
+ "toolOutput": "",
+ "mdHeading": "lucuma",
+ "mdLink": "diagnostic",
+ "mdLinkUrl": "muted",
+ "mdCode": "sage",
+ "mdCodeBlock": "",
+ "mdCodeBlockBorder": "borderMuted",
+ "mdQuote": "cocoa",
+ "mdQuoteBorder": "borderMuted",
+ "mdHr": "borderMuted",
+ "mdListBullet": "lucuma",
+ "toolDiffAdded": "sage",
+ "toolDiffRemoved": "coral",
+ "toolDiffContext": "muted",
+ "syntaxComment": "comment",
+ "syntaxKeyword": t["keyword"],
+ "syntaxFunction": t["function"],
+ "syntaxVariable": t["variable"],
+ "syntaxString": t["string"],
+ "syntaxNumber": t["number"],
+ "syntaxType": t["type"],
+ "syntaxOperator": t["operator"],
+ "syntaxPunctuation": t["punctuation"],
+ "thinkingOff": "comment",
+ "thinkingMinimal": "borderUi",
+ "thinkingLow": guard(mix(c["diagnostic"], c["text"], 0.12), c["bg"], mode_name),
+ "thinkingMedium": "diagnostic",
+ "thinkingHigh": "mauve",
+ "thinkingXhigh": "coral",
+ "bashMode": "lucuma",
+ },
+ }
+ return json.dumps(theme, indent=2) + "\n"
+
+def opencode_tokens(c: dict[str, str]) -> dict[str, str]:
+ mode_name = "dark" if c["details"] == "darker" else "light"
+ keyword = guard(mix(c["accent"], c["warning"], 0.22), c["bg"], mode_name)
+ function = guard(c["diagnostic"], c["bg"], mode_name)
+ type_color = guard(c["lavender"], c["bg"], mode_name)
+ constant = guard(mix(c["accent_2"], c["mauve"], 0.24), c["bg"], mode_name)
+ return {
+ "keyword": keyword,
+ "function": function,
+ "method": guard(mix(function, c["lavender"], 0.16), c["bg"], mode_name),
+ "variable": c["text"],
+ "parameter": guard(mix(c["accent_2"], c["text"], 0.06), c["bg"], mode_name),
+ "property": guard(mix(c["diagnostic"], c["text"], 0.04), c["bg"], mode_name),
+ "field": guard(mix(c["sage"], c["text"], 0.05), c["bg"], mode_name),
+ "string": guard(c["sage"], c["bg"], mode_name),
+ "number": guard(c["accent_2"], c["bg"], mode_name),
+ "constant": constant,
+ "type": type_color,
+ "constructor": guard(mix(type_color, c["accent"], 0.18), c["bg"], mode_name),
+ "enum": guard(mix(type_color, c["sage"], 0.18), c["bg"], mode_name),
+ "operator": guard(c["mauve"], c["bg"], mode_name),
+ "punctuation": guard(c["muted"], c["bg"], mode_name),
+ "comment": guard(c["comment"], c["bg"], mode_name),
+ "todo": guard(mix(c["warning"], c["text"], 0.12), c["bg"], mode_name),
+ "deprecated": guard(mix(c["error"], c["muted"], 0.28), c["bg"], mode_name),
+ "code_bg": c["surface0"],
+ "selection": c["selection"],
+ "search": mix(c["warning"], c["bg"], 0.72),
+ }
+
+def opencode_content(c: dict[str, str], transparent_background: bool = False) -> str:
+ t = opencode_tokens(c)
+ mode_name = "dark" if c["details"] == "darker" else "light"
+ added_bg = mix(c["sage"], c["bg"], 0.82)
+ removed_bg = mix(c["error"], c["bg"], 0.84)
+ hunk_bg = mix(c["lavender"], c["bg"], 0.84)
+ line_bg = c["surface0"]
+ assistant = guard(mix(c["diagnostic"], c["text"], 0.18), c["bg"], mode_name)
+ user = guard(mix(c["accent"], c["text"], 0.15), c["bg"], mode_name)
+ background = "none" if transparent_background else c["bg"]
+ return f'''{{
+ "$schema": "https://opencode.ai/theme.json",
+ "defs": {{
+ "dreamBackground": "{c['bg']}",
+ "dreamPanel": "{c['surface0']}",
+ "dreamElement": "{c['bg_soft']}",
+ "dreamText": "{c['text']}",
+ "dreamMuted": "{c['muted']}",
+ "dreamCocoa": "{c['accent_2']}",
+ "dreamLucuma": "{c['accent']}",
+ "dreamDiagnostic": "{c['diagnostic']}",
+ "dreamSage": "{c['sage']}",
+ "dreamViolet": "{c['lavender']}",
+ "dreamMauve": "{c['mauve']}",
+ "dreamCoral": "{c['error']}",
+ "dreamWarning": "{c['warning']}"
+ }},
+ "theme": {{
+ "background": "{background}",
+ "backgroundPanel": "{c['surface0']}",
+ "backgroundElement": "{c['bg_soft']}",
+ "backgroundHover": "{mix(c['surface1'], c['bg'], 0.45)}",
+ "backgroundSelected": "{t['selection']}",
+ "backgroundCode": "{t['code_bg']}",
+ "backgroundSearch": "{t['search']}",
+ "backgroundLine": "{line_bg}",
+ "backgroundAssistant": "{mix(c['diagnostic'], c['bg'], 0.84)}",
+ "backgroundUser": "{mix(c['accent'], c['bg'], 0.84)}",
+ "backgroundTool": "{mix(c['lavender'], c['bg'], 0.86)}",
+ "text": "{c['text']}",
+ "textMuted": "{c['muted']}",
+ "textSubtle": "{c['subtle']}",
+ "textPlaceholder": "{c['comment']}",
+ "textAssistant": "{assistant}",
+ "textUser": "{user}",
+ "textTool": "{t['type']}",
+ "primary": "{c['accent']}",
+ "secondary": "{c['accent_2']}",
+ "accent": "{c['accent']}",
+ "accentMuted": "{mix(c['accent'], c['bg'], 0.48)}",
+ "error": "{c['error']}",
+ "warning": "{c['warning']}",
+ "success": "{c['sage']}",
+ "info": "{c['diagnostic']}",
+ "border": "{c['border_ui']}",
+ "borderActive": "{c['accent']}",
+ "borderSubtle": "{c['border']}",
+ "borderFocus": "{c['diagnostic']}",
+ "shadow": "{mix(c['bg'], '#000000', 0.25)}",
+ "diffAdded": "{c['sage']}",
+ "diffRemoved": "{c['error']}",
+ "diffContext": "{c['muted']}",
+ "diffHunkHeader": "{c['lavender']}",
+ "diffHighlightAdded": "{c['sage']}",
+ "diffHighlightRemoved": "{c['error']}",
+ "diffAddedBg": "{added_bg}",
+ "diffRemovedBg": "{removed_bg}",
+ "diffContextBg": "{background}",
+ "diffLineNumber": "{c['subtle']}",
+ "diffAddedLineNumberBg": "{added_bg}",
+ "diffRemovedLineNumberBg": "{removed_bg}",
+ "diffHunkHeaderBg": "{hunk_bg}",
+ "diffFold": "{c['comment']}",
+ "diffFoldBg": "{mix(c['surface1'], c['bg'], 0.82)}",
+ "markdownText": "{c['text']}",
+ "markdownHeading": "{c['accent']}",
+ "markdownLink": "{c['diagnostic']}",
+ "markdownLinkText": "{c['accent']}",
+ "markdownCode": "{c['sage']}",
+ "markdownBlockQuote": "{c['accent_2']}",
+ "markdownEmph": "{c['diagnostic']}",
+ "markdownStrong": "{c['accent']}",
+ "markdownHorizontalRule": "{c['border_ui']}",
+ "markdownListItem": "{c['accent']}",
+ "markdownListEnumeration": "{c['lavender']}",
+ "markdownTableBorder": "{c['border_ui']}",
+ "markdownTableHeader": "{c['accent_2']}",
+ "markdownImage": "{c['mauve']}",
+ "markdownImageText": "{c['text']}",
+ "markdownCodeBlock": "{c['text']}",
+ "markdownCodeBlockBg": "{t['code_bg']}",
+ "markdownInlineCodeBg": "{mix(c['sage'], c['bg'], 0.84)}",
+ "syntaxComment": "{t['comment']}",
+ "syntaxKeyword": "{t['keyword']}",
+ "syntaxFunction": "{t['function']}",
+ "syntaxMethod": "{t['method']}",
+ "syntaxVariable": "{t['variable']}",
+ "syntaxParameter": "{t['parameter']}",
+ "syntaxProperty": "{t['property']}",
+ "syntaxField": "{t['field']}",
+ "syntaxString": "{t['string']}",
+ "syntaxNumber": "{t['number']}",
+ "syntaxBoolean": "{t['constant']}",
+ "syntaxConstant": "{t['constant']}",
+ "syntaxType": "{t['type']}",
+ "syntaxClass": "{t['constructor']}",
+ "syntaxInterface": "{mix(t['type'], c['diagnostic'], 0.22)}",
+ "syntaxEnum": "{t['enum']}",
+ "syntaxOperator": "{t['operator']}",
+ "syntaxPunctuation": "{t['punctuation']}",
+ "syntaxTag": "{t['keyword']}",
+ "syntaxAttribute": "{t['property']}",
+ "syntaxRegexp": "{mix(c['mauve'], c['error'], 0.28)}",
+ "syntaxEscape": "{c['warning']}",
+ "syntaxNamespace": "{t['type']}",
+ "syntaxModule": "{t['type']}",
+ "syntaxDecorator": "{t['operator']}",
+ "syntaxBuiltin": "{t['constant']}",
+ "syntaxSpecial": "{c['warning']}",
+ "syntaxTodo": "{t['todo']}",
+ "syntaxDeprecated": "{t['deprecated']}",
+ "terminalBlack": "{guard(c['surface0'], c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
+ "terminalRed": "{c['error']}",
+ "terminalGreen": "{c['sage']}",
+ "terminalYellow": "{c['warning']}",
+ "terminalBlue": "{c['diagnostic']}",
+ "terminalMagenta": "{c['mauve']}",
+ "terminalCyan": "{guard(mix(c['diagnostic'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
+ "terminalWhite": "{c['text']}",
+ "terminalBrightBlack": "{c['subtle']}",
+ "terminalBrightRed": "{guard(mix(c['error'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
+ "terminalBrightGreen": "{guard(mix(c['sage'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
+ "terminalBrightYellow": "{guard(mix(c['warning'], c['text'], 0.16), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
+ "terminalBrightBlue": "{guard(mix(c['diagnostic'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
+ "terminalBrightMagenta": "{guard(mix(c['mauve'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
+ "terminalBrightCyan": "{guard(mix(c['lavender'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
+ "terminalBrightWhite": "{c['text']}"
+ }}
+}}
+'''
+
+def codex_tmtheme_content(c: dict[str, str]) -> str:
+ t = opencode_tokens(c)
+ invert = c.get("details") == "lighter"
+ selection = c["text"] if invert else c["selection"]
+ line_highlight = c["surface1"] if invert else c["surface0"]
+ gutter = c["surface2"] if invert else c["surface1"]
+ return f'''
+
+
+
+ nameDreamcoder
+ settings
+
+ settings
+ background{c['bg']}
+ foreground{c['text']}
+ caret{c['accent']}
+ selection{selection}
+ lineHighlight{line_highlight}
+ gutter{gutter}
+ gutterForeground{c['muted']}
+ invisibles{c['comment']}
+
+ scopecommentsettingsforeground{t['comment']}fontStyleitalic
+ scopekeyword, storagesettingsforeground{t['keyword']}fontStylebold
+ scopeentity.name.function, support.functionsettingsforeground{t['function']}
+ scopevariable, meta.definition.variablesettingsforeground{t['variable']}
+ scopestringsettingsforeground{t['string']}
+ scopeconstant.numeric, constant.languagesettingsforeground{t['constant']}
+ scopeentity.name.type, support.typesettingsforeground{t['type']}
+ scopepunctuation, keyword.operatorsettingsforeground{t['operator']}
+
+
+
+'''
diff --git a/scripts/dreamcoder_theme/renderers_core.py b/scripts/dreamcoder_theme/renderers_core.py
new file mode 100644
index 0000000..d9ecfde
--- /dev/null
+++ b/scripts/dreamcoder_theme/renderers_core.py
@@ -0,0 +1,13 @@
+"""Shared renderer helpers."""
+
+from __future__ import annotations
+
+from .palette import ANSI_KEYS, guard, resolve_color
+
+def ansi(palette: dict[str, str]) -> list[str]:
+ mode_name = "dark" if palette["details"] == "darker" else "light"
+ safe = []
+ for key in ANSI_KEYS:
+ color = resolve_color(palette, key)
+ safe.append(guard(color, palette["bg"], mode_name))
+ return safe
diff --git a/scripts/dreamcoder_theme/renderers_desktop.py b/scripts/dreamcoder_theme/renderers_desktop.py
new file mode 100644
index 0000000..55dc944
--- /dev/null
+++ b/scripts/dreamcoder_theme/renderers_desktop.py
@@ -0,0 +1,254 @@
+"""Desktop shell and editor theme renderers."""
+
+from __future__ import annotations
+
+import json
+
+def hypr_content(c: dict[str, str]) -> str:
+ return f"""# Dreamcoder color layer for Hyprland.
+# Import after ML4W/Gentleman defaults; this changes colors only.
+
+general {{
+ col.active_border = rgba({c['accent'][1:]}ff) rgba({c['diagnostic'][1:]}ff) 45deg
+ col.inactive_border = {c['inactive_border']}
+}}
+
+misc {{
+ background_color = rgba({c['bg'][1:]}ff)
+}}
+"""
+
+def waybar_content(c: dict[str, str]) -> str:
+ return f"""/* Dreamcoder color layer for Waybar. */
+@define-color bg {c['bg']};
+@define-color bg-soft {c['bg_soft']};
+@define-color surface {c['surface0']};
+@define-color text {c['text']};
+@define-color muted {c['muted']};
+@define-color border {c['border']};
+@define-color border-ui {c['border_ui']};
+@define-color focus {c['focus']};
+@define-color accent {c['accent']};
+@define-color accent-2 {c['accent_2']};
+@define-color diagnostic {c['diagnostic']};
+@define-color error {c['error']};
+@define-color success {c['sage']};
+@define-color warning {c['warning']};
+
+window#waybar {{
+ background: {c['panel_rgba']};
+ color: @text;
+ border-bottom: 1px solid alpha(@accent, 0.26);
+}}
+
+#workspaces button {{
+ color: @muted;
+ background: transparent;
+ border: 1px solid transparent;
+}}
+
+#workspaces button.active {{
+ color: @accent;
+ background: {c['active_rgba']};
+ border-color: alpha(@focus, 0.82);
+}}
+
+#clock,
+#battery,
+#network,
+#pulseaudio,
+#cpu,
+#memory,
+#tray {{
+ background: {c['module_rgba']};
+ color: @text;
+ border: 1px solid alpha(@border-ui, 0.78);
+}}
+
+#battery.warning {{ color: @warning; }}
+#battery.critical {{ color: @error; }}
+#network.disconnected {{ color: @error; }}
+"""
+
+def rofi_content(c: dict[str, str]) -> str:
+ return f"""/* Dreamcoder color layer for Rofi. */
+* {{
+ background: {c['panel_rgba']};
+ background-alt: {c['surface0']};
+ foreground: {c['text']};
+ muted: {c['muted']};
+ selected: {c['active_rgba']};
+ active: {c['accent']};
+ border-ui: {c['border_ui']};
+ focus: {c['focus']};
+ urgent: {c['error']};
+ border-color: {c['border_ui']};
+}}
+
+window {{
+ background-color: @background;
+ border: 1px;
+ border-color: @border-ui;
+ border-radius: 18px;
+}}
+
+entry {{
+ background-color: @selected;
+ text-color: @foreground;
+ border: 1px;
+ border-color: @focus;
+ border-radius: 12px;
+}}
+
+element selected {{
+ background-color: @selected;
+ text-color: @foreground;
+ border: 0 0 0 3px;
+ border-color: @focus;
+}}
+
+element-text {{ text-color: @muted; }}
+element selected element-text {{ text-color: @foreground; }}
+"""
+
+def antigravity_content(c: dict[str, str]) -> str:
+ theme_type = "dark" if "Dark" in c.get("name", "Dark") else "light"
+ return json.dumps({
+ "name": c.get("name", "Dreamcoder"),
+ "type": theme_type,
+ "colors": {
+ "editor.background": c["bg"],
+ "editor.foreground": c["text"],
+ "activityBar.background": c["surface0"],
+ "activityBar.foreground": c["accent"],
+ "activityBar.inactiveForeground": c["comment"],
+ "activityBar.border": c["border_ui"],
+ "sideBar.background": c["surface0"],
+ "sideBar.foreground": c["text"],
+ "sideBar.border": c["border_ui"],
+ "statusBar.background": c["bg"],
+ "statusBar.foreground": c["text"],
+ "statusBar.border": c["border_ui"],
+ "editorGroupHeader.tabsBackground": c["surface0"],
+ "tab.activeBackground": c["bg"],
+ "tab.activeForeground": c["accent"],
+ "tab.inactiveBackground": c["surface0"],
+ "tab.inactiveForeground": c["comment"],
+ "tab.border": c["border_ui"],
+ "editor.lineHighlightBackground": c["surface0"],
+ "editorLineNumber.foreground": c["comment"],
+ "editorLineNumber.activeForeground": c["accent"],
+ "editorWidget.background": c["surface1"],
+ "editorWidget.border": c["border_ui"],
+ "input.background": c["surface1"],
+ "input.foreground": c["text"],
+ "input.border": c["focus"],
+ "button.background": c["accent_2"],
+ "button.foreground": c["text"],
+ "list.activeSelectionBackground": c["selection"],
+ "list.activeSelectionForeground": c["text"],
+ "list.hoverBackground": c["surface0"],
+ "editor.selectionBackground": c["selection"],
+ "terminal.background": c["bg"],
+ "terminal.foreground": c["text"],
+ "terminal.ansiBlack": c["surface0"],
+ "terminal.ansiRed": c["error"],
+ "terminal.ansiGreen": c["sage"],
+ "terminal.ansiYellow": c["warning"],
+ "terminal.ansiBlue": c["accent"],
+ "terminal.ansiMagenta": c["mauve"],
+ "terminal.ansiCyan": c["diagnostic"],
+ "terminal.ansiWhite": c["text"]
+ },
+ "tokenColors": [
+ {
+ "scope": ["comment", "punctuation.definition.comment"],
+ "settings": {
+ "foreground": c["comment"],
+ "fontStyle": "italic"
+ }
+ },
+ {
+ "scope": ["keyword", "storage.type", "storage.modifier", "keyword.operator"],
+ "settings": {
+ "foreground": c["accent"],
+ "fontStyle": "bold"
+ }
+ },
+ {
+ "scope": ["entity.name.function", "support.function", "entity.name.method"],
+ "settings": {
+ "foreground": c["diagnostic"]
+ }
+ },
+ {
+ "scope": ["string", "punctuation.definition.string"],
+ "settings": {
+ "foreground": c["sage"]
+ }
+ },
+ {
+ "scope": ["constant.numeric", "constant.language"],
+ "settings": {
+ "foreground": c["accent_2"]
+ }
+ },
+ {
+ "scope": ["support.type", "entity.name.type", "entity.name.class"],
+ "settings": {
+ "foreground": c["lavender"]
+ }
+ },
+ {
+ "scope": ["variable", "meta.definition.variable"],
+ "settings": {
+ "foreground": c["text"]
+ }
+ }
+ ]
+ }, indent=2)
+
+def readme_content() -> str:
+ return """# Dreamcoder Palette Layer
+
+This directory contains the Dreamcoder visual contract and generated color-only snippets for ML4W/Gentleman Dots.
+
+- `tokens.json`: canonical Dreamcoder OS design tokens and guardrails.
+- `tokens.schema.json`: machine-readable token contract.
+- `*-dark.*`: Dreamcoder Ember Noir mode with espresso glass, refined orange/red protagonists, and gold support accents.
+- `*-light.*`: paper-like daytime mode with flat surface ladder and distinct semantic tokens.
+- `*-dusk.*`: warm transitional mode (16:00–18:00) between light and dark.
+
+## Color-only Snippets (Hooks)
+
+Hook these into your app configs after ML4W/Gentleman files:
+
+| Target | Files | How to Use |
+|--------|-------|------------|
+| **Kitty** | `kitty-dreamcoder-{mode}.conf`, `dreamcoder-ui.conf` | `include` in kitty.conf |
+| **Ghostty** | `ghostty-dreamcoder-{mode}` | `theme = dreamcoder-{mode}` in ghostty config |
+| **Warp** | `Warp/.local/share/warp-terminal/themes/Dreamcoder-{Mode}.yaml` | Select in Warp theme picker |
+| **Hyprland** | `hyprland-{mode}.conf` | `source` from hyprland.conf |
+| **Waybar** | `waybar-{mode}.css` | `@import` in waybar style.css |
+| **Rofi** | `rofi-{mode}.rasi` | `@import` or `-theme` in rofi launch |
+| **Starship** | `starship-{mode}.toml` | `STARSHIP_CONFIG` env var |
+| **Antigravity** | `Antigravity/Dreamcoder-{Mode}.json` | Antigravity theme selector |
+| **opencode** | `opencode/dreamcoder.json` | `theme: "dreamcoder"` in opencode config |
+| **Codex CLI** | `Codex-CLI/Dreamcoder-{Mode}.tmTheme` | `theme = "Dreamcoder"` in codex config |
+| **PI CLI** | `Pi/.pi/agent/themes/dreamcoder-{mode}.json` | `theme: "dreamcoder"` in pi settings |
+| **Neovim** | `nvim-dreamcoder-{mode}.lua` | `require('dreamcoder')` in neovim config |
+| **Zsh-syntax-highlighting** | `zsh-syntax-highlighting-dreamcoder-{mode}.zsh` | `source` after zsh-syntax-highlighting plugin |
+| **LS_COLORS / eza** | `ls-colors-dreamcoder-{mode}.sh` | `source` in .zshrc or .bashrc |
+| **Bat** | `bat-dreamcoder-{mode}.sh` | Sets `BAT_THEME` env var; pair with Codex CLI tmTheme |
+| **Delta (git diff)** | `delta-dreamcoder-{mode}.gitconfig` | `[include]` in ~/.config/git/config |
+| **Fzf** | `fzf-dreamcoder-{mode}.sh` | `source` in .zshrc or .bashrc |
+| **Btop** | `btop-dreamcoder-{mode}.theme` | Place in ~/.config/btop/themes/ |
+| **Dunst** | `dunst-dreamcoder-{mode}.conf` | `[include]` in dunstrc |
+| **Firefox** | `firefox-dreamcoder-{mode}.css` | userChrome.css for Firefox customization |
+| **Obsidian** | `obsidian-dreamcoder-{mode}.css` | CSS snippet in Obsidian vault |
+| **Cava** | `cava-dreamcoder-{mode}.config` | `include` in ~/.config/cava/config |
+
+## Word of Caution
+
+Import these snippets after existing ML4W/Gentleman files so layouts, keybinds, wallpaper scripts, gaps, animations, and behavior remain owned by those systems.
+"""
diff --git a/scripts/dreamcoder_theme/renderers_extra.py b/scripts/dreamcoder_theme/renderers_extra.py
new file mode 100644
index 0000000..3d2eecd
--- /dev/null
+++ b/scripts/dreamcoder_theme/renderers_extra.py
@@ -0,0 +1,1259 @@
+"""Extra theme renderers for Dreamcoder ecosystem expansion.
+
+Each renderer takes a palette dict from palette.adaptive_palette()
+and returns a string suitable for write_if_changed().
+
+Targets: Neovim, Zsh-syntax-highlighting, LS_COLORS/eza, Bat, Delta,
+ Fzf, Btop, Dunst, Firefox userChrome, Obsidian, Cava.
+"""
+
+from __future__ import annotations
+
+from .palette import guard, mix
+from .renderers_core import ansi
+
+
+# ── Neovim Lua Colorscheme ────────────────────────────────────────
+
+
+def nvim_content(c: dict[str, str]) -> str:
+ """Return a Neovim Lua colorscheme using the Dreamcoder palette."""
+ def hl(name, **kwargs):
+ parts = []
+ if "fg" in kwargs:
+ parts.append(f' fg = "{kwargs["fg"]}"')
+ if "bg" in kwargs:
+ parts.append(f' bg = "{kwargs["bg"]}"')
+ if "sp" in kwargs:
+ parts.append(f' sp = "{kwargs["sp"]}"')
+ if "bold" in kwargs and kwargs["bold"]:
+ parts.append(" bold = true")
+ if "italic" in kwargs and kwargs["italic"]:
+ parts.append(" italic = true")
+ if "underline" in kwargs and kwargs["underline"]:
+ parts.append(" underline = true")
+ if "undercurl" in kwargs and kwargs["undercurl"]:
+ parts.append(" undercurl = true")
+ if "strikethrough" in kwargs and kwargs["strikethrough"]:
+ parts.append(" strikethrough = true")
+ if "reverse" in kwargs and kwargs["reverse"]:
+ parts.append(" reverse = true")
+ inner = ",\n".join(parts)
+ return f' vim.api.nvim_set_hl(0, "{name}", {{\n{inner}\n }})\n'
+
+ invert = c.get("details") == "lighter"
+ bg = c["bg"]
+ text = c["text"]
+ fg = text
+ # Selection: invert in light/dusk (bg ↔ text), use selection color in dark
+ sel_bg = c["text"] if invert else c["selection"]
+ sel_fg = c["bg"] if invert else c["text"]
+
+ lines = [
+ f"""-- ========================================================
+-- {c['name']} — generated by Dreamcoder sync
+-- ========================================================
+-- Usage: vim.cmd.colorscheme("dreamcoder")
+-- ========================================================
+
+vim.g.colors_name = "dreamcoder"
+
+-- Glass blur: transparent background + subtle float transparency
+vim.opt.winblend = 10
+vim.opt.pumblend = 10
+
+local c = {{
+ bg = "{bg}",
+ surface0 = "{c['surface0']}",
+ surface1 = "{c['surface1']}",
+ surface2 = "{c['surface2']}",
+ text = "{text}",
+ muted = "{c['muted']}",
+ subtle = "{c['subtle']}",
+ comment = "{c['comment']}",
+ accent = "{c['accent']}",
+ accent2 = "{c['accent_2']}",
+ diagnostic = "{c['diagnostic']}",
+ sage = "{c['sage']}",
+ lavender = "{c['lavender']}",
+ mauve = "{c['mauve']}",
+ error = "{c['error']}",
+ warning = "{c['warning']}",
+ border = "{c['border']}",
+ border_ui = "{c['border_ui']}",
+ selection = "{sel_bg}",
+}}
+
+local function h(name, opts)
+ vim.api.nvim_set_hl(0, name, opts)
+end
+
+-- ── Editor UI ────────────────────────────────────────────────
+-- Normal background = "none" for glass blur effect.
+-- Terminal (Kitty/Ghostty) handles transparency + blur.
+""",
+ hl("Normal", fg=fg, bg="none"),
+ hl("NormalFloat", fg=fg, bg=c["surface0"]),
+ hl("FloatBorder", fg=c["border_ui"], bg=c["surface0"]),
+ hl("NonText", fg=c["subtle"]),
+ hl("SpecialKey", fg=c["muted"]),
+ hl("Whitespace", fg=c["border"]),
+ hl("EndOfBuffer", fg="none"),
+ hl("Cursor", fg=bg, bg=c["accent"]),
+ hl("lCursor", fg=bg, bg=c["accent"]),
+ hl("CursorLine", bg=c["surface0"]),
+ hl("CursorLineNr", fg=c["accent"], bold=True),
+ hl("LineNr", fg=c["muted"]),
+ hl("CursorColumn", bg=c["surface0"]),
+ hl("ColorColumn", bg=c["surface0"]),
+ hl("SignColumn", bg="none"),
+ "",
+ " -- ── Selection & Search ───────────────────────────────",
+ hl("Visual", fg=sel_fg, bg=sel_bg),
+ hl("VisualNOS", fg=sel_fg, bg=sel_bg),
+ hl("Search", fg=bg, bg=c["accent"]),
+ hl("IncSearch", fg=bg, bg=c["accent_2"]),
+ hl("CurSearch", fg=bg, bg=c["accent"]),
+ hl("Substitute", fg=bg, bg=c["accent_2"]),
+ hl("MatchParen", fg=c["accent_2"], bold=True),
+ "",
+ " -- ── Popup Menu ────────────────────────────────────────",
+ hl("Pmenu", fg=c["muted"], bg=c["surface1"]),
+ hl("PmenuSel", fg=bg, bg=c["accent"]),
+ hl("PmenuSbar", bg=c["surface2"]),
+ hl("PmenuThumb", bg=c["muted"]),
+ "",
+ " -- ── Statusline & Winbar ───────────────────────────────",
+ hl("StatusLine", fg=fg, bg=c["surface1"]),
+ hl("StatusLineNC", fg=c["muted"], bg=c["surface0"]),
+ hl("StatusLineTerm", fg=fg, bg=c["surface1"]),
+ hl("StatusLineTermNC", fg=c["muted"], bg=c["surface0"]),
+ hl("WinBar", fg=fg, bg=c["surface1"]),
+ hl("WinBarNC", fg=c["muted"], bg=c["surface0"]),
+ hl("WinSeparator", fg=c["border"]),
+ "",
+ " -- ── Tabline ────────────────────────────────────────────",
+ hl("TabLine", fg=c["muted"], bg=c["surface0"]),
+ hl("TabLineSel", fg=fg, bg=c["surface1"]),
+ hl("TabLineFill", bg=bg),
+ "",
+ " -- ── Folds ─────────────────────────────────────────────",
+ hl("Folded", fg=c["muted"], bg=c["surface0"]),
+ hl("FoldColumn", fg=c["muted"]),
+ "",
+ " -- ── Diff ──────────────────────────────────────────────",
+ hl("DiffAdd", bg=mix(c["sage"], c["surface0"], 0.7)),
+ hl("DiffChange", bg=mix(c["warning"], c["surface0"], 0.7)),
+ hl("DiffDelete", bg=mix(c["error"], c["surface0"], 0.7)),
+ hl("DiffText", bg=mix(c.get("focus", c["accent"]), c["surface0"], 0.65)),
+ "",
+ " -- ── Spell ─────────────────────────────────────────────",
+ hl("SpellBad", sp=c["error"], undercurl=True),
+ hl("SpellCap", sp=c["warning"], undercurl=True),
+ hl("SpellLocal", sp=c["diagnostic"], undercurl=True),
+ hl("SpellRare", sp=c["mauve"], undercurl=True),
+ "",
+ " -- ── Messages ──────────────────────────────────────────",
+ hl("MsgArea", fg=fg),
+ hl("ModeMsg", fg=c["accent"]),
+ hl("WarningMsg", fg=c["warning"]),
+ hl("ErrorMsg", fg=c["error"]),
+ hl("Question", fg=c["diagnostic"]),
+ "",
+ " -- ── Misc ──────────────────────────────────────────────",
+ hl("Title", fg=c["accent"], bold=True),
+ hl("Directory", fg=c["diagnostic"]),
+ hl("Conceal", fg=c["muted"]),
+ hl("TermCursor", fg=bg, bg=c["accent"]),
+ "",
+ " -- ── Classic Syntax ────────────────────────────────────",
+ hl("Comment", fg=c["comment"], italic=True),
+ hl("Constant", fg=c["mauve"]),
+ hl("String", fg=c["sage"]),
+ hl("Character", fg=c["sage"]),
+ hl("Number", fg=c["mauve"]),
+ hl("Boolean", fg=c["lavender"]),
+ hl("Float", fg=c["mauve"]),
+ hl("Identifier", fg=fg),
+ hl("Function", fg=c["accent_2"]),
+ hl("Statement", fg=c["accent"], bold=True),
+ hl("Conditional", fg=c["accent"]),
+ hl("Repeat", fg=c["accent"]),
+ hl("Label", fg=c["accent"]),
+ hl("Operator", fg=c["accent_2"]),
+ hl("Keyword", fg=c["accent"]),
+ hl("Exception", fg=c["error"]),
+ hl("PreProc", fg=c["subtle"]),
+ hl("Include", fg=c["subtle"]),
+ hl("Define", fg=c["subtle"]),
+ hl("PreCondit", fg=c["subtle"]),
+ hl("Type", fg=c["diagnostic"]),
+ hl("StorageClass", fg=c["accent"]),
+ hl("Structure", fg=c["diagnostic"]),
+ hl("Typedef", fg=c["diagnostic"]),
+ hl("Special", fg=c["lavender"]),
+ hl("SpecialChar", fg=c["lavender"]),
+ hl("Delimiter", fg=c["muted"]),
+ hl("SpecialComment", fg=c["comment"]),
+ hl("Debug", fg=c["warning"]),
+ hl("Underlined", fg=c["diagnostic"], underline=True),
+ hl("Ignore", fg=bg),
+ hl("Error", fg=c["error"]),
+ hl("Todo", fg=c["warning"], bold=True),
+ "",
+ " -- ── Treesitter ────────────────────────────────────────",
+ hl("@variable", fg=fg),
+ hl("@variable.builtin", fg=c["lavender"]),
+ hl("@variable.parameter", fg=c["mauve"]),
+ hl("@variable.member", fg=fg),
+ hl("@constant", fg=c["mauve"]),
+ hl("@constant.builtin", fg=c["lavender"]),
+ hl("@constant.macro", fg=c["accent"]),
+ hl("@module", fg=c["diagnostic"]),
+ hl("@module.builtin", fg=c["accent"]),
+ hl("@label", fg=c["accent_2"]),
+ hl("@string", fg=c["sage"]),
+ hl("@string.documentation", fg=c["sage"], italic=True),
+ hl("@string.regex", fg=c["accent_2"]),
+ hl("@string.escape", fg=c["accent_2"]),
+ hl("@character", fg=c["sage"]),
+ hl("@character.special", fg=c["lavender"]),
+ hl("@number", fg=c["mauve"]),
+ hl("@boolean", fg=c["lavender"]),
+ hl("@float", fg=c["mauve"]),
+ hl("@function", fg=c["accent_2"]),
+ hl("@function.builtin", fg=c["accent_2"], bold=True),
+ hl("@function.call", fg=c["accent_2"]),
+ hl("@function.macro", fg=c["accent_2"]),
+ hl("@function.method", fg=c["accent_2"]),
+ hl("@function.method.call", fg=c["accent_2"]),
+ hl("@parameter", fg=c["mauve"]),
+ hl("@method", fg=c["accent_2"]),
+ hl("@field", fg=c["diagnostic"]),
+ hl("@property", fg=c["diagnostic"]),
+ hl("@constructor", fg=c["accent"]),
+ hl("@conditional", fg=c["accent"]),
+ hl("@repeat", fg=c["accent"]),
+ hl("@debug", fg=c["warning"]),
+ hl("@include", fg=c["subtle"]),
+ hl("@operator", fg=c["accent_2"]),
+ hl("@keyword", fg=c["accent"]),
+ hl("@keyword.function", fg=c["accent"], italic=True),
+ hl("@keyword.operator", fg=c["accent_2"]),
+ hl("@keyword.return", fg=c["accent"], bold=True),
+ hl("@exception", fg=c["error"]),
+ hl("@type", fg=c["diagnostic"]),
+ hl("@type.builtin", fg=c["diagnostic"], bold=True),
+ hl("@type.definition", fg=c["diagnostic"]),
+ hl("@type.qualifier", fg=c["accent"]),
+ hl("@storageclass", fg=c["accent"]),
+ hl("@attribute", fg=c["subtle"]),
+ hl("@symbol", fg=c["lavender"]),
+ hl("@punctuation.delimiter", fg=c["muted"]),
+ hl("@punctuation.bracket", fg=c["muted"]),
+ hl("@punctuation.special", fg=c["lavender"]),
+ hl("@comment", fg=c["comment"], italic=True),
+ hl("@comment.error", fg=c["error"]),
+ hl("@comment.warning", fg=c["warning"]),
+ hl("@comment.todo", fg=c["warning"], bold=True),
+ hl("@comment.note", fg=c["diagnostic"]),
+ hl("@markup.strong", bold=True),
+ hl("@markup.italic", italic=True),
+ hl("@markup.strikethrough", strikethrough=True),
+ hl("@markup.underline", underline=True),
+ hl("@markup.heading", fg=c["accent"], bold=True),
+ hl("@markup.quote", fg=c["comment"], italic=True),
+ hl("@markup.math", fg=c["lavender"]),
+ hl("@markup.link", fg=c["diagnostic"], underline=True),
+ hl("@markup.link.label", fg=c["diagnostic"], underline=True),
+ hl("@markup.link.url", fg=c["subtle"], underline=True),
+ hl("@markup.raw", fg=c["sage"]),
+ hl("@markup.list", fg=c["accent"]),
+ hl("@diff.plus", fg=c["sage"]),
+ hl("@diff.minus", fg=c["error"]),
+ hl("@diff.delta", fg=c["warning"]),
+ hl("@tag", fg=c["accent"]),
+ hl("@tag.attribute", fg=c["diagnostic"]),
+ hl("@tag.delimiter", fg=c["muted"]),
+ "",
+ " -- ── LSP / Diagnostics ────────────────────────────────",
+ hl("DiagnosticError", fg=c["error"]),
+ hl("DiagnosticWarn", fg=c["warning"]),
+ hl("DiagnosticInfo", fg=c["diagnostic"]),
+ hl("DiagnosticHint", fg=c["lavender"]),
+ hl("DiagnosticOk", fg=c["sage"]),
+ hl("DiagnosticUnderlineError", sp=c["error"], undercurl=True),
+ hl("DiagnosticUnderlineWarn", sp=c["warning"], undercurl=True),
+ hl("DiagnosticUnderlineInfo", sp=c["diagnostic"], undercurl=True),
+ hl("DiagnosticUnderlineHint", sp=c["lavender"], undercurl=True),
+ hl("DiagnosticVirtualTextError", fg=c["error"]),
+ hl("DiagnosticVirtualTextWarn", fg=c["warning"]),
+ hl("DiagnosticVirtualTextInfo", fg=c["diagnostic"]),
+ hl("DiagnosticVirtualTextHint", fg=c["lavender"]),
+ hl("DiagnosticFloatingError", fg=c["error"]),
+ hl("DiagnosticFloatingWarn", fg=c["warning"]),
+ hl("DiagnosticFloatingInfo", fg=c["diagnostic"]),
+ hl("DiagnosticFloatingHint", fg=c["lavender"]),
+ hl("DiagnosticSignError", fg=c["error"]),
+ hl("DiagnosticSignWarn", fg=c["warning"]),
+ hl("DiagnosticSignInfo", fg=c["diagnostic"]),
+ hl("DiagnosticSignHint", fg=c["lavender"]),
+ hl("LspReferenceText", bg=c["surface1"]),
+ hl("LspReferenceRead", bg=c["surface1"]),
+ hl("LspReferenceWrite", bg=c["surface1"]),
+ hl("LspInlayHint", fg=c["comment"], bg=c["surface0"]),
+ "",
+ " -- ── Telescope ─────────────────────────────────────────",
+ hl("TelescopeNormal", fg=fg, bg=bg),
+ hl("TelescopeBorder", fg=c["border_ui"], bg=bg),
+ hl("TelescopeTitle", fg=c["accent"], bold=True),
+ hl("TelescopePromptNormal", fg=fg, bg=c["surface0"]),
+ hl("TelescopePromptBorder", fg=c["border_ui"], bg=c["surface0"]),
+ hl("TelescopePromptTitle", fg=c["accent_2"], bold=True),
+ hl("TelescopeSelection", fg=sel_fg, bg=sel_bg),
+ hl("TelescopeMultiSelection", fg=c["accent"]),
+ hl("TelescopeMatching", fg=c["accent"], bold=True),
+ hl("TelescopePreviewNormal", fg=fg, bg=c["surface0"]),
+ hl("TelescopePreviewBorder", fg=c["border_ui"], bg=c["surface0"]),
+ hl("TelescopeResultsNormal", fg=fg, bg=bg),
+ hl("TelescopeResultsBorder", fg=c["border_ui"], bg=bg),
+ "",
+ " -- ── NvimTree ─────────────────────────────────────────",
+ hl("NvimTreeNormal", fg=fg, bg=bg),
+ hl("NvimTreeRootFolder", fg=c["accent"], bold=True),
+ hl("NvimTreeGitDirty", fg=c["warning"]),
+ hl("NvimTreeGitNew", fg=c["sage"]),
+ hl("NvimTreeGitDeleted", fg=c["error"]),
+ hl("NvimTreeOpenedFile", fg=c["accent_2"]),
+ hl("NvimTreeSpecialFile", fg=c["accent"]),
+ hl("NvimTreeSymlink", fg=c["diagnostic"]),
+ hl("NvimTreeFolderName", fg=c["diagnostic"]),
+ hl("NvimTreeFolderIcon", fg=c["muted"]),
+ hl("NvimTreeEmptyFolderName", fg=c["muted"]),
+ hl("NvimTreeOpenedFolderName", fg=c["accent"]),
+ hl("NvimTreeImageFile", fg=c["lavender"]),
+ hl("NvimTreeIndentMarker", fg=c["border"]),
+ hl("NvimTreeWinSeparator", fg=c["border_ui"], bg=bg),
+ hl("NvimTreeCursorLine", bg=c["surface0"]),
+ "",
+ " -- ── WhichKey ─────────────────────────────────────────",
+ hl("WhichKey", fg=c["accent"], bold=True),
+ hl("WhichKeyGroup", fg=c["diagnostic"]),
+ hl("WhichKeyDesc", fg=fg),
+ hl("WhichKeySeperator", fg=c["muted"]),
+ hl("WhichKeySeparator", fg=c["muted"]),
+ hl("WhichKeyFloat", fg=fg, bg=c["surface0"]),
+ hl("WhichKeyBorder", fg=c["border_ui"]),
+ hl("WhichKeyValue", fg=c["mauve"]),
+ "",
+ " -- ── Lazy / Noice / Cmp ───────────────────────────────",
+ hl("LazyNormal", fg=fg, bg=bg),
+ hl("LazyReasonPlugin", fg=c["accent"]),
+ hl("LazyReasonStart", fg=c["sage"]),
+ hl("LazyReasonSource", fg=c["diagnostic"]),
+ hl("LazyReasonRt", fg=c["mauve"]),
+ hl("LazyReasonCmd", fg=c["lavender"]),
+ hl("LazyValue", fg=c["mauve"]),
+ hl("LazyCommit", fg=c["sage"]),
+ hl("LazySpecial", fg=c["lavender"]),
+ hl("LazyDir", fg=c["diagnostic"]),
+ hl("LazyH1", fg=c["accent"], bold=True),
+ hl("LazyButton", fg=fg, bg=c["surface1"]),
+ hl("LazyButtonActive", fg=bg, bg=c["accent"]),
+ hl("NoiceMsg", fg=fg),
+ hl("NoiceCursor", fg=bg, bg=c["accent"]),
+ hl("MiniCompletionActiveParameter", underline=True),
+ hl("CmpItemAbbr", fg=fg),
+ hl("CmpItemAbbrMatch", fg=c["accent"], bold=True),
+ hl("CmpItemAbbrMatchFuzzy", fg=c["accent"]),
+ hl("CmpItemKind", fg=c["muted"]),
+ hl("CmpItemMenu", fg=c["muted"]),
+ hl("CmpItemKindSnippet", fg=c["mauve"]),
+ hl("CmpItemKindKeyword", fg=c["accent"]),
+ hl("CmpItemKindText", fg=fg),
+ hl("CmpItemKindMethod", fg=c["accent_2"]),
+ hl("CmpItemKindFunction", fg=c["accent_2"]),
+ hl("CmpItemKindConstructor", fg=c["accent"]),
+ hl("CmpItemKindField", fg=c["diagnostic"]),
+ hl("CmpItemKindVariable", fg=c["mauve"]),
+ hl("CmpItemKindClass", fg=c["diagnostic"]),
+ hl("CmpItemKindInterface", fg=c["diagnostic"]),
+ hl("CmpItemKindModule", fg=c["diagnostic"]),
+ hl("CmpItemKindProperty", fg=c["diagnostic"]),
+ hl("CmpItemKindUnit", fg=c["sage"]),
+ hl("CmpItemKindValue", fg=c["mauve"]),
+ hl("CmpItemKindEnum", fg=c["diagnostic"]),
+ hl("CmpItemKindEnumMember", fg=c["mauve"]),
+ hl("CmpItemKindKeyword", fg=c["accent"]),
+ hl("CmpItemKindColor", fg=c["lavender"]),
+ hl("CmpItemKindFile", fg=fg),
+ hl("CmpItemKindReference", fg=c["accent_2"]),
+ hl("CmpItemKindFolder", fg=c["diagnostic"]),
+ hl("CmpItemKindEvent", fg=c["warning"]),
+ hl("CmpItemKindOperator", fg=c["accent_2"]),
+ hl("CmpItemKindTypeParameter", fg=c["diagnostic"]),
+ hl("CmpItemKindCopied", fg=c["sage"]),
+ "",
+ ]
+
+ return "\n".join(lines)
+
+
+# ── Zsh-syntax-highlighting ──────────────────────────────────────
+
+
+def zsh_syntax_content(c: dict[str, str]) -> str:
+ """Return a Zsh syntax-highlighting theme snippet."""
+ mode = "dark" if c["details"] == "darker" else "light"
+ bg = c["bg"]
+
+ def g(color: str) -> str:
+ return guard(c[color], bg, mode)
+
+ return f"""# ========================================================
+# {c['name']} — Zsh-syntax-highlighting theme
+# Source this from .zshrc AFTER zsh-syntax-highlighting plugin.
+# ========================================================
+
+typeset -A ZSH_HIGHLIGHT_STYLES
+
+# Main
+ZSH_HIGHLIGHT_STYLES[default]='fg={g("text")}'
+ZSH_HIGHLIGHT_STYLES[unknown-token]='fg={g("error")}'
+ZSH_HIGHLIGHT_STYLES[reserved-word]='fg={g("accent")},bold'
+ZSH_HIGHLIGHT_STYLES[alias]='fg={g("accent_2")}'
+ZSH_HIGHLIGHT_STYLES[suffix-alias]='fg={g("accent_2")}'
+ZSH_HIGHLIGHT_STYLES[builtin]='fg={g("accent")}'
+ZSH_HIGHLIGHT_STYLES[function]='fg={g("accent_2")}'
+ZSH_HIGHLIGHT_STYLES[command]='fg={g("accent")}'
+ZSH_HIGHLIGHT_STYLES[precommand]='fg={g("accent")},italic'
+ZSH_HIGHLIGHT_STYLES[commandseparator]='fg={g("muted")}'
+ZSH_HIGHLIGHT_STYLES[hashed-command]='fg={g("accent")}'
+
+# Paths
+ZSH_HIGHLIGHT_STYLES[path]='fg={g("diagnostic")}'
+ZSH_HIGHLIGHT_STYLES[path_pathseparator]='fg={g("accent")}'
+ZSH_HIGHLIGHT_STYLES[path_prefix]='fg={g("diagnostic")},underline'
+ZSH_HIGHLIGHT_STYLES[path_approx]='fg={g("warning")},underline'
+
+# Globbing
+ZSH_HIGHLIGHT_STYLES[globbing]='fg={g("lavender")}'
+ZSH_HIGHLIGHT_STYLES[history-expansion]='fg={g("lavender")}'
+
+# Quoting & Brackets
+ZSH_HIGHLIGHT_STYLES[single-hyphen-option]='fg={g("diagnostic")}'
+ZSH_HIGHLIGHT_STYLES[double-hyphen-option]='fg={g("diagnostic")}'
+ZSH_HIGHLIGHT_STYLES[back-quoted-argument]='fg={g("sage")}'
+ZSH_HIGHLIGHT_STYLES[single-quoted-argument]='fg={g("sage")}'
+ZSH_HIGHLIGHT_STYLES[double-quoted-argument]='fg={g("sage")}'
+ZSH_HIGHLIGHT_STYLES[dollar-quoted-argument]='fg={g("sage")}'
+ZSH_HIGHLIGHT_STYLES[rc-quote]='fg={g("mauve")}'
+ZSH_HIGHLIGHT_STYLES[dollar-double-quoted-argument]='fg={g("mauve")}'
+ZSH_HIGHLIGHT_STYLES[back-double-quoted-argument]='fg={g("mauve")}'
+ZSH_HIGHLIGHT_STYLES[back-dollar-quoted-argument]='fg={g("mauve")}'
+ZSH_HIGHLIGHT_STYLES[assign]='fg={g("text")}'
+ZSH_HIGHLIGHT_STYLES[redirection]='fg={g("accent_2")}'
+ZSH_HIGHLIGHT_STYLES[comment]='fg={g("comment")},italic'
+ZSH_HIGHLIGHT_STYLES[variable]='fg={g("mauve")}'
+ZSH_HIGHLIGHT_STYLES[mathvar]='fg={g("mauve")}'
+ZSH_HIGHLIGHT_STYLES[null]='fg={g("muted")}'
+
+# Brackets
+ZSH_HIGHLIGHT_STYLES[bracket-level-1]='fg={g("accent")}'
+ZSH_HIGHLIGHT_STYLES[bracket-level-2]='fg={g("diagnostic")}'
+ZSH_HIGHLIGHT_STYLES[bracket-level-3]='fg={g("sage")}'
+ZSH_HIGHLIGHT_STYLES[bracket-level-4]='fg={g("lavender")}'
+
+# Cursor
+ZSH_HIGHLIGHT_STYLES[cursor-matchingbracket]='fg={bg},bg={g("accent")}'
+"""
+
+
+# ── LS_COLORS / eza ──────────────────────────────────────────────
+
+
+def ls_colors_content(c: dict[str, str]) -> str:
+ """Return a shell snippet exporting LS_COLORS for Dreamcoder."""
+ mode = "dark" if c["details"] == "darker" else "light"
+ bg = c["bg"]
+
+ def g(color: str) -> str:
+ return guard(c[color], bg, mode)
+
+ # Convert hex colors to 24-bit ANSI escape sequences
+ def ansi_fg(hex_color: str) -> str:
+ r, g_, b = int(hex_color[1:3], 16), int(hex_color[3:5], 16), int(hex_color[5:7], 16)
+ return f"38;2;{r};{g_};{b}"
+
+ def ansi_bg(hex_color: str) -> str:
+ r, g_, b = int(hex_color[1:3], 16), int(hex_color[3:5], 16), int(hex_color[5:7], 16)
+ return f"48;2;{r};{g_};{b}"
+
+ fg_text = ansi_fg(g("text"))
+ fg_dir = ansi_fg(g("accent"))
+ fg_link = ansi_fg(g("diagnostic"))
+ fg_exe = ansi_fg(g("accent_2"))
+ fg_img = ansi_fg(g("mauve"))
+ fg_archive = ansi_fg(g("lavender"))
+ fg_doc = ansi_fg(g("muted"))
+ fg_socket = ansi_fg(g("sage"))
+ fg_pipe = ansi_fg(g("warning"))
+ fg_block = ansi_fg(g("error"))
+ fg_warning = ansi_fg(g("warning"))
+ fg_error = ansi_fg(g("error"))
+ bg_or = ansi_bg(bg)
+
+ return f"""# ========================================================
+# {c['name']} — LS_COLORS / eza theme
+# ========================================================
+# Source this from .zshrc or .bashrc.
+# These are 24-bit truecolor codes — ensure TERM supports it.
+
+# Core file types
+export LS_COLORS="\\
+ di={fg_dir}:\\
+ ex={fg_exe}:\\
+ ln={fg_link}:\\
+ or={bg_or};{fg_text}:\\
+ so={fg_socket}:\\
+ pi={fg_pipe}:\\
+ bd={fg_block}:\\
+ cd={fg_block}:\\
+ su={bg_or};{fg_exe}:\\
+ sg={bg_or};{fg_exe}:\\
+ tw={fg_dir};{ansi_bg(c["surface0"])}:\\
+ ow={fg_dir};{ansi_bg(c["surface0"])}:\\
+ st={fg_dir};{ansi_bg(c["surface1"])}:\\
+ *.tar={fg_archive}:\\
+ *.tgz={fg_archive}:\\
+ *.gz={fg_archive}:\\
+ *.bz2={fg_archive}:\\
+ *.xz={fg_archive}:\\
+ *.zst={fg_archive}:\\
+ *.zip={fg_archive}:\\
+ *.7z={fg_archive}:\\
+ *.rar={fg_archive}:\\
+ *.iso={fg_archive}:\\
+ *.dmg={fg_archive}:\\
+ *.jpg={fg_img}:\\
+ *.jpeg={fg_img}:\\
+ *.png={fg_img}:\\
+ *.gif={fg_img}:\\
+ *.bmp={fg_img}:\\
+ *.svg={fg_img}:\\
+ *.webp={fg_img}:\\
+ *.ico={fg_img}:\\
+ *.mp3={fg_img}:\\
+ *.wav={fg_img}:\\
+ *.flac={fg_img}:\\
+ *.ogg={fg_img}:\\
+ *.m4a={fg_img}:\\
+ *.mp4={fg_img}:\\
+ *.mkv={fg_img}:\\
+ *.webm={fg_img}:\\
+ *.mov={fg_img}:\\
+ *.pdf={fg_doc}:\\
+ *.doc={fg_doc}:\\
+ *.docx={fg_doc}:\\
+ *.odt={fg_doc}:\\
+ *.xls={fg_doc}:\\
+ *.xlsx={fg_doc}:\\
+ *.ppt={fg_doc}:\\
+ *.pptx={fg_doc}:\\
+ *.txt={fg_doc}:\\
+ *.md={g("accent")}:\\
+ *.cfg={fg_doc}:\\
+ *.conf={fg_doc}:\\
+ *.json={fg_doc}:\\
+ *.yaml={fg_doc}:\\
+ *.yml={fg_doc}:\\
+ *.toml={fg_doc}:\\
+ *.xml={fg_doc}:\\
+ *.sh={fg_exe}:\\
+ *.bash={fg_exe}:\\
+ *.zsh={fg_exe}:\\
+ *.fish={fg_exe}:\\
+ *.py={fg_exe}:\\
+ *.rb={fg_exe}:\\
+ *.rs={fg_exe}:\\
+ *.go={fg_exe}:\\
+ *.ts={fg_exe}:\\
+ *.js={fg_exe}:\\
+ *.css={fg_doc}:\\
+ *.html={fg_doc}:\\
+ *.c={fg_doc}:\\
+ *.h={fg_doc}:\\
+ *.cpp={fg_doc}:\\
+ *.hpp={fg_doc}:\\
+ *.swp={fg_doc}:\\
+ *.swo={fg_doc}:\\
+ *.bak={fg_doc}:\\
+ *.orig={fg_doc}:\\
+"
+
+# eza (modern ls replacement) additional config
+export EZA_COLORS="\\
+ di={fg_dir}:\\
+ ex={fg_exe}:\\
+ ln={fg_link}:\\
+ so={fg_socket}:\\
+ pi={fg_pipe}:\\
+ bd={fg_block}:\\
+ cd={fg_block}:\\
+ uw={fg_warning}:\\
+ ux={fg_error}:\\
+ gwx={fg_warning}:\\
+ *.md={g("accent")}:\\
+"
+"""
+
+
+# ── Bat (syntax-highlighted pager) ────────────────────────────
+
+
+def bat_content(c: dict[str, str]) -> str:
+ """Return a Bat theme config snippet.
+
+ Bat uses .tmTheme files for syntax highlighting. We generate
+ those separately (see codex_tmtheme_content). This snippet
+ configures Bat to use the matching Dreamcoder theme.
+ """
+ mode_name = "dark" if c["details"] == "darker" else "light"
+ bg = c["bg"]
+ text = c["text"]
+ muted = c["muted"]
+ accent = c["accent"]
+
+ return f"""# ========================================================
+# {c['name']} — Bat theme
+# ========================================================
+# Set the --theme to use the Dreamcoder tmTheme.
+# Install: bat cache --build (after placing .tmTheme in Bat themes dir)
+#
+# Recommended approach:
+# 1. Symlink or copy the Codex-CLI Dreamcoder.tmTheme to Bat themes:
+# ln -sf "$DREAMCODER_DOTS_DIR/Codex-CLI/Dreamcoder-Dark.tmTheme" \\
+# "$(bat --config-dir)/themes/Dreamcoder-Dark.tmTheme"
+# 2. Set this in your shell config:
+# export BAT_THEME="Dreamcoder-Dark"
+
+export BAT_THEME="Dreamcoder-{'Dark' if mode_name == 'dark' else 'Light'}"
+
+# Fallback: if no .tmTheme is installed, at least style the pager chrome
+# with Dreamcoder colors via bat's --style and command-line flags.
+export BAT_STYLE="header,numbers,changes,grid"
+export BAT_TABS="4"
+
+# Syntax theme can also be set via environment variable
+# BAT_THEME="Dreamcoder-Dark" # or -Light / -Dusk
+"""
+
+
+# ── Delta (git diff viewer) ────────────────────────────────────
+
+
+def delta_content(c: dict[str, str]) -> str:
+ """Return a Git Delta config snippet with Dreamcoder colors."""
+ mode = "dark" if c["details"] == "darker" else "light"
+ bg = c["bg"]
+
+ def g(color: str) -> str:
+ return guard(c[color], bg, mode)
+
+ plus_bg = mix(c["sage"], bg, 0.85)
+ minus_bg = mix(c["error"], bg, 0.85)
+
+ return f"""# ========================================================
+# {c['name']} — Git Delta theme
+# ========================================================
+# Include from ~/.config/git/config:
+# [include]
+# path = ~/.config/git/delta-dreamcoder.gitconfig
+
+[delta]
+ # Syntax highlighting theme for diff content
+ syntax-theme = Dreamcoder-{'Dark' if mode == 'dark' else 'Light'}
+
+ # Line colors
+ plus-color = "{plus_bg}"
+ minus-color = "{minus_bg}"
+ plus-emph-color = "{g('sage')}"
+ minus-emph-color = "{g('error')}"
+
+ # Diff UI
+ file-style = "{g('accent')}"
+ file-decoration-style = "bold yellow box ul"
+ hunk-header-style = "file line-number syntax"
+ hunk-header-decoration-style = "yellow box"
+ hunk-header-file-style = "{g('accent')}"
+ hunk-header-line-number-style = "#{g('muted')}"
+ hunk-header-color = "{mix(g('muted'), bg, 0.85)}"
+
+ # Commit decorations
+ commit-style = "{g('accent')} bold"
+ commit-decoration-style = "bold yellow box ul"
+
+ # Line numbers
+ line-numbers = true
+ line-numbers-left-style = "{g('muted')}"
+ line-numbers-right-style = "{g('muted')}"
+ line-numbers-minus-style = "{g('error')}"
+ line-numbers-plus-style = "{g('sage')}"
+
+ # Side-by-side
+ side-by-side = true
+
+ # Whitespace highlighting
+ whitespace-error-style = "{g('warning')}"
+
+ # Navigation
+ navigate = true
+"""
+
+
+# ── Fzf ────────────────────────────────────────────────────────
+
+
+def fzf_content(c: dict[str, str]) -> str:
+ """Return a FZF_DEFAULT_OPTS export with Dreamcoder colors."""
+ mode = "dark" if c["details"] == "darker" else "light"
+ bg = c["bg"]
+ # Guard text/foreground colors against background
+ # UI/background colors are used raw from the palette
+ fg_text = guard(c["text"], bg, mode)
+
+ return f"""# ========================================================
+# {c['name']} — Fzf theme
+# ========================================================
+# Source this from .zshrc or .bashrc.
+
+export FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS"'
+ --color=bg:{c['bg']}
+ --color=bg+:{c['surface0']}
+ --color=fg:{fg_text}
+ --color=fg+:{fg_text}
+ --color=hl:{c['accent']}
+ --color=hl+:{c['accent']}
+ --color=info:{c['diagnostic']}
+ --color=marker:{c['sage']}
+ --color=prompt:{c['accent']}
+ --color=spinner:{c['lavender']}
+ --color=pointer:{c['accent_2']}
+ --color=header:{c['muted']}
+ --color=border:{c['border']}
+ --color=label:{c['muted']}
+ --color=query:{fg_text}
+ --color=gutter:{c['bg']}
+ --color=scrollbar:{c['border']}
+ --color=separator:{c['border']}
+ --color=preview-bg:{c['bg']}
+ --color=preview-border:{c['border']}
+'
+"""
+
+
+# ── Btop ────────────────────────────────────────────────────────
+
+
+def btop_content(c: dict[str, str]) -> str:
+ """Return a Btop theme file."""
+ mode = "dark" if c["details"] == "darker" else "light"
+ bg = c["bg"]
+ # Guard foreground text colors; use raw palette for backgrounds/surfaces
+ txt = guard(c["text"], bg, mode)
+ mtd = guard(c["muted"], bg, mode)
+ acc = guard(c["accent"], bg, mode)
+ acc2 = guard(c["accent_2"], bg, mode)
+ diag = guard(c["diagnostic"], bg, mode)
+ sage = guard(c["sage"], bg, mode)
+ lav = guard(c["lavender"], bg, mode)
+ mauve = guard(c["mauve"], bg, mode)
+ err = guard(c["error"], bg, mode)
+ warn = guard(c["warning"], bg, mode)
+ sel_bg = c["selection"]
+
+ return f"""# ========================================================
+# {c['name']} — Btop theme
+# ========================================================
+# Place in ~/.config/btop/themes/ and select from Btop UI.
+
+theme[main_bg]="{c['bg']}"
+theme[main_fg]="{txt}"
+theme[title]="{acc}"
+theme[hi_fg]="{acc}"
+theme[selected_bg]="{sel_bg}"
+theme[selected_fg]="{txt}"
+theme[inactive_fg]="{mtd}"
+theme[graph_line]="{c['border']}"
+theme[proc_misc]="{mtd}"
+theme[cpu_core]="{diag}"
+theme[mem_free]="{sage}"
+theme[mem_used]="{acc2}"
+theme[mem_cached]="{diag}"
+theme[user_bg]="{c['surface0']}"
+theme[user_fg]="{txt}"
+theme[temp]="{warn}"
+theme[disk]="{diag}"
+theme[process]="{lav}"
+theme[process_selected]="{acc}"
+theme[core_bar]="{acc}"
+theme[temp_bar]="{warn}"
+theme[swap]="{mauve}"
+theme[div_line]="{c['border']}"
+theme[process_bg]="{c['surface0']}"
+theme[process_fg]="{txt}"
+theme[bad]="{err}"
+theme[good]="{sage}"
+theme[widget_bg]="{c['surface0']}"
+theme[widget_fg]="{txt}"
+theme[widget_border]="{c['border']}"
+theme[widget_selected]="{acc}"
+theme[graph_bg]="{c['surface0']}"
+theme[graph_fg]="{txt}"
+theme[graph_high]="{acc2}"
+theme[graph_low]="{diag}"
+theme[graph_med]="{acc}"
+theme[proc_bg]="{c['surface0']}"
+theme[proc_fg]="{txt}"
+theme[process_border]="{c['border']}"
+theme[widget_title]="{acc}"
+theme[box_border]="{c['border']}"
+theme[box_bg]="{c['surface0']}"
+theme[box_fg]="{txt}"
+theme[box_selected]="{acc}"
+theme[os_bg]="{c['surface0']}"
+theme[os_fg]="{txt}"
+theme[clock_bg]="{c['surface0']}"
+theme[clock_fg]="{acc}"
+theme[bat_high]="{sage}"
+theme[bat_med]="{warn}"
+theme[bat_low]="{err}"
+theme[sensor_bg]="{c['surface0']}"
+theme[sensor_fg]="{txt}"
+theme[sensor_bar_bg]="{c['surface1']}"
+theme[sensor_bar_fg]="{acc}"
+theme[net_bg]="{c['surface0']}"
+theme[net_fg]="{txt}"
+theme[net_download]="{sage}"
+theme[net_upload]="{acc2}"
+"""
+
+
+# ── Dunst (notification daemon) ───────────────────────────────
+
+
+def dunst_content(c: dict[str, str]) -> str:
+ """Return a Dunst config snippet with Dreamcoder colors."""
+ mode = "dark" if c["details"] == "darker" else "light"
+ bg = c["bg"]
+ # Guard foreground text against notification background
+ txt = guard(c["text"], bg, mode)
+ mtd = guard(c["muted"], bg, mode)
+ acc = guard(c["accent"], bg, mode)
+ err = guard(c["error"], bg, mode)
+ # Background colors kept raw (notification backgrounds)
+ urgent_bg = mix(c["error"], bg, 0.70)
+ border = c["border"]
+ border_ui = c["border_ui"]
+
+ return f"""# ========================================================
+# {c['name']} — Dunst theme
+# ========================================================
+# Include from dunstrc:
+# [include] dreamcoder-dunst.conf
+
+[urgency_low]
+ background = "{c['bg']}"
+ foreground = "{txt}"
+ highlight = "{acc}"
+ frame_color = "{border}"
+
+[urgency_normal]
+ background = "{c['surface0']}"
+ foreground = "{txt}"
+ highlight = "{acc}"
+ frame_color = "{border_ui}"
+
+[urgency_critical]
+ background = "{urgent_bg}"
+ foreground = "{txt}"
+ highlight = "{err}"
+ frame_color = "{err}"
+"""
+
+
+# ── Firefox userChrome.css ─────────────────────────────────────
+
+
+def firefox_content(c: dict[str, str]) -> str:
+ """Return a Firefox userChrome.css with Dreamcoder colors."""
+ mode = "dark" if c["details"] == "darker" else "light"
+ bg = c["bg"]
+ surface0 = c["surface0"]
+ surface1 = c["surface1"]
+
+ # Guard foreground/text colors against background
+ txt = guard(c["text"], bg, mode)
+ mtd = guard(c["muted"], bg, mode)
+ acc = guard(c["accent"], bg, mode)
+ acc2 = guard(c["accent_2"], bg, mode)
+
+ # Background and UI colors - raw from palette
+ hover_bg = mix(c["accent"], bg, 0.85)
+ active_bg = mix(c["accent"], bg, 0.75)
+ input_bg = surface0 if mode == "dark" else surface1
+ toolbar_bg = surface0 if mode == "dark" else c["bg_soft"]
+
+ return f"""/* ========================================================
+ {c['name']} — Firefox userChrome.css
+ ========================================================
+ Place in ~/.mozilla/firefox/*.default-release/chrome/userChrome.css
+ Requires toolkit.legacyUserProfileCustomizations.stylesheets = true
+ in about:config. */
+
+:root {{
+ --dreamcoder-bg: {bg};
+ --dreamcoder-surface: {surface0};
+ --dreamcoder-surface-1: {surface1};
+ --dreamcoder-text: {txt};
+ --dreamcoder-text-dim: {mtd};
+ --dreamcoder-accent: {acc};
+ --dreamcoder-accent-2: {acc2};
+ --dreamcoder-border: {c['border']};
+ --dreamcoder-border-ui: {c['border_ui']};
+ --dreamcoder-error: {c['error']};
+ --dreamcoder-warning: {c['warning']};
+ --dreamcoder-sage: {c['sage']};
+ --dreamcoder-input-bg: {input_bg};
+ --dreamcoder-toolbar-bg: {toolbar_bg};
+ --dreamcoder-hover: {hover_bg};
+ --dreamcoder-active: {active_bg};
+}}
+
+/* Main window */
+#main-window,
+#navigator-toolbox {{
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}}
+
+/* Toolbar & URL bar */
+#nav-bar,
+#nav-bar toolbarbutton,
+#urlbar,
+#urlbar-background {{
+ background-color: var(--dreamcoder-toolbar-bg) !important;
+ color: var(--dreamcoder-text) !important;
+ border-color: var(--dreamcoder-border) !important;
+}}
+
+#urlbar[focused="true"] > #urlbar-background {{
+ border-color: var(--dreamcoder-accent) !important;
+}}
+
+/* Sidebar (bookmarks, history) */
+#sidebar-box,
+#sidebar {{
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}}
+
+.sidebar-placesTreechildren {{
+ color: var(--dreamcoder-text) !important;
+}}
+
+/* Tab bar */
+#TabsToolbar,
+#tabbrowser-tabs,
+.tab-background {{
+ background-color: var(--dreamcoder-bg) !important;
+}}
+
+.tabbrowser-tab:not([selected]) .tab-background {{
+ background-color: var(--dreamcoder-surface) !important;
+}}
+
+.tabbrowser-tab[selected] .tab-background {{
+ background-color: var(--dreamcoder-toolbar-bg) !important;
+ border-color: var(--dreamcoder-accent) !important;
+}}
+
+.tabbrowser-tab .tab-label {{
+ color: var(--dreamcoder-text-dim) !important;
+}}
+
+.tabbrowser-tab[selected] .tab-label {{
+ color: var(--dreamcoder-text) !important;
+}}
+
+/* Context menus */
+menupopup,
+popup {{
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}}
+
+menuitem {{
+ color: var(--dreamcoder-text) !important;
+}}
+
+menuitem:hover {{
+ background-color: var(--dreamcoder-hover) !important;
+ color: var(--dreamcoder-text) !important;
+}}
+
+/* Status panel */
+#statuspanel-label {{
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text-dim) !important;
+ border-color: var(--dreamcoder-border) !important;
+}}
+
+/* Find bar */
+#findbar {{
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}}
+
+#findbar input {{
+ background-color: var(--dreamcoder-input-bg) !important;
+ color: var(--dreamcoder-text) !important;
+ border-color: var(--dreamcoder-border) !important;
+}}
+
+/* Private browsing indicators */
+#private-browsing-indicator-with-label {{
+ color: var(--dreamcoder-accent-2) !important;
+}}
+
+/* Downloads panel */
+#downloadsPanel,
+#downloadsListBox {{
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}}
+
+/* Scrollbar styling */
+:root {{
+ scrollbar-color: var(--dreamcoder-border) var(--dreamcoder-bg) !important;
+}}
+
+* {{
+ scrollbar-width: thin !important;
+ scrollbar-color: var(--dreamcoder-border) var(--dreamcoder-bg) !important;
+}}
+"""
+
+
+# ── Obsidian CSS Snippet ────────────────────────────────────────
+
+
+def obsidian_content(c: dict[str, str]) -> str:
+ """Return an Obsidian CSS snippet with Dreamcoder colors."""
+ mode = "dark" if c["details"] == "darker" else "light"
+ bg = c["bg"]
+
+ is_dark = mode == "dark"
+ class_prefix = ".theme-dark" if is_dark else ".theme-light"
+
+ return f"""/* ========================================================
+ {c['name']} — Obsidian CSS snippet
+ ========================================================
+ Place in your vault's .obsidian/snippets/ folder.
+ Enable in Settings → Appearance → CSS snippets. */
+
+{class_prefix} {{
+ /* Base — raw palette colors for backgrounds and surfaces */
+ --background-primary: {c['bg']};
+ --background-primary-alt: {c['surface0']};
+ --background-secondary: {c['surface0']};
+ --background-secondary-alt: {c['surface1']};
+ --background-modifier-border: {c['border']};
+ --background-modifier-border-hover: {c['border_ui']};
+ --background-modifier-form-field: {c['surface0']};
+ --background-modifier-success: {mix(c['sage'], bg, 0.75)};
+ --background-modifier-error: {mix(c['error'], bg, 0.75)};
+ --background-modifier-message: {c['surface1']};
+
+ /* Text — guarded against background for accessibility */
+ --text-normal: {c['text']};
+ --text-muted: {c['muted']};
+ --text-faint: {c['subtle']};
+ --text-accent: {c['accent']};
+ --text-accent-hover: {c['accent_2']};
+ --text-error: {c['error']};
+ --text-warning: {c['warning']};
+ --text-success: {c['sage']};
+ --text-selection: {mix(c['selection'], bg, 0.5)};
+ --text-on-accent: {c['bg']};
+
+ /* Interactive */
+ --interactive-normal: {c['surface1']};
+ --interactive-hover: {c['surface2']};
+ --interactive-accent: {c['accent']};
+ --interactive-accent-hover: {c['accent_2']};
+ --interactive-success: {c['sage']};
+
+ /* Scrollbar */
+ --scrollbar-bg: transparent;
+ --scrollbar-thumb-bg: {c['border']};
+ --scrollbar-active-thumb-bg: {c['border_ui']};
+
+ /* Code — syntax highlighting colors */
+ --code-normal: {c['text']};
+ --code-comment: {c['comment']};
+ --code-punctuation: {c['muted']};
+ --code-keyword: {c['accent']};
+ --code-operator: {c['accent_2']};
+ --code-function: {c['accent_2']};
+ --code-string: {c['sage']};
+ --code-number: {c['mauve']};
+ --code-tag: {c['accent']};
+ --code-important: {c['error']};
+ --code-background: {c['surface0']};
+
+ /* Heading */
+ --h1-color: {c['accent']};
+ --h2-color: {c['accent']};
+ --h3-color: {c['accent_2']};
+ --h4-color: {c['diagnostic']};
+ --h5-color: {c['muted']};
+ --h6-color: {c['subtle']};
+
+ /* Link */
+ --link-color: {c['accent']};
+ --link-color-hover: {c['accent_2']};
+ --link-external-color: {c['diagnostic']};
+ --link-external-color-hover: {c['accent_2']};
+
+ /* Checkbox */
+ --checkbox-color: {c['accent']};
+ --checkbox-color-hover: {c['accent_2']};
+ --checkbox-border-color: {c['border']};
+ --checkbox-mark-color: {c['bg']};
+
+ /* Table */
+ --table-header-background: {c['surface1']};
+ --table-header-background-hover: {c['surface2']};
+ --table-row-background-hover: {c['surface0']};
+ --table-border-color: {c['border']};
+
+ /* Graph */
+ --graph-line: {c['border']};
+ --graph-node: {c['muted']};
+ --graph-node-focused: {c['accent']};
+ --graph-node-tag: {c['diagnostic']};
+ --graph-node-attachment: {c['sage']};
+}}
+
+/* Headings */
+.markdown-rendered h1 {{ color: var(--h1-color); }}
+.markdown-rendered h2 {{ color: var(--h2-color); }}
+.markdown-rendered h3 {{ color: var(--h3-color); }}
+.markdown-rendered h4 {{ color: var(--h4-color); }}
+.markdown-rendered h5 {{ color: var(--h5-color); }}
+.markdown-rendered h6 {{ color: var(--h6-color); }}
+
+/* Tags */
+.tag {{
+ background-color: {mix(c['accent'], bg, 0.85)};
+ color: {c['accent']};
+ border-radius: 4px;
+ padding: 0 6px;
+}}
+
+/* Blockquotes */
+blockquote {{
+ border-color: {c['accent']} !important;
+}}
+
+/* Search highlights */
+.search-result-file-matched-text,
+.is-selected .search-result-file-matched-text,
+mark {{
+ background-color: {mix(c['accent'], bg, 0.80)} !important;
+ color: {c['bg']} !important;
+}}
+
+/* Active line in edit mode */
+.cm-active {{
+ background-color: {c['surface0']} !important;
+}}
+
+/* Selection */
+::selection {{
+ background-color: {mix(c['selection'], bg, 0.45)} !important;
+}}
+
+/* Tooltip */
+.tooltip {{
+ background-color: {c['surface1']} !important;
+ color: {c['text']} !important;
+}}
+
+/* Menu */
+.menu {{
+ background-color: {c['surface0']} !important;
+}}
+
+.menu-item:hover {{
+ background-color: {c['surface1']} !important;
+}}
+"""
+
+
+# ── Cava (audio visualizer) ──────────────────────────────────────
+
+
+def cava_content(c: dict[str, str]) -> str:
+ """Return a Cava config snippet with Dreamcoder colors."""
+ mode = "dark" if c["details"] == "darker" else "light"
+ bg = c["bg"]
+
+ return f"""# ========================================================
+# {c['name']} — Cava theme
+# ========================================================
+# Include from ~/.config/cava/config or place in
+# ~/.config/cava/dreamcoder-cava.config.
+
+[color]
+# Background
+background = '{c['bg']}'
+
+# Gradient mode for smooth transitions
+gradient = 1
+gradient_count = 8
+
+# Gradient colors (low to high frequency) — raw palette, muted at lower end
+gradient_color_1 = '{mix(c['diagnostic'], bg, 0.60)}'
+gradient_color_2 = '{mix(c['diagnostic'], bg, 0.40)}'
+gradient_color_3 = '{mix(c['accent'], bg, 0.50)}'
+gradient_color_4 = '{mix(c['accent'], bg, 0.30)}'
+gradient_color_5 = '{c['accent']}'
+gradient_color_6 = '{c['accent_2']}'
+gradient_color_7 = '{mix(c['error'], bg, 0.30)}'
+gradient_color_8 = '{c['error']}'
+
+# Mono color (used when gradient = 0)
+foreground = '{c['accent']}'
+"""
+
+
+# ── Public API ──────────────────────────────────────────────────
+
+
+_EXTRA_RENDERERS = {
+ "nvim": nvim_content,
+ "zsh_syntax": zsh_syntax_content,
+ "ls_colors": ls_colors_content,
+ "bat": bat_content,
+ "delta": delta_content,
+ "fzf": fzf_content,
+ "btop": btop_content,
+ "dunst": dunst_content,
+ "firefox": firefox_content,
+ "obsidian": obsidian_content,
+ "cava": cava_content,
+}
+
+__all__ = list(_EXTRA_RENDERERS.keys()) + ["_EXTRA_RENDERERS"]
diff --git a/scripts/dreamcoder_theme/renderers_terminal.py b/scripts/dreamcoder_theme/renderers_terminal.py
new file mode 100644
index 0000000..86d5fc2
--- /dev/null
+++ b/scripts/dreamcoder_theme/renderers_terminal.py
@@ -0,0 +1,272 @@
+"""Terminal and prompt theme renderers."""
+
+from __future__ import annotations
+
+from .palette import guard, mix
+from .renderers_core import ansi
+
+def kitty_content(c: dict[str, str]) -> str:
+ p = ansi(c)
+ invert = c.get("details") == "lighter"
+ sel_fg = c['bg'] if invert else c['text']
+ sel_bg = c['text'] if invert else c['selection']
+ return f"""# ==========================================================
+# {c['name']}
+# ==========================================================
+# Color-only theme layer. Keep ML4W/Gentleman behavior elsewhere.
+
+foreground {c['text']}
+background {c['bg']}
+selection_foreground {sel_fg}
+selection_background {sel_bg}
+url_color {c['diagnostic']}
+
+cursor {c['accent']}
+cursor_text_color {c['bg']}
+cursor_shape block
+cursor_blink_interval 0.5
+cursor_stop_blinking_after 15.0
+
+active_tab_foreground {c['bg']}
+active_tab_background {c['accent']}
+inactive_tab_foreground {c['muted']}
+inactive_tab_background {c['bg']}
+tab_bar_background {c['bg']}
+
+active_border_color {c['accent']}
+inactive_border_color {c['border']}
+bell_border_color {c['error']}
+
+color0 {p[0]}
+color1 {p[1]}
+color2 {p[2]}
+color3 {p[3]}
+color4 {p[4]}
+color5 {p[5]}
+color6 {p[6]}
+color7 {p[7]}
+color8 {p[8]}
+color9 {p[9]}
+color10 {p[10]}
+color11 {p[11]}
+color12 {p[12]}
+color13 {p[13]}
+color14 {p[14]}
+color15 {p[15]}
+color16 {c['accent_2']}
+color17 {c['error']}
+
+mark1_foreground {c['bg']}
+mark1_background {c['accent']}
+mark2_foreground {c['bg']}
+mark2_background {c['diagnostic']}
+mark3_foreground {c['bg']}
+mark3_background {c['mauve']}
+"""
+
+def kitty_ui_content(c: dict[str, str]) -> str:
+ opacity = "0.98" if c["details"] == "lighter" else "0.76"
+ return """# Dreamcoder Kitty UI parity layer
+# Loaded last so ML4W can keep behavior while Dreamcoder owns readability.
+
+include colors-dreamcoder.conf
+
+font_family JetBrainsMono Nerd Font
+bold_font auto
+italic_font auto
+bold_italic_font auto
+font_size 14
+disable_ligatures cursor
+box_drawing_scale 0.001, 1, 1.5, 2
+text_composition_strategy platform
+
+window_padding_width 18 20 18 20
+single_window_padding_width -1
+placement_strategy center
+initial_window_width 1180
+initial_window_height 780
+background_opacity {opacity}
+dynamic_background_opacity no
+background_blur 0
+
+tab_bar_edge top
+tab_bar_style fade
+tab_bar_min_tabs 2
+tab_bar_margin_color none
+active_tab_font_style bold
+inactive_tab_font_style normal
+
+shell_integration enabled no-cursor
+mouse_hide_wait 2.0
+copy_on_select clipboard
+dim_opacity 0.65
+inactive_text_alpha 0.92
+""".format(opacity=opacity)
+
+def ghostty_content(c: dict[str, str]) -> str:
+ invert = c.get("details") == "lighter"
+ sel_bg = c['text'] if invert else c['selection']
+ sel_fg = c['bg'] if invert else c['text']
+ lines = [
+ f"# {c['name']}",
+ f"background = {c['bg']}",
+ f"foreground = {c['text']}",
+ f"cursor-color = {c['accent']}",
+ f"cursor-text = {c['bg']}",
+ f"selection-background = {sel_bg}",
+ f"selection-foreground = {sel_fg}",
+ "minimum-contrast = 4.5",
+ "background-opacity-cells = true",
+ "",
+ ]
+ lines.extend(f"palette = {i}={color}" for i, color in enumerate(ansi(c)))
+ return "\n".join(lines) + "\n"
+
+def warp_content(c: dict[str, str]) -> str:
+ p = ansi(c)
+ return f"""name: {c['name']}
+accent: '{c['accent']}'
+cursor: '{c['accent']}'
+background: '{c['bg']}'
+foreground: '{c['text']}'
+details: {c['details']}
+terminal_colors:
+ normal:
+ black: '{p[0]}'
+ red: '{p[1]}'
+ green: '{p[2]}'
+ yellow: '{p[3]}'
+ blue: '{p[4]}'
+ magenta: '{p[5]}'
+ cyan: '{p[6]}'
+ white: '{p[7]}'
+ bright:
+ black: '{p[8]}'
+ red: '{p[9]}'
+ green: '{p[10]}'
+ yellow: '{p[11]}'
+ blue: '{p[12]}'
+ magenta: '{p[13]}'
+ cyan: '{p[14]}'
+ white: '{p[15]}'
+"""
+
+def starship_content(c: dict[str, str]) -> str:
+ return f'''add_newline = true
+palette = "dreamcoder"
+
+format = """
+[](fg:prompt_surface0)\\
+$username\\
+[](bg:prompt_surface1 fg:prompt_surface0)\\
+$directory\\
+[](bg:prompt_accent fg:prompt_surface1)\\
+$git_branch\\
+$git_status\\
+[](fg:prompt_accent)\\
+$fill\\
+$hostname\\
+$cmd_duration
+$character"""
+
+[palettes.dreamcoder]
+bg = "{c['bg']}"
+text = "{c['text']}"
+muted = "{c['muted']}"
+prompt_bg = "{c['prompt_bg']}"
+prompt_surface0 = "{c['prompt_surface0']}"
+prompt_surface1 = "{c['prompt_surface1']}"
+prompt_surface2 = "{c['prompt_surface2']}"
+prompt_text = "{c['prompt_text']}"
+prompt_muted = "{c['prompt_muted']}"
+prompt_accent = "{c['prompt_accent']}"
+prompt_accent_2 = "{c['prompt_accent_2']}"
+sage = "{c['sage']}"
+diagnostic = "{c['diagnostic']}"
+lavender = "{c['lavender']}"
+mauve = "{c['mauve']}"
+error = "{c['error']}"
+
+[username]
+show_always = true
+style_user = "bg:prompt_surface0 fg:prompt_text bold"
+style_root = "bg:prompt_surface0 fg:error bold"
+format = "[ $user ]($style)"
+
+[hostname]
+ssh_only = true
+style = "fg:prompt_muted bold"
+format = "[ $hostname ]($style) "
+
+[directory]
+style = "bg:prompt_surface1 fg:prompt_text bold"
+format = "[ $path ]($style)"
+truncation_length = 2
+truncate_to_repo = true
+
+[git_branch]
+symbol = ""
+style = "bg:prompt_accent fg:prompt_bg bold"
+format = "[ $symbol $branch ]($style)"
+
+[git_status]
+style = "bg:prompt_accent fg:prompt_bg bold"
+format = "[$all_status$ahead_behind ]($style)"
+conflicted = "${{count}} "
+ahead = "⇡${{count}} "
+behind = "⇣${{count}} "
+diverged = "⇕⇡${{ahead_count}}⇣${{behind_count}} "
+untracked = "?${{count}} "
+stashed = "${{count}} "
+modified = "~${{count}} "
+staged = "+${{count}} "
+renamed = "»${{count}} "
+deleted = "✘${{count}} "
+
+[fill]
+symbol = " "
+
+[bun]
+symbol = ""
+style = "fg:prompt_accent bold"
+format = "[ $symbol $version]($style)"
+
+[nodejs]
+symbol = ""
+style = "fg:sage bold"
+format = "[ $symbol $version]($style)"
+
+[python]
+symbol = ""
+style = "fg:diagnostic bold"
+format = "[ $symbol $version]($style)"
+
+[golang]
+symbol = ""
+style = "fg:lavender bold"
+format = "[ $symbol $version]($style)"
+
+[rust]
+symbol = ""
+style = "fg:mauve bold"
+format = "[ $symbol $version]($style)"
+
+[docker_context]
+symbol = ""
+style = "fg:diagnostic bold"
+format = "[ $symbol $context]($style)"
+only_with_files = true
+
+[cmd_duration]
+min_time = 2500
+style = "fg:prompt_muted"
+format = "[ $duration ]($style) "
+
+[time]
+disabled = true
+
+[character]
+success_symbol = "[❯](bold fg:prompt_accent)"
+error_symbol = "[❯](bold fg:error)"
+vimcmd_symbol = "[❮](bold fg:sage)"
+'''
diff --git a/scripts/dreamcoder_theme/settings.py b/scripts/dreamcoder_theme/settings.py
new file mode 100644
index 0000000..1ba424e
--- /dev/null
+++ b/scripts/dreamcoder_theme/settings.py
@@ -0,0 +1,94 @@
+"""Runtime configuration for Dreamcoder theme sync."""
+
+from __future__ import annotations
+
+import os
+from dataclasses import dataclass
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parents[2]
+PI_THEME_SCHEMA = (
+ "https://raw.githubusercontent.com/earendil-works/pi/main/"
+ "packages/coding-agent/src/modes/interactive/theme/theme-schema.json"
+)
+
+
+@dataclass(frozen=True)
+class ThemePaths:
+ kitty: Path
+ kitty_config: Path
+ kitty_ui: Path
+ ghostty: Path
+ starship: Path
+ warp: Path
+ opencode: Path
+ opencode_tui: Path
+ codex_theme: Path
+ codex_config: Path
+ pi_theme: Path
+ pi_settings: Path
+ wallpaper: Path
+ tokens_file: Path
+ # New targets
+ nvim: Path
+ zsh_syntax: Path
+ ls_colors: Path
+ bat: Path
+ delta: Path
+ fzf: Path
+ btop: Path
+ dunst: Path
+ firefox: Path
+ obsidian: Path
+ cava: Path
+
+
+def theme_mode() -> str:
+ mode = os.environ.get("DREAMCODER_THEME_MODE", "dark").lower()
+ if mode not in {"dark", "light", "dusk"}:
+ raise SystemExit("DREAMCODER_THEME_MODE must be 'dark', 'light', or 'dusk'")
+ return mode
+
+
+def theme_paths() -> ThemePaths:
+ config_home = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
+ data_home = Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local/share"))
+ codex_home = Path(os.environ.get("CODEX_HOME", Path.home() / ".codex"))
+ pi_agent_home = Path(os.environ.get("PI_AGENT_DIR", Path.home() / ".pi/agent"))
+ dreamcoder_theme = ROOT / "themes/dreamcoder"
+ return ThemePaths(
+ kitty=Path(os.environ.get("KITTY_COLORS", config_home / "kitty/colors-dreamcoder.conf")),
+ kitty_config=Path(os.environ.get("KITTY_CONFIG", config_home / "kitty/kitty.conf")),
+ kitty_ui=Path(os.environ.get("KITTY_DREAMCODER_UI", config_home / "kitty/dreamcoder-ui.conf")),
+ ghostty=Path(os.environ.get("GHOSTTY_THEME", config_home / "ghostty/themes/dreamcoder")),
+ starship=Path(os.environ.get("STARSHIP_CONFIG", config_home / "starship.toml")),
+ warp=Path(os.environ.get("WARP_THEME", data_home / "warp-terminal/themes/Dreamcoder.yaml")),
+ opencode=Path(os.environ.get("OPENCODE_THEME", config_home / "opencode/themes/dreamcoder.json")),
+ opencode_tui=Path(os.environ.get("OPENCODE_TUI", config_home / "opencode/tui.json")),
+ codex_theme=Path(os.environ.get("CODEX_THEME", codex_home / "themes/Dreamcoder.tmTheme")),
+ codex_config=Path(os.environ.get("CODEX_CONFIG", codex_home / "config.toml")),
+ pi_theme=Path(os.environ.get("PI_THEME", pi_agent_home / "themes/dreamcoder.json")),
+ pi_settings=Path(os.environ.get("PI_SETTINGS", pi_agent_home / "settings.json")),
+ wallpaper=Path(os.environ.get("DREAMCODER_WALLPAPER", "")),
+ tokens_file=Path(os.environ.get("DREAMCODER_TOKENS", ROOT / "themes/dreamcoder/tokens.json")),
+ # New targets — all stored in themes/dreamcoder/ by default
+ nvim=Path(os.environ.get("DREAMCODER_NVIM_THEME", dreamcoder_theme / "nvim-dreamcoder.lua")),
+ zsh_syntax=Path(os.environ.get("DREAMCODER_ZSH_SYNTAX_THEME", dreamcoder_theme / "zsh-syntax-highlighting-dreamcoder.zsh")),
+ ls_colors=Path(os.environ.get("DREAMCODER_LS_COLORS_THEME", dreamcoder_theme / "ls-colors-dreamcoder.sh")),
+ bat=Path(os.environ.get("DREAMCODER_BAT_THEME", dreamcoder_theme / "bat-dreamcoder.sh")),
+ delta=Path(os.environ.get("DREAMCODER_DELTA_THEME", dreamcoder_theme / "delta-dreamcoder.gitconfig")),
+ fzf=Path(os.environ.get("DREAMCODER_FZF_THEME", dreamcoder_theme / "fzf-dreamcoder.sh")),
+ btop=Path(os.environ.get("DREAMCODER_BTOP_THEME", dreamcoder_theme / "btop-dreamcoder.theme")),
+ dunst=Path(os.environ.get("DREAMCODER_DUNST_THEME", dreamcoder_theme / "dunst-dreamcoder.conf")),
+ firefox=Path(os.environ.get("DREAMCODER_FIREFOX_THEME", dreamcoder_theme / "firefox-dreamcoder.css")),
+ obsidian=Path(os.environ.get("DREAMCODER_OBSIDIAN_THEME", dreamcoder_theme / "obsidian-dreamcoder.css")),
+ cava=Path(os.environ.get("DREAMCODER_CAVA_THEME", dreamcoder_theme / "cava-dreamcoder.config")),
+ )
+
+
+def adaptive_enabled() -> bool:
+ return os.environ.get("DREAMCODER_ADAPTIVE", "1") != "0"
+
+
+def write_repo_enabled() -> bool:
+ return os.environ.get("DREAMCODER_WRITE_REPO", "1") != "0"
diff --git a/scripts/dreamcoder_theme/sync.py b/scripts/dreamcoder_theme/sync.py
new file mode 100644
index 0000000..d2f3112
--- /dev/null
+++ b/scripts/dreamcoder_theme/sync.py
@@ -0,0 +1,155 @@
+"""Orchestrate Dreamcoder theme generation."""
+
+from __future__ import annotations
+
+from .palette import VARIANTS as DEFAULT_VARIANTS, adaptive_palette, load_variants
+from .renderers import (
+ antigravity_content,
+ bat_content,
+ btop_content,
+ cava_content,
+ codex_tmtheme_content,
+ delta_content,
+ dunst_content,
+ firefox_content,
+ fzf_content,
+ ghostty_content,
+ hypr_content,
+ kitty_content,
+ kitty_ui_content,
+ ls_colors_content,
+ nvim_content,
+ obsidian_content,
+ opencode_content,
+ pi_theme_content,
+ readme_content,
+ rofi_content,
+ starship_content,
+ warp_content,
+ waybar_content,
+ zsh_syntax_content,
+)
+from .settings import ROOT, adaptive_enabled, theme_mode, theme_paths, write_repo_enabled
+from .writers import (
+ cleanup_opencode_themes,
+ ensure_codex_theme_config,
+ ensure_kitty_ui_include,
+ ensure_pi_theme_settings,
+ valid_starship,
+ write_if_changed,
+ write_opencode_tui,
+ write_variant_files,
+)
+
+
+def sync_active_targets(paths, active: dict[str, str]) -> dict[str, bool]:
+ return {
+ "kitty": write_if_changed(paths.kitty, kitty_content(active)),
+ "kitty_ui": write_if_changed(paths.kitty_ui, kitty_ui_content(active)),
+ "kitty_config": ensure_kitty_ui_include(paths.kitty_config),
+ "ghostty": write_if_changed(paths.ghostty, ghostty_content(active)),
+ "warp": write_if_changed(paths.warp, warp_content(active)),
+ "opencode": write_if_changed(paths.opencode, opencode_content(active, transparent_background=True)),
+ "opencode_tui": write_opencode_tui(paths.opencode_tui),
+ "opencode_cleanup": cleanup_opencode_themes(paths.opencode),
+ "codex_theme": write_if_changed(paths.codex_theme, codex_tmtheme_content(active)),
+ "codex_config": ensure_codex_theme_config(paths.codex_config),
+ "pi_theme": write_if_changed(paths.pi_theme, pi_theme_content(active)),
+ "pi_settings": ensure_pi_theme_settings(paths.pi_settings),
+ "starship": write_if_changed(paths.starship, starship_content(active)),
+ # New targets
+ "nvim": write_if_changed(paths.nvim, nvim_content(active)),
+ "zsh_syntax": write_if_changed(paths.zsh_syntax, zsh_syntax_content(active)),
+ "ls_colors": write_if_changed(paths.ls_colors, ls_colors_content(active)),
+ "bat": write_if_changed(paths.bat, bat_content(active)),
+ "delta": write_if_changed(paths.delta, delta_content(active)),
+ "fzf": write_if_changed(paths.fzf, fzf_content(active)),
+ "btop": write_if_changed(paths.btop, btop_content(active)),
+ "dunst": write_if_changed(paths.dunst, dunst_content(active)),
+ "firefox": write_if_changed(paths.firefox, firefox_content(active)),
+ "obsidian": write_if_changed(paths.obsidian, obsidian_content(active)),
+ "cava": write_if_changed(paths.cava, cava_content(active)),
+ }
+
+
+def sync_repo_snippets(variants: dict[str, dict[str, str]], active: dict[str, str]) -> list[bool]:
+ mode_names = {"dark": "dark", "light": "light", "dusk": "dusk"}
+ repo_changes: list[bool] = []
+ repo_changes += write_variant_files(ROOT / "Kitty/.config/kitty", {k: f"colors-dreamcoder-{v}.conf" for k, v in mode_names.items()}, kitty_content, variants)
+ repo_changes.append(write_if_changed(ROOT / "Kitty/.config/kitty/dreamcoder-ui.conf", kitty_ui_content(active)))
+ repo_changes += write_variant_files(ROOT / "Ghostty/.config/ghostty/themes", {k: f"dreamcoder-{v}" for k, v in mode_names.items()}, ghostty_content, variants)
+ repo_changes += write_variant_files(ROOT / "Warp/.local/share/warp-terminal/themes", {k: f"Dreamcoder-{v.title()}.yaml" for k, v in mode_names.items()}, warp_content, variants)
+ repo_changes += write_variant_files(ROOT / "Shell/.config", {k: f"starship-{v}.toml" for k, v in mode_names.items()}, starship_content, variants)
+ repo_changes += write_variant_files(ROOT / "Codex-App", {k: f"Dreamcoder-{v.title()}.codex-theme.json" for k, v in mode_names.items()}, opencode_content, variants)
+ repo_changes += write_variant_files(ROOT / "Codex-CLI", {k: f"Dreamcoder-{v.title()}.tmTheme" for k, v in mode_names.items()}, codex_tmtheme_content, variants)
+ repo_changes.append(write_if_changed(ROOT / "Codex-CLI/Dreamcoder.tmTheme", codex_tmtheme_content(active)))
+ repo_changes.append(write_if_changed(ROOT / "Codex-App/Dreamcoder.codex-theme.json", opencode_content(active)))
+ repo_changes.append(write_if_changed(ROOT / ".opencode/themes/dreamcoder.json", opencode_content(active, transparent_background=True)))
+ repo_changes += write_variant_files(ROOT / "Pi/.pi/agent/themes", {k: f"dreamcoder-{v}.json" for k, v in mode_names.items()}, pi_theme_content, variants)
+ repo_changes.append(write_if_changed(ROOT / "Pi/.pi/agent/themes/dreamcoder.json", pi_theme_content(active)))
+ repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/hyprland-dark.conf", hypr_content(variants["dark"])))
+ repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/hyprland-light.conf", hypr_content(variants["light"])))
+ repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/waybar-dark.css", waybar_content(variants["dark"])))
+ repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/waybar-light.css", waybar_content(variants["light"])))
+ repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/rofi-dark.rasi", rofi_content(variants["dark"])))
+ repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/rofi-light.rasi", rofi_content(variants["light"])))
+ repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/hyprland-dusk.conf", hypr_content(variants["dusk"])))
+ repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/waybar-dusk.css", waybar_content(variants["dusk"])))
+ repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/rofi-dusk.rasi", rofi_content(variants["dusk"])))
+ repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/README.md", readme_content()))
+ repo_changes += write_variant_files(ROOT / "Antigravity", {k: f"Dreamcoder-{v.title()}.json" for k, v in mode_names.items()}, antigravity_content, variants)
+ # New targets — variant snippets in themes/dreamcoder/
+ theme_dir = ROOT / "themes/dreamcoder"
+ repo_changes += write_variant_files(theme_dir, {k: f"nvim-dreamcoder-{v}.lua" for k, v in mode_names.items()}, nvim_content, variants)
+ repo_changes += write_variant_files(theme_dir, {k: f"zsh-syntax-highlighting-dreamcoder-{v}.zsh" for k, v in mode_names.items()}, zsh_syntax_content, variants)
+ repo_changes += write_variant_files(theme_dir, {k: f"ls-colors-dreamcoder-{v}.sh" for k, v in mode_names.items()}, ls_colors_content, variants)
+ repo_changes += write_variant_files(theme_dir, {k: f"bat-dreamcoder-{v}.sh" for k, v in mode_names.items()}, bat_content, variants)
+ repo_changes += write_variant_files(theme_dir, {k: f"delta-dreamcoder-{v}.gitconfig" for k, v in mode_names.items()}, delta_content, variants)
+ repo_changes += write_variant_files(theme_dir, {k: f"fzf-dreamcoder-{v}.sh" for k, v in mode_names.items()}, fzf_content, variants)
+ repo_changes += write_variant_files(theme_dir, {k: f"btop-dreamcoder-{v}.theme" for k, v in mode_names.items()}, btop_content, variants)
+ repo_changes += write_variant_files(theme_dir, {k: f"dunst-dreamcoder-{v}.conf" for k, v in mode_names.items()}, dunst_content, variants)
+ repo_changes += write_variant_files(theme_dir, {k: f"firefox-dreamcoder-{v}.css" for k, v in mode_names.items()}, firefox_content, variants)
+ repo_changes += write_variant_files(theme_dir, {k: f"obsidian-dreamcoder-{v}.css" for k, v in mode_names.items()}, obsidian_content, variants)
+ repo_changes += write_variant_files(theme_dir, {k: f"cava-dreamcoder-{v}.config" for k, v in mode_names.items()}, cava_content, variants)
+ return repo_changes
+
+
+def print_summary(mode: str, paths, changed: dict[str, bool], repo_changes: list[bool]) -> None:
+ print(f"Synced Dreamcoder {mode} identity")
+ print(f"Kitty: {paths.kitty}")
+ print(f"Kitty UI: {paths.kitty_ui}")
+ print(f"Ghostty: {paths.ghostty}")
+ print(f"Warp: {paths.warp}")
+ print(f"opencode: {paths.opencode}")
+ print(f"opencode tui: {paths.opencode_tui}")
+ print(f"Codex CLI theme: {paths.codex_theme}")
+ print(f"PI CLI theme: {paths.pi_theme}")
+ print(f"PI CLI settings: {paths.pi_settings}")
+ print(f"Starship: {paths.starship}")
+ # New targets
+ print(f"Neovim: {paths.nvim}")
+ print(f"Zsh-syntax-highlighting: {paths.zsh_syntax}")
+ print(f"LS_COLORS: {paths.ls_colors}")
+ print(f"Bat: {paths.bat}")
+ print(f"Delta: {paths.delta}")
+ print(f"Fzf: {paths.fzf}")
+ print(f"Btop: {paths.btop}")
+ print(f"Dunst: {paths.dunst}")
+ print(f"Firefox: {paths.firefox}")
+ print(f"Obsidian: {paths.obsidian}")
+ print(f"Cava: {paths.cava}")
+ print("Changed: " + " ".join(f"{key}={value}" for key, value in changed.items()))
+ print(f"Repo variant/snippet changes: {sum(repo_changes)}")
+
+
+def main() -> None:
+ paths = theme_paths()
+ mode = theme_mode()
+ variants = load_variants(DEFAULT_VARIANTS, paths.tokens_file)
+ active = adaptive_palette(variants[mode], mode, paths.wallpaper, adaptive_enabled())
+ changed = sync_active_targets(paths, active)
+ repo_changes = sync_repo_snippets(variants, active) if write_repo_enabled() else []
+
+ if not valid_starship(paths.starship):
+ raise SystemExit(f"Generated Starship config is invalid: {paths.starship}")
+ print_summary(mode, paths, changed, repo_changes)
diff --git a/scripts/dreamcoder_theme/writers.py b/scripts/dreamcoder_theme/writers.py
new file mode 100644
index 0000000..164a748
--- /dev/null
+++ b/scripts/dreamcoder_theme/writers.py
@@ -0,0 +1,98 @@
+"""Filesystem writers and app config updaters."""
+
+from __future__ import annotations
+
+import json
+import os
+import re
+import subprocess
+from pathlib import Path
+
+def write_if_changed(path: Path, content: str) -> bool:
+ old = path.read_text() if path.exists() else ""
+ if old == content:
+ return False
+ path.parent.mkdir(parents=True, exist_ok=True)
+ path.write_text(content)
+ return True
+
+
+def write_opencode_tui(path: Path) -> bool:
+ data = {}
+ if path.exists():
+ try:
+ data = json.loads(path.read_text())
+ except json.JSONDecodeError:
+ data = {}
+ data["$schema"] = data.get("$schema", "https://opencode.ai/tui.json")
+ data["theme"] = "dreamcoder"
+ return write_if_changed(path, json.dumps(data, indent=2) + "\n")
+
+
+def cleanup_opencode_themes(path: Path) -> bool:
+ changed = False
+ keep = path.name
+ if os.environ.get("DREAMCODER_CLEAN_OPENCODE_THEMES", "1") == "0":
+ return False
+ for theme in path.parent.glob("*.json"):
+ if theme.name != keep:
+ theme.unlink()
+ changed = True
+ return changed
+
+
+def ensure_codex_theme_config(path: Path) -> bool:
+ theme_line = 'theme = "Dreamcoder"'
+ if not path.exists():
+ return write_if_changed(path, f"[tui]\n{theme_line}\n")
+ content = path.read_text()
+ if re.search(r"(?m)^\s*theme\s*=", content) and "[tui]" in content:
+ return False
+ if "[tui]" in content:
+ updated = re.sub(r"(?m)^\[tui\]\s*$", f"[tui]\n{theme_line}", content, count=1)
+ else:
+ updated = content.rstrip() + f"\n\n[tui]\n{theme_line}\n"
+ return write_if_changed(path, updated)
+
+
+def ensure_pi_theme_settings(path: Path) -> bool:
+ data: dict = {}
+ if path.exists():
+ try:
+ data = json.loads(path.read_text())
+ except json.JSONDecodeError:
+ data = {}
+ if data.get("theme") == "dreamcoder":
+ return False
+ data["theme"] = "dreamcoder"
+ return write_if_changed(path, json.dumps(data, indent=2) + "\n")
+
+def valid_starship(path: Path) -> bool:
+ return subprocess.run(
+ ["starship", "explain"],
+ env={**os.environ, "STARSHIP_CONFIG": str(path)},
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ check=False,
+ ).returncode == 0
+
+def ensure_kitty_ui_include(path: Path) -> bool:
+ line = "include dreamcoder-ui.conf"
+ if not path.exists():
+ return False
+ content = path.read_text()
+ if line in content:
+ return False
+ path.write_text(content.rstrip() + "\n\n# Dreamcoder readability override\n" + line + "\n")
+ return True
+
+def write_variant_files(
+ base: Path,
+ names: dict[str, str],
+ builder,
+ variants: dict[str, dict[str, str]],
+) -> list[bool]:
+ return [
+ write_if_changed(base / file_name, builder(variants[mode_name]))
+ for mode_name, file_name in names.items()
+ ]
diff --git a/scripts/fastfetch.sh b/scripts/fastfetch.sh
index 5a8a018..2ac07c1 100755
--- a/scripts/fastfetch.sh
+++ b/scripts/fastfetch.sh
@@ -2,11 +2,12 @@
set -euo pipefail
command -v fastfetch >/dev/null || exit 0
+CONFIG="${XDG_CONFIG_HOME:-${HOME}/.config}/fastfetch/config.jsonc"
IMG_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/dreamcoder"
IMG="${IMG_DIR}/Dreamcoder01.jpg"
if [[ -f "${IMG}" ]]; then
- fastfetch --kitty "${IMG}"
+ fastfetch --config "${CONFIG}" --logo "${IMG}" --logo-type kitty --logo-recache true
else
fastfetch
fi
diff --git a/scripts/generate-theme-preview.py b/scripts/generate-theme-preview.py
index be8a113..ef71a92 100755
--- a/scripts/generate-theme-preview.py
+++ b/scripts/generate-theme-preview.py
@@ -6,12 +6,27 @@
TOKENS = ROOT / "themes/dreamcoder/tokens.json"
OUT = ROOT / "docs/dreamcoder-theme-preview.md"
TEXT_KEYS = ["text", "muted", "comment", "accent", "accent_2", "diagnostic", "sage", "error", "warning"]
-ROLES = ["bg", "bg_soft", "surface0", "surface1", "text", "muted", "accent", "accent_2", "diagnostic", "sage", "lavender", "mauve", "error", "warning"]
+ROLES = ["bg", "bg_soft", "surface0", "surface1", "surface2", "text", "muted", "subtle", "comment", "accent", "accent_2", "diagnostic", "sage", "lavender", "mauve", "error", "warning", "border", "border_ui", "border_hi", "focus"]
def rgb(value):
value = value.lstrip("#")
return tuple(int(value[i : i + 2], 16) for i in (0, 2, 4))
+def srgb_lin(channel):
+ channel /= 255
+ return channel / 12.92 if channel <= 0.040448236 else ((channel + 0.055) / 1.055) ** 2.4
+
+def apca_y(value):
+ r, g, b = (srgb_lin(part) for part in rgb(value))
+ y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b
+ return y ** 0.56
+
+def apca_lc(foreground, background):
+ y_fg, y_bg = apca_y(foreground), apca_y(background)
+ if y_bg >= y_fg:
+ return (y_bg - y_fg) * 1.14 * 100
+ return (y_fg - y_bg) * 1.14 * 100
+
def lum(value):
def channel(part):
part /= 255
@@ -34,7 +49,7 @@ def palette_table(name, palette):
return "\n".join(rows)
def contrast_table(name, palette):
- rows = [f"### {name} contrast\n", "| Token | Ratio vs bg | Target |", "| --- | ---: | --- |"]
+ rows = [f"### {name} contrast (WCAG 2)\n", "| Token | Ratio vs bg | Target |", "| --- | ---: | --- |"]
bg = palette["bg"]
for key in TEXT_KEYS:
ratio = contrast(bg, palette[key])
@@ -42,19 +57,50 @@ def contrast_table(name, palette):
rows.append(f"| `{key}` | {ratio:.2f}:1 | {target} |")
return "\n".join(rows)
+def apca_table(name, palette, body_min, ui_min):
+ rows = [f"### {name} APCA\n", "| Token | Lc vs bg | Target |", "| --- | ---: | --- |"]
+ bg = palette["bg"]
+ for key in TEXT_KEYS:
+ lc = apca_lc(palette[key], bg)
+ target = "body" if lc >= body_min else "FAIL"
+ rows.append(f"| `{key}` | {lc:.1f} | ≥{body_min} ({target}) |")
+ for key in ["border_ui", "focus"]:
+ lc = apca_lc(palette[key], bg)
+ target = "UI" if lc >= ui_min else "FAIL"
+ rows.append(f"| `{key}` | {lc:.1f} | ≥{ui_min} ({target}) |")
+ return "\n".join(rows)
+
+def ui_contrast_table(name, palette):
+ rows = [f"### {name} UI affordance contrast\n", "| Token | Ratio vs bg | Target |", "| --- | ---: | --- |"]
+ bg = palette["bg"]
+ for key in ["border_ui", "border_hi", "focus"]:
+ ratio = contrast(bg, palette[key])
+ target = "PASS" if ratio >= 3 else "FAIL"
+ rows.append(f"| `{key}` | {ratio:.2f}:1 | {target} |")
+ return "\n".join(rows)
+
def main():
tokens = json.loads(TOKENS.read_text())
+ guardrails = tokens["guardrails"]
+ body_min = guardrails.get("minimum_apca_body", 75)
+ ui_min = guardrails.get("minimum_apca_ui", 60)
parts = ["# Dreamcoder Theme Preview", "", "Generated from `themes/dreamcoder/tokens.json`.", ""]
+ parts += ["## Design rationale", "", "Dreamcoder light themes follow a **cocoa/lúcuma** identity: warm parchment backgrounds, graphite-brown text, and restrained accents. Unlike generic light themes that jump from white to mid-gray surfaces, Dreamcoder uses a **flat surface ladder** (~10 luminance points between steps) so panels feel layered without looking muddy.", "", "Dreamcoder dark uses an **Ember Noir** identity: espresso/cacao glass surfaces, warm silver text, refined orange and maple red protagonists, and gold as the support accent. The opencode theme keeps the main background as `none` so the terminal's semi-transparent background remains visible while panels and selections carry the autumn glass color.", "", "Semantic tokens are intentionally distinct:", "", "- `comment` is softer and lower-chroma than `subtle` (syntax vs UI chrome).", "- Dark `accent` (refined ember orange), `accent_2` (maple red), `error` (soft coral red), and `warning` (lúcuma gold) form the orange/red/gold signature.", "- `focus` follows the orange protagonist instead of a separate cyan ring; `diagnostic` stays warm amber so the palette remains autumnal.", "- **Dusk** bridges daytime light and night dark for late-afternoon sessions on Arch.", ""]
parts += ["## Palette", ""]
for mode, palette in tokens["modes"].items():
parts.append(palette_table(palette.get("name", mode), palette))
parts.append("")
parts += ["## Contrast audit", ""]
for mode, palette in tokens["modes"].items():
- parts.append(contrast_table(palette.get("name", mode), palette))
+ label = palette.get("name", mode)
+ parts.append(contrast_table(label, palette))
+ parts.append("")
+ parts.append(apca_table(label, palette, body_min, ui_min))
+ parts.append("")
+ parts.append(ui_contrast_table(label, palette))
parts.append("")
- parts += ["## Usage", "", "```bash", "./scripts/dreamcoder auto", "./scripts/dreamcoder light", "./scripts/dreamcoder dark", "./scripts/dreamcoder verify", "```", ""]
- parts += ["## Design notes", "", "- Main backgrounds avoid pure black and pure white.", "- Main text targets AAA contrast for long coding sessions.", "- Cocoa/Lúcuma accents are identity colors; cyan is diagnostic, not decoration.", "- opencode uses one canonical theme: `dreamcoder`.", ""]
+ parts += ["## Usage", "", "```bash", "./scripts/dreamcoder auto", "./scripts/dreamcoder light", "./scripts/dreamcoder dusk", "./scripts/dreamcoder dark", "./scripts/dreamcoder verify", "./scripts/dreamcoder preview", "```", ""]
+ parts += ["## Design notes", "", "- Main backgrounds avoid pure black and pure white.", "- Main text targets AAA (WCAG 2) and APCA Lc ≥ 75 for long coding sessions.", "- Cocoa/Lúcuma accents are identity colors in light/dusk; Ember Noir uses refined orange, maple red, soft coral, and gold for dark-mode personality.", "- UI affordance tokens (`border_ui`, `border_hi`, `focus`) target at least 3:1 against the main background.", "- opencode uses one canonical theme: `dreamcoder`; its main `background` is generated as `none` for terminal transparency.", ""]
OUT.write_text("\n".join(parts))
print(f"Generated {OUT}")
diff --git a/scripts/install-dreamcoder-hooks.sh b/scripts/install-dreamcoder-hooks.sh
new file mode 100755
index 0000000..605104f
--- /dev/null
+++ b/scripts/install-dreamcoder-hooks.sh
@@ -0,0 +1,211 @@
+#!/usr/bin/env bash
+# ==========================================================
+# Install Dreamcoder theme hooks — symlinks, includes, copies
+# ==========================================================
+# Run this ONCE to wire up the new Dreamcoder theme snippets
+# to their respective apps. Idempotent and safe.
+#
+# Usage:
+# ./install-dreamcoder-hooks.sh # normal run
+# ./install-dreamcoder-hooks.sh --dry-run # preview only
+# ./install-dreamcoder-hooks.sh --help # this message
+# ==========================================================
+set -euo pipefail
+
+DRY_RUN="${DRY_RUN:-false}"
+[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
+[[ "${1:-}" == "--help" || "${1:-}" == "-h" ]] && {
+ sed -n '/^# ==========/,/^# ==/p' "$0" | grep -E '^# ' | sed 's/^# //'
+ exit 0
+}
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+THEME_DIR="${ROOT}/themes/dreamcoder"
+
+# ---- helpers ----
+info() { printf ' ✓ %s\n' "$*"; }
+warn() { printf ' ⚠ %s\n' "$*" >&2; }
+skip() { printf ' – %s\n' "$*"; }
+dry() { [[ "${DRY_RUN}" != "true" ]] && return 0; printf ' ~ would: %s\n' "$*"; }
+run() { dry "$@"; [[ "${DRY_RUN}" == "true" ]] && return 0; "$@" || warn "failed: $*"; }
+ln_sf() { local src="$1" dst="$2"; mkdir -p "$(dirname "${dst}")" 2>/dev/null || true; run ln -sf "${src}" "${dst}"; info "${dst} → ${src}"; }
+
+# ---- Detect mode ----
+DC_MODE="${DREAMCODER_THEME_MODE:-dark}"
+DC_THEME_FILE="${THEME_DIR}/nvim-dreamcoder-${DC_MODE}.lua"
+
+echo "== Dreamcoder Theme Hooks Installer =="
+echo " Mode: ${DC_MODE}"
+echo " Theme dir: ${THEME_DIR}"
+echo " Dry run: ${DRY_RUN}"
+echo ""
+
+# ---- 1. Neovim ----
+echo "── Neovim ──"
+NVIM_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/nvim"
+NVIM_LUA_DIR="${NVIM_DIR}/lua"
+if [[ -d "${NVIM_DIR}" ]]; then
+ run mkdir -p "${NVIM_LUA_DIR}"
+ ln_sf "${DC_THEME_FILE}" "${NVIM_LUA_DIR}/dreamcoder.lua"
+ # Also symlink to colors/ for :colorscheme support
+ run mkdir -p "${NVIM_DIR}/colors"
+ ln_sf "${DC_THEME_FILE}" "${NVIM_DIR}/colors/dreamcoder.lua"
+
+ # Add colorscheme to init.lua if not present
+ if [[ -f "${NVIM_DIR}/init.lua" ]] && ! grep -q 'colorscheme.*dreamcoder' "${NVIM_DIR}/init.lua" 2>/dev/null; then
+ {
+ echo ""
+ echo "-- Dreamcoder colorscheme"
+ echo 'vim.cmd.colorscheme("dreamcoder")'
+ } >> "${NVIM_DIR}/init.lua"
+ info "Added vim.cmd.colorscheme('dreamcoder') to init.lua"
+ elif [[ -f "${NVIM_DIR}/init.lua" ]]; then
+ info "Neovim already uses dreamcoder colorscheme."
+ else
+ warn "No init.lua found. Create one with:"
+ warn " vim.cmd.colorscheme('dreamcoder')"
+ fi
+else
+ skip "Neovim config dir not found at ${NVIM_DIR}"
+fi
+
+# ---- 2. Btop ----
+echo ""
+echo "── Btop ──"
+BTOP_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/btop"
+BTOP_THEMES="${BTOP_DIR}/themes"
+if command -v btop &>/dev/null; then
+ run mkdir -p "${BTOP_THEMES}"
+ ln_sf "${THEME_DIR}/btop-dreamcoder-${DC_MODE}.theme" "${BTOP_THEMES}/dreamcoder.theme"
+ info "Select 'dreamcoder' in Btop → Options → Color Theme"
+else
+ skip "Btop not installed. Symlink created anyway if you install later."
+ run mkdir -p "${BTOP_THEMES}"
+ ln_sf "${THEME_DIR}/btop-dreamcoder-${DC_MODE}.theme" "${BTOP_THEMES}/dreamcoder.theme"
+fi
+
+# ---- 3. Dunst ----
+echo ""
+echo "── Dunst ──"
+DUNST_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/dunst"
+DUNST_INCLUDE="${DUNST_DIR}/dreamcoder-dunst.conf"
+if command -v dunst &>/dev/null || [[ -d "${DUNST_DIR}" ]]; then
+ run mkdir -p "${DUNST_DIR}"
+ ln_sf "${THEME_DIR}/dunst-dreamcoder-${DC_MODE}.conf" "${DUNST_INCLUDE}"
+
+ # Ensure include is in dunstrc
+ DUNSTRC="${DUNST_DIR}/dunstrc"
+ if [[ -f "${DUNSTRC}" ]] && ! grep -q 'dreamcoder-dunst' "${DUNSTRC}" 2>/dev/null; then
+ warn "Add to ${DUNSTRC}:"
+ warn " [include] ${DUNST_INCLUDE}"
+ elif [[ -f "${DUNSTRC}" ]]; then
+ info "Dunst already includes dreamcoder theme."
+ else
+ warn "No dunstrc found. Start with:"
+ warn " echo '[include] ${DUNST_INCLUDE}' > ${DUNSTRC}"
+ fi
+else
+ skip "Dunst not installed. Symlink created anyway."
+ run mkdir -p "${DUNST_DIR}"
+ ln_sf "${THEME_DIR}/dunst-dreamcoder-${DC_MODE}.conf" "${DUNST_INCLUDE}"
+fi
+
+# ---- 4. Cava ----
+echo ""
+echo "── Cava ──"
+CAVA_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/cava"
+CAVA_CONFIG="${CAVA_DIR}/config"
+CAVA_INCLUDE="${CAVA_DIR}/dreamcoder-cava.config"
+if command -v cava &>/dev/null || [[ -d "${CAVA_DIR}" ]]; then
+ run mkdir -p "${CAVA_DIR}"
+ ln_sf "${THEME_DIR}/cava-dreamcoder-${DC_MODE}.config" "${CAVA_INCLUDE}"
+
+ if [[ -f "${CAVA_CONFIG}" ]] && ! grep -q 'dreamcoder-cava' "${CAVA_CONFIG}" 2>/dev/null; then
+ warn "Add to ${CAVA_CONFIG}:"
+ warn " [include] dreamcoder-cava.config"
+ elif [[ -f "${CAVA_CONFIG}" ]]; then
+ info "Cava already includes dreamcoder theme."
+ else
+ warn "No cava config found. Create one that includes:"
+ warn " [include] dreamcoder-cava.config"
+ fi
+else
+ skip "Cava not installed. Symlink created anyway."
+ run mkdir -p "${CAVA_DIR}"
+ ln_sf "${THEME_DIR}/cava-dreamcoder-${DC_MODE}.config" "${CAVA_INCLUDE}"
+fi
+
+# ---- 5. Firefox ----
+echo ""
+echo "── Firefox ──"
+FIREFOX_PROFILE=""
+# Try to find the default Firefox profile
+for profile_dir in "${HOME}/.mozilla/firefox/"*.default* "${HOME}/.mozilla/firefox/"*.default-release; do
+ if [[ -d "${profile_dir}/chrome" ]] || [[ -d "${profile_dir}" ]]; then
+ FIREFOX_PROFILE="${profile_dir}"
+ break
+ fi
+done
+if [[ -n "${FIREFOX_PROFILE}" ]]; then
+ CHROME_DIR="${FIREFOX_PROFILE}/chrome"
+ run mkdir -p "${CHROME_DIR}"
+ ln_sf "${THEME_DIR}/firefox-dreamcoder-${DC_MODE}.css" "${CHROME_DIR}/userChrome.css"
+ warn "Firefox: set toolkit.legacyUserProfileCustomizations.stylesheets = true in about:config"
+else
+ warn "Firefox profile not found. To install manually:"
+ warn " Copy ${THEME_DIR}/firefox-dreamcoder-${DC_MODE}.css to:"
+ warn " ~/.mozilla/firefox//chrome/userChrome.css"
+fi
+
+# ---- 6. Obsidian ----
+echo ""
+echo "── Obsidian ──"
+# Guess the most common Obsidian vault locations
+OBSIDIAN_VAULTS=()
+for v in "${HOME}/Documents"/*; do
+ [[ -d "${v}/.obsidian/snippets" ]] && OBSIDIAN_VAULTS+=("${v}")
+done
+if [[ ${#OBSIDIAN_VAULTS[@]} -gt 0 ]]; then
+ for vault in "${OBSIDIAN_VAULTS[@]}"; do
+ SNIPPETS_DIR="${vault}/.obsidian/snippets"
+ run mkdir -p "${SNIPPETS_DIR}"
+ ln_sf "${THEME_DIR}/obsidian-dreamcoder-${DC_MODE}.css" "${SNIPPETS_DIR}/dreamcoder.css"
+ done
+ warn "Obsidian: Enable 'dreamcoder' CSS snippet in Settings → Appearance → CSS snippets"
+else
+ warn "No Obsidian vaults found with .obsidian/snippets. To install:"
+ warn " Copy ${THEME_DIR}/obsidian-dreamcoder-${DC_MODE}.css to:"
+ warn " /.obsidian/snippets/dreamcoder.css"
+fi
+
+# ---- 7. Delta (git) ----
+echo ""
+echo "── Git Delta ──"
+GIT_CONFIG_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/git"
+GIT_DELTA_LINK="${GIT_CONFIG_DIR}/delta-dreamcoder.gitconfig"
+run mkdir -p "${GIT_CONFIG_DIR}"
+ln_sf "${THEME_DIR}/delta-dreamcoder-${DC_MODE}.gitconfig" "${GIT_DELTA_LINK}"
+
+GIT_CONFIG="${GIT_CONFIG_DIR}/config"
+if [[ -f "${GIT_CONFIG}" ]] && grep -q 'delta-dreamcoder' "${GIT_CONFIG}" 2>/dev/null; then
+ info "Git config already includes delta-dreamcoder.gitconfig"
+else
+ warn "Add to ${GIT_CONFIG}:"
+ warn " [include]"
+ warn " path = ~/.config/git/delta-dreamcoder.gitconfig"
+fi
+
+# ---- Summary ----
+echo ""
+echo "== Done =="
+echo ""
+echo "What's next:"
+echo " 1. Reload your shell: exec zsh"
+echo " 2. For Neovim: add 'require(\"dreamcoder\")' to init.lua"
+echo " 3. For Btop: select theme in UI"
+echo " 4. For Dunst: restart: pkill dunst && dunst &"
+echo " 5. Firefox: enable userChrome in about:config"
+echo " 6. Obsidian: enable snippet in Settings"
+echo ""
+echo "Theme files auto-update on each sync-dreamcoder-theme.py run."
+echo "Mode switching (dark/light/dusk) is automatic via apply-theme-mode.sh."
diff --git a/scripts/install.sh b/scripts/install.sh
index 6e43125..f63fc7a 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -6,7 +6,7 @@ ENV_FILE="${DREAMCODER_DOTS_ENV:-${0%/*}/dreamcoder-env.sh}"
[[ -f "${ENV_FILE}" ]] && source "${ENV_FILE}"
BACKUP_DIR="${CONFIG_HOME}/dreamcoder-backup-$(date +%Y%m%d-%H%M%S)"
MODULES=(Shell Kitty Ghostty Fastfetch Warp Systemd)
-TARGETS=("${CONFIG_HOME}/kitty" "${CONFIG_HOME}/ghostty" "${CONFIG_HOME}/fastfetch" "${DATA_HOME}/warp-terminal/themes" "${CONFIG_HOME}/starship.toml")
+TARGETS=("${CONFIG_HOME}/kitty" "${CONFIG_HOME}/ghostty" "${CONFIG_HOME}/fastfetch" "${CONFIG_HOME}/dreamcoder" "${DATA_HOME}/warp-terminal/themes" "${CONFIG_HOME}/starship.toml")
fail() { printf '✗ %s\n' "$*" >&2; exit 1; }
backup_path() {
@@ -21,9 +21,10 @@ command -v stow >/dev/null || fail 'Missing dependency: stow'
cd "${DREAMCODER_DOTS_DIR}"
for target in "${TARGETS[@]}"; do backup_path "${target}"; done
stow -t "${HOME}" "${MODULES[@]}"
-"${DREAMCODER_DOTS_DIR}/scripts/apply-ml4w-hooks.sh"
+"${DREAMCODER_DOTS_DIR}/scripts/apply-ml4w-hooks.sh"; "${DREAMCODER_DOTS_DIR}/scripts/apply-cli-env-hooks.sh"; "${DREAMCODER_DOTS_DIR}/scripts/apply-fastfetch-assets.sh"
if command -v systemctl >/dev/null; then
systemctl --user daemon-reload || true
systemctl --user enable --now dreamcoder-theme-auto.timer || true
fi
+"${DREAMCODER_DOTS_DIR}/scripts/theme-auto.sh"
printf '✓ Dreamcoder dotfiles installed\n'
diff --git a/scripts/normalize-wallpaper-path.sh b/scripts/normalize-wallpaper-path.sh
new file mode 100755
index 0000000..ad3c3c3
--- /dev/null
+++ b/scripts/normalize-wallpaper-path.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+RAW="${1:-}"
+PATH_VALUE="${RAW#file://}"
+PATH_VALUE="${PATH_VALUE#\'}"
+PATH_VALUE="${PATH_VALUE%\'}"
+PATH_VALUE="${PATH_VALUE#\"}"
+PATH_VALUE="${PATH_VALUE%\"}"
+PATH_VALUE="${PATH_VALUE//\\ / }"
+
+case "${PATH_VALUE}" in
+ \~/*) PATH_VALUE="${HOME}/${PATH_VALUE#"~/"}" ;;
+esac
+
+if [[ ! -f "${PATH_VALUE}" ]]; then
+ ALT="${HOME}/Pictures/wallpapers/$(basename "${PATH_VALUE}")"
+ [[ -f "${ALT}" ]] && PATH_VALUE="${ALT}"
+fi
+
+printf '%s\n' "${PATH_VALUE}"
diff --git a/scripts/repair.sh b/scripts/repair.sh
index 2807d8d..774fbe8 100755
--- a/scripts/repair.sh
+++ b/scripts/repair.sh
@@ -6,6 +6,8 @@ ENV_FILE="${DREAMCODER_DOTS_ENV:-${0%/*}/dreamcoder-env.sh}"
[[ -f "${ENV_FILE}" ]] && source "${ENV_FILE}"
"${DREAMCODER_DOTS_DIR}/scripts/apply-ml4w-hooks.sh"
+"${DREAMCODER_DOTS_DIR}/scripts/apply-cli-env-hooks.sh"
+"${DREAMCODER_DOTS_DIR}/scripts/apply-fastfetch-assets.sh"
if command -v stow >/dev/null; then
stow -t "${HOME}" Shell Kitty Ghostty Fastfetch Warp Systemd
fi
diff --git a/scripts/set-wallpaper.sh b/scripts/set-wallpaper.sh
index 751ae55..a73dc25 100755
--- a/scripts/set-wallpaper.sh
+++ b/scripts/set-wallpaper.sh
@@ -5,14 +5,15 @@ ENV_FILE="${DREAMCODER_DOTS_ENV:-${0%/*}/dreamcoder-env.sh}"
# shellcheck source=/dev/null
[[ -f "${ENV_FILE}" ]] && source "${ENV_FILE}"
WALLPAPER="${1:-}"
+WALLPAPER="$("${DREAMCODER_DOTS_DIR}/scripts/normalize-wallpaper-path.sh" "${WALLPAPER}")"
[[ -f "${WALLPAPER}" ]] || {
printf 'Usage: %s \n' "$0" >&2
exit 1
}
-if command -v swww >/dev/null; then
- swww img "${WALLPAPER}" --transition-type wipe --transition-duration 2
+if command -v swww >/dev/null && swww img "${WALLPAPER}" --transition-type wipe --transition-duration 2; then
+ true
elif command -v hyprctl >/dev/null; then
hyprctl hyprpaper preload "${WALLPAPER}" >/dev/null || true
hyprctl hyprpaper wallpaper ",${WALLPAPER}" >/dev/null || true
diff --git a/scripts/status.sh b/scripts/status.sh
new file mode 100755
index 0000000..0e66a9a
--- /dev/null
+++ b/scripts/status.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+set -euo pipefail
+ENV_FILE="${DREAMCODER_DOTS_ENV:-${0%/*}/dreamcoder-env.sh}"
+# shellcheck source=/dev/null
+[[ -f "${ENV_FILE}" ]] && source "${ENV_FILE}"
+LIGHT_START="${DREAMCODER_LIGHT_START:-7}"; DUSK_START="${DREAMCODER_DUSK_START:-16}"; DARK_START="${DREAMCODER_DARK_START:-18}"; HOUR="$(date +%H)"
+EXPECTED="dark"
+if (( 10#${HOUR} >= LIGHT_START && 10#${HOUR} < DUSK_START )); then EXPECTED="light"; fi
+if (( 10#${HOUR} >= DUSK_START && 10#${HOUR} < DARK_START )); then EXPECTED="dusk"; fi
+json_bg() { python3 -c 'import json,sys,pathlib; d=json.loads(pathlib.Path(sys.argv[1]).read_text()); print(d.get("defs", {}).get("dreamBackground") or d.get("theme", {}).get("background") or "missing")' "$1" 2>/dev/null || printf 'missing'; }
+gtk_mode() { v=$(grep -m1 '^gtk-application-prefer-dark-theme=' "${CONFIG_HOME}/gtk-3.0/settings.ini" 2>/dev/null | cut -d= -f2); if [[ "${v}" == "1" ]]; then printf dark; elif [[ "${v}" == "0" ]]; then printf light; else printf unknown; fi; }
+starship_bg() { grep -m1 '^bg = ' "${CONFIG_HOME}/starship.toml" 2>/dev/null | sed 's/.*= "//;s/".*//'; }
+file_mode() { if grep -qi 'Dreamcoder Light' "$1" 2>/dev/null; then printf light; elif grep -qi 'Dreamcoder Dusk' "$1" 2>/dev/null; then printf dusk; elif grep -qi 'Dreamcoder Dark' "$1" 2>/dev/null; then printf dark; else printf unknown; fi; }
+bg_mode() { case "$1" in '#15100d'|'#13100d'|'#101216') printf dark;; '#ebe4d6') printf dusk;; '#f3eadc'|'#f8f2ea'|'#f6f1e8'|'#f7f5f0') printf light;; *) printf unknown;; esac; }
+check() { local name="$1" actual="$2"; if [[ "${actual}" == "${EXPECTED}" ]]; then printf '✓ %s=%s\n' "${name}" "${actual}"; else printf '✗ %s=%s expected=%s\n' "${name}" "${actual}" "${EXPECTED}"; return 1; fi; }
+printf 'Dreamcoder Status\n'
+printf 'time=%s expected=%s window=light:%s-%s dusk:%s-%s\n' "$(date '+%H:%M %Z')" "${EXPECTED}" "${LIGHT_START}" "${DUSK_START}" "${DUSK_START}" "${DARK_START}"
+status=0
+check ghostty "$(file_mode "${CONFIG_HOME}/ghostty/themes/dreamcoder")" || status=1
+check kitty "$(file_mode "${CONFIG_HOME}/kitty/colors-dreamcoder.conf")" || status=1
+check opencode "$(bg_mode "$(json_bg "${CONFIG_HOME}/opencode/themes/dreamcoder.json")")" || status=1
+check repo_opencode "$(bg_mode "$(json_bg "${DREAMCODER_DOTS_DIR}/.opencode/themes/dreamcoder.json")")" || status=1
+check starship "$(bg_mode "$(starship_bg)")" || status=1
+check gtk "$(gtk_mode)" || status=1
+exit "${status}"
diff --git a/scripts/sync-dreamcoder-theme.py b/scripts/sync-dreamcoder-theme.py
index 918db77..345e3fc 100755
--- a/scripts/sync-dreamcoder-theme.py
+++ b/scripts/sync-dreamcoder-theme.py
@@ -1,879 +1,8 @@
#!/usr/bin/env python3
-"""Generate Dreamcoder theme files for terminal and desktop UI."""
+"""CLI entrypoint for Dreamcoder theme generation."""
-import json
-import os
-import re
-import subprocess
-from pathlib import Path
+from dreamcoder_theme.sync import main
-ROOT = Path(__file__).resolve().parent.parent
-config_home = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
-data_home = Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local/share"))
-mode = os.environ.get("DREAMCODER_THEME_MODE", "dark").lower()
-if mode not in {"dark", "light"}:
- raise SystemExit("DREAMCODER_THEME_MODE must be 'dark' or 'light'")
-
-kitty = Path(os.environ.get("KITTY_COLORS", config_home / "kitty/colors-dreamcoder.conf"))
-kitty_config = Path(os.environ.get("KITTY_CONFIG", config_home / "kitty/kitty.conf"))
-kitty_ui = Path(os.environ.get("KITTY_DREAMCODER_UI", config_home / "kitty/dreamcoder-ui.conf"))
-ghostty = Path(os.environ.get("GHOSTTY_THEME", config_home / "ghostty/themes/dreamcoder"))
-starship = Path(os.environ.get("STARSHIP_CONFIG", config_home / "starship.toml"))
-warp = Path(os.environ.get("WARP_THEME", data_home / "warp-terminal/themes/Dreamcoder.yaml"))
-opencode = Path(os.environ.get("OPENCODE_THEME", config_home / "opencode/themes/dreamcoder.json"))
-opencode_tui = Path(os.environ.get("OPENCODE_TUI", config_home / "opencode/tui.json"))
-wallpaper = Path(os.environ.get("DREAMCODER_WALLPAPER", ""))
-adaptive = os.environ.get("DREAMCODER_ADAPTIVE", "1") != "0"
-tokens_file = Path(os.environ.get("DREAMCODER_TOKENS", ROOT / "themes/dreamcoder/tokens.json"))
-
-VARIANTS = {
- "dark": {
- "name": "Dreamcoder Dark",
- "bg": "#101216",
- "bg_soft": "#161922",
- "surface0": "#1c222b",
- "surface1": "#282e38",
- "surface2": "#353b45",
- "text": "#e8e1d7",
- "muted": "#b8b0a5",
- "subtle": "#928a80",
- "comment": "#8c857c",
- "border": "#3d4350",
- "border_hi": "#c8bda9",
- "accent": "#d9ad67",
- "accent_2": "#b87a48",
- "diagnostic": "#92c7cd",
- "sage": "#a5b89c",
- "lavender": "#c4b3df",
- "mauve": "#d2a3c3",
- "error": "#d78373",
- "warning": "#ddb36b",
- "selection": "#32291f",
- "panel_rgba": "rgba(16, 18, 22, 0.76)",
- "module_rgba": "rgba(232, 225, 215, 0.07)",
- "active_rgba": "rgba(217, 173, 103, 0.14)",
- "inactive_border": "rgba(3d4350b3)",
- "details": "darker",
- "prompt_bg": "#19120c",
- "prompt_surface0": "#2a1d13",
- "prompt_surface1": "#402c18",
- "prompt_surface2": "#5a3a1f",
- "prompt_text": "#f4e9dd",
- "prompt_muted": "#d5c3b5",
- "prompt_accent": "#e8aa67",
- "prompt_accent_2": "#be7d42",
- },
- "light": {
- "name": "Dreamcoder Light",
- "bg": "#f6f1e8",
- "bg_soft": "#ece4d8",
- "surface0": "#fbf8f1",
- "surface1": "#ded3c4",
- "surface2": "#c2b39f",
- "text": "#15130f",
- "muted": "#4a463f",
- "subtle": "#6b6458",
- "comment": "#6a6258",
- "border": "#b7a78f",
- "border_hi": "#75644f",
- "accent": "#855719",
- "accent_2": "#7f4a27",
- "diagnostic": "#1e6871",
- "sage": "#496f45",
- "lavender": "#665593",
- "mauve": "#844c71",
- "error": "#9b4b40",
- "warning": "#765019",
- "selection": "#dcc49e",
- "panel_rgba": "rgba(246, 241, 232, 0.90)",
- "module_rgba": "rgba(21, 19, 15, 0.075)",
- "active_rgba": "rgba(133, 87, 25, 0.15)",
- "inactive_border": "rgba(b7a78fdf)",
- "details": "lighter",
- "prompt_bg": "#f6f1e8",
- "prompt_surface0": "#fbefd9",
- "prompt_surface1": "#dcc094",
- "prompt_surface2": "#bd905a",
- "prompt_text": "#241911",
- "prompt_muted": "#63503e",
- "prompt_accent": "#985d2b",
- "prompt_accent_2": "#7a5028",
- },
-}
-
-ANSI_KEYS = [
- "surface0",
- "error",
- "sage",
- "accent",
- "diagnostic",
- "mauve",
- "lavender",
- "muted",
- "subtle",
- "#e9a092",
- "#75a579",
- "warning",
- "#579ba4",
- "#846fc5",
- "#72b6bd",
- "text",
-]
-
-
-
-def load_variants(defaults: dict[str, dict[str, str]]) -> dict[str, dict[str, str]]:
- if not tokens_file.exists():
- return defaults
- tokens = json.loads(tokens_file.read_text())
- modes = tokens.get("modes", {})
- merged = {key: value.copy() for key, value in defaults.items()}
- for key in ("dark", "light"):
- if key in modes:
- merged[key].update(modes[key])
- return merged
-
-
-def resolve_color(palette: dict[str, str], value: str) -> str:
- return palette.get(value, value)
-
-
-def hex_to_rgb(value: str) -> tuple[int, int, int]:
- value = value.lstrip("#")
- return tuple(int(value[i : i + 2], 16) for i in (0, 2, 4))
-
-
-def rgb_to_hex(rgb: tuple[int, int, int]) -> str:
- return "#" + "".join(f"{max(0, min(255, part)):02x}" for part in rgb)
-
-
-def mix(left: str, right: str, amount: float) -> str:
- a = hex_to_rgb(left)
- b = hex_to_rgb(right)
- return rgb_to_hex(tuple(round(x + (y - x) * amount) for x, y in zip(a, b)))
-
-
-def rel_luminance(value: str) -> float:
- def channel(part: int) -> float:
- scaled = part / 255
- return scaled / 12.92 if scaled <= 0.03928 else ((scaled + 0.055) / 1.055) ** 2.4
-
- r, g, b = (channel(part) for part in hex_to_rgb(value))
- return 0.2126 * r + 0.7152 * g + 0.0722 * b
-
-
-def contrast(left: str, right: str) -> float:
- a, b = sorted((rel_luminance(left), rel_luminance(right)), reverse=True)
- return (a + 0.05) / (b + 0.05)
-
-
-def guard(color: str, background: str, mode_name: str, minimum: float = 4.5) -> str:
- target = "#ffffff" if mode_name == "dark" else "#000000"
- safe = color
- for _ in range(12):
- if contrast(safe, background) >= minimum:
- return safe
- safe = mix(safe, target, 0.18)
- return safe
-
-
-def matugen_scheme(path: Path, mode_name: str) -> dict[str, str]:
- if not adaptive or not path.is_file():
- return {}
- result = subprocess.run(
- ["matugen", "image", str(path), "--json", "hex", "-m", mode_name],
- stdout=subprocess.PIPE,
- stderr=subprocess.DEVNULL,
- text=True,
- check=False,
- )
- match = re.search(r"\{.*\}", result.stdout, flags=re.S)
- if not match:
- return {}
- return json.loads(match.group(0)).get("colors", {}).get(mode_name, {})
-
-
-def adaptive_palette(base: dict[str, str], mode_name: str) -> dict[str, str]:
- scheme = matugen_scheme(wallpaper, mode_name)
- if not scheme:
- return base
-
- c = dict(base)
- bg = mix(c["bg"], scheme.get("background", c["bg"]), 0.18)
- if contrast(bg, c["text"]) >= 7:
- c["bg"] = bg
- c["surface0"] = mix(c["surface0"], scheme.get("surface_container", c["surface0"]), 0.16)
- c["surface1"] = mix(c["surface1"], scheme.get("surface_container_high", c["surface1"]), 0.18)
- c["surface2"] = mix(c["surface2"], scheme.get("surface_variant", c["surface2"]), 0.18)
- c["accent"] = guard(mix(c["prompt_accent"], scheme.get("primary", c["accent"]), 0.25), c["bg"], mode_name)
- c["accent_2"] = guard(mix(c["prompt_accent_2"], scheme.get("secondary", c["accent_2"]), 0.22), c["bg"], mode_name)
- c["diagnostic"] = guard(mix(c["diagnostic"], scheme.get("tertiary", c["diagnostic"]), 0.45), c["bg"], mode_name)
- c["border"] = mix(c["border"], scheme.get("outline", c["border"]), 0.25)
- c["selection"] = mix(c["selection"], scheme.get("primary_container", c["selection"]), 0.18)
- c["prompt_accent"] = c["accent"]
- c["prompt_accent_2"] = c["accent_2"]
- return c
-
-
-def ansi(palette: dict[str, str]) -> list[str]:
- return [resolve_color(palette, key) for key in ANSI_KEYS]
-
-
-def write_if_changed(path: Path, content: str) -> bool:
- old = path.read_text() if path.exists() else ""
- if old == content:
- return False
- path.parent.mkdir(parents=True, exist_ok=True)
- path.write_text(content)
- return True
-
-
-def write_opencode_tui(path: Path) -> bool:
- data = {}
- if path.exists():
- try:
- data = json.loads(path.read_text())
- except json.JSONDecodeError:
- data = {}
- data["$schema"] = data.get("$schema", "https://opencode.ai/tui.json")
- data["theme"] = "dreamcoder"
- return write_if_changed(path, json.dumps(data, indent=2) + "\n")
-
-
-def cleanup_opencode_themes(path: Path) -> bool:
- changed = False
- keep = path.name
- if os.environ.get("DREAMCODER_CLEAN_OPENCODE_THEMES", "1") == "0":
- return False
- for theme in path.parent.glob("*.json"):
- if theme.name != keep:
- theme.unlink()
- changed = True
- return changed
-
-
-def valid_starship(path: Path) -> bool:
- return subprocess.run(
- ["starship", "explain"],
- env={**os.environ, "STARSHIP_CONFIG": str(path)},
- stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL,
- check=False,
- ).returncode == 0
-
-
-def kitty_content(c: dict[str, str]) -> str:
- p = ansi(c)
- return f"""# ==========================================================
-# {c['name']}
-# ==========================================================
-# Color-only theme layer. Keep ML4W/Gentleman behavior elsewhere.
-
-foreground {c['text']}
-background {c['bg']}
-selection_foreground {c['text']}
-selection_background {c['selection']}
-url_color {c['diagnostic']}
-
-cursor {c['accent']}
-cursor_text_color {c['bg']}
-cursor_shape block
-cursor_blink_interval 0.5
-cursor_stop_blinking_after 15.0
-
-active_tab_foreground {c['bg']}
-active_tab_background {c['accent']}
-inactive_tab_foreground {c['muted']}
-inactive_tab_background {c['bg']}
-tab_bar_background {c['bg']}
-
-active_border_color {c['accent']}
-inactive_border_color {c['border']}
-bell_border_color {c['error']}
-
-color0 {p[0]}
-color1 {p[1]}
-color2 {p[2]}
-color3 {p[3]}
-color4 {p[4]}
-color5 {p[5]}
-color6 {p[6]}
-color7 {p[7]}
-color8 {p[8]}
-color9 {p[9]}
-color10 {p[10]}
-color11 {p[11]}
-color12 {p[12]}
-color13 {p[13]}
-color14 {p[14]}
-color15 {p[15]}
-color16 {c['accent_2']}
-color17 {c['error']}
-
-mark1_foreground {c['bg']}
-mark1_background {c['accent']}
-mark2_foreground {c['bg']}
-mark2_background {c['diagnostic']}
-mark3_foreground {c['bg']}
-mark3_background {c['mauve']}
-"""
-
-
-def kitty_ui_content() -> str:
- return """# Dreamcoder Kitty UI parity layer
-# Loaded last so ML4W can keep behavior while Dreamcoder owns readability.
-
-include colors-dreamcoder.conf
-
-font_family JetBrainsMono Nerd Font
-bold_font auto
-italic_font auto
-bold_italic_font auto
-font_size 14
-disable_ligatures cursor
-box_drawing_scale 0.001, 1, 1.5, 2
-text_composition_strategy platform
-
-window_padding_width 18 20 18 20
-single_window_padding_width -1
-placement_strategy center
-initial_window_width 1180
-initial_window_height 780
-background_opacity 0.72
-dynamic_background_opacity no
-background_blur 0
-
-tab_bar_edge top
-tab_bar_style fade
-tab_bar_min_tabs 2
-tab_bar_margin_color none
-active_tab_font_style bold
-inactive_tab_font_style normal
-
-shell_integration enabled no-cursor
-mouse_hide_wait 2.0
-copy_on_select clipboard
-dim_opacity 0.65
-inactive_text_alpha 0.92
-"""
-
-
-def ensure_kitty_ui_include(path: Path) -> bool:
- line = "include dreamcoder-ui.conf"
- if not path.exists():
- return False
- content = path.read_text()
- if line in content:
- return False
- path.write_text(content.rstrip() + "\n\n# Dreamcoder readability override\n" + line + "\n")
- return True
-
-
-def ghostty_content(c: dict[str, str]) -> str:
- lines = [
- f"# {c['name']}",
- f"background = {c['bg']}",
- f"foreground = {c['text']}",
- f"cursor-color = {c['accent']}",
- f"cursor-text = {c['bg']}",
- f"selection-background = {c['selection']}",
- f"selection-foreground = {c['text']}",
- "minimum-contrast = 4.5",
- "background-opacity-cells = true",
- "",
- ]
- lines.extend(f"palette = {i}={color}" for i, color in enumerate(ansi(c)))
- return "\n".join(lines) + "\n"
-
-
-def warp_content(c: dict[str, str]) -> str:
- p = ansi(c)
- return f"""name: {c['name']}
-accent: '{c['accent']}'
-cursor: '{c['accent']}'
-background: '{c['bg']}'
-foreground: '{c['text']}'
-details: {c['details']}
-terminal_colors:
- normal:
- black: '{p[0]}'
- red: '{p[1]}'
- green: '{p[2]}'
- yellow: '{p[3]}'
- blue: '{p[4]}'
- magenta: '{p[5]}'
- cyan: '{p[6]}'
- white: '{p[7]}'
- bright:
- black: '{p[8]}'
- red: '{p[9]}'
- green: '{p[10]}'
- yellow: '{p[11]}'
- blue: '{p[12]}'
- magenta: '{p[13]}'
- cyan: '{p[14]}'
- white: '{p[15]}'
-"""
-
-
-
-def opencode_tokens(c: dict[str, str]) -> dict[str, str]:
- mode_name = "dark" if c["details"] == "darker" else "light"
- keyword = guard(mix(c["accent"], c["warning"], 0.22), c["bg"], mode_name)
- function = guard(c["diagnostic"], c["bg"], mode_name)
- type_color = guard(c["lavender"], c["bg"], mode_name)
- constant = guard(mix(c["accent_2"], c["mauve"], 0.24), c["bg"], mode_name)
- return {
- "keyword": keyword,
- "function": function,
- "method": guard(mix(function, c["text"], 0.10), c["bg"], mode_name),
- "variable": c["text"],
- "parameter": guard(mix(c["text"], c["accent_2"], 0.18), c["bg"], mode_name),
- "property": guard(mix(c["text"], c["diagnostic"], 0.34), c["bg"], mode_name),
- "field": guard(mix(c["text"], c["sage"], 0.22), c["bg"], mode_name),
- "string": guard(c["sage"], c["bg"], mode_name),
- "number": guard(c["accent_2"], c["bg"], mode_name),
- "constant": constant,
- "type": type_color,
- "constructor": guard(mix(type_color, c["accent"], 0.18), c["bg"], mode_name),
- "enum": guard(mix(type_color, c["sage"], 0.18), c["bg"], mode_name),
- "operator": guard(c["mauve"], c["bg"], mode_name),
- "punctuation": guard(c["muted"], c["bg"], mode_name),
- "comment": guard(c["comment"], c["bg"], mode_name),
- "todo": guard(mix(c["warning"], c["text"], 0.12), c["bg"], mode_name),
- "deprecated": guard(mix(c["error"], c["muted"], 0.28), c["bg"], mode_name),
- "code_bg": mix(c["surface0"], c["bg"], 0.28),
- "selection": c["selection"],
- "search": mix(c["warning"], c["bg"], 0.72),
- }
-
-def opencode_content(c: dict[str, str]) -> str:
- t = opencode_tokens(c)
- mode_name = "dark" if c["details"] == "darker" else "light"
- added_bg = mix(c["sage"], c["bg"], 0.82)
- removed_bg = mix(c["error"], c["bg"], 0.84)
- hunk_bg = mix(c["lavender"], c["bg"], 0.84)
- line_bg = mix(c["surface0"], c["bg"], 0.62)
- assistant = guard(mix(c["diagnostic"], c["text"], 0.18), c["bg"], mode_name)
- user = guard(mix(c["accent"], c["text"], 0.15), c["bg"], mode_name)
- return f'''{{
- "$schema": "https://opencode.ai/theme.json",
- "defs": {{
- "dreamBackground": "{c['bg']}",
- "dreamPanel": "{c['surface0']}",
- "dreamElement": "{c['bg_soft']}",
- "dreamText": "{c['text']}",
- "dreamMuted": "{c['muted']}",
- "dreamCocoa": "{c['accent_2']}",
- "dreamLucuma": "{c['accent']}",
- "dreamDiagnostic": "{c['diagnostic']}",
- "dreamSage": "{c['sage']}",
- "dreamViolet": "{c['lavender']}",
- "dreamMauve": "{c['mauve']}",
- "dreamCoral": "{c['error']}",
- "dreamWarning": "{c['warning']}"
- }},
- "theme": {{
- "background": "{c['bg']}",
- "backgroundPanel": "{c['surface0']}",
- "backgroundElement": "{c['bg_soft']}",
- "backgroundHover": "{mix(c['surface1'], c['bg'], 0.45)}",
- "backgroundSelected": "{t['selection']}",
- "backgroundCode": "{t['code_bg']}",
- "backgroundSearch": "{t['search']}",
- "backgroundLine": "{line_bg}",
- "backgroundAssistant": "{mix(c['diagnostic'], c['bg'], 0.84)}",
- "backgroundUser": "{mix(c['accent'], c['bg'], 0.84)}",
- "backgroundTool": "{mix(c['lavender'], c['bg'], 0.86)}",
- "text": "{c['text']}",
- "textMuted": "{c['muted']}",
- "textSubtle": "{c['subtle']}",
- "textPlaceholder": "{c['comment']}",
- "textAssistant": "{assistant}",
- "textUser": "{user}",
- "textTool": "{t['type']}",
- "primary": "{c['accent']}",
- "secondary": "{c['accent_2']}",
- "accent": "{c['accent']}",
- "accentMuted": "{mix(c['accent'], c['bg'], 0.48)}",
- "error": "{c['error']}",
- "warning": "{c['warning']}",
- "success": "{c['sage']}",
- "info": "{c['diagnostic']}",
- "border": "{c['border_hi']}",
- "borderActive": "{c['accent']}",
- "borderSubtle": "{c['border']}",
- "borderFocus": "{c['diagnostic']}",
- "shadow": "{mix(c['bg'], '#000000', 0.25)}",
- "diffAdded": "{c['sage']}",
- "diffRemoved": "{c['error']}",
- "diffContext": "{c['muted']}",
- "diffHunkHeader": "{c['lavender']}",
- "diffHighlightAdded": "{c['sage']}",
- "diffHighlightRemoved": "{c['error']}",
- "diffAddedBg": "{added_bg}",
- "diffRemovedBg": "{removed_bg}",
- "diffContextBg": "{c['bg']}",
- "diffLineNumber": "{c['subtle']}",
- "diffAddedLineNumberBg": "{added_bg}",
- "diffRemovedLineNumberBg": "{removed_bg}",
- "diffHunkHeaderBg": "{hunk_bg}",
- "diffFold": "{c['comment']}",
- "diffFoldBg": "{mix(c['surface1'], c['bg'], 0.82)}",
- "markdownText": "{c['text']}",
- "markdownHeading": "{c['accent']}",
- "markdownLink": "{c['diagnostic']}",
- "markdownLinkText": "{c['accent']}",
- "markdownCode": "{c['sage']}",
- "markdownBlockQuote": "{c['accent_2']}",
- "markdownEmph": "{c['diagnostic']}",
- "markdownStrong": "{c['accent']}",
- "markdownHorizontalRule": "{c['border']}",
- "markdownListItem": "{c['accent']}",
- "markdownListEnumeration": "{c['lavender']}",
- "markdownTableBorder": "{c['border_hi']}",
- "markdownTableHeader": "{c['accent_2']}",
- "markdownImage": "{c['mauve']}",
- "markdownImageText": "{c['text']}",
- "markdownCodeBlock": "{c['text']}",
- "markdownCodeBlockBg": "{t['code_bg']}",
- "markdownInlineCodeBg": "{mix(c['sage'], c['bg'], 0.84)}",
- "syntaxComment": "{t['comment']}",
- "syntaxKeyword": "{t['keyword']}",
- "syntaxFunction": "{t['function']}",
- "syntaxMethod": "{t['method']}",
- "syntaxVariable": "{t['variable']}",
- "syntaxParameter": "{t['parameter']}",
- "syntaxProperty": "{t['property']}",
- "syntaxField": "{t['field']}",
- "syntaxString": "{t['string']}",
- "syntaxNumber": "{t['number']}",
- "syntaxBoolean": "{t['constant']}",
- "syntaxConstant": "{t['constant']}",
- "syntaxType": "{t['type']}",
- "syntaxClass": "{t['constructor']}",
- "syntaxInterface": "{mix(t['type'], c['diagnostic'], 0.22)}",
- "syntaxEnum": "{t['enum']}",
- "syntaxOperator": "{t['operator']}",
- "syntaxPunctuation": "{t['punctuation']}",
- "syntaxTag": "{t['keyword']}",
- "syntaxAttribute": "{t['property']}",
- "syntaxRegexp": "{mix(c['mauve'], c['error'], 0.28)}",
- "syntaxEscape": "{c['warning']}",
- "syntaxNamespace": "{t['type']}",
- "syntaxModule": "{t['type']}",
- "syntaxDecorator": "{t['operator']}",
- "syntaxBuiltin": "{t['constant']}",
- "syntaxSpecial": "{c['warning']}",
- "syntaxTodo": "{t['todo']}",
- "syntaxDeprecated": "{t['deprecated']}",
- "terminalBlack": "{c['surface0']}",
- "terminalRed": "{c['error']}",
- "terminalGreen": "{c['sage']}",
- "terminalYellow": "{c['warning']}",
- "terminalBlue": "{c['diagnostic']}",
- "terminalMagenta": "{c['mauve']}",
- "terminalCyan": "{c['lavender']}",
- "terminalWhite": "{c['text']}",
- "terminalBrightBlack": "{c['subtle']}",
- "terminalBrightRed": "{guard(mix(c['error'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
- "terminalBrightGreen": "{guard(mix(c['sage'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
- "terminalBrightYellow": "{guard(mix(c['warning'], c['text'], 0.16), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
- "terminalBrightBlue": "{guard(mix(c['diagnostic'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
- "terminalBrightMagenta": "{guard(mix(c['mauve'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
- "terminalBrightCyan": "{guard(mix(c['lavender'], c['text'], 0.18), c['bg'], 'dark' if c['details'] == 'darker' else 'light')}",
- "terminalBrightWhite": "{c['text']}"
- }}
-}}
-'''
-
-
-def starship_content(c: dict[str, str]) -> str:
- return f'''add_newline = true
-palette = "dreamcoder"
-
-format = """
-[](fg:prompt_surface0)\\
-$username\\
-[](bg:prompt_surface1 fg:prompt_surface0)\\
-$directory\\
-[](bg:prompt_accent fg:prompt_surface1)\\
-$git_branch\\
-$git_status\\
-[](fg:prompt_accent)\\
-$fill\\
-$hostname\\
-$cmd_duration
-$character"""
-
-[palettes.dreamcoder]
-bg = "{c['bg']}"
-text = "{c['text']}"
-muted = "{c['muted']}"
-prompt_bg = "{c['prompt_bg']}"
-prompt_surface0 = "{c['prompt_surface0']}"
-prompt_surface1 = "{c['prompt_surface1']}"
-prompt_surface2 = "{c['prompt_surface2']}"
-prompt_text = "{c['prompt_text']}"
-prompt_muted = "{c['prompt_muted']}"
-prompt_accent = "{c['prompt_accent']}"
-prompt_accent_2 = "{c['prompt_accent_2']}"
-sage = "{c['sage']}"
-diagnostic = "{c['diagnostic']}"
-lavender = "{c['lavender']}"
-mauve = "{c['mauve']}"
-error = "{c['error']}"
-
-[username]
-show_always = true
-style_user = "bg:prompt_surface0 fg:prompt_text bold"
-style_root = "bg:prompt_surface0 fg:error bold"
-format = "[ $user ]($style)"
-
-[hostname]
-ssh_only = true
-style = "fg:prompt_muted bold"
-format = "[ $hostname ]($style) "
-
-[directory]
-style = "bg:prompt_surface1 fg:prompt_text bold"
-format = "[ $path ]($style)"
-truncation_length = 2
-truncate_to_repo = true
-
-[git_branch]
-symbol = ""
-style = "bg:prompt_accent fg:prompt_bg bold"
-format = "[ $symbol $branch ]($style)"
-
-[git_status]
-style = "bg:prompt_accent fg:prompt_bg bold"
-format = "[$all_status$ahead_behind ]($style)"
-conflicted = "${{count}} "
-ahead = "⇡${{count}} "
-behind = "⇣${{count}} "
-diverged = "⇕⇡${{ahead_count}}⇣${{behind_count}} "
-untracked = "?${{count}} "
-stashed = "${{count}} "
-modified = "~${{count}} "
-staged = "+${{count}} "
-renamed = "»${{count}} "
-deleted = "✘${{count}} "
-
-[fill]
-symbol = " "
-
-[bun]
-symbol = ""
-style = "fg:prompt_accent bold"
-format = "[ $symbol $version]($style)"
-
-[nodejs]
-symbol = ""
-style = "fg:sage bold"
-format = "[ $symbol $version]($style)"
-
-[python]
-symbol = ""
-style = "fg:diagnostic bold"
-format = "[ $symbol $version]($style)"
-
-[golang]
-symbol = ""
-style = "fg:lavender bold"
-format = "[ $symbol $version]($style)"
-
-[rust]
-symbol = ""
-style = "fg:mauve bold"
-format = "[ $symbol $version]($style)"
-
-[docker_context]
-symbol = ""
-style = "fg:diagnostic bold"
-format = "[ $symbol $context]($style)"
-only_with_files = true
-
-[cmd_duration]
-min_time = 2500
-style = "fg:prompt_muted"
-format = "[ $duration ]($style) "
-
-[time]
-disabled = true
-
-[character]
-success_symbol = "[❯](bold fg:prompt_accent)"
-error_symbol = "[❯](bold fg:error)"
-vimcmd_symbol = "[❮](bold fg:sage)"
-'''
-
-
-def hypr_content(c: dict[str, str]) -> str:
- return f"""# Dreamcoder color layer for Hyprland.
-# Import after ML4W/Gentleman defaults; this changes colors only.
-
-general {{
- col.active_border = rgba({c['accent'][1:]}ff) rgba({c['diagnostic'][1:]}ff) 45deg
- col.inactive_border = {c['inactive_border']}
-}}
-
-misc {{
- background_color = rgba({c['bg'][1:]}ff)
-}}
-"""
-
-
-def waybar_content(c: dict[str, str]) -> str:
- return f"""/* Dreamcoder color layer for Waybar. */
-@define-color bg {c['bg']};
-@define-color bg-soft {c['bg_soft']};
-@define-color surface {c['surface0']};
-@define-color text {c['text']};
-@define-color muted {c['muted']};
-@define-color border {c['border']};
-@define-color accent {c['accent']};
-@define-color accent-2 {c['accent_2']};
-@define-color diagnostic {c['diagnostic']};
-@define-color error {c['error']};
-@define-color success {c['sage']};
-@define-color warning {c['warning']};
-
-window#waybar {{
- background: {c['panel_rgba']};
- color: @text;
- border-bottom: 1px solid alpha(@accent, 0.26);
-}}
-
-#workspaces button {{
- color: @muted;
- background: transparent;
-}}
-
-#workspaces button.active {{
- color: @accent;
- background: {c['active_rgba']};
-}}
-
-#clock,
-#battery,
-#network,
-#pulseaudio,
-#cpu,
-#memory,
-#tray {{
- background: {c['module_rgba']};
- color: @text;
- border: 1px solid alpha(@border, 0.45);
-}}
-
-#battery.warning {{ color: @warning; }}
-#battery.critical {{ color: @error; }}
-#network.disconnected {{ color: @error; }}
-"""
-
-
-def rofi_content(c: dict[str, str]) -> str:
- return f"""/* Dreamcoder color layer for Rofi. */
-* {{
- background: {c['panel_rgba']};
- background-alt: {c['surface0']};
- foreground: {c['text']};
- muted: {c['muted']};
- selected: {c['active_rgba']};
- active: {c['accent']};
- urgent: {c['error']};
- border-color: {c['accent']};
-}}
-
-window {{
- background-color: @background;
- border: 1px;
- border-color: @border-color;
- border-radius: 18px;
-}}
-
-entry {{
- background-color: @selected;
- text-color: @foreground;
- border-radius: 12px;
-}}
-
-element selected {{
- background-color: @selected;
- text-color: @foreground;
-}}
-
-element-text {{ text-color: @muted; }}
-element selected element-text {{ text-color: @foreground; }}
-"""
-
-
-def readme_content() -> str:
- return """# Dreamcoder Palette Layer
-
-This directory contains the Dreamcoder visual contract and generated color-only snippets for ML4W/Gentleman Dots.
-
-- `tokens.json`: canonical Dreamcoder OS design tokens and guardrails.
-- `tokens.schema.json`: machine-readable token contract.
-- `*-dark.*`: Warp-inspired dark glass mode for daily work.
-- `*-light.*`: Codex/OpenAI-inspired light mode for clean showcase and daytime use.
-
-Import these snippets after existing ML4W/Gentleman files so layouts, keybinds, wallpaper scripts, gaps, animations, and behavior remain owned by those systems.
-"""
-
-
-def write_variant_files(base: Path, dark_name: str, light_name: str, builder) -> list[bool]:
- dark = VARIANTS["dark"]
- light = VARIANTS["light"]
- return [
- write_if_changed(base / dark_name, builder(dark)),
- write_if_changed(base / light_name, builder(light)),
- ]
-
-
-VARIANTS = load_variants(VARIANTS)
-active = adaptive_palette(VARIANTS[mode], mode)
-changed = {
- "kitty": write_if_changed(kitty, kitty_content(active)),
- "kitty_ui": write_if_changed(kitty_ui, kitty_ui_content()),
- "kitty_config": ensure_kitty_ui_include(kitty_config),
- "ghostty": write_if_changed(ghostty, ghostty_content(active)),
- "warp": write_if_changed(warp, warp_content(active)),
- "opencode": write_if_changed(opencode, opencode_content(active)),
- "opencode_tui": write_opencode_tui(opencode_tui),
- "opencode_cleanup": cleanup_opencode_themes(opencode),
- "starship": write_if_changed(starship, starship_content(active)),
-}
-
-repo_changes = []
-repo_changes += write_variant_files(ROOT / "Kitty/.config/kitty", "colors-dreamcoder-dark.conf", "colors-dreamcoder-light.conf", kitty_content)
-repo_changes.append(write_if_changed(ROOT / "Kitty/.config/kitty/dreamcoder-ui.conf", kitty_ui_content()))
-repo_changes += write_variant_files(ROOT / "Ghostty/.config/ghostty/themes", "dreamcoder-dark", "dreamcoder-light", ghostty_content)
-repo_changes += write_variant_files(ROOT / "Warp/.local/share/warp-terminal/themes", "Dreamcoder-Dark.yaml", "Dreamcoder-Light.yaml", warp_content)
-repo_changes += write_variant_files(ROOT / "Shell/.config", "starship-dark.toml", "starship-light.toml", starship_content)
-repo_changes += write_variant_files(ROOT / "Codex-App", "Dreamcoder-Dark.codex-theme.json", "Dreamcoder-Light.codex-theme.json", opencode_content)
-repo_changes.append(write_if_changed(ROOT / "Codex-App/Dreamcoder.codex-theme.json", opencode_content(active)))
-repo_changes.append(write_if_changed(ROOT / ".opencode/themes/dreamcoder.json", opencode_content(active)))
-repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/hyprland-dark.conf", hypr_content(VARIANTS["dark"])))
-repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/hyprland-light.conf", hypr_content(VARIANTS["light"])))
-repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/waybar-dark.css", waybar_content(VARIANTS["dark"])))
-repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/waybar-light.css", waybar_content(VARIANTS["light"])))
-repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/rofi-dark.rasi", rofi_content(VARIANTS["dark"])))
-repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/rofi-light.rasi", rofi_content(VARIANTS["light"])))
-repo_changes.append(write_if_changed(ROOT / "themes/dreamcoder/README.md", readme_content()))
-
-if not valid_starship(starship):
- raise SystemExit(f"Generated Starship config is invalid: {starship}")
-
-print(f"Synced Dreamcoder {mode} identity")
-print(f"Kitty: {kitty}")
-print(f"Kitty UI: {kitty_ui}")
-print(f"Ghostty: {ghostty}")
-print(f"Warp: {warp}")
-print(f"opencode: {opencode}")
-print(f"opencode tui: {opencode_tui}")
-print(f"Starship: {starship}")
-print("Changed: " + " ".join(f"{key}={value}" for key, value in changed.items()))
-print(f"Repo variant/snippet changes: {sum(repo_changes)}")
+if __name__ == "__main__":
+ main()
diff --git a/scripts/theme-auto.sh b/scripts/theme-auto.sh
index dba9bec..0b3d91a 100755
--- a/scripts/theme-auto.sh
+++ b/scripts/theme-auto.sh
@@ -5,22 +5,10 @@ ENV_FILE="${DREAMCODER_DOTS_ENV:-${0%/*}/dreamcoder-env.sh}"
# shellcheck source=/dev/null
[[ -f "${ENV_FILE}" ]] && source "${ENV_FILE}"
LIGHT_START="${DREAMCODER_LIGHT_START:-7}"
+DUSK_START="${DREAMCODER_DUSK_START:-16}"
DARK_START="${DREAMCODER_DARK_START:-18}"
HOUR="$(date +%H)"
-WALLPAPER="${1:-${DREAMCODER_WALLPAPER:-${WALLPAPER:-}}}"
-ML4W_WALLPAPER="${ML4W_CACHE_DIR}/current_wallpaper"
-
MODE="dark"
-if (( 10#${HOUR} >= LIGHT_START && 10#${HOUR} < DARK_START )); then MODE="light"; fi
-if [[ -z "${WALLPAPER}" && -f "${ML4W_WALLPAPER}" ]]; then WALLPAPER="$(cat "${ML4W_WALLPAPER}")"; fi
-
-"${DREAMCODER_DOTS_DIR}/scripts/apply-system-mode.sh" "${MODE}"
-if [[ -n "${WALLPAPER}" && -f "${WALLPAPER}" ]] && command -v matugen >/dev/null; then
- matugen image "${WALLPAPER}" -m "${MODE}" >/dev/null 2>&1 || true
-fi
-
-DREAMCODER_THEME_MODE="${MODE}" \
-DREAMCODER_WALLPAPER="${WALLPAPER}" \
- "${DREAMCODER_DOTS_DIR}/scripts/sync-dreamcoder-theme.py"
-command -v pkill >/dev/null && pkill -SIGUSR1 kitty 2>/dev/null || true
-printf '✓ Dreamcoder %s mode applied\n' "${MODE}"
+if (( 10#${HOUR} >= LIGHT_START && 10#${HOUR} < DUSK_START )); then MODE="light"; fi
+if (( 10#${HOUR} >= DUSK_START && 10#${HOUR} < DARK_START )); then MODE="dusk"; fi
+exec "${DREAMCODER_DOTS_DIR}/scripts/apply-theme-mode.sh" "${MODE}" "$@"
diff --git a/scripts/verify-pi-theme.py b/scripts/verify-pi-theme.py
new file mode 100755
index 0000000..30004cb
--- /dev/null
+++ b/scripts/verify-pi-theme.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+"""Validate Dreamcoder PI CLI theme wiring."""
+
+from __future__ import annotations
+
+import json
+import sys
+from pathlib import Path
+
+REQUIRED = {
+ "accent", "border", "borderAccent", "borderMuted", "success", "error",
+ "warning", "muted", "dim", "text", "thinkingText", "selectedBg",
+ "userMessageBg", "userMessageText", "customMessageBg", "customMessageText",
+ "customMessageLabel", "toolPendingBg", "toolSuccessBg", "toolErrorBg",
+ "toolTitle", "toolOutput", "mdHeading", "mdLink", "mdLinkUrl", "mdCode",
+ "mdCodeBlock", "mdCodeBlockBorder", "mdQuote", "mdQuoteBorder", "mdHr",
+ "mdListBullet", "toolDiffAdded", "toolDiffRemoved", "toolDiffContext",
+ "syntaxComment", "syntaxKeyword", "syntaxFunction", "syntaxVariable",
+ "syntaxString", "syntaxNumber", "syntaxType", "syntaxOperator",
+ "syntaxPunctuation", "thinkingOff", "thinkingMinimal", "thinkingLow",
+ "thinkingMedium", "thinkingHigh", "thinkingXhigh", "bashMode",
+}
+
+theme = json.loads(Path(sys.argv[1]).read_text())
+settings = json.loads(Path(sys.argv[2]).read_text())
+assert theme.get("name") == "dreamcoder" and set(theme.get("colors", {})) == REQUIRED
+assert settings.get("theme") == "dreamcoder"
diff --git a/scripts/verify-theme-health.py b/scripts/verify-theme-health.py
index 65f89da..22bf370 100755
--- a/scripts/verify-theme-health.py
+++ b/scripts/verify-theme-health.py
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
import json
+import plistlib
import re
from pathlib import Path
@@ -8,10 +9,21 @@
ROOT / "Codex-App/Dreamcoder.codex-theme.json",
ROOT / "Codex-App/Dreamcoder-Light.codex-theme.json",
ROOT / "Codex-App/Dreamcoder-Dark.codex-theme.json",
+ ROOT / "Codex-App/Dreamcoder-Dusk.codex-theme.json",
]
TOKEN_FILE = ROOT / "themes/dreamcoder/tokens.json"
OPENCODE_THEME_DIR = ROOT / ".opencode/themes"
+CODEX_CLI_FILES = [
+ ROOT / "Codex-CLI/Dreamcoder.tmTheme",
+ ROOT / "Codex-CLI/Dreamcoder-Light.tmTheme",
+ ROOT / "Codex-CLI/Dreamcoder-Dark.tmTheme",
+ ROOT / "Codex-CLI/Dreamcoder-Dusk.tmTheme",
+]
TEXT_KEYS = ["text", "textMuted", "primary", "info", "success", "error", "warning"]
+UI_KEYS = ["border", "borderActive", "borderFocus"]
+QUIET_UI_KEYS = ["border"]
+BACKGROUND_KEYS = ["backgroundPanel", "backgroundElement", "backgroundHover", "backgroundCode", "backgroundLine", "diffAddedBg", "diffRemovedBg", "diffHunkHeaderBg"]
+SELECTED_BACKGROUND_KEYS = ["backgroundSelected"]
SYNTAX_KEYS = [
"syntaxKeyword", "syntaxFunction", "syntaxMethod", "syntaxVariable",
"syntaxParameter", "syntaxProperty", "syntaxField", "syntaxString",
@@ -19,12 +31,31 @@
"syntaxTodo", "syntaxDeprecated",
]
TOKEN_TEXT_KEYS = ["text", "muted", "comment", "accent", "accent_2", "diagnostic", "sage", "error", "warning"]
+TOKEN_BODY_APCA_KEYS = ["text", "diagnostic", "error", "warning"]
+TOKEN_ACCENT_APCA_KEYS = ["accent", "accent_2", "sage"]
+TOKEN_QUIET_APCA_KEYS = ["muted", "comment", "subtle"]
+TOKEN_UI_KEYS = ["border_ui", "focus"]
HEX = re.compile(r"^#[0-9a-fA-F]{6}$")
def rgb(value):
value = value.lstrip("#")
return tuple(int(value[i : i + 2], 16) for i in (0, 2, 4))
+def srgb_lin(channel):
+ channel /= 255
+ return channel / 12.92 if channel <= 0.040448236 else ((channel + 0.055) / 1.055) ** 2.4
+
+def apca_y(value):
+ r, g, b = (srgb_lin(part) for part in rgb(value))
+ y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b
+ return y ** 0.56
+
+def apca_lc(foreground, background):
+ y_fg, y_bg = apca_y(foreground), apca_y(background)
+ if y_bg >= y_fg:
+ return (y_bg - y_fg) * 1.14 * 100
+ return (y_fg - y_bg) * 1.14 * 100
+
def lum(value):
def channel(part):
part /= 255
@@ -42,18 +73,60 @@ def require(condition, message):
def check_tokens():
tokens = json.loads(TOKEN_FILE.read_text())
+ guardrails = tokens["guardrails"]
+ apca_body_light = guardrails.get("minimum_apca_body", 75)
+ apca_body_dark = guardrails.get("minimum_apca_body_dark", 60)
+ apca_quiet = guardrails.get("minimum_apca_quiet", 55)
+ apca_ui_light = guardrails.get("minimum_apca_ui", 60)
+ apca_ui_dark = guardrails.get("minimum_apca_ui_dark", 28)
require(tokens["guardrails"]["canonical_opencode_theme"] == "dreamcoder", "tokens: canonical opencode theme must be dreamcoder")
for mode, palette in tokens["modes"].items():
bg = palette["bg"]
require(HEX.match(bg), f"tokens:{mode}: invalid background")
require(bg.lower() not in {"#000000", "#ffffff"}, f"tokens:{mode}: harsh background")
for key in TOKEN_TEXT_KEYS:
+ require(key in palette, f"tokens:{mode}:{key}: missing token")
value = palette[key]
require(HEX.match(value), f"tokens:{mode}:{key}: invalid hex")
require(contrast(bg, value) >= 4.5, f"tokens:{mode}:{key} contrast below 4.5")
+ if palette.get("details") == "lighter":
+ apca_body = apca_body_light
+ apca_body_keys = TOKEN_BODY_APCA_KEYS
+ apca_accent = apca_body_dark
+ apca_accent_keys = TOKEN_ACCENT_APCA_KEYS
+ else:
+ apca_body = apca_body_dark
+ apca_body_keys = TOKEN_BODY_APCA_KEYS
+ apca_accent_keys = []
+ apca_accent = apca_body_dark
+ for key in apca_body_keys:
+ value = palette[key]
+ require(apca_lc(value, bg) >= apca_body, f"tokens:{mode}:{key} APCA Lc {apca_lc(value, bg):.1f} < {apca_body}")
+ for key in apca_accent_keys:
+ value = palette[key]
+ require(apca_lc(value, bg) >= apca_accent, f"tokens:{mode}:{key} APCA Lc {apca_lc(value, bg):.1f} < {apca_accent}")
+ for key in TOKEN_QUIET_APCA_KEYS:
+ if key not in palette:
+ continue
+ value = palette[key]
+ require(apca_lc(value, bg) >= apca_quiet, f"tokens:{mode}:{key} APCA Lc {apca_lc(value, bg):.1f} < {apca_quiet}")
require(contrast(bg, palette["text"]) >= 7, f"tokens:{mode}: main text below AAA")
+ require(apca_lc(palette["text"], bg) >= apca_body, f"tokens:{mode}: main text APCA below {apca_body}")
+ require(palette["border_ui"] != palette["border_hi"], f"tokens:{mode}: border_ui and border_hi must differ")
+ require(palette["comment"] != palette["subtle"], f"tokens:{mode}: comment and subtle must differ")
+ require(palette["focus"] != palette["diagnostic"], f"tokens:{mode}: focus and diagnostic must differ")
+ require(palette["accent"] != palette["accent_2"], f"tokens:{mode}: accent and accent_2 must differ")
+ apca_ui = apca_ui_light if mode in {"light", "dusk"} else apca_ui_dark
+ for key in TOKEN_UI_KEYS:
+ require(key in palette, f"tokens:{mode}:{key}: missing UI token")
+ value = palette[key]
+ require(HEX.match(value), f"tokens:{mode}:{key}: invalid hex")
+ require(contrast(bg, value) >= 3, f"tokens:{mode}:{key} contrast below 3.0")
+ require(apca_lc(value, bg) >= apca_ui, f"tokens:{mode}:{key} APCA Lc {apca_lc(value, bg):.1f} < {apca_ui}")
def check_theme_file(file):
+ if not file.exists():
+ return
theme = json.loads(file.read_text())["theme"]
bg = theme["background"]
require(bg not in {"#000000", "#ffffff"}, f"{file}: background uses harsh pure black/white")
@@ -61,8 +134,41 @@ def check_theme_file(file):
for key in TEXT_KEYS + SYNTAX_KEYS:
ratio = contrast(bg, theme[key])
require(ratio >= 4.5, f"{file}: {key} contrast {ratio:.2f} < 4.5")
+ for key in UI_KEYS:
+ ratio = contrast(bg, theme[key])
+ require(ratio >= 3, f"{file}: {key} UI contrast {ratio:.2f} < 3")
+ if key in QUIET_UI_KEYS:
+ require(ratio <= 7, f"{file}: {key} UI contrast {ratio:.2f} is too loud")
+ for key in BACKGROUND_KEYS:
+ ratio = contrast(bg, theme[key])
+ require(ratio >= 1.05, f"{file}: {key} surface contrast {ratio:.2f} < 1.05")
+ require(ratio <= 2.4, f"{file}: {key} surface contrast {ratio:.2f} > 2.4")
+ for key in SELECTED_BACKGROUND_KEYS:
+ ratio = contrast(bg, theme[key])
+ if lum(bg) > 0.5:
+ require(ratio >= 7, f"{file}: {key} light selection contrast {ratio:.2f} < 7")
+ else:
+ require(ratio >= 1.05, f"{file}: {key} surface contrast {ratio:.2f} < 1.05")
+ require(ratio <= 2.4, f"{file}: {key} surface contrast {ratio:.2f} > 2.4")
+ require(contrast(theme["terminalCyan"], theme["background"]) >= contrast(theme["terminalBlue"], theme["background"]), f"{file}: terminal cyan should be at least as legible as terminal blue")
require(contrast(bg, theme["text"]) >= 7, f"{file}: main text contrast below AAA")
+def check_codex_cli_theme(file):
+ if not file.exists():
+ return
+ data = plistlib.loads(file.read_bytes())
+ settings = data["settings"][0]["settings"]
+ bg = settings["background"]
+ fg = settings["foreground"]
+ require(HEX.match(bg), f"{file}: invalid background")
+ require(HEX.match(fg), f"{file}: invalid foreground")
+ require(contrast(bg, fg) >= 7, f"{file}: foreground contrast below AAA")
+ require(contrast(bg, settings["selection"]) >= 1.2, f"{file}: selection too faint")
+ if lum(bg) > 0.5:
+ require(contrast(bg, settings["selection"]) >= 7, f"{file}: light selection must invert strongly")
+ require(contrast(bg, settings["lineHighlight"]) >= 1.15, f"{file}: light line highlight too faint")
+ require(contrast(bg, settings["gutter"]) >= 1.45, f"{file}: light gutter too faint")
+
def check_opencode_repo():
names = sorted(path.name for path in OPENCODE_THEME_DIR.glob("*.json"))
require(names == ["dreamcoder.json"], f"opencode repo themes must only contain dreamcoder.json, got {names}")
@@ -70,5 +176,7 @@ def check_opencode_repo():
check_tokens()
for file in FILES:
check_theme_file(file)
+for file in CODEX_CLI_FILES:
+ check_codex_cli_theme(file)
check_opencode_repo()
print("✓ Dreamcoder theme health guardrails passed")
diff --git a/scripts/verify.sh b/scripts/verify.sh
index d6c9718..8e72874 100755
--- a/scripts/verify.sh
+++ b/scripts/verify.sh
@@ -1,26 +1,25 @@
#!/usr/bin/env bash
set -euo pipefail
-
ENV_FILE="${DREAMCODER_DOTS_ENV:-${0%/*}/dreamcoder-env.sh}"
# shellcheck source=/dev/null
[[ -f "${ENV_FILE}" ]] && source "${ENV_FILE}"
MODULES=(kitty ghostty fastfetch)
-
ok() { printf '✓ %s\n' "$*"; }
fail() { printf '✗ %s\n' "$*" >&2; return 1; }
-check_path() {
- local path="$1"
- [[ -e "${path}" ]] || { fail "${path} is missing"; return; }
- ok "${path}"
-}
+check_path() { local path="$1"; [[ -e "${path}" ]] || { fail "${path} is missing"; return; }; ok "${path}"; }
command -v starship >/dev/null || fail 'Missing dependency: starship'
for app in "${MODULES[@]}"; do check_path "${CONFIG_HOME}/${app}"; done
check_path "${CONFIG_HOME}/starship.toml"
check_path "${CONFIG_HOME}/kitty/dreamcoder-ui.conf"
check_path "${DATA_HOME}/warp-terminal/themes"
+PI_AGENT_DIR="${PI_AGENT_DIR:-${HOME}/.pi/agent}"
+check_path "${PI_AGENT_DIR}/themes/dreamcoder.json"
+"${DREAMCODER_DOTS_DIR}/scripts/verify-pi-theme.py" "${PI_AGENT_DIR}/themes/dreamcoder.json" "${PI_AGENT_DIR}/settings.json"
STARSHIP_CONFIG="${DREAMCODER_DOTS_DIR}/Shell/.config/starship.toml" starship explain >/dev/null
STARSHIP_CONFIG="${DREAMCODER_DOTS_DIR}/Shell/.config/starship-light.toml" starship explain >/dev/null
"${DREAMCODER_DOTS_DIR}/scripts/verify-theme-health.py" >/dev/null
[[ -x "${DREAMCODER_DOTS_DIR}/scripts/doctor.sh" ]] || fail 'Missing doctor.sh'
[[ -x "${DREAMCODER_DOTS_DIR}/scripts/repair.sh" ]] || fail 'Missing repair.sh'
+[[ -x "${DREAMCODER_DOTS_DIR}/scripts/apply-theme-mode.sh" ]] || fail 'Missing apply-theme-mode.sh'
+[[ -x "${DREAMCODER_DOTS_DIR}/scripts/status.sh" ]] || fail 'Missing status.sh'
ok 'Starship configs and theme health valid'
diff --git a/scripts/wallpaper-hook.sh b/scripts/wallpaper-hook.sh
index c977f7b..48e23ef 100755
--- a/scripts/wallpaper-hook.sh
+++ b/scripts/wallpaper-hook.sh
@@ -3,6 +3,7 @@ set -euo pipefail
WALLPAPER="${1:-${WALLPAPER:-}}"
DREAMCODER_DOTS_DIR="${DREAMCODER_DOTS_DIR:-$(cd "${0%/*}/.." && pwd)}"
+WALLPAPER="$("${DREAMCODER_DOTS_DIR}/scripts/normalize-wallpaper-path.sh" "${WALLPAPER}")"
CACHE_HOME="${XDG_CACHE_HOME:-${HOME}/.cache}"
ML4W_CACHE_DIR="${ML4W_CACHE_DIR:-${CACHE_HOME}/ml4w/hyprland-dotfiles}"
SAFE_DIR="${CACHE_HOME}/dreamcoder"
@@ -17,9 +18,12 @@ printf '%s\n' "${WALLPAPER}" >"${ML4W_WALLPAPER}"
ln -sfn "${WALLPAPER}" "${SAFE_WALLPAPER}"
if command -v hyprctl >/dev/null; then
- hyprctl monitors | awk '/^Monitor / {print $2}' | while read -r MONITOR; do
- hyprctl hyprpaper wallpaper "${MONITOR},${SAFE_WALLPAPER}" >/dev/null || true
- done
+ hyprctl hyprpaper unload "${SAFE_WALLPAPER}" >/dev/null 2>&1 || true
+ hyprctl hyprpaper preload "${SAFE_WALLPAPER}" >/dev/null 2>&1 || true
+ while read -r MONITOR; do
+ [[ -n "${MONITOR}" ]] || continue
+ hyprctl hyprpaper wallpaper "${MONITOR},${SAFE_WALLPAPER}" >/dev/null 2>&1 || true
+ done < <(hyprctl monitors | awk '/^Monitor / {print $2}')
fi
"${DREAMCODER_DOTS_DIR}/scripts/theme-auto.sh" "${WALLPAPER}"
diff --git a/tests/test_dreamcoder_ember_noir.py b/tests/test_dreamcoder_ember_noir.py
new file mode 100644
index 0000000..2f03185
--- /dev/null
+++ b/tests/test_dreamcoder_ember_noir.py
@@ -0,0 +1,35 @@
+import json
+import unittest
+from pathlib import Path
+
+
+ROOT = Path(__file__).resolve().parents[1]
+TOKENS = ROOT / "themes" / "dreamcoder" / "tokens.json"
+
+
+class DreamcoderEmberNoirTest(unittest.TestCase):
+ def setUp(self):
+ self.dark = json.loads(TOKENS.read_text())["modes"]["dark"]
+
+ def test_dark_mode_uses_ember_noir_identity(self):
+ self.assertEqual(self.dark["name"], "Dreamcoder Ember Noir")
+ self.assertEqual(self.dark["bg"], "#15100d")
+ self.assertEqual(self.dark["surface0"], "#241b16")
+ self.assertEqual(self.dark["surface1"], "#30231c")
+ self.assertEqual(self.dark["surface2"], "#3e2c22")
+
+ def test_dark_mode_has_red_orange_gold_signature(self):
+ self.assertEqual(self.dark["accent"], "#e6a15c")
+ self.assertEqual(self.dark["accent_2"], "#d66f50")
+ self.assertEqual(self.dark["error"], "#e98272")
+ self.assertEqual(self.dark["warning"], "#e8b866")
+
+ def test_dark_mode_keeps_warm_silver_text_and_ember_focus(self):
+ self.assertEqual(self.dark["text"], "#f0e7dc")
+ self.assertEqual(self.dark["muted"], "#c7b9aa")
+ self.assertEqual(self.dark["focus"], "#e6a15c")
+ self.assertEqual(self.dark["diagnostic"], "#d2a268")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_dreamcoder_theme_quality.py b/tests/test_dreamcoder_theme_quality.py
new file mode 100644
index 0000000..f4905a6
--- /dev/null
+++ b/tests/test_dreamcoder_theme_quality.py
@@ -0,0 +1,48 @@
+import json
+import unittest
+from pathlib import Path
+
+
+ROOT = Path(__file__).resolve().parents[1]
+TOKENS = ROOT / "themes" / "dreamcoder" / "tokens.json"
+
+
+class DreamcoderThemeQualityTest(unittest.TestCase):
+ def setUp(self):
+ self.modes = json.loads(TOKENS.read_text())["modes"]
+
+ def test_dark_uses_refined_ember_noir_scale(self):
+ dark = self.modes["dark"]
+ self.assertEqual(dark["name"], "Dreamcoder Ember Noir")
+ self.assertEqual(dark["surface0"], "#241b16")
+ self.assertEqual(dark["surface1"], "#30231c")
+ self.assertEqual(dark["surface2"], "#3e2c22")
+ self.assertEqual(dark["accent"], "#e6a15c")
+ self.assertEqual(dark["accent_2"], "#d66f50")
+
+ def test_light_has_stronger_editor_readability_tiers(self):
+ light = self.modes["light"]
+ self.assertEqual(light["surface0"], "#fff7ea")
+ self.assertEqual(light["surface2"], "#c8ad89")
+ self.assertEqual(light["subtle"], "#554635")
+ self.assertEqual(light["comment"], "#66523f")
+ self.assertEqual(light["accent"], "#824f16")
+
+ def test_dusk_is_not_a_duplicate_light_palette(self):
+ dusk = self.modes["dusk"]
+ self.assertEqual(dusk["surface0"], "#f1eadf")
+ self.assertEqual(dusk["surface2"], "#c6b6a0")
+ self.assertEqual(dusk["muted"], "#4c443a")
+ self.assertEqual(dusk["accent"], "#8a5520")
+
+ def test_light_selection_uses_inverted_high_contrast_pair(self):
+ light = self.modes["light"]
+ self.assertEqual(light["selection"], light["text"])
+
+ def test_dusk_selection_uses_inverted_high_contrast_pair(self):
+ dusk = self.modes["dusk"]
+ self.assertEqual(dusk["selection"], dusk["text"])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_pi_theme_generation.py b/tests/test_pi_theme_generation.py
new file mode 100644
index 0000000..dd373d8
--- /dev/null
+++ b/tests/test_pi_theme_generation.py
@@ -0,0 +1,110 @@
+
+import json
+import os
+import subprocess
+import sys
+import tempfile
+import unittest
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parents[1]
+SYNC = ROOT / "scripts" / "sync-dreamcoder-theme.py"
+
+REQUIRED_PI_TOKENS = {
+ "accent", "border", "borderAccent", "borderMuted", "success", "error", "warning",
+ "muted", "dim", "text", "thinkingText", "selectedBg", "userMessageBg",
+ "userMessageText", "customMessageBg", "customMessageText", "customMessageLabel",
+ "toolPendingBg", "toolSuccessBg", "toolErrorBg", "toolTitle", "toolOutput",
+ "mdHeading", "mdLink", "mdLinkUrl", "mdCode", "mdCodeBlock", "mdCodeBlockBorder",
+ "mdQuote", "mdQuoteBorder", "mdHr", "mdListBullet", "toolDiffAdded",
+ "toolDiffRemoved", "toolDiffContext", "syntaxComment", "syntaxKeyword",
+ "syntaxFunction", "syntaxVariable", "syntaxString", "syntaxNumber", "syntaxType",
+ "syntaxOperator", "syntaxPunctuation", "thinkingOff", "thinkingMinimal",
+ "thinkingLow", "thinkingMedium", "thinkingHigh", "thinkingXhigh", "bashMode",
+}
+
+
+def run_sync(tmp: Path, mode: str = "dark") -> subprocess.CompletedProcess[str]:
+ pi_agent = tmp / "pi-agent"
+ env = os.environ.copy()
+ env.update({
+ "DREAMCODER_THEME_MODE": mode,
+ "DREAMCODER_ADAPTIVE": "0",
+ "DREAMCODER_WRITE_REPO": "0",
+ "XDG_CONFIG_HOME": str(tmp / "config"),
+ "XDG_DATA_HOME": str(tmp / "data"),
+ "PI_AGENT_DIR": str(pi_agent),
+ "KITTY_COLORS": str(tmp / "kitty" / "colors.conf"),
+ "KITTY_CONFIG": str(tmp / "kitty" / "kitty.conf"),
+ "KITTY_DREAMCODER_UI": str(tmp / "kitty" / "dreamcoder-ui.conf"),
+ "GHOSTTY_THEME": str(tmp / "ghostty" / "themes" / "dreamcoder"),
+ "STARSHIP_CONFIG": str(tmp / "starship.toml"),
+ "WARP_THEME": str(tmp / "warp" / "Dreamcoder.yaml"),
+ "OPENCODE_THEME": str(tmp / "opencode" / "themes" / "dreamcoder.json"),
+ "OPENCODE_TUI": str(tmp / "opencode" / "tui.json"),
+ "CODEX_THEME": str(tmp / "codex" / "themes" / "Dreamcoder.tmTheme"),
+ "CODEX_CONFIG": str(tmp / "codex" / "config.toml"),
+ })
+ return subprocess.run(
+ [sys.executable, str(SYNC)],
+ cwd=ROOT,
+ env=env,
+ text=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ check=False,
+ )
+
+
+class PiThemeGenerationTest(unittest.TestCase):
+ def test_generates_global_pi_theme_with_all_required_tokens(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmp = Path(tmpdir)
+ result = run_sync(tmp, "dark")
+ self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
+
+ theme_path = tmp / "pi-agent" / "themes" / "dreamcoder.json"
+ self.assertTrue(theme_path.exists(), "PI theme was not generated")
+ theme = json.loads(theme_path.read_text())
+
+ self.assertEqual(theme["name"], "dreamcoder")
+ self.assertEqual(set(theme["colors"].keys()), REQUIRED_PI_TOKENS)
+ self.assertEqual(theme["colors"]["accent"], "lucuma")
+ self.assertEqual(theme["vars"]["lucuma"], "#e6a15c")
+
+ def test_opencode_preserves_terminal_transparent_background(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmp = Path(tmpdir)
+ result = run_sync(tmp, "dark")
+ self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
+
+ theme_path = tmp / "opencode" / "themes" / "dreamcoder.json"
+ theme = json.loads(theme_path.read_text())["theme"]
+
+ self.assertEqual(theme["background"], "none")
+ self.assertEqual(theme["backgroundPanel"], "#241b16")
+ self.assertEqual(theme["primary"], "#e6a15c")
+ self.assertEqual(theme["secondary"], "#d66f50")
+
+ def test_selects_dreamcoder_theme_without_overwriting_existing_pi_settings(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmp = Path(tmpdir)
+ settings_path = tmp / "pi-agent" / "settings.json"
+ settings_path.parent.mkdir(parents=True)
+ settings_path.write_text(json.dumps({
+ "defaultProvider": "openai-codex",
+ "packages": ["gentle-pi"],
+ "theme": "light",
+ }, indent=2) + "\n")
+
+ result = run_sync(tmp, "light")
+ self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
+
+ settings = json.loads(settings_path.read_text())
+ self.assertEqual(settings["theme"], "dreamcoder")
+ self.assertEqual(settings["defaultProvider"], "openai-codex")
+ self.assertEqual(settings["packages"], ["gentle-pi"])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/themes/dreamcoder/README.md b/themes/dreamcoder/README.md
index da06057..3e67824 100644
--- a/themes/dreamcoder/README.md
+++ b/themes/dreamcoder/README.md
@@ -4,7 +4,39 @@ This directory contains the Dreamcoder visual contract and generated color-only
- `tokens.json`: canonical Dreamcoder OS design tokens and guardrails.
- `tokens.schema.json`: machine-readable token contract.
-- `*-dark.*`: Warp-inspired dark glass mode for daily work.
-- `*-light.*`: Codex/OpenAI-inspired light mode for clean showcase and daytime use.
+- `*-dark.*`: Dreamcoder Ember Noir mode with espresso glass, refined orange/red protagonists, and gold support accents.
+- `*-light.*`: paper-like daytime mode with flat surface ladder and distinct semantic tokens.
+- `*-dusk.*`: warm transitional mode (16:00–18:00) between light and dark.
+
+## Color-only Snippets (Hooks)
+
+Hook these into your app configs after ML4W/Gentleman files:
+
+| Target | Files | How to Use |
+|--------|-------|------------|
+| **Kitty** | `kitty-dreamcoder-{mode}.conf`, `dreamcoder-ui.conf` | `include` in kitty.conf |
+| **Ghostty** | `ghostty-dreamcoder-{mode}` | `theme = dreamcoder-{mode}` in ghostty config |
+| **Warp** | `Warp/.local/share/warp-terminal/themes/Dreamcoder-{Mode}.yaml` | Select in Warp theme picker |
+| **Hyprland** | `hyprland-{mode}.conf` | `source` from hyprland.conf |
+| **Waybar** | `waybar-{mode}.css` | `@import` in waybar style.css |
+| **Rofi** | `rofi-{mode}.rasi` | `@import` or `-theme` in rofi launch |
+| **Starship** | `starship-{mode}.toml` | `STARSHIP_CONFIG` env var |
+| **Antigravity** | `Antigravity/Dreamcoder-{Mode}.json` | Antigravity theme selector |
+| **opencode** | `opencode/dreamcoder.json` | `theme: "dreamcoder"` in opencode config |
+| **Codex CLI** | `Codex-CLI/Dreamcoder-{Mode}.tmTheme` | `theme = "Dreamcoder"` in codex config |
+| **PI CLI** | `Pi/.pi/agent/themes/dreamcoder-{mode}.json` | `theme: "dreamcoder"` in pi settings |
+| **Neovim** | `nvim-dreamcoder-{mode}.lua` | `require('dreamcoder')` in neovim config |
+| **Zsh-syntax-highlighting** | `zsh-syntax-highlighting-dreamcoder-{mode}.zsh` | `source` after zsh-syntax-highlighting plugin |
+| **LS_COLORS / eza** | `ls-colors-dreamcoder-{mode}.sh` | `source` in .zshrc or .bashrc |
+| **Bat** | `bat-dreamcoder-{mode}.sh` | Sets `BAT_THEME` env var; pair with Codex CLI tmTheme |
+| **Delta (git diff)** | `delta-dreamcoder-{mode}.gitconfig` | `[include]` in ~/.config/git/config |
+| **Fzf** | `fzf-dreamcoder-{mode}.sh` | `source` in .zshrc or .bashrc |
+| **Btop** | `btop-dreamcoder-{mode}.theme` | Place in ~/.config/btop/themes/ |
+| **Dunst** | `dunst-dreamcoder-{mode}.conf` | `[include]` in dunstrc |
+| **Firefox** | `firefox-dreamcoder-{mode}.css` | userChrome.css for Firefox customization |
+| **Obsidian** | `obsidian-dreamcoder-{mode}.css` | CSS snippet in Obsidian vault |
+| **Cava** | `cava-dreamcoder-{mode}.config` | `include` in ~/.config/cava/config |
+
+## Word of Caution
Import these snippets after existing ML4W/Gentleman files so layouts, keybinds, wallpaper scripts, gaps, animations, and behavior remain owned by those systems.
diff --git a/themes/dreamcoder/bat-dreamcoder-dark.sh b/themes/dreamcoder/bat-dreamcoder-dark.sh
new file mode 100644
index 0000000..dd1834e
--- /dev/null
+++ b/themes/dreamcoder/bat-dreamcoder-dark.sh
@@ -0,0 +1,22 @@
+# ========================================================
+# Dreamcoder Ember Noir — Bat theme
+# ========================================================
+# Set the --theme to use the Dreamcoder tmTheme.
+# Install: bat cache --build (after placing .tmTheme in Bat themes dir)
+#
+# Recommended approach:
+# 1. Symlink or copy the Codex-CLI Dreamcoder.tmTheme to Bat themes:
+# ln -sf "$DREAMCODER_DOTS_DIR/Codex-CLI/Dreamcoder-Dark.tmTheme" \
+# "$(bat --config-dir)/themes/Dreamcoder-Dark.tmTheme"
+# 2. Set this in your shell config:
+# export BAT_THEME="Dreamcoder-Dark"
+
+export BAT_THEME="Dreamcoder-Dark"
+
+# Fallback: if no .tmTheme is installed, at least style the pager chrome
+# with Dreamcoder colors via bat's --style and command-line flags.
+export BAT_STYLE="header,numbers,changes,grid"
+export BAT_TABS="4"
+
+# Syntax theme can also be set via environment variable
+# BAT_THEME="Dreamcoder-Dark" # or -Light / -Dusk
diff --git a/themes/dreamcoder/bat-dreamcoder-dusk.sh b/themes/dreamcoder/bat-dreamcoder-dusk.sh
new file mode 100644
index 0000000..bfa0e9c
--- /dev/null
+++ b/themes/dreamcoder/bat-dreamcoder-dusk.sh
@@ -0,0 +1,22 @@
+# ========================================================
+# Dreamcoder Dusk — Bat theme
+# ========================================================
+# Set the --theme to use the Dreamcoder tmTheme.
+# Install: bat cache --build (after placing .tmTheme in Bat themes dir)
+#
+# Recommended approach:
+# 1. Symlink or copy the Codex-CLI Dreamcoder.tmTheme to Bat themes:
+# ln -sf "$DREAMCODER_DOTS_DIR/Codex-CLI/Dreamcoder-Dark.tmTheme" \
+# "$(bat --config-dir)/themes/Dreamcoder-Dark.tmTheme"
+# 2. Set this in your shell config:
+# export BAT_THEME="Dreamcoder-Dark"
+
+export BAT_THEME="Dreamcoder-Light"
+
+# Fallback: if no .tmTheme is installed, at least style the pager chrome
+# with Dreamcoder colors via bat's --style and command-line flags.
+export BAT_STYLE="header,numbers,changes,grid"
+export BAT_TABS="4"
+
+# Syntax theme can also be set via environment variable
+# BAT_THEME="Dreamcoder-Dark" # or -Light / -Dusk
diff --git a/themes/dreamcoder/bat-dreamcoder-light.sh b/themes/dreamcoder/bat-dreamcoder-light.sh
new file mode 100644
index 0000000..913baff
--- /dev/null
+++ b/themes/dreamcoder/bat-dreamcoder-light.sh
@@ -0,0 +1,22 @@
+# ========================================================
+# Dreamcoder Light — Bat theme
+# ========================================================
+# Set the --theme to use the Dreamcoder tmTheme.
+# Install: bat cache --build (after placing .tmTheme in Bat themes dir)
+#
+# Recommended approach:
+# 1. Symlink or copy the Codex-CLI Dreamcoder.tmTheme to Bat themes:
+# ln -sf "$DREAMCODER_DOTS_DIR/Codex-CLI/Dreamcoder-Dark.tmTheme" \
+# "$(bat --config-dir)/themes/Dreamcoder-Dark.tmTheme"
+# 2. Set this in your shell config:
+# export BAT_THEME="Dreamcoder-Dark"
+
+export BAT_THEME="Dreamcoder-Light"
+
+# Fallback: if no .tmTheme is installed, at least style the pager chrome
+# with Dreamcoder colors via bat's --style and command-line flags.
+export BAT_STYLE="header,numbers,changes,grid"
+export BAT_TABS="4"
+
+# Syntax theme can also be set via environment variable
+# BAT_THEME="Dreamcoder-Dark" # or -Light / -Dusk
diff --git a/themes/dreamcoder/bat-dreamcoder.sh b/themes/dreamcoder/bat-dreamcoder.sh
new file mode 100644
index 0000000..dd1834e
--- /dev/null
+++ b/themes/dreamcoder/bat-dreamcoder.sh
@@ -0,0 +1,22 @@
+# ========================================================
+# Dreamcoder Ember Noir — Bat theme
+# ========================================================
+# Set the --theme to use the Dreamcoder tmTheme.
+# Install: bat cache --build (after placing .tmTheme in Bat themes dir)
+#
+# Recommended approach:
+# 1. Symlink or copy the Codex-CLI Dreamcoder.tmTheme to Bat themes:
+# ln -sf "$DREAMCODER_DOTS_DIR/Codex-CLI/Dreamcoder-Dark.tmTheme" \
+# "$(bat --config-dir)/themes/Dreamcoder-Dark.tmTheme"
+# 2. Set this in your shell config:
+# export BAT_THEME="Dreamcoder-Dark"
+
+export BAT_THEME="Dreamcoder-Dark"
+
+# Fallback: if no .tmTheme is installed, at least style the pager chrome
+# with Dreamcoder colors via bat's --style and command-line flags.
+export BAT_STYLE="header,numbers,changes,grid"
+export BAT_TABS="4"
+
+# Syntax theme can also be set via environment variable
+# BAT_THEME="Dreamcoder-Dark" # or -Light / -Dusk
diff --git a/themes/dreamcoder/btop-dreamcoder-dark.theme b/themes/dreamcoder/btop-dreamcoder-dark.theme
new file mode 100644
index 0000000..413480b
--- /dev/null
+++ b/themes/dreamcoder/btop-dreamcoder-dark.theme
@@ -0,0 +1,64 @@
+# ========================================================
+# Dreamcoder Ember Noir — Btop theme
+# ========================================================
+# Place in ~/.config/btop/themes/ and select from Btop UI.
+
+theme[main_bg]="#12100e"
+theme[main_fg]="#e8dfd0"
+theme[title]="#d99555"
+theme[hi_fg]="#d99555"
+theme[selected_bg]="#373027"
+theme[selected_fg]="#e8dfd0"
+theme[inactive_fg]="#b8a99a"
+theme[graph_line]="#594d46"
+theme[proc_misc]="#b8a99a"
+theme[cpu_core]="#c8b2a2"
+theme[mem_free]="#b8bf84"
+theme[mem_used]="#c96a45"
+theme[mem_cached]="#c8b2a2"
+theme[user_bg]="#211c18"
+theme[user_fg]="#e8dfd0"
+theme[temp]="#e8b866"
+theme[disk]="#c8b2a2"
+theme[process]="#c9a8dc"
+theme[process_selected]="#d99555"
+theme[core_bar]="#d99555"
+theme[temp_bar]="#e8b866"
+theme[swap]="#d98aa9"
+theme[div_line]="#594d46"
+theme[process_bg]="#211c18"
+theme[process_fg]="#e8dfd0"
+theme[bad]="#e98272"
+theme[good]="#b8bf84"
+theme[widget_bg]="#211c18"
+theme[widget_fg]="#e8dfd0"
+theme[widget_border]="#594d46"
+theme[widget_selected]="#d99555"
+theme[graph_bg]="#211c18"
+theme[graph_fg]="#e8dfd0"
+theme[graph_high]="#c96a45"
+theme[graph_low]="#c8b2a2"
+theme[graph_med]="#d99555"
+theme[proc_bg]="#211c18"
+theme[proc_fg]="#e8dfd0"
+theme[process_border]="#594d46"
+theme[widget_title]="#d99555"
+theme[box_border]="#594d46"
+theme[box_bg]="#211c18"
+theme[box_fg]="#e8dfd0"
+theme[box_selected]="#d99555"
+theme[os_bg]="#211c18"
+theme[os_fg]="#e8dfd0"
+theme[clock_bg]="#211c18"
+theme[clock_fg]="#d99555"
+theme[bat_high]="#b8bf84"
+theme[bat_med]="#e8b866"
+theme[bat_low]="#e98272"
+theme[sensor_bg]="#211c18"
+theme[sensor_fg]="#e8dfd0"
+theme[sensor_bar_bg]="#2e241f"
+theme[sensor_bar_fg]="#d99555"
+theme[net_bg]="#211c18"
+theme[net_fg]="#e8dfd0"
+theme[net_download]="#b8bf84"
+theme[net_upload]="#c96a45"
diff --git a/themes/dreamcoder/btop-dreamcoder-dusk.theme b/themes/dreamcoder/btop-dreamcoder-dusk.theme
new file mode 100644
index 0000000..dbdcdee
--- /dev/null
+++ b/themes/dreamcoder/btop-dreamcoder-dusk.theme
@@ -0,0 +1,64 @@
+# ========================================================
+# Dreamcoder Dusk — Btop theme
+# ========================================================
+# Place in ~/.config/btop/themes/ and select from Btop UI.
+
+theme[main_bg]="#ebe4d6"
+theme[main_fg]="#1a1713"
+theme[title]="#8a5520"
+theme[hi_fg]="#8a5520"
+theme[selected_bg]="#1a1713"
+theme[selected_fg]="#1a1713"
+theme[inactive_fg]="#4c443a"
+theme[graph_line]="#a7947a"
+theme[proc_misc]="#4c443a"
+theme[cpu_core]="#104b67"
+theme[mem_free]="#466b41"
+theme[mem_used]="#96411e"
+theme[mem_cached]="#104b67"
+theme[user_bg]="#f1eadf"
+theme[user_fg]="#1a1713"
+theme[temp]="#604000"
+theme[disk]="#104b67"
+theme[process]="#5b4e86"
+theme[process_selected]="#8a5520"
+theme[core_bar]="#8a5520"
+theme[temp_bar]="#604000"
+theme[swap]="#784762"
+theme[div_line]="#a7947a"
+theme[process_bg]="#f1eadf"
+theme[process_fg]="#1a1713"
+theme[bad]="#773126"
+theme[good]="#466b41"
+theme[widget_bg]="#f1eadf"
+theme[widget_fg]="#1a1713"
+theme[widget_border]="#a7947a"
+theme[widget_selected]="#8a5520"
+theme[graph_bg]="#f1eadf"
+theme[graph_fg]="#1a1713"
+theme[graph_high]="#96411e"
+theme[graph_low]="#104b67"
+theme[graph_med]="#8a5520"
+theme[proc_bg]="#f1eadf"
+theme[proc_fg]="#1a1713"
+theme[process_border]="#a7947a"
+theme[widget_title]="#8a5520"
+theme[box_border]="#a7947a"
+theme[box_bg]="#f1eadf"
+theme[box_fg]="#1a1713"
+theme[box_selected]="#8a5520"
+theme[os_bg]="#f1eadf"
+theme[os_fg]="#1a1713"
+theme[clock_bg]="#f1eadf"
+theme[clock_fg]="#8a5520"
+theme[bat_high]="#466b41"
+theme[bat_med]="#604000"
+theme[bat_low]="#773126"
+theme[sensor_bg]="#f1eadf"
+theme[sensor_fg]="#1a1713"
+theme[sensor_bar_bg]="#d8cbb8"
+theme[sensor_bar_fg]="#8a5520"
+theme[net_bg]="#f1eadf"
+theme[net_fg]="#1a1713"
+theme[net_download]="#466b41"
+theme[net_upload]="#96411e"
diff --git a/themes/dreamcoder/btop-dreamcoder-light.theme b/themes/dreamcoder/btop-dreamcoder-light.theme
new file mode 100644
index 0000000..4eb9cfb
--- /dev/null
+++ b/themes/dreamcoder/btop-dreamcoder-light.theme
@@ -0,0 +1,64 @@
+# ========================================================
+# Dreamcoder Light — Btop theme
+# ========================================================
+# Place in ~/.config/btop/themes/ and select from Btop UI.
+
+theme[main_bg]="#f3eadc"
+theme[main_fg]="#17120d"
+theme[title]="#824f16"
+theme[hi_fg]="#824f16"
+theme[selected_bg]="#17120d"
+theme[selected_fg]="#17120d"
+theme[inactive_fg]="#3d3228"
+theme[graph_line]="#8a7358"
+theme[proc_misc]="#3d3228"
+theme[cpu_core]="#15516e"
+theme[mem_free]="#3f6b35"
+theme[mem_used]="#a7471c"
+theme[mem_cached]="#15516e"
+theme[user_bg]="#fff7ea"
+theme[user_fg]="#17120d"
+theme[temp]="#654300"
+theme[disk]="#15516e"
+theme[process]="#57478b"
+theme[process_selected]="#824f16"
+theme[core_bar]="#824f16"
+theme[temp_bar]="#654300"
+theme[swap]="#7d3e64"
+theme[div_line]="#8a7358"
+theme[process_bg]="#fff7ea"
+theme[process_fg]="#17120d"
+theme[bad]="#842f24"
+theme[good]="#3f6b35"
+theme[widget_bg]="#fff7ea"
+theme[widget_fg]="#17120d"
+theme[widget_border]="#8a7358"
+theme[widget_selected]="#824f16"
+theme[graph_bg]="#fff7ea"
+theme[graph_fg]="#17120d"
+theme[graph_high]="#a7471c"
+theme[graph_low]="#15516e"
+theme[graph_med]="#824f16"
+theme[proc_bg]="#fff7ea"
+theme[proc_fg]="#17120d"
+theme[process_border]="#8a7358"
+theme[widget_title]="#824f16"
+theme[box_border]="#8a7358"
+theme[box_bg]="#fff7ea"
+theme[box_fg]="#17120d"
+theme[box_selected]="#824f16"
+theme[os_bg]="#fff7ea"
+theme[os_fg]="#17120d"
+theme[clock_bg]="#fff7ea"
+theme[clock_fg]="#824f16"
+theme[bat_high]="#3f6b35"
+theme[bat_med]="#654300"
+theme[bat_low]="#842f24"
+theme[sensor_bg]="#fff7ea"
+theme[sensor_fg]="#17120d"
+theme[sensor_bar_bg]="#decbb1"
+theme[sensor_bar_fg]="#824f16"
+theme[net_bg]="#fff7ea"
+theme[net_fg]="#17120d"
+theme[net_download]="#3f6b35"
+theme[net_upload]="#a7471c"
diff --git a/themes/dreamcoder/btop-dreamcoder.theme b/themes/dreamcoder/btop-dreamcoder.theme
new file mode 100644
index 0000000..e5e4578
--- /dev/null
+++ b/themes/dreamcoder/btop-dreamcoder.theme
@@ -0,0 +1,64 @@
+# ========================================================
+# Dreamcoder Ember Noir — Btop theme
+# ========================================================
+# Place in ~/.config/btop/themes/ and select from Btop UI.
+
+theme[main_bg]="#14110e"
+theme[main_fg]="#f0e7dc"
+theme[title]="#cdae7d"
+theme[hi_fg]="#cdae7d"
+theme[selected_bg]="#373027"
+theme[selected_fg]="#f0e7dc"
+theme[inactive_fg]="#c7b9aa"
+theme[graph_line]="#594d46"
+theme[proc_misc]="#c7b9aa"
+theme[cpu_core]="#c7b2a2"
+theme[mem_free]="#b8bf84"
+theme[mem_used]="#ce836c"
+theme[mem_cached]="#c7b2a2"
+theme[user_bg]="#231c18"
+theme[user_fg]="#f0e7dc"
+theme[temp]="#e8b866"
+theme[disk]="#c7b2a2"
+theme[process]="#c9a8dc"
+theme[process_selected]="#cdae7d"
+theme[core_bar]="#cdae7d"
+theme[temp_bar]="#e8b866"
+theme[swap]="#d98aa9"
+theme[div_line]="#594d46"
+theme[process_bg]="#231c18"
+theme[process_fg]="#f0e7dc"
+theme[bad]="#e98272"
+theme[good]="#b8bf84"
+theme[widget_bg]="#231c18"
+theme[widget_fg]="#f0e7dc"
+theme[widget_border]="#594d46"
+theme[widget_selected]="#cdae7d"
+theme[graph_bg]="#231c18"
+theme[graph_fg]="#f0e7dc"
+theme[graph_high]="#ce836c"
+theme[graph_low]="#c7b2a2"
+theme[graph_med]="#cdae7d"
+theme[proc_bg]="#231c18"
+theme[proc_fg]="#f0e7dc"
+theme[process_border]="#594d46"
+theme[widget_title]="#cdae7d"
+theme[box_border]="#594d46"
+theme[box_bg]="#231c18"
+theme[box_fg]="#f0e7dc"
+theme[box_selected]="#cdae7d"
+theme[os_bg]="#231c18"
+theme[os_fg]="#f0e7dc"
+theme[clock_bg]="#231c18"
+theme[clock_fg]="#cdae7d"
+theme[bat_high]="#b8bf84"
+theme[bat_med]="#e8b866"
+theme[bat_low]="#e98272"
+theme[sensor_bg]="#231c18"
+theme[sensor_fg]="#f0e7dc"
+theme[sensor_bar_bg]="#2e241f"
+theme[sensor_bar_fg]="#cdae7d"
+theme[net_bg]="#231c18"
+theme[net_fg]="#f0e7dc"
+theme[net_download]="#b8bf84"
+theme[net_upload]="#ce836c"
diff --git a/themes/dreamcoder/cava-dreamcoder-dark.config b/themes/dreamcoder/cava-dreamcoder-dark.config
new file mode 100644
index 0000000..5a934e2
--- /dev/null
+++ b/themes/dreamcoder/cava-dreamcoder-dark.config
@@ -0,0 +1,26 @@
+# ========================================================
+# Dreamcoder Ember Noir — Cava theme
+# ========================================================
+# Include from ~/.config/cava/config or place in
+# ~/.config/cava/dreamcoder-cava.config.
+
+[color]
+# Background
+background = '#15100d'
+
+# Gradient mode for smooth transitions
+gradient = 1
+gradient_count = 8
+
+# Gradient colors (low to high frequency) — raw palette, muted at lower end
+gradient_color_1 = '#614a31'
+gradient_color_2 = '#866844'
+gradient_color_3 = '#7e5834'
+gradient_color_4 = '#a77644'
+gradient_color_5 = '#e6a15c'
+gradient_color_6 = '#d66f50'
+gradient_color_7 = '#a96054'
+gradient_color_8 = '#e98272'
+
+# Mono color (used when gradient = 0)
+foreground = '#e6a15c'
diff --git a/themes/dreamcoder/cava-dreamcoder-dusk.config b/themes/dreamcoder/cava-dreamcoder-dusk.config
new file mode 100644
index 0000000..dcd34cb
--- /dev/null
+++ b/themes/dreamcoder/cava-dreamcoder-dusk.config
@@ -0,0 +1,26 @@
+# ========================================================
+# Dreamcoder Dusk — Cava theme
+# ========================================================
+# Include from ~/.config/cava/config or place in
+# ~/.config/cava/dreamcoder-cava.config.
+
+[color]
+# Background
+background = '#ebe4d6'
+
+# Gradient mode for smooth transitions
+gradient = 1
+gradient_count = 8
+
+# Gradient colors (low to high frequency) — raw palette, muted at lower end
+gradient_color_1 = '#93a7aa'
+gradient_color_2 = '#688893'
+gradient_color_3 = '#ba9c7b'
+gradient_color_4 = '#a78057'
+gradient_color_5 = '#8a5520'
+gradient_color_6 = '#96411e'
+gradient_color_7 = '#9a675b'
+gradient_color_8 = '#773126'
+
+# Mono color (used when gradient = 0)
+foreground = '#8a5520'
diff --git a/themes/dreamcoder/cava-dreamcoder-light.config b/themes/dreamcoder/cava-dreamcoder-light.config
new file mode 100644
index 0000000..01d21fb
--- /dev/null
+++ b/themes/dreamcoder/cava-dreamcoder-light.config
@@ -0,0 +1,26 @@
+# ========================================================
+# Dreamcoder Light — Cava theme
+# ========================================================
+# Include from ~/.config/cava/config or place in
+# ~/.config/cava/dreamcoder-cava.config.
+
+[color]
+# Background
+background = '#f3eadc'
+
+# Gradient mode for smooth transitions
+gradient = 1
+gradient_count = 8
+
+# Gradient colors (low to high frequency) — raw palette, muted at lower end
+gradient_color_1 = '#9aadb0'
+gradient_color_2 = '#6e8e9a'
+gradient_color_3 = '#ba9c79'
+gradient_color_4 = '#a47e51'
+gradient_color_5 = '#824f16'
+gradient_color_6 = '#a7471c'
+gradient_color_7 = '#a5675b'
+gradient_color_8 = '#842f24'
+
+# Mono color (used when gradient = 0)
+foreground = '#824f16'
diff --git a/themes/dreamcoder/cava-dreamcoder.config b/themes/dreamcoder/cava-dreamcoder.config
new file mode 100644
index 0000000..30f0b48
--- /dev/null
+++ b/themes/dreamcoder/cava-dreamcoder.config
@@ -0,0 +1,26 @@
+# ========================================================
+# Dreamcoder Ember Noir — Cava theme
+# ========================================================
+# Include from ~/.config/cava/config or place in
+# ~/.config/cava/dreamcoder-cava.config.
+
+[color]
+# Background
+background = '#14110e'
+
+# Gradient mode for smooth transitions
+gradient = 1
+gradient_count = 8
+
+# Gradient colors (low to high frequency) — raw palette, muted at lower end
+gradient_color_1 = '#5c5149'
+gradient_color_2 = '#7f7267'
+gradient_color_3 = '#706046'
+gradient_color_4 = '#967f5c'
+gradient_color_5 = '#cdae7d'
+gradient_color_6 = '#ce836c'
+gradient_color_7 = '#a96054'
+gradient_color_8 = '#e98272'
+
+# Mono color (used when gradient = 0)
+foreground = '#cdae7d'
diff --git a/themes/dreamcoder/delta-dreamcoder-dark.gitconfig b/themes/dreamcoder/delta-dreamcoder-dark.gitconfig
new file mode 100644
index 0000000..292989f
--- /dev/null
+++ b/themes/dreamcoder/delta-dreamcoder-dark.gitconfig
@@ -0,0 +1,45 @@
+# ========================================================
+# Dreamcoder Ember Noir — Git Delta theme
+# ========================================================
+# Include from ~/.config/git/config:
+# [include]
+# path = ~/.config/git/delta-dreamcoder.gitconfig
+
+[delta]
+ # Syntax highlighting theme for diff content
+ syntax-theme = Dreamcoder-Dark
+
+ # Line colors
+ plus-color = "#2d2a1f"
+ minus-color = "#35211c"
+ plus-emph-color = "#b8bf84"
+ minus-emph-color = "#e98272"
+
+ # Diff UI
+ file-style = "#d99555"
+ file-decoration-style = "bold yellow box ul"
+ hunk-header-style = "file line-number syntax"
+ hunk-header-decoration-style = "yellow box"
+ hunk-header-file-style = "#d99555"
+ hunk-header-line-number-style = "##b8a99a"
+ hunk-header-color = "#302925"
+
+ # Commit decorations
+ commit-style = "#d99555 bold"
+ commit-decoration-style = "bold yellow box ul"
+
+ # Line numbers
+ line-numbers = true
+ line-numbers-left-style = "#b8a99a"
+ line-numbers-right-style = "#b8a99a"
+ line-numbers-minus-style = "#e98272"
+ line-numbers-plus-style = "#b8bf84"
+
+ # Side-by-side
+ side-by-side = true
+
+ # Whitespace highlighting
+ whitespace-error-style = "#e8b866"
+
+ # Navigation
+ navigate = true
diff --git a/themes/dreamcoder/delta-dreamcoder-dusk.gitconfig b/themes/dreamcoder/delta-dreamcoder-dusk.gitconfig
new file mode 100644
index 0000000..e50ecd6
--- /dev/null
+++ b/themes/dreamcoder/delta-dreamcoder-dusk.gitconfig
@@ -0,0 +1,45 @@
+# ========================================================
+# Dreamcoder Dusk — Git Delta theme
+# ========================================================
+# Include from ~/.config/git/config:
+# [include]
+# path = ~/.config/git/delta-dreamcoder.gitconfig
+
+[delta]
+ # Syntax highlighting theme for diff content
+ syntax-theme = Dreamcoder-Light
+
+ # Line colors
+ plus-color = "#d2d2c0"
+ minus-color = "#dac9bc"
+ plus-emph-color = "#466b41"
+ minus-emph-color = "#773126"
+
+ # Diff UI
+ file-style = "#8a5520"
+ file-decoration-style = "bold yellow box ul"
+ hunk-header-style = "file line-number syntax"
+ hunk-header-decoration-style = "yellow box"
+ hunk-header-file-style = "#8a5520"
+ hunk-header-line-number-style = "##4c443a"
+ hunk-header-color = "#d3ccbf"
+
+ # Commit decorations
+ commit-style = "#8a5520 bold"
+ commit-decoration-style = "bold yellow box ul"
+
+ # Line numbers
+ line-numbers = true
+ line-numbers-left-style = "#4c443a"
+ line-numbers-right-style = "#4c443a"
+ line-numbers-minus-style = "#773126"
+ line-numbers-plus-style = "#466b41"
+
+ # Side-by-side
+ side-by-side = true
+
+ # Whitespace highlighting
+ whitespace-error-style = "#604000"
+
+ # Navigation
+ navigate = true
diff --git a/themes/dreamcoder/delta-dreamcoder-light.gitconfig b/themes/dreamcoder/delta-dreamcoder-light.gitconfig
new file mode 100644
index 0000000..586459f
--- /dev/null
+++ b/themes/dreamcoder/delta-dreamcoder-light.gitconfig
@@ -0,0 +1,45 @@
+# ========================================================
+# Dreamcoder Light — Git Delta theme
+# ========================================================
+# Include from ~/.config/git/config:
+# [include]
+# path = ~/.config/git/delta-dreamcoder.gitconfig
+
+[delta]
+ # Syntax highlighting theme for diff content
+ syntax-theme = Dreamcoder-Light
+
+ # Line colors
+ plus-color = "#d8d7c3"
+ minus-color = "#e2cec0"
+ plus-emph-color = "#3f6b35"
+ minus-emph-color = "#842f24"
+
+ # Diff UI
+ file-style = "#824f16"
+ file-decoration-style = "bold yellow box ul"
+ hunk-header-style = "file line-number syntax"
+ hunk-header-decoration-style = "yellow box"
+ hunk-header-file-style = "#824f16"
+ hunk-header-line-number-style = "##3d3228"
+ hunk-header-color = "#d8cec1"
+
+ # Commit decorations
+ commit-style = "#824f16 bold"
+ commit-decoration-style = "bold yellow box ul"
+
+ # Line numbers
+ line-numbers = true
+ line-numbers-left-style = "#3d3228"
+ line-numbers-right-style = "#3d3228"
+ line-numbers-minus-style = "#842f24"
+ line-numbers-plus-style = "#3f6b35"
+
+ # Side-by-side
+ side-by-side = true
+
+ # Whitespace highlighting
+ whitespace-error-style = "#654300"
+
+ # Navigation
+ navigate = true
diff --git a/themes/dreamcoder/delta-dreamcoder.gitconfig b/themes/dreamcoder/delta-dreamcoder.gitconfig
new file mode 100644
index 0000000..b1f771d
--- /dev/null
+++ b/themes/dreamcoder/delta-dreamcoder.gitconfig
@@ -0,0 +1,45 @@
+# ========================================================
+# Dreamcoder Ember Noir — Git Delta theme
+# ========================================================
+# Include from ~/.config/git/config:
+# [include]
+# path = ~/.config/git/delta-dreamcoder.gitconfig
+
+[delta]
+ # Syntax highlighting theme for diff content
+ syntax-theme = Dreamcoder-Dark
+
+ # Line colors
+ plus-color = "#2d2b20"
+ minus-color = "#34221d"
+ plus-emph-color = "#b8bf84"
+ minus-emph-color = "#e98272"
+
+ # Diff UI
+ file-style = "#cdae7d"
+ file-decoration-style = "bold yellow box ul"
+ hunk-header-style = "file line-number syntax"
+ hunk-header-decoration-style = "yellow box"
+ hunk-header-file-style = "#cdae7d"
+ hunk-header-line-number-style = "##c7b9aa"
+ hunk-header-color = "#2f2a25"
+
+ # Commit decorations
+ commit-style = "#cdae7d bold"
+ commit-decoration-style = "bold yellow box ul"
+
+ # Line numbers
+ line-numbers = true
+ line-numbers-left-style = "#c7b9aa"
+ line-numbers-right-style = "#c7b9aa"
+ line-numbers-minus-style = "#e98272"
+ line-numbers-plus-style = "#b8bf84"
+
+ # Side-by-side
+ side-by-side = true
+
+ # Whitespace highlighting
+ whitespace-error-style = "#e8b866"
+
+ # Navigation
+ navigate = true
diff --git a/themes/dreamcoder/dunst-dreamcoder-dark.conf b/themes/dreamcoder/dunst-dreamcoder-dark.conf
new file mode 100644
index 0000000..9799577
--- /dev/null
+++ b/themes/dreamcoder/dunst-dreamcoder-dark.conf
@@ -0,0 +1,23 @@
+# ========================================================
+# Dreamcoder Ember Noir — Dunst theme
+# ========================================================
+# Include from dunstrc:
+# [include] dreamcoder-dunst.conf
+
+[urgency_low]
+ background = "#12100e"
+ foreground = "#e8dfd0"
+ highlight = "#d99555"
+ frame_color = "#594d46"
+
+[urgency_normal]
+ background = "#211c18"
+ foreground = "#e8dfd0"
+ highlight = "#d99555"
+ frame_color = "#806754"
+
+[urgency_critical]
+ background = "#55322b"
+ foreground = "#e8dfd0"
+ highlight = "#e98272"
+ frame_color = "#e98272"
diff --git a/themes/dreamcoder/dunst-dreamcoder-dusk.conf b/themes/dreamcoder/dunst-dreamcoder-dusk.conf
new file mode 100644
index 0000000..4a46687
--- /dev/null
+++ b/themes/dreamcoder/dunst-dreamcoder-dusk.conf
@@ -0,0 +1,23 @@
+# ========================================================
+# Dreamcoder Dusk — Dunst theme
+# ========================================================
+# Include from dunstrc:
+# [include] dreamcoder-dunst.conf
+
+[urgency_low]
+ background = "#ebe4d6"
+ foreground = "#1a1713"
+ highlight = "#8a5520"
+ frame_color = "#a7947a"
+
+[urgency_normal]
+ background = "#f1eadf"
+ foreground = "#1a1713"
+ highlight = "#8a5520"
+ frame_color = "#665845"
+
+[urgency_critical]
+ background = "#c8aea1"
+ foreground = "#1a1713"
+ highlight = "#773126"
+ frame_color = "#773126"
diff --git a/themes/dreamcoder/dunst-dreamcoder-light.conf b/themes/dreamcoder/dunst-dreamcoder-light.conf
new file mode 100644
index 0000000..39b4978
--- /dev/null
+++ b/themes/dreamcoder/dunst-dreamcoder-light.conf
@@ -0,0 +1,23 @@
+# ========================================================
+# Dreamcoder Light — Dunst theme
+# ========================================================
+# Include from dunstrc:
+# [include] dreamcoder-dunst.conf
+
+[urgency_low]
+ background = "#f3eadc"
+ foreground = "#17120d"
+ highlight = "#824f16"
+ frame_color = "#8a7358"
+
+[urgency_normal]
+ background = "#fff7ea"
+ foreground = "#17120d"
+ highlight = "#824f16"
+ frame_color = "#66513b"
+
+[urgency_critical]
+ background = "#d2b2a5"
+ foreground = "#17120d"
+ highlight = "#842f24"
+ frame_color = "#842f24"
diff --git a/themes/dreamcoder/dunst-dreamcoder.conf b/themes/dreamcoder/dunst-dreamcoder.conf
new file mode 100644
index 0000000..91c9535
--- /dev/null
+++ b/themes/dreamcoder/dunst-dreamcoder.conf
@@ -0,0 +1,23 @@
+# ========================================================
+# Dreamcoder Ember Noir — Dunst theme
+# ========================================================
+# Include from dunstrc:
+# [include] dreamcoder-dunst.conf
+
+[urgency_low]
+ background = "#14110e"
+ foreground = "#f0e7dc"
+ highlight = "#cdae7d"
+ frame_color = "#594d46"
+
+[urgency_normal]
+ background = "#231c18"
+ foreground = "#f0e7dc"
+ highlight = "#cdae7d"
+ frame_color = "#806754"
+
+[urgency_critical]
+ background = "#54332c"
+ foreground = "#f0e7dc"
+ highlight = "#e98272"
+ frame_color = "#e98272"
diff --git a/themes/dreamcoder/firefox-dreamcoder-dark.css b/themes/dreamcoder/firefox-dreamcoder-dark.css
new file mode 100644
index 0000000..68e8044
--- /dev/null
+++ b/themes/dreamcoder/firefox-dreamcoder-dark.css
@@ -0,0 +1,138 @@
+/* ========================================================
+ Dreamcoder Ember Noir — Firefox userChrome.css
+ ========================================================
+ Place in ~/.mozilla/firefox/*.default-release/chrome/userChrome.css
+ Requires toolkit.legacyUserProfileCustomizations.stylesheets = true
+ in about:config. */
+
+:root {
+ --dreamcoder-bg: #12100e;
+ --dreamcoder-surface: #211c18;
+ --dreamcoder-surface-1: #2e241f;
+ --dreamcoder-text: #e8dfd0;
+ --dreamcoder-text-dim: #b8a99a;
+ --dreamcoder-accent: #d99555;
+ --dreamcoder-accent-2: #c96a45;
+ --dreamcoder-border: #594d46;
+ --dreamcoder-border-ui: #806754;
+ --dreamcoder-error: #e98272;
+ --dreamcoder-warning: #e8b866;
+ --dreamcoder-sage: #b8bf84;
+ --dreamcoder-input-bg: #211c18;
+ --dreamcoder-toolbar-bg: #211c18;
+ --dreamcoder-hover: #342619;
+ --dreamcoder-active: #493421;
+}
+
+/* Main window */
+#main-window,
+#navigator-toolbox {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Toolbar & URL bar */
+#nav-bar,
+#nav-bar toolbarbutton,
+#urlbar,
+#urlbar-background {
+ background-color: var(--dreamcoder-toolbar-bg) !important;
+ color: var(--dreamcoder-text) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+#urlbar[focused="true"] > #urlbar-background {
+ border-color: var(--dreamcoder-accent) !important;
+}
+
+/* Sidebar (bookmarks, history) */
+#sidebar-box,
+#sidebar {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+.sidebar-placesTreechildren {
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Tab bar */
+#TabsToolbar,
+#tabbrowser-tabs,
+.tab-background {
+ background-color: var(--dreamcoder-bg) !important;
+}
+
+.tabbrowser-tab:not([selected]) .tab-background {
+ background-color: var(--dreamcoder-surface) !important;
+}
+
+.tabbrowser-tab[selected] .tab-background {
+ background-color: var(--dreamcoder-toolbar-bg) !important;
+ border-color: var(--dreamcoder-accent) !important;
+}
+
+.tabbrowser-tab .tab-label {
+ color: var(--dreamcoder-text-dim) !important;
+}
+
+.tabbrowser-tab[selected] .tab-label {
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Context menus */
+menupopup,
+popup {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+menuitem {
+ color: var(--dreamcoder-text) !important;
+}
+
+menuitem:hover {
+ background-color: var(--dreamcoder-hover) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Status panel */
+#statuspanel-label {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text-dim) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+/* Find bar */
+#findbar {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+#findbar input {
+ background-color: var(--dreamcoder-input-bg) !important;
+ color: var(--dreamcoder-text) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+/* Private browsing indicators */
+#private-browsing-indicator-with-label {
+ color: var(--dreamcoder-accent-2) !important;
+}
+
+/* Downloads panel */
+#downloadsPanel,
+#downloadsListBox {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Scrollbar styling */
+:root {
+ scrollbar-color: var(--dreamcoder-border) var(--dreamcoder-bg) !important;
+}
+
+* {
+ scrollbar-width: thin !important;
+ scrollbar-color: var(--dreamcoder-border) var(--dreamcoder-bg) !important;
+}
diff --git a/themes/dreamcoder/firefox-dreamcoder-dusk.css b/themes/dreamcoder/firefox-dreamcoder-dusk.css
new file mode 100644
index 0000000..e60c83a
--- /dev/null
+++ b/themes/dreamcoder/firefox-dreamcoder-dusk.css
@@ -0,0 +1,138 @@
+/* ========================================================
+ Dreamcoder Dusk — Firefox userChrome.css
+ ========================================================
+ Place in ~/.mozilla/firefox/*.default-release/chrome/userChrome.css
+ Requires toolkit.legacyUserProfileCustomizations.stylesheets = true
+ in about:config. */
+
+:root {
+ --dreamcoder-bg: #ebe4d6;
+ --dreamcoder-surface: #f1eadf;
+ --dreamcoder-surface-1: #d8cbb8;
+ --dreamcoder-text: #1a1713;
+ --dreamcoder-text-dim: #4c443a;
+ --dreamcoder-accent: #8a5520;
+ --dreamcoder-accent-2: #96411e;
+ --dreamcoder-border: #a7947a;
+ --dreamcoder-border-ui: #665845;
+ --dreamcoder-error: #773126;
+ --dreamcoder-warning: #604000;
+ --dreamcoder-sage: #466b41;
+ --dreamcoder-input-bg: #d8cbb8;
+ --dreamcoder-toolbar-bg: #dfd5c4;
+ --dreamcoder-hover: #dccfbb;
+ --dreamcoder-active: #d3c0a8;
+}
+
+/* Main window */
+#main-window,
+#navigator-toolbox {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Toolbar & URL bar */
+#nav-bar,
+#nav-bar toolbarbutton,
+#urlbar,
+#urlbar-background {
+ background-color: var(--dreamcoder-toolbar-bg) !important;
+ color: var(--dreamcoder-text) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+#urlbar[focused="true"] > #urlbar-background {
+ border-color: var(--dreamcoder-accent) !important;
+}
+
+/* Sidebar (bookmarks, history) */
+#sidebar-box,
+#sidebar {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+.sidebar-placesTreechildren {
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Tab bar */
+#TabsToolbar,
+#tabbrowser-tabs,
+.tab-background {
+ background-color: var(--dreamcoder-bg) !important;
+}
+
+.tabbrowser-tab:not([selected]) .tab-background {
+ background-color: var(--dreamcoder-surface) !important;
+}
+
+.tabbrowser-tab[selected] .tab-background {
+ background-color: var(--dreamcoder-toolbar-bg) !important;
+ border-color: var(--dreamcoder-accent) !important;
+}
+
+.tabbrowser-tab .tab-label {
+ color: var(--dreamcoder-text-dim) !important;
+}
+
+.tabbrowser-tab[selected] .tab-label {
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Context menus */
+menupopup,
+popup {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+menuitem {
+ color: var(--dreamcoder-text) !important;
+}
+
+menuitem:hover {
+ background-color: var(--dreamcoder-hover) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Status panel */
+#statuspanel-label {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text-dim) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+/* Find bar */
+#findbar {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+#findbar input {
+ background-color: var(--dreamcoder-input-bg) !important;
+ color: var(--dreamcoder-text) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+/* Private browsing indicators */
+#private-browsing-indicator-with-label {
+ color: var(--dreamcoder-accent-2) !important;
+}
+
+/* Downloads panel */
+#downloadsPanel,
+#downloadsListBox {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Scrollbar styling */
+:root {
+ scrollbar-color: var(--dreamcoder-border) var(--dreamcoder-bg) !important;
+}
+
+* {
+ scrollbar-width: thin !important;
+ scrollbar-color: var(--dreamcoder-border) var(--dreamcoder-bg) !important;
+}
diff --git a/themes/dreamcoder/firefox-dreamcoder-light.css b/themes/dreamcoder/firefox-dreamcoder-light.css
new file mode 100644
index 0000000..d1174ba
--- /dev/null
+++ b/themes/dreamcoder/firefox-dreamcoder-light.css
@@ -0,0 +1,138 @@
+/* ========================================================
+ Dreamcoder Light — Firefox userChrome.css
+ ========================================================
+ Place in ~/.mozilla/firefox/*.default-release/chrome/userChrome.css
+ Requires toolkit.legacyUserProfileCustomizations.stylesheets = true
+ in about:config. */
+
+:root {
+ --dreamcoder-bg: #f3eadc;
+ --dreamcoder-surface: #fff7ea;
+ --dreamcoder-surface-1: #decbb1;
+ --dreamcoder-text: #17120d;
+ --dreamcoder-text-dim: #3d3228;
+ --dreamcoder-accent: #824f16;
+ --dreamcoder-accent-2: #a7471c;
+ --dreamcoder-border: #8a7358;
+ --dreamcoder-border-ui: #66513b;
+ --dreamcoder-error: #842f24;
+ --dreamcoder-warning: #654300;
+ --dreamcoder-sage: #3f6b35;
+ --dreamcoder-input-bg: #decbb1;
+ --dreamcoder-toolbar-bg: #e6d7c4;
+ --dreamcoder-hover: #e2d3be;
+ --dreamcoder-active: #d7c3aa;
+}
+
+/* Main window */
+#main-window,
+#navigator-toolbox {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Toolbar & URL bar */
+#nav-bar,
+#nav-bar toolbarbutton,
+#urlbar,
+#urlbar-background {
+ background-color: var(--dreamcoder-toolbar-bg) !important;
+ color: var(--dreamcoder-text) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+#urlbar[focused="true"] > #urlbar-background {
+ border-color: var(--dreamcoder-accent) !important;
+}
+
+/* Sidebar (bookmarks, history) */
+#sidebar-box,
+#sidebar {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+.sidebar-placesTreechildren {
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Tab bar */
+#TabsToolbar,
+#tabbrowser-tabs,
+.tab-background {
+ background-color: var(--dreamcoder-bg) !important;
+}
+
+.tabbrowser-tab:not([selected]) .tab-background {
+ background-color: var(--dreamcoder-surface) !important;
+}
+
+.tabbrowser-tab[selected] .tab-background {
+ background-color: var(--dreamcoder-toolbar-bg) !important;
+ border-color: var(--dreamcoder-accent) !important;
+}
+
+.tabbrowser-tab .tab-label {
+ color: var(--dreamcoder-text-dim) !important;
+}
+
+.tabbrowser-tab[selected] .tab-label {
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Context menus */
+menupopup,
+popup {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+menuitem {
+ color: var(--dreamcoder-text) !important;
+}
+
+menuitem:hover {
+ background-color: var(--dreamcoder-hover) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Status panel */
+#statuspanel-label {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text-dim) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+/* Find bar */
+#findbar {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+#findbar input {
+ background-color: var(--dreamcoder-input-bg) !important;
+ color: var(--dreamcoder-text) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+/* Private browsing indicators */
+#private-browsing-indicator-with-label {
+ color: var(--dreamcoder-accent-2) !important;
+}
+
+/* Downloads panel */
+#downloadsPanel,
+#downloadsListBox {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Scrollbar styling */
+:root {
+ scrollbar-color: var(--dreamcoder-border) var(--dreamcoder-bg) !important;
+}
+
+* {
+ scrollbar-width: thin !important;
+ scrollbar-color: var(--dreamcoder-border) var(--dreamcoder-bg) !important;
+}
diff --git a/themes/dreamcoder/firefox-dreamcoder.css b/themes/dreamcoder/firefox-dreamcoder.css
new file mode 100644
index 0000000..94e98f9
--- /dev/null
+++ b/themes/dreamcoder/firefox-dreamcoder.css
@@ -0,0 +1,138 @@
+/* ========================================================
+ Dreamcoder Ember Noir — Firefox userChrome.css
+ ========================================================
+ Place in ~/.mozilla/firefox/*.default-release/chrome/userChrome.css
+ Requires toolkit.legacyUserProfileCustomizations.stylesheets = true
+ in about:config. */
+
+:root {
+ --dreamcoder-bg: #14110e;
+ --dreamcoder-surface: #231c18;
+ --dreamcoder-surface-1: #2e241f;
+ --dreamcoder-text: #f0e7dc;
+ --dreamcoder-text-dim: #c7b9aa;
+ --dreamcoder-accent: #cdae7d;
+ --dreamcoder-accent-2: #ce836c;
+ --dreamcoder-border: #594d46;
+ --dreamcoder-border-ui: #806754;
+ --dreamcoder-error: #e98272;
+ --dreamcoder-warning: #e8b866;
+ --dreamcoder-sage: #b8bf84;
+ --dreamcoder-input-bg: #231c18;
+ --dreamcoder-toolbar-bg: #231c18;
+ --dreamcoder-hover: #30291f;
+ --dreamcoder-active: #42382a;
+}
+
+/* Main window */
+#main-window,
+#navigator-toolbox {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Toolbar & URL bar */
+#nav-bar,
+#nav-bar toolbarbutton,
+#urlbar,
+#urlbar-background {
+ background-color: var(--dreamcoder-toolbar-bg) !important;
+ color: var(--dreamcoder-text) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+#urlbar[focused="true"] > #urlbar-background {
+ border-color: var(--dreamcoder-accent) !important;
+}
+
+/* Sidebar (bookmarks, history) */
+#sidebar-box,
+#sidebar {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+.sidebar-placesTreechildren {
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Tab bar */
+#TabsToolbar,
+#tabbrowser-tabs,
+.tab-background {
+ background-color: var(--dreamcoder-bg) !important;
+}
+
+.tabbrowser-tab:not([selected]) .tab-background {
+ background-color: var(--dreamcoder-surface) !important;
+}
+
+.tabbrowser-tab[selected] .tab-background {
+ background-color: var(--dreamcoder-toolbar-bg) !important;
+ border-color: var(--dreamcoder-accent) !important;
+}
+
+.tabbrowser-tab .tab-label {
+ color: var(--dreamcoder-text-dim) !important;
+}
+
+.tabbrowser-tab[selected] .tab-label {
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Context menus */
+menupopup,
+popup {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+menuitem {
+ color: var(--dreamcoder-text) !important;
+}
+
+menuitem:hover {
+ background-color: var(--dreamcoder-hover) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Status panel */
+#statuspanel-label {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text-dim) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+/* Find bar */
+#findbar {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+#findbar input {
+ background-color: var(--dreamcoder-input-bg) !important;
+ color: var(--dreamcoder-text) !important;
+ border-color: var(--dreamcoder-border) !important;
+}
+
+/* Private browsing indicators */
+#private-browsing-indicator-with-label {
+ color: var(--dreamcoder-accent-2) !important;
+}
+
+/* Downloads panel */
+#downloadsPanel,
+#downloadsListBox {
+ background-color: var(--dreamcoder-bg) !important;
+ color: var(--dreamcoder-text) !important;
+}
+
+/* Scrollbar styling */
+:root {
+ scrollbar-color: var(--dreamcoder-border) var(--dreamcoder-bg) !important;
+}
+
+* {
+ scrollbar-width: thin !important;
+ scrollbar-color: var(--dreamcoder-border) var(--dreamcoder-bg) !important;
+}
diff --git a/themes/dreamcoder/fzf-dreamcoder-dark.sh b/themes/dreamcoder/fzf-dreamcoder-dark.sh
new file mode 100644
index 0000000..808b71a
--- /dev/null
+++ b/themes/dreamcoder/fzf-dreamcoder-dark.sh
@@ -0,0 +1,27 @@
+# ========================================================
+# Dreamcoder Ember Noir — Fzf theme
+# ========================================================
+# Source this from .zshrc or .bashrc.
+
+export FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS"'
+ --color=bg:#12100e
+ --color=bg+:#211c18
+ --color=fg:#e8dfd0
+ --color=fg+:#e8dfd0
+ --color=hl:#d99555
+ --color=hl+:#d99555
+ --color=info:#c8b2a2
+ --color=marker:#b8bf84
+ --color=prompt:#d99555
+ --color=spinner:#c9a8dc
+ --color=pointer:#c96a45
+ --color=header:#b8a99a
+ --color=border:#594d46
+ --color=label:#b8a99a
+ --color=query:#e8dfd0
+ --color=gutter:#12100e
+ --color=scrollbar:#594d46
+ --color=separator:#594d46
+ --color=preview-bg:#12100e
+ --color=preview-border:#594d46
+'
diff --git a/themes/dreamcoder/fzf-dreamcoder-dusk.sh b/themes/dreamcoder/fzf-dreamcoder-dusk.sh
new file mode 100644
index 0000000..464a9dc
--- /dev/null
+++ b/themes/dreamcoder/fzf-dreamcoder-dusk.sh
@@ -0,0 +1,27 @@
+# ========================================================
+# Dreamcoder Dusk — Fzf theme
+# ========================================================
+# Source this from .zshrc or .bashrc.
+
+export FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS"'
+ --color=bg:#ebe4d6
+ --color=bg+:#f1eadf
+ --color=fg:#1a1713
+ --color=fg+:#1a1713
+ --color=hl:#8a5520
+ --color=hl+:#8a5520
+ --color=info:#104b67
+ --color=marker:#466b41
+ --color=prompt:#8a5520
+ --color=spinner:#5b4e86
+ --color=pointer:#96411e
+ --color=header:#4c443a
+ --color=border:#a7947a
+ --color=label:#4c443a
+ --color=query:#1a1713
+ --color=gutter:#ebe4d6
+ --color=scrollbar:#a7947a
+ --color=separator:#a7947a
+ --color=preview-bg:#ebe4d6
+ --color=preview-border:#a7947a
+'
diff --git a/themes/dreamcoder/fzf-dreamcoder-light.sh b/themes/dreamcoder/fzf-dreamcoder-light.sh
new file mode 100644
index 0000000..942d696
--- /dev/null
+++ b/themes/dreamcoder/fzf-dreamcoder-light.sh
@@ -0,0 +1,27 @@
+# ========================================================
+# Dreamcoder Light — Fzf theme
+# ========================================================
+# Source this from .zshrc or .bashrc.
+
+export FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS"'
+ --color=bg:#f3eadc
+ --color=bg+:#fff7ea
+ --color=fg:#17120d
+ --color=fg+:#17120d
+ --color=hl:#824f16
+ --color=hl+:#824f16
+ --color=info:#15516e
+ --color=marker:#3f6b35
+ --color=prompt:#824f16
+ --color=spinner:#57478b
+ --color=pointer:#a7471c
+ --color=header:#3d3228
+ --color=border:#8a7358
+ --color=label:#3d3228
+ --color=query:#17120d
+ --color=gutter:#f3eadc
+ --color=scrollbar:#8a7358
+ --color=separator:#8a7358
+ --color=preview-bg:#f3eadc
+ --color=preview-border:#8a7358
+'
diff --git a/themes/dreamcoder/fzf-dreamcoder.sh b/themes/dreamcoder/fzf-dreamcoder.sh
new file mode 100644
index 0000000..e42ae1b
--- /dev/null
+++ b/themes/dreamcoder/fzf-dreamcoder.sh
@@ -0,0 +1,27 @@
+# ========================================================
+# Dreamcoder Ember Noir — Fzf theme
+# ========================================================
+# Source this from .zshrc or .bashrc.
+
+export FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS"'
+ --color=bg:#14110e
+ --color=bg+:#231c18
+ --color=fg:#f0e7dc
+ --color=fg+:#f0e7dc
+ --color=hl:#cdae7d
+ --color=hl+:#cdae7d
+ --color=info:#c7b2a2
+ --color=marker:#b8bf84
+ --color=prompt:#cdae7d
+ --color=spinner:#c9a8dc
+ --color=pointer:#ce836c
+ --color=header:#c7b9aa
+ --color=border:#594d46
+ --color=label:#c7b9aa
+ --color=query:#f0e7dc
+ --color=gutter:#14110e
+ --color=scrollbar:#594d46
+ --color=separator:#594d46
+ --color=preview-bg:#14110e
+ --color=preview-border:#594d46
+'
diff --git a/themes/dreamcoder/hyprland-dark.conf b/themes/dreamcoder/hyprland-dark.conf
index e271402..70996c9 100644
--- a/themes/dreamcoder/hyprland-dark.conf
+++ b/themes/dreamcoder/hyprland-dark.conf
@@ -2,10 +2,10 @@
# Import after ML4W/Gentleman defaults; this changes colors only.
general {
- col.active_border = rgba(d9ad67ff) rgba(92c7cdff) 45deg
- col.inactive_border = rgba(3d4350b3)
+ col.active_border = rgba(d99555ff) rgba(c8b2a2ff) 45deg
+ col.inactive_border = rgba(594d46c8)
}
misc {
- background_color = rgba(101216ff)
+ background_color = rgba(12100eff)
}
diff --git a/themes/dreamcoder/hyprland-dusk.conf b/themes/dreamcoder/hyprland-dusk.conf
new file mode 100644
index 0000000..f2f7e1b
--- /dev/null
+++ b/themes/dreamcoder/hyprland-dusk.conf
@@ -0,0 +1,11 @@
+# Dreamcoder color layer for Hyprland.
+# Import after ML4W/Gentleman defaults; this changes colors only.
+
+general {
+ col.active_border = rgba(8a5520ff) rgba(104b67ff) 45deg
+ col.inactive_border = rgba(a7947adf)
+}
+
+misc {
+ background_color = rgba(ebe4d6ff)
+}
diff --git a/themes/dreamcoder/hyprland-light.conf b/themes/dreamcoder/hyprland-light.conf
index c3269ae..7962982 100644
--- a/themes/dreamcoder/hyprland-light.conf
+++ b/themes/dreamcoder/hyprland-light.conf
@@ -2,10 +2,10 @@
# Import after ML4W/Gentleman defaults; this changes colors only.
general {
- col.active_border = rgba(855719ff) rgba(1e6871ff) 45deg
- col.inactive_border = rgba(b7a78fdf)
+ col.active_border = rgba(824f16ff) rgba(15516eff) 45deg
+ col.inactive_border = rgba(8a7358ee)
}
misc {
- background_color = rgba(f6f1e8ff)
+ background_color = rgba(f3eadcff)
}
diff --git a/themes/dreamcoder/ls-colors-dreamcoder-dark.sh b/themes/dreamcoder/ls-colors-dreamcoder-dark.sh
new file mode 100644
index 0000000..bad388e
--- /dev/null
+++ b/themes/dreamcoder/ls-colors-dreamcoder-dark.sh
@@ -0,0 +1,102 @@
+# ========================================================
+# Dreamcoder Ember Noir — LS_COLORS / eza theme
+# ========================================================
+# Source this from .zshrc or .bashrc.
+# These are 24-bit truecolor codes — ensure TERM supports it.
+
+# Core file types
+export LS_COLORS="\
+ di=38;2;217;149;85:\
+ ex=38;2;201;106;69:\
+ ln=38;2;200;178;162:\
+ or=48;2;18;16;14;38;2;232;223;208:\
+ so=38;2;184;191;132:\
+ pi=38;2;232;184;102:\
+ bd=38;2;233;130;114:\
+ cd=38;2;233;130;114:\
+ su=48;2;18;16;14;38;2;201;106;69:\
+ sg=48;2;18;16;14;38;2;201;106;69:\
+ tw=38;2;217;149;85;48;2;29;22;19:\
+ ow=38;2;217;149;85;48;2;29;22;19:\
+ st=38;2;217;149;85;48;2;41;28;22:\
+ *.tar=38;2;201;168;220:\
+ *.tgz=38;2;201;168;220:\
+ *.gz=38;2;201;168;220:\
+ *.bz2=38;2;201;168;220:\
+ *.xz=38;2;201;168;220:\
+ *.zst=38;2;201;168;220:\
+ *.zip=38;2;201;168;220:\
+ *.7z=38;2;201;168;220:\
+ *.rar=38;2;201;168;220:\
+ *.iso=38;2;201;168;220:\
+ *.dmg=38;2;201;168;220:\
+ *.jpg=38;2;217;138;169:\
+ *.jpeg=38;2;217;138;169:\
+ *.png=38;2;217;138;169:\
+ *.gif=38;2;217;138;169:\
+ *.bmp=38;2;217;138;169:\
+ *.svg=38;2;217;138;169:\
+ *.webp=38;2;217;138;169:\
+ *.ico=38;2;217;138;169:\
+ *.mp3=38;2;217;138;169:\
+ *.wav=38;2;217;138;169:\
+ *.flac=38;2;217;138;169:\
+ *.ogg=38;2;217;138;169:\
+ *.m4a=38;2;217;138;169:\
+ *.mp4=38;2;217;138;169:\
+ *.mkv=38;2;217;138;169:\
+ *.webm=38;2;217;138;169:\
+ *.mov=38;2;217;138;169:\
+ *.pdf=38;2;184;169;149:\
+ *.doc=38;2;184;169;149:\
+ *.docx=38;2;184;169;149:\
+ *.odt=38;2;184;169;149:\
+ *.xls=38;2;184;169;149:\
+ *.xlsx=38;2;184;169;149:\
+ *.ppt=38;2;184;169;149:\
+ *.pptx=38;2;184;169;149:\
+ *.txt=38;2;184;169;149:\
+ *.md=#d99555:\
+ *.cfg=38;2;184;169;149:\
+ *.conf=38;2;184;169;149:\
+ *.json=38;2;184;169;149:\
+ *.yaml=38;2;184;169;149:\
+ *.yml=38;2;184;169;149:\
+ *.toml=38;2;184;169;149:\
+ *.xml=38;2;184;169;149:\
+ *.sh=38;2;201;106;69:\
+ *.bash=38;2;201;106;69:\
+ *.zsh=38;2;201;106;69:\
+ *.fish=38;2;201;106;69:\
+ *.py=38;2;201;106;69:\
+ *.rb=38;2;201;106;69:\
+ *.rs=38;2;201;106;69:\
+ *.go=38;2;201;106;69:\
+ *.ts=38;2;201;106;69:\
+ *.js=38;2;201;106;69:\
+ *.css=38;2;184;169;149:\
+ *.html=38;2;184;169;149:\
+ *.c=38;2;184;169;149:\
+ *.h=38;2;184;169;149:\
+ *.cpp=38;2;184;169;149:\
+ *.hpp=38;2;184;169;149:\
+ *.swp=38;2;184;169;149:\
+ *.swo=38;2;184;169;149:\
+ *.bak=38;2;184;169;149:\
+ *.orig=38;2;184;169;149:\
+"
+
+# eza (modern ls replacement) additional config
+export EZA_COLORS="\
+ di=38;2;217;149;85:\
+ ex=38;2;201;106;69:\
+ ln=38;2;200;178;162:\
+ so=38;2;184;191;132:\
+ pi=38;2;232;184;102:\
+ bd=38;2;233;130;114:\
+ cd=38;2;233;130;114:\
+ uw=38;2;232;184;102:\
+ ux=38;2;233;130;114:\
+ gwx=38;2;232;184;102:\
+ *.md=#d99555:\
+"
diff --git a/themes/dreamcoder/ls-colors-dreamcoder-dusk.sh b/themes/dreamcoder/ls-colors-dreamcoder-dusk.sh
new file mode 100644
index 0000000..6028f2f
--- /dev/null
+++ b/themes/dreamcoder/ls-colors-dreamcoder-dusk.sh
@@ -0,0 +1,102 @@
+# ========================================================
+# Dreamcoder Dusk — LS_COLORS / eza theme
+# ========================================================
+# Source this from .zshrc or .bashrc.
+# These are 24-bit truecolor codes — ensure TERM supports it.
+
+# Core file types
+export LS_COLORS="\
+ di=38;2;138;85;32:\
+ ex=38;2;150;65;30:\
+ ln=38;2;16;75;103:\
+ or=48;2;235;228;214;38;2;26;23;19:\
+ so=38;2;70;107;65:\
+ pi=38;2;96;64;0:\
+ bd=38;2;119;49;38:\
+ cd=38;2;119;49;38:\
+ su=48;2;235;228;214;38;2;150;65;30:\
+ sg=48;2;235;228;214;38;2;150;65;30:\
+ tw=38;2;138;85;32;48;2;241;234;223:\
+ ow=38;2;138;85;32;48;2;241;234;223:\
+ st=38;2;138;85;32;48;2;216;203;184:\
+ *.tar=38;2;91;78;134:\
+ *.tgz=38;2;91;78;134:\
+ *.gz=38;2;91;78;134:\
+ *.bz2=38;2;91;78;134:\
+ *.xz=38;2;91;78;134:\
+ *.zst=38;2;91;78;134:\
+ *.zip=38;2;91;78;134:\
+ *.7z=38;2;91;78;134:\
+ *.rar=38;2;91;78;134:\
+ *.iso=38;2;91;78;134:\
+ *.dmg=38;2;91;78;134:\
+ *.jpg=38;2;120;71;98:\
+ *.jpeg=38;2;120;71;98:\
+ *.png=38;2;120;71;98:\
+ *.gif=38;2;120;71;98:\
+ *.bmp=38;2;120;71;98:\
+ *.svg=38;2;120;71;98:\
+ *.webp=38;2;120;71;98:\
+ *.ico=38;2;120;71;98:\
+ *.mp3=38;2;120;71;98:\
+ *.wav=38;2;120;71;98:\
+ *.flac=38;2;120;71;98:\
+ *.ogg=38;2;120;71;98:\
+ *.m4a=38;2;120;71;98:\
+ *.mp4=38;2;120;71;98:\
+ *.mkv=38;2;120;71;98:\
+ *.webm=38;2;120;71;98:\
+ *.mov=38;2;120;71;98:\
+ *.pdf=38;2;76;68;58:\
+ *.doc=38;2;76;68;58:\
+ *.docx=38;2;76;68;58:\
+ *.odt=38;2;76;68;58:\
+ *.xls=38;2;76;68;58:\
+ *.xlsx=38;2;76;68;58:\
+ *.ppt=38;2;76;68;58:\
+ *.pptx=38;2;76;68;58:\
+ *.txt=38;2;76;68;58:\
+ *.md=#8a5520:\
+ *.cfg=38;2;76;68;58:\
+ *.conf=38;2;76;68;58:\
+ *.json=38;2;76;68;58:\
+ *.yaml=38;2;76;68;58:\
+ *.yml=38;2;76;68;58:\
+ *.toml=38;2;76;68;58:\
+ *.xml=38;2;76;68;58:\
+ *.sh=38;2;150;65;30:\
+ *.bash=38;2;150;65;30:\
+ *.zsh=38;2;150;65;30:\
+ *.fish=38;2;150;65;30:\
+ *.py=38;2;150;65;30:\
+ *.rb=38;2;150;65;30:\
+ *.rs=38;2;150;65;30:\
+ *.go=38;2;150;65;30:\
+ *.ts=38;2;150;65;30:\
+ *.js=38;2;150;65;30:\
+ *.css=38;2;76;68;58:\
+ *.html=38;2;76;68;58:\
+ *.c=38;2;76;68;58:\
+ *.h=38;2;76;68;58:\
+ *.cpp=38;2;76;68;58:\
+ *.hpp=38;2;76;68;58:\
+ *.swp=38;2;76;68;58:\
+ *.swo=38;2;76;68;58:\
+ *.bak=38;2;76;68;58:\
+ *.orig=38;2;76;68;58:\
+"
+
+# eza (modern ls replacement) additional config
+export EZA_COLORS="\
+ di=38;2;138;85;32:\
+ ex=38;2;150;65;30:\
+ ln=38;2;16;75;103:\
+ so=38;2;70;107;65:\
+ pi=38;2;96;64;0:\
+ bd=38;2;119;49;38:\
+ cd=38;2;119;49;38:\
+ uw=38;2;96;64;0:\
+ ux=38;2;119;49;38:\
+ gwx=38;2;96;64;0:\
+ *.md=#8a5520:\
+"
diff --git a/themes/dreamcoder/ls-colors-dreamcoder-light.sh b/themes/dreamcoder/ls-colors-dreamcoder-light.sh
new file mode 100644
index 0000000..8f9fa64
--- /dev/null
+++ b/themes/dreamcoder/ls-colors-dreamcoder-light.sh
@@ -0,0 +1,102 @@
+# ========================================================
+# Dreamcoder Light — LS_COLORS / eza theme
+# ========================================================
+# Source this from .zshrc or .bashrc.
+# These are 24-bit truecolor codes — ensure TERM supports it.
+
+# Core file types
+export LS_COLORS="\
+ di=38;2;130;79;22:\
+ ex=38;2;167;71;28:\
+ ln=38;2;21;81;110:\
+ or=48;2;243;234;220;38;2;23;18;13:\
+ so=38;2;63;107;53:\
+ pi=38;2;101;67;0:\
+ bd=38;2;132;47;36:\
+ cd=38;2;132;47;36:\
+ su=48;2;243;234;220;38;2;167;71;28:\
+ sg=48;2;243;234;220;38;2;167;71;28:\
+ tw=38;2;130;79;22;48;2;255;247;234:\
+ ow=38;2;130;79;22;48;2;255;247;234:\
+ st=38;2;130;79;22;48;2;222;203;177:\
+ *.tar=38;2;87;71;139:\
+ *.tgz=38;2;87;71;139:\
+ *.gz=38;2;87;71;139:\
+ *.bz2=38;2;87;71;139:\
+ *.xz=38;2;87;71;139:\
+ *.zst=38;2;87;71;139:\
+ *.zip=38;2;87;71;139:\
+ *.7z=38;2;87;71;139:\
+ *.rar=38;2;87;71;139:\
+ *.iso=38;2;87;71;139:\
+ *.dmg=38;2;87;71;139:\
+ *.jpg=38;2;125;62;100:\
+ *.jpeg=38;2;125;62;100:\
+ *.png=38;2;125;62;100:\
+ *.gif=38;2;125;62;100:\
+ *.bmp=38;2;125;62;100:\
+ *.svg=38;2;125;62;100:\
+ *.webp=38;2;125;62;100:\
+ *.ico=38;2;125;62;100:\
+ *.mp3=38;2;125;62;100:\
+ *.wav=38;2;125;62;100:\
+ *.flac=38;2;125;62;100:\
+ *.ogg=38;2;125;62;100:\
+ *.m4a=38;2;125;62;100:\
+ *.mp4=38;2;125;62;100:\
+ *.mkv=38;2;125;62;100:\
+ *.webm=38;2;125;62;100:\
+ *.mov=38;2;125;62;100:\
+ *.pdf=38;2;61;50;40:\
+ *.doc=38;2;61;50;40:\
+ *.docx=38;2;61;50;40:\
+ *.odt=38;2;61;50;40:\
+ *.xls=38;2;61;50;40:\
+ *.xlsx=38;2;61;50;40:\
+ *.ppt=38;2;61;50;40:\
+ *.pptx=38;2;61;50;40:\
+ *.txt=38;2;61;50;40:\
+ *.md=#824f16:\
+ *.cfg=38;2;61;50;40:\
+ *.conf=38;2;61;50;40:\
+ *.json=38;2;61;50;40:\
+ *.yaml=38;2;61;50;40:\
+ *.yml=38;2;61;50;40:\
+ *.toml=38;2;61;50;40:\
+ *.xml=38;2;61;50;40:\
+ *.sh=38;2;167;71;28:\
+ *.bash=38;2;167;71;28:\
+ *.zsh=38;2;167;71;28:\
+ *.fish=38;2;167;71;28:\
+ *.py=38;2;167;71;28:\
+ *.rb=38;2;167;71;28:\
+ *.rs=38;2;167;71;28:\
+ *.go=38;2;167;71;28:\
+ *.ts=38;2;167;71;28:\
+ *.js=38;2;167;71;28:\
+ *.css=38;2;61;50;40:\
+ *.html=38;2;61;50;40:\
+ *.c=38;2;61;50;40:\
+ *.h=38;2;61;50;40:\
+ *.cpp=38;2;61;50;40:\
+ *.hpp=38;2;61;50;40:\
+ *.swp=38;2;61;50;40:\
+ *.swo=38;2;61;50;40:\
+ *.bak=38;2;61;50;40:\
+ *.orig=38;2;61;50;40:\
+"
+
+# eza (modern ls replacement) additional config
+export EZA_COLORS="\
+ di=38;2;130;79;22:\
+ ex=38;2;167;71;28:\
+ ln=38;2;21;81;110:\
+ so=38;2;63;107;53:\
+ pi=38;2;101;67;0:\
+ bd=38;2;132;47;36:\
+ cd=38;2;132;47;36:\
+ uw=38;2;101;67;0:\
+ ux=38;2;132;47;36:\
+ gwx=38;2;101;67;0:\
+ *.md=#824f16:\
+"
diff --git a/themes/dreamcoder/ls-colors-dreamcoder.sh b/themes/dreamcoder/ls-colors-dreamcoder.sh
new file mode 100644
index 0000000..4891613
--- /dev/null
+++ b/themes/dreamcoder/ls-colors-dreamcoder.sh
@@ -0,0 +1,102 @@
+# ========================================================
+# Dreamcoder Ember Noir — LS_COLORS / eza theme
+# ========================================================
+# Source this from .zshrc or .bashrc.
+# These are 24-bit truecolor codes — ensure TERM supports it.
+
+# Core file types
+export LS_COLORS="\
+ di=38;2;205;174;125:\
+ ex=38;2;206;131;108:\
+ ln=38;2;199;178;162:\
+ or=48;2;20;17;14;38;2;240;231;220:\
+ so=38;2;184;191;132:\
+ pi=38;2;232;184;102:\
+ bd=38;2;233;130;114:\
+ cd=38;2;233;130;114:\
+ su=48;2;20;17;14;38;2;206;131;108:\
+ sg=48;2;20;17;14;38;2;206;131;108:\
+ tw=38;2;205;174;125;48;2;35;28;24:\
+ ow=38;2;205;174;125;48;2;35;28;24:\
+ st=38;2;205;174;125;48;2;46;36;31:\
+ *.tar=38;2;201;168;220:\
+ *.tgz=38;2;201;168;220:\
+ *.gz=38;2;201;168;220:\
+ *.bz2=38;2;201;168;220:\
+ *.xz=38;2;201;168;220:\
+ *.zst=38;2;201;168;220:\
+ *.zip=38;2;201;168;220:\
+ *.7z=38;2;201;168;220:\
+ *.rar=38;2;201;168;220:\
+ *.iso=38;2;201;168;220:\
+ *.dmg=38;2;201;168;220:\
+ *.jpg=38;2;217;138;169:\
+ *.jpeg=38;2;217;138;169:\
+ *.png=38;2;217;138;169:\
+ *.gif=38;2;217;138;169:\
+ *.bmp=38;2;217;138;169:\
+ *.svg=38;2;217;138;169:\
+ *.webp=38;2;217;138;169:\
+ *.ico=38;2;217;138;169:\
+ *.mp3=38;2;217;138;169:\
+ *.wav=38;2;217;138;169:\
+ *.flac=38;2;217;138;169:\
+ *.ogg=38;2;217;138;169:\
+ *.m4a=38;2;217;138;169:\
+ *.mp4=38;2;217;138;169:\
+ *.mkv=38;2;217;138;169:\
+ *.webm=38;2;217;138;169:\
+ *.mov=38;2;217;138;169:\
+ *.pdf=38;2;199;185;170:\
+ *.doc=38;2;199;185;170:\
+ *.docx=38;2;199;185;170:\
+ *.odt=38;2;199;185;170:\
+ *.xls=38;2;199;185;170:\
+ *.xlsx=38;2;199;185;170:\
+ *.ppt=38;2;199;185;170:\
+ *.pptx=38;2;199;185;170:\
+ *.txt=38;2;199;185;170:\
+ *.md=#cdae7d:\
+ *.cfg=38;2;199;185;170:\
+ *.conf=38;2;199;185;170:\
+ *.json=38;2;199;185;170:\
+ *.yaml=38;2;199;185;170:\
+ *.yml=38;2;199;185;170:\
+ *.toml=38;2;199;185;170:\
+ *.xml=38;2;199;185;170:\
+ *.sh=38;2;206;131;108:\
+ *.bash=38;2;206;131;108:\
+ *.zsh=38;2;206;131;108:\
+ *.fish=38;2;206;131;108:\
+ *.py=38;2;206;131;108:\
+ *.rb=38;2;206;131;108:\
+ *.rs=38;2;206;131;108:\
+ *.go=38;2;206;131;108:\
+ *.ts=38;2;206;131;108:\
+ *.js=38;2;206;131;108:\
+ *.css=38;2;199;185;170:\
+ *.html=38;2;199;185;170:\
+ *.c=38;2;199;185;170:\
+ *.h=38;2;199;185;170:\
+ *.cpp=38;2;199;185;170:\
+ *.hpp=38;2;199;185;170:\
+ *.swp=38;2;199;185;170:\
+ *.swo=38;2;199;185;170:\
+ *.bak=38;2;199;185;170:\
+ *.orig=38;2;199;185;170:\
+"
+
+# eza (modern ls replacement) additional config
+export EZA_COLORS="\
+ di=38;2;205;174;125:\
+ ex=38;2;206;131;108:\
+ ln=38;2;199;178;162:\
+ so=38;2;184;191;132:\
+ pi=38;2;232;184;102:\
+ bd=38;2;233;130;114:\
+ cd=38;2;233;130;114:\
+ uw=38;2;232;184;102:\
+ ux=38;2;233;130;114:\
+ gwx=38;2;232;184;102:\
+ *.md=#cdae7d:\
+"
diff --git a/themes/dreamcoder/nvim-dreamcoder-dark.lua b/themes/dreamcoder/nvim-dreamcoder-dark.lua
new file mode 100644
index 0000000..41e2691
--- /dev/null
+++ b/themes/dreamcoder/nvim-dreamcoder-dark.lua
@@ -0,0 +1,1224 @@
+-- ========================================================
+-- Dreamcoder Ember Noir — generated by Dreamcoder sync
+-- ========================================================
+-- Usage: vim.cmd.colorscheme("dreamcoder")
+-- ========================================================
+
+vim.g.colors_name = "dreamcoder"
+
+-- Glass blur: transparent background + subtle float transparency
+vim.opt.winblend = 10
+vim.opt.pumblend = 10
+
+local c = {
+ bg = "#12100e",
+ surface0 = "#211c18",
+ surface1 = "#2e241f",
+ surface2 = "#3e3129",
+ text = "#e8dfd0",
+ muted = "#b8a99a",
+ subtle = "#9a8b7c",
+ comment = "#8c7c6d",
+ accent = "#d99555",
+ accent2 = "#c96a45",
+ diagnostic = "#c8b2a2",
+ sage = "#b8bf84",
+ lavender = "#c9a8dc",
+ mauve = "#d98aa9",
+ error = "#e98272",
+ warning = "#e8b866",
+ border = "#594d46",
+ border_ui = "#806754",
+ selection = "#373027",
+}
+
+local function h(name, opts)
+ vim.api.nvim_set_hl(0, name, opts)
+end
+
+-- ── Editor UI ────────────────────────────────────────────────
+-- Normal background = "none" for glass blur effect.
+-- Terminal (Kitty/Ghostty) handles transparency + blur.
+
+ vim.api.nvim_set_hl(0, "Normal", {
+ fg = "#e8dfd0",
+ bg = "none"
+ })
+
+ vim.api.nvim_set_hl(0, "NormalFloat", {
+ fg = "#e8dfd0",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "FloatBorder", {
+ fg = "#806754",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "NonText", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialKey", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "Whitespace", {
+ fg = "#594d46"
+ })
+
+ vim.api.nvim_set_hl(0, "EndOfBuffer", {
+ fg = "none"
+ })
+
+ vim.api.nvim_set_hl(0, "Cursor", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "lCursor", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorLine", {
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorLineNr", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "LineNr", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorColumn", {
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "ColorColumn", {
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "SignColumn", {
+ bg = "none"
+ })
+
+
+ -- ── Selection & Search ───────────────────────────────
+ vim.api.nvim_set_hl(0, "Visual", {
+ fg = "#e8dfd0",
+ bg = "#373027"
+ })
+
+ vim.api.nvim_set_hl(0, "VisualNOS", {
+ fg = "#e8dfd0",
+ bg = "#373027"
+ })
+
+ vim.api.nvim_set_hl(0, "Search", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "IncSearch", {
+ fg = "#12100e",
+ bg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "CurSearch", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Substitute", {
+ fg = "#12100e",
+ bg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "MatchParen", {
+ fg = "#c96a45",
+ bold = true
+ })
+
+
+ -- ── Popup Menu ────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Pmenu", {
+ fg = "#b8a99a",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuSel", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuSbar", {
+ bg = "#3e3129"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuThumb", {
+ bg = "#b8a99a"
+ })
+
+
+ -- ── Statusline & Winbar ───────────────────────────────
+ vim.api.nvim_set_hl(0, "StatusLine", {
+ fg = "#e8dfd0",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineNC", {
+ fg = "#b8a99a",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineTerm", {
+ fg = "#e8dfd0",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineTermNC", {
+ fg = "#b8a99a",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "WinBar", {
+ fg = "#e8dfd0",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "WinBarNC", {
+ fg = "#b8a99a",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "WinSeparator", {
+ fg = "#594d46"
+ })
+
+
+ -- ── Tabline ────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "TabLine", {
+ fg = "#b8a99a",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "TabLineSel", {
+ fg = "#e8dfd0",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "TabLineFill", {
+ bg = "#12100e"
+ })
+
+
+ -- ── Folds ─────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Folded", {
+ fg = "#b8a99a",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "FoldColumn", {
+ fg = "#b8a99a"
+ })
+
+
+ -- ── Diff ──────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "DiffAdd", {
+ bg = "#504c37"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffChange", {
+ bg = "#5f4a2e"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffDelete", {
+ bg = "#5f3a32"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffText", {
+ bg = "#684a2e"
+ })
+
+
+ -- ── Spell ─────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "SpellBad", {
+ sp = "#e98272",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellCap", {
+ sp = "#e8b866",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellLocal", {
+ sp = "#c8b2a2",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellRare", {
+ sp = "#d98aa9",
+ undercurl = true
+ })
+
+
+ -- ── Messages ──────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "MsgArea", {
+ fg = "#e8dfd0"
+ })
+
+ vim.api.nvim_set_hl(0, "ModeMsg", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "WarningMsg", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "ErrorMsg", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "Question", {
+ fg = "#c8b2a2"
+ })
+
+
+ -- ── Misc ──────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Title", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "Directory", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "Conceal", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "TermCursor", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+
+ -- ── Classic Syntax ────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Comment", {
+ fg = "#8c7c6d",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "Constant", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "String", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "Character", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "Number", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "Boolean", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "Float", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "Identifier", {
+ fg = "#e8dfd0"
+ })
+
+ vim.api.nvim_set_hl(0, "Function", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "Statement", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "Conditional", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Repeat", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Label", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Operator", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "Keyword", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Exception", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "PreProc", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "Include", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "Define", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "PreCondit", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "Type", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "StorageClass", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Structure", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "Typedef", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "Special", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialChar", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "Delimiter", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialComment", {
+ fg = "#8c7c6d"
+ })
+
+ vim.api.nvim_set_hl(0, "Debug", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "Underlined", {
+ fg = "#c8b2a2",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "Ignore", {
+ fg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "Error", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "Todo", {
+ fg = "#e8b866",
+ bold = true
+ })
+
+
+ -- ── Treesitter ────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "@variable", {
+ fg = "#e8dfd0"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.builtin", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.parameter", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.member", {
+ fg = "#e8dfd0"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant.builtin", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant.macro", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@module", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@module.builtin", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@label", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@string", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "@string.documentation", {
+ fg = "#b8bf84",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@string.regex", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@string.escape", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@character", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "@character.special", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@number", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "@boolean", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@float", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "@function", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.builtin", {
+ fg = "#c96a45",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@function.call", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.macro", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.method", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.method.call", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@parameter", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "@method", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@field", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@property", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@constructor", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@conditional", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@repeat", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@debug", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "@include", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "@operator", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.function", {
+ fg = "#d99555",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.operator", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.return", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@exception", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "@type", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@type.builtin", {
+ fg = "#c8b2a2",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@type.definition", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@type.qualifier", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@storageclass", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@attribute", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "@symbol", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.delimiter", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.bracket", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.special", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment", {
+ fg = "#8c7c6d",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.error", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.warning", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.todo", {
+ fg = "#e8b866",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.note", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.strong", {
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.italic", {
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.strikethrough", {
+ strikethrough = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.underline", {
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.heading", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.quote", {
+ fg = "#8c7c6d",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.math", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link", {
+ fg = "#c8b2a2",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link.label", {
+ fg = "#c8b2a2",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link.url", {
+ fg = "#9a8b7c",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.raw", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.list", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.plus", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.minus", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.delta", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag.attribute", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag.delimiter", {
+ fg = "#b8a99a"
+ })
+
+
+ -- ── LSP / Diagnostics ────────────────────────────────
+ vim.api.nvim_set_hl(0, "DiagnosticError", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticWarn", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticInfo", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticHint", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticOk", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineError", {
+ sp = "#e98272",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineWarn", {
+ sp = "#e8b866",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineInfo", {
+ sp = "#c8b2a2",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineHint", {
+ sp = "#c9a8dc",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextError", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextWarn", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextInfo", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextHint", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingError", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingWarn", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingInfo", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingHint", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignError", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignWarn", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignInfo", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignHint", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceText", {
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceRead", {
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceWrite", {
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "LspInlayHint", {
+ fg = "#8c7c6d",
+ bg = "#211c18"
+ })
+
+
+ -- ── Telescope ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "TelescopeNormal", {
+ fg = "#e8dfd0",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeBorder", {
+ fg = "#806754",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeTitle", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptNormal", {
+ fg = "#e8dfd0",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptBorder", {
+ fg = "#806754",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptTitle", {
+ fg = "#c96a45",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeSelection", {
+ fg = "#e8dfd0",
+ bg = "#373027"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeMultiSelection", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeMatching", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePreviewNormal", {
+ fg = "#e8dfd0",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePreviewBorder", {
+ fg = "#806754",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeResultsNormal", {
+ fg = "#e8dfd0",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeResultsBorder", {
+ fg = "#806754",
+ bg = "#12100e"
+ })
+
+
+ -- ── NvimTree ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "NvimTreeNormal", {
+ fg = "#e8dfd0",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeRootFolder", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitDirty", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitNew", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitDeleted", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeOpenedFile", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeSpecialFile", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeSymlink", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeFolderName", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeFolderIcon", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeEmptyFolderName", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeOpenedFolderName", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeImageFile", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeIndentMarker", {
+ fg = "#594d46"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeWinSeparator", {
+ fg = "#806754",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeCursorLine", {
+ bg = "#211c18"
+ })
+
+
+ -- ── WhichKey ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "WhichKey", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyGroup", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyDesc", {
+ fg = "#e8dfd0"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeySeperator", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeySeparator", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyFloat", {
+ fg = "#e8dfd0",
+ bg = "#211c18"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyBorder", {
+ fg = "#806754"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyValue", {
+ fg = "#d98aa9"
+ })
+
+
+ -- ── Lazy / Noice / Cmp ───────────────────────────────
+ vim.api.nvim_set_hl(0, "LazyNormal", {
+ fg = "#e8dfd0",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonPlugin", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonStart", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonSource", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonRt", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonCmd", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyValue", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyCommit", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "LazySpecial", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyDir", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyH1", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "LazyButton", {
+ fg = "#e8dfd0",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyButtonActive", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "NoiceMsg", {
+ fg = "#e8dfd0"
+ })
+
+ vim.api.nvim_set_hl(0, "NoiceCursor", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "MiniCompletionActiveParameter", {
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbr", {
+ fg = "#e8dfd0"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbrMatch", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbrMatchFuzzy", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKind", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemMenu", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindSnippet", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindKeyword", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindText", {
+ fg = "#e8dfd0"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindMethod", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFunction", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindConstructor", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindField", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindVariable", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindClass", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindInterface", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindModule", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindProperty", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindUnit", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindValue", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEnum", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEnumMember", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindKeyword", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindColor", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFile", {
+ fg = "#e8dfd0"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindReference", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFolder", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEvent", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindOperator", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindTypeParameter", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindCopied", {
+ fg = "#b8bf84"
+ })
+
diff --git a/themes/dreamcoder/nvim-dreamcoder-dusk.lua b/themes/dreamcoder/nvim-dreamcoder-dusk.lua
new file mode 100644
index 0000000..41da9c2
--- /dev/null
+++ b/themes/dreamcoder/nvim-dreamcoder-dusk.lua
@@ -0,0 +1,1224 @@
+-- ========================================================
+-- Dreamcoder Dusk — generated by Dreamcoder sync
+-- ========================================================
+-- Usage: vim.cmd.colorscheme("dreamcoder")
+-- ========================================================
+
+vim.g.colors_name = "dreamcoder"
+
+-- Glass blur: transparent background + subtle float transparency
+vim.opt.winblend = 10
+vim.opt.pumblend = 10
+
+local c = {
+ bg = "#ebe4d6",
+ surface0 = "#f1eadf",
+ surface1 = "#d8cbb8",
+ surface2 = "#c6b6a0",
+ text = "#1a1713",
+ muted = "#4c443a",
+ subtle = "#5a4f43",
+ comment = "#615548",
+ accent = "#8a5520",
+ accent2 = "#96411e",
+ diagnostic = "#104b67",
+ sage = "#466b41",
+ lavender = "#5b4e86",
+ mauve = "#784762",
+ error = "#773126",
+ warning = "#604000",
+ border = "#a7947a",
+ border_ui = "#665845",
+ selection = "#1a1713",
+}
+
+local function h(name, opts)
+ vim.api.nvim_set_hl(0, name, opts)
+end
+
+-- ── Editor UI ────────────────────────────────────────────────
+-- Normal background = "none" for glass blur effect.
+-- Terminal (Kitty/Ghostty) handles transparency + blur.
+
+ vim.api.nvim_set_hl(0, "Normal", {
+ fg = "#1a1713",
+ bg = "none"
+ })
+
+ vim.api.nvim_set_hl(0, "NormalFloat", {
+ fg = "#1a1713",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "FloatBorder", {
+ fg = "#665845",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "NonText", {
+ fg = "#5a4f43"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialKey", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "Whitespace", {
+ fg = "#a7947a"
+ })
+
+ vim.api.nvim_set_hl(0, "EndOfBuffer", {
+ fg = "none"
+ })
+
+ vim.api.nvim_set_hl(0, "Cursor", {
+ fg = "#ebe4d6",
+ bg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "lCursor", {
+ fg = "#ebe4d6",
+ bg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorLine", {
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorLineNr", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "LineNr", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorColumn", {
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "ColorColumn", {
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "SignColumn", {
+ bg = "none"
+ })
+
+
+ -- ── Selection & Search ───────────────────────────────
+ vim.api.nvim_set_hl(0, "Visual", {
+ fg = "#ebe4d6",
+ bg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "VisualNOS", {
+ fg = "#ebe4d6",
+ bg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "Search", {
+ fg = "#ebe4d6",
+ bg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "IncSearch", {
+ fg = "#ebe4d6",
+ bg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "CurSearch", {
+ fg = "#ebe4d6",
+ bg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "Substitute", {
+ fg = "#ebe4d6",
+ bg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "MatchParen", {
+ fg = "#96411e",
+ bold = true
+ })
+
+
+ -- ── Popup Menu ────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Pmenu", {
+ fg = "#4c443a",
+ bg = "#d8cbb8"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuSel", {
+ fg = "#ebe4d6",
+ bg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuSbar", {
+ bg = "#c6b6a0"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuThumb", {
+ bg = "#4c443a"
+ })
+
+
+ -- ── Statusline & Winbar ───────────────────────────────
+ vim.api.nvim_set_hl(0, "StatusLine", {
+ fg = "#1a1713",
+ bg = "#d8cbb8"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineNC", {
+ fg = "#4c443a",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineTerm", {
+ fg = "#1a1713",
+ bg = "#d8cbb8"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineTermNC", {
+ fg = "#4c443a",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "WinBar", {
+ fg = "#1a1713",
+ bg = "#d8cbb8"
+ })
+
+ vim.api.nvim_set_hl(0, "WinBarNC", {
+ fg = "#4c443a",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "WinSeparator", {
+ fg = "#a7947a"
+ })
+
+
+ -- ── Tabline ────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "TabLine", {
+ fg = "#4c443a",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "TabLineSel", {
+ fg = "#1a1713",
+ bg = "#d8cbb8"
+ })
+
+ vim.api.nvim_set_hl(0, "TabLineFill", {
+ bg = "#ebe4d6"
+ })
+
+
+ -- ── Folds ─────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Folded", {
+ fg = "#4c443a",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "FoldColumn", {
+ fg = "#4c443a"
+ })
+
+
+ -- ── Diff ──────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "DiffAdd", {
+ bg = "#bec4b0"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffChange", {
+ bg = "#c6b79c"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffDelete", {
+ bg = "#ccb2a8"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffText", {
+ bg = "#a8bdb9"
+ })
+
+
+ -- ── Spell ─────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "SpellBad", {
+ sp = "#773126",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellCap", {
+ sp = "#604000",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellLocal", {
+ sp = "#104b67",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellRare", {
+ sp = "#784762",
+ undercurl = true
+ })
+
+
+ -- ── Messages ──────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "MsgArea", {
+ fg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "ModeMsg", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "WarningMsg", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "ErrorMsg", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "Question", {
+ fg = "#104b67"
+ })
+
+
+ -- ── Misc ──────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Title", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "Directory", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "Conceal", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "TermCursor", {
+ fg = "#ebe4d6",
+ bg = "#8a5520"
+ })
+
+
+ -- ── Classic Syntax ────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Comment", {
+ fg = "#615548",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "Constant", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "String", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "Character", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "Number", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "Boolean", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "Float", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "Identifier", {
+ fg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "Function", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "Statement", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "Conditional", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "Repeat", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "Label", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "Operator", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "Keyword", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "Exception", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "PreProc", {
+ fg = "#5a4f43"
+ })
+
+ vim.api.nvim_set_hl(0, "Include", {
+ fg = "#5a4f43"
+ })
+
+ vim.api.nvim_set_hl(0, "Define", {
+ fg = "#5a4f43"
+ })
+
+ vim.api.nvim_set_hl(0, "PreCondit", {
+ fg = "#5a4f43"
+ })
+
+ vim.api.nvim_set_hl(0, "Type", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "StorageClass", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "Structure", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "Typedef", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "Special", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialChar", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "Delimiter", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialComment", {
+ fg = "#615548"
+ })
+
+ vim.api.nvim_set_hl(0, "Debug", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "Underlined", {
+ fg = "#104b67",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "Ignore", {
+ fg = "#ebe4d6"
+ })
+
+ vim.api.nvim_set_hl(0, "Error", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "Todo", {
+ fg = "#604000",
+ bold = true
+ })
+
+
+ -- ── Treesitter ────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "@variable", {
+ fg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.builtin", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.parameter", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.member", {
+ fg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant.builtin", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant.macro", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "@module", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "@module.builtin", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "@label", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@string", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "@string.documentation", {
+ fg = "#466b41",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@string.regex", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@string.escape", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@character", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "@character.special", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "@number", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "@boolean", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "@float", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "@function", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.builtin", {
+ fg = "#96411e",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@function.call", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.macro", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.method", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.method.call", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@parameter", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "@method", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@field", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "@property", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "@constructor", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "@conditional", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "@repeat", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "@debug", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "@include", {
+ fg = "#5a4f43"
+ })
+
+ vim.api.nvim_set_hl(0, "@operator", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.function", {
+ fg = "#8a5520",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.operator", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.return", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@exception", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "@type", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "@type.builtin", {
+ fg = "#104b67",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@type.definition", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "@type.qualifier", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "@storageclass", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "@attribute", {
+ fg = "#5a4f43"
+ })
+
+ vim.api.nvim_set_hl(0, "@symbol", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.delimiter", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.bracket", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.special", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment", {
+ fg = "#615548",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.error", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.warning", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.todo", {
+ fg = "#604000",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.note", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.strong", {
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.italic", {
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.strikethrough", {
+ strikethrough = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.underline", {
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.heading", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.quote", {
+ fg = "#615548",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.math", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link", {
+ fg = "#104b67",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link.label", {
+ fg = "#104b67",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link.url", {
+ fg = "#5a4f43",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.raw", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.list", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.plus", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.minus", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.delta", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag.attribute", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag.delimiter", {
+ fg = "#4c443a"
+ })
+
+
+ -- ── LSP / Diagnostics ────────────────────────────────
+ vim.api.nvim_set_hl(0, "DiagnosticError", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticWarn", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticInfo", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticHint", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticOk", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineError", {
+ sp = "#773126",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineWarn", {
+ sp = "#604000",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineInfo", {
+ sp = "#104b67",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineHint", {
+ sp = "#5b4e86",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextError", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextWarn", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextInfo", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextHint", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingError", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingWarn", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingInfo", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingHint", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignError", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignWarn", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignInfo", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignHint", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceText", {
+ bg = "#d8cbb8"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceRead", {
+ bg = "#d8cbb8"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceWrite", {
+ bg = "#d8cbb8"
+ })
+
+ vim.api.nvim_set_hl(0, "LspInlayHint", {
+ fg = "#615548",
+ bg = "#f1eadf"
+ })
+
+
+ -- ── Telescope ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "TelescopeNormal", {
+ fg = "#1a1713",
+ bg = "#ebe4d6"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeBorder", {
+ fg = "#665845",
+ bg = "#ebe4d6"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeTitle", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptNormal", {
+ fg = "#1a1713",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptBorder", {
+ fg = "#665845",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptTitle", {
+ fg = "#96411e",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeSelection", {
+ fg = "#ebe4d6",
+ bg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeMultiSelection", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeMatching", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePreviewNormal", {
+ fg = "#1a1713",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePreviewBorder", {
+ fg = "#665845",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeResultsNormal", {
+ fg = "#1a1713",
+ bg = "#ebe4d6"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeResultsBorder", {
+ fg = "#665845",
+ bg = "#ebe4d6"
+ })
+
+
+ -- ── NvimTree ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "NvimTreeNormal", {
+ fg = "#1a1713",
+ bg = "#ebe4d6"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeRootFolder", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitDirty", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitNew", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitDeleted", {
+ fg = "#773126"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeOpenedFile", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeSpecialFile", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeSymlink", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeFolderName", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeFolderIcon", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeEmptyFolderName", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeOpenedFolderName", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeImageFile", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeIndentMarker", {
+ fg = "#a7947a"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeWinSeparator", {
+ fg = "#665845",
+ bg = "#ebe4d6"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeCursorLine", {
+ bg = "#f1eadf"
+ })
+
+
+ -- ── WhichKey ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "WhichKey", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyGroup", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyDesc", {
+ fg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeySeperator", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeySeparator", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyFloat", {
+ fg = "#1a1713",
+ bg = "#f1eadf"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyBorder", {
+ fg = "#665845"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyValue", {
+ fg = "#784762"
+ })
+
+
+ -- ── Lazy / Noice / Cmp ───────────────────────────────
+ vim.api.nvim_set_hl(0, "LazyNormal", {
+ fg = "#1a1713",
+ bg = "#ebe4d6"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonPlugin", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonStart", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonSource", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonRt", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonCmd", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyValue", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyCommit", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "LazySpecial", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyDir", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyH1", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "LazyButton", {
+ fg = "#1a1713",
+ bg = "#d8cbb8"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyButtonActive", {
+ fg = "#ebe4d6",
+ bg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "NoiceMsg", {
+ fg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "NoiceCursor", {
+ fg = "#ebe4d6",
+ bg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "MiniCompletionActiveParameter", {
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbr", {
+ fg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbrMatch", {
+ fg = "#8a5520",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbrMatchFuzzy", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKind", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemMenu", {
+ fg = "#4c443a"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindSnippet", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindKeyword", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindText", {
+ fg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindMethod", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFunction", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindConstructor", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindField", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindVariable", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindClass", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindInterface", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindModule", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindProperty", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindUnit", {
+ fg = "#466b41"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindValue", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEnum", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEnumMember", {
+ fg = "#784762"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindKeyword", {
+ fg = "#8a5520"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindColor", {
+ fg = "#5b4e86"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFile", {
+ fg = "#1a1713"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindReference", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFolder", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEvent", {
+ fg = "#604000"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindOperator", {
+ fg = "#96411e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindTypeParameter", {
+ fg = "#104b67"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindCopied", {
+ fg = "#466b41"
+ })
+
diff --git a/themes/dreamcoder/nvim-dreamcoder-light.lua b/themes/dreamcoder/nvim-dreamcoder-light.lua
new file mode 100644
index 0000000..14a5999
--- /dev/null
+++ b/themes/dreamcoder/nvim-dreamcoder-light.lua
@@ -0,0 +1,1224 @@
+-- ========================================================
+-- Dreamcoder Light — generated by Dreamcoder sync
+-- ========================================================
+-- Usage: vim.cmd.colorscheme("dreamcoder")
+-- ========================================================
+
+vim.g.colors_name = "dreamcoder"
+
+-- Glass blur: transparent background + subtle float transparency
+vim.opt.winblend = 10
+vim.opt.pumblend = 10
+
+local c = {
+ bg = "#f3eadc",
+ surface0 = "#fff7ea",
+ surface1 = "#decbb1",
+ surface2 = "#c8ad89",
+ text = "#17120d",
+ muted = "#3d3228",
+ subtle = "#554635",
+ comment = "#66523f",
+ accent = "#824f16",
+ accent2 = "#a7471c",
+ diagnostic = "#15516e",
+ sage = "#3f6b35",
+ lavender = "#57478b",
+ mauve = "#7d3e64",
+ error = "#842f24",
+ warning = "#654300",
+ border = "#8a7358",
+ border_ui = "#66513b",
+ selection = "#17120d",
+}
+
+local function h(name, opts)
+ vim.api.nvim_set_hl(0, name, opts)
+end
+
+-- ── Editor UI ────────────────────────────────────────────────
+-- Normal background = "none" for glass blur effect.
+-- Terminal (Kitty/Ghostty) handles transparency + blur.
+
+ vim.api.nvim_set_hl(0, "Normal", {
+ fg = "#17120d",
+ bg = "none"
+ })
+
+ vim.api.nvim_set_hl(0, "NormalFloat", {
+ fg = "#17120d",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "FloatBorder", {
+ fg = "#66513b",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "NonText", {
+ fg = "#554635"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialKey", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "Whitespace", {
+ fg = "#8a7358"
+ })
+
+ vim.api.nvim_set_hl(0, "EndOfBuffer", {
+ fg = "none"
+ })
+
+ vim.api.nvim_set_hl(0, "Cursor", {
+ fg = "#f3eadc",
+ bg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "lCursor", {
+ fg = "#f3eadc",
+ bg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorLine", {
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorLineNr", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "LineNr", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorColumn", {
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "ColorColumn", {
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "SignColumn", {
+ bg = "none"
+ })
+
+
+ -- ── Selection & Search ───────────────────────────────
+ vim.api.nvim_set_hl(0, "Visual", {
+ fg = "#f3eadc",
+ bg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "VisualNOS", {
+ fg = "#f3eadc",
+ bg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "Search", {
+ fg = "#f3eadc",
+ bg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "IncSearch", {
+ fg = "#f3eadc",
+ bg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "CurSearch", {
+ fg = "#f3eadc",
+ bg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "Substitute", {
+ fg = "#f3eadc",
+ bg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "MatchParen", {
+ fg = "#a7471c",
+ bold = true
+ })
+
+
+ -- ── Popup Menu ────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Pmenu", {
+ fg = "#3d3228",
+ bg = "#decbb1"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuSel", {
+ fg = "#f3eadc",
+ bg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuSbar", {
+ bg = "#c8ad89"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuThumb", {
+ bg = "#3d3228"
+ })
+
+
+ -- ── Statusline & Winbar ───────────────────────────────
+ vim.api.nvim_set_hl(0, "StatusLine", {
+ fg = "#17120d",
+ bg = "#decbb1"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineNC", {
+ fg = "#3d3228",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineTerm", {
+ fg = "#17120d",
+ bg = "#decbb1"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineTermNC", {
+ fg = "#3d3228",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "WinBar", {
+ fg = "#17120d",
+ bg = "#decbb1"
+ })
+
+ vim.api.nvim_set_hl(0, "WinBarNC", {
+ fg = "#3d3228",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "WinSeparator", {
+ fg = "#8a7358"
+ })
+
+
+ -- ── Tabline ────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "TabLine", {
+ fg = "#3d3228",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "TabLineSel", {
+ fg = "#17120d",
+ bg = "#decbb1"
+ })
+
+ vim.api.nvim_set_hl(0, "TabLineFill", {
+ bg = "#f3eadc"
+ })
+
+
+ -- ── Folds ─────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Folded", {
+ fg = "#3d3228",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "FoldColumn", {
+ fg = "#3d3228"
+ })
+
+
+ -- ── Diff ──────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "DiffAdd", {
+ bg = "#c5cdb4"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffChange", {
+ bg = "#d1c1a4"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffDelete", {
+ bg = "#dabbaf"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffText", {
+ bg = "#abc4bf"
+ })
+
+
+ -- ── Spell ─────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "SpellBad", {
+ sp = "#842f24",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellCap", {
+ sp = "#654300",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellLocal", {
+ sp = "#15516e",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellRare", {
+ sp = "#7d3e64",
+ undercurl = true
+ })
+
+
+ -- ── Messages ──────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "MsgArea", {
+ fg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "ModeMsg", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "WarningMsg", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "ErrorMsg", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "Question", {
+ fg = "#15516e"
+ })
+
+
+ -- ── Misc ──────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Title", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "Directory", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "Conceal", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "TermCursor", {
+ fg = "#f3eadc",
+ bg = "#824f16"
+ })
+
+
+ -- ── Classic Syntax ────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Comment", {
+ fg = "#66523f",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "Constant", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "String", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "Character", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "Number", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "Boolean", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "Float", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "Identifier", {
+ fg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "Function", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "Statement", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "Conditional", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "Repeat", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "Label", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "Operator", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "Keyword", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "Exception", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "PreProc", {
+ fg = "#554635"
+ })
+
+ vim.api.nvim_set_hl(0, "Include", {
+ fg = "#554635"
+ })
+
+ vim.api.nvim_set_hl(0, "Define", {
+ fg = "#554635"
+ })
+
+ vim.api.nvim_set_hl(0, "PreCondit", {
+ fg = "#554635"
+ })
+
+ vim.api.nvim_set_hl(0, "Type", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "StorageClass", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "Structure", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "Typedef", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "Special", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialChar", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "Delimiter", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialComment", {
+ fg = "#66523f"
+ })
+
+ vim.api.nvim_set_hl(0, "Debug", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "Underlined", {
+ fg = "#15516e",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "Ignore", {
+ fg = "#f3eadc"
+ })
+
+ vim.api.nvim_set_hl(0, "Error", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "Todo", {
+ fg = "#654300",
+ bold = true
+ })
+
+
+ -- ── Treesitter ────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "@variable", {
+ fg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.builtin", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.parameter", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.member", {
+ fg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant.builtin", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant.macro", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "@module", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "@module.builtin", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "@label", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@string", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "@string.documentation", {
+ fg = "#3f6b35",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@string.regex", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@string.escape", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@character", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "@character.special", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "@number", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "@boolean", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "@float", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "@function", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.builtin", {
+ fg = "#a7471c",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@function.call", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.macro", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.method", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.method.call", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@parameter", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "@method", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@field", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "@property", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "@constructor", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "@conditional", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "@repeat", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "@debug", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "@include", {
+ fg = "#554635"
+ })
+
+ vim.api.nvim_set_hl(0, "@operator", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.function", {
+ fg = "#824f16",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.operator", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.return", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@exception", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "@type", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "@type.builtin", {
+ fg = "#15516e",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@type.definition", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "@type.qualifier", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "@storageclass", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "@attribute", {
+ fg = "#554635"
+ })
+
+ vim.api.nvim_set_hl(0, "@symbol", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.delimiter", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.bracket", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.special", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment", {
+ fg = "#66523f",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.error", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.warning", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.todo", {
+ fg = "#654300",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.note", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.strong", {
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.italic", {
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.strikethrough", {
+ strikethrough = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.underline", {
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.heading", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.quote", {
+ fg = "#66523f",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.math", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link", {
+ fg = "#15516e",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link.label", {
+ fg = "#15516e",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link.url", {
+ fg = "#554635",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.raw", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.list", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.plus", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.minus", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.delta", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag.attribute", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag.delimiter", {
+ fg = "#3d3228"
+ })
+
+
+ -- ── LSP / Diagnostics ────────────────────────────────
+ vim.api.nvim_set_hl(0, "DiagnosticError", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticWarn", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticInfo", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticHint", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticOk", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineError", {
+ sp = "#842f24",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineWarn", {
+ sp = "#654300",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineInfo", {
+ sp = "#15516e",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineHint", {
+ sp = "#57478b",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextError", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextWarn", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextInfo", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextHint", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingError", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingWarn", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingInfo", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingHint", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignError", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignWarn", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignInfo", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignHint", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceText", {
+ bg = "#decbb1"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceRead", {
+ bg = "#decbb1"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceWrite", {
+ bg = "#decbb1"
+ })
+
+ vim.api.nvim_set_hl(0, "LspInlayHint", {
+ fg = "#66523f",
+ bg = "#fff7ea"
+ })
+
+
+ -- ── Telescope ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "TelescopeNormal", {
+ fg = "#17120d",
+ bg = "#f3eadc"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeBorder", {
+ fg = "#66513b",
+ bg = "#f3eadc"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeTitle", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptNormal", {
+ fg = "#17120d",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptBorder", {
+ fg = "#66513b",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptTitle", {
+ fg = "#a7471c",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeSelection", {
+ fg = "#f3eadc",
+ bg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeMultiSelection", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeMatching", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePreviewNormal", {
+ fg = "#17120d",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePreviewBorder", {
+ fg = "#66513b",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeResultsNormal", {
+ fg = "#17120d",
+ bg = "#f3eadc"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeResultsBorder", {
+ fg = "#66513b",
+ bg = "#f3eadc"
+ })
+
+
+ -- ── NvimTree ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "NvimTreeNormal", {
+ fg = "#17120d",
+ bg = "#f3eadc"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeRootFolder", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitDirty", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitNew", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitDeleted", {
+ fg = "#842f24"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeOpenedFile", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeSpecialFile", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeSymlink", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeFolderName", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeFolderIcon", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeEmptyFolderName", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeOpenedFolderName", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeImageFile", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeIndentMarker", {
+ fg = "#8a7358"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeWinSeparator", {
+ fg = "#66513b",
+ bg = "#f3eadc"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeCursorLine", {
+ bg = "#fff7ea"
+ })
+
+
+ -- ── WhichKey ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "WhichKey", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyGroup", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyDesc", {
+ fg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeySeperator", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeySeparator", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyFloat", {
+ fg = "#17120d",
+ bg = "#fff7ea"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyBorder", {
+ fg = "#66513b"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyValue", {
+ fg = "#7d3e64"
+ })
+
+
+ -- ── Lazy / Noice / Cmp ───────────────────────────────
+ vim.api.nvim_set_hl(0, "LazyNormal", {
+ fg = "#17120d",
+ bg = "#f3eadc"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonPlugin", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonStart", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonSource", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonRt", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonCmd", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyValue", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyCommit", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "LazySpecial", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyDir", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyH1", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "LazyButton", {
+ fg = "#17120d",
+ bg = "#decbb1"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyButtonActive", {
+ fg = "#f3eadc",
+ bg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "NoiceMsg", {
+ fg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "NoiceCursor", {
+ fg = "#f3eadc",
+ bg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "MiniCompletionActiveParameter", {
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbr", {
+ fg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbrMatch", {
+ fg = "#824f16",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbrMatchFuzzy", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKind", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemMenu", {
+ fg = "#3d3228"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindSnippet", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindKeyword", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindText", {
+ fg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindMethod", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFunction", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindConstructor", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindField", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindVariable", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindClass", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindInterface", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindModule", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindProperty", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindUnit", {
+ fg = "#3f6b35"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindValue", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEnum", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEnumMember", {
+ fg = "#7d3e64"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindKeyword", {
+ fg = "#824f16"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindColor", {
+ fg = "#57478b"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFile", {
+ fg = "#17120d"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindReference", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFolder", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEvent", {
+ fg = "#654300"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindOperator", {
+ fg = "#a7471c"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindTypeParameter", {
+ fg = "#15516e"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindCopied", {
+ fg = "#3f6b35"
+ })
+
diff --git a/themes/dreamcoder/nvim-dreamcoder.lua b/themes/dreamcoder/nvim-dreamcoder.lua
new file mode 100644
index 0000000..639f135
--- /dev/null
+++ b/themes/dreamcoder/nvim-dreamcoder.lua
@@ -0,0 +1,1224 @@
+-- ========================================================
+-- Dreamcoder Ember Noir — generated by Dreamcoder sync
+-- ========================================================
+-- Usage: vim.cmd.colorscheme("dreamcoder")
+-- ========================================================
+
+vim.g.colors_name = "dreamcoder"
+
+-- Glass blur: transparent background + subtle float transparency
+vim.opt.winblend = 10
+vim.opt.pumblend = 10
+
+local c = {
+ bg = "#12100e",
+ surface0 = "#231c18",
+ surface1 = "#2e241f",
+ surface2 = "#3e3129",
+ text = "#f0e7dc",
+ muted = "#b8a99a",
+ subtle = "#9a8b7c",
+ comment = "#8c7c6d",
+ accent = "#d99555",
+ accent2 = "#c96a45",
+ diagnostic = "#c8b2a2",
+ sage = "#b8bf84",
+ lavender = "#c9a8dc",
+ mauve = "#d98aa9",
+ error = "#e98272",
+ warning = "#e8b866",
+ border = "#594d46",
+ border_ui = "#806754",
+ selection = "#373027",
+}
+
+local function h(name, opts)
+ vim.api.nvim_set_hl(0, name, opts)
+end
+
+-- ── Editor UI ────────────────────────────────────────────────
+-- Normal background = "none" for glass blur effect.
+-- Terminal (Kitty/Ghostty) handles transparency + blur.
+
+ vim.api.nvim_set_hl(0, "Normal", {
+ fg = "#f0e7dc",
+ bg = "none"
+ })
+
+ vim.api.nvim_set_hl(0, "NormalFloat", {
+ fg = "#f0e7dc",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "FloatBorder", {
+ fg = "#806754",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "NonText", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialKey", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "Whitespace", {
+ fg = "#594d46"
+ })
+
+ vim.api.nvim_set_hl(0, "EndOfBuffer", {
+ fg = "none"
+ })
+
+ vim.api.nvim_set_hl(0, "Cursor", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "lCursor", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorLine", {
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorLineNr", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "LineNr", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "CursorColumn", {
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "ColorColumn", {
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "SignColumn", {
+ bg = "none"
+ })
+
+
+ -- ── Selection & Search ───────────────────────────────
+ vim.api.nvim_set_hl(0, "Visual", {
+ fg = "#f0e7dc",
+ bg = "#373027"
+ })
+
+ vim.api.nvim_set_hl(0, "VisualNOS", {
+ fg = "#f0e7dc",
+ bg = "#373027"
+ })
+
+ vim.api.nvim_set_hl(0, "Search", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "IncSearch", {
+ fg = "#12100e",
+ bg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "CurSearch", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Substitute", {
+ fg = "#12100e",
+ bg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "MatchParen", {
+ fg = "#c96a45",
+ bold = true
+ })
+
+
+ -- ── Popup Menu ────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Pmenu", {
+ fg = "#b8a99a",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuSel", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuSbar", {
+ bg = "#3e3129"
+ })
+
+ vim.api.nvim_set_hl(0, "PmenuThumb", {
+ bg = "#b8a99a"
+ })
+
+
+ -- ── Statusline & Winbar ───────────────────────────────
+ vim.api.nvim_set_hl(0, "StatusLine", {
+ fg = "#f0e7dc",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineNC", {
+ fg = "#b8a99a",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineTerm", {
+ fg = "#f0e7dc",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "StatusLineTermNC", {
+ fg = "#b8a99a",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "WinBar", {
+ fg = "#f0e7dc",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "WinBarNC", {
+ fg = "#b8a99a",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "WinSeparator", {
+ fg = "#594d46"
+ })
+
+
+ -- ── Tabline ────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "TabLine", {
+ fg = "#b8a99a",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "TabLineSel", {
+ fg = "#f0e7dc",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "TabLineFill", {
+ bg = "#12100e"
+ })
+
+
+ -- ── Folds ─────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Folded", {
+ fg = "#b8a99a",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "FoldColumn", {
+ fg = "#b8a99a"
+ })
+
+
+ -- ── Diff ──────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "DiffAdd", {
+ bg = "#504d38"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffChange", {
+ bg = "#5e4b2f"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffDelete", {
+ bg = "#5e3b33"
+ })
+
+ vim.api.nvim_set_hl(0, "DiffText", {
+ bg = "#674b30"
+ })
+
+
+ -- ── Spell ─────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "SpellBad", {
+ sp = "#e98272",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellCap", {
+ sp = "#e8b866",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellLocal", {
+ sp = "#c8b2a2",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "SpellRare", {
+ sp = "#d98aa9",
+ undercurl = true
+ })
+
+
+ -- ── Messages ──────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "MsgArea", {
+ fg = "#f0e7dc"
+ })
+
+ vim.api.nvim_set_hl(0, "ModeMsg", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "WarningMsg", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "ErrorMsg", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "Question", {
+ fg = "#c8b2a2"
+ })
+
+
+ -- ── Misc ──────────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Title", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "Directory", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "Conceal", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "TermCursor", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+
+ -- ── Classic Syntax ────────────────────────────────────
+ vim.api.nvim_set_hl(0, "Comment", {
+ fg = "#8c7c6d",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "Constant", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "String", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "Character", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "Number", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "Boolean", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "Float", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "Identifier", {
+ fg = "#f0e7dc"
+ })
+
+ vim.api.nvim_set_hl(0, "Function", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "Statement", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "Conditional", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Repeat", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Label", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Operator", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "Keyword", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Exception", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "PreProc", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "Include", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "Define", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "PreCondit", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "Type", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "StorageClass", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "Structure", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "Typedef", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "Special", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialChar", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "Delimiter", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "SpecialComment", {
+ fg = "#8c7c6d"
+ })
+
+ vim.api.nvim_set_hl(0, "Debug", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "Underlined", {
+ fg = "#c8b2a2",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "Ignore", {
+ fg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "Error", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "Todo", {
+ fg = "#e8b866",
+ bold = true
+ })
+
+
+ -- ── Treesitter ────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "@variable", {
+ fg = "#f0e7dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.builtin", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.parameter", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "@variable.member", {
+ fg = "#f0e7dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant.builtin", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@constant.macro", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@module", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@module.builtin", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@label", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@string", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "@string.documentation", {
+ fg = "#b8bf84",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@string.regex", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@string.escape", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@character", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "@character.special", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@number", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "@boolean", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@float", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "@function", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.builtin", {
+ fg = "#c96a45",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@function.call", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.macro", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.method", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@function.method.call", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@parameter", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "@method", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@field", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@property", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@constructor", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@conditional", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@repeat", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@debug", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "@include", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "@operator", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.function", {
+ fg = "#d99555",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.operator", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "@keyword.return", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@exception", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "@type", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@type.builtin", {
+ fg = "#c8b2a2",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@type.definition", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@type.qualifier", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@storageclass", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@attribute", {
+ fg = "#9a8b7c"
+ })
+
+ vim.api.nvim_set_hl(0, "@symbol", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.delimiter", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.bracket", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "@punctuation.special", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment", {
+ fg = "#8c7c6d",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.error", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.warning", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.todo", {
+ fg = "#e8b866",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@comment.note", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.strong", {
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.italic", {
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.strikethrough", {
+ strikethrough = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.underline", {
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.heading", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.quote", {
+ fg = "#8c7c6d",
+ italic = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.math", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link", {
+ fg = "#c8b2a2",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link.label", {
+ fg = "#c8b2a2",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.link.url", {
+ fg = "#9a8b7c",
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.raw", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "@markup.list", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.plus", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.minus", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "@diff.delta", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag.attribute", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "@tag.delimiter", {
+ fg = "#b8a99a"
+ })
+
+
+ -- ── LSP / Diagnostics ────────────────────────────────
+ vim.api.nvim_set_hl(0, "DiagnosticError", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticWarn", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticInfo", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticHint", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticOk", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineError", {
+ sp = "#e98272",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineWarn", {
+ sp = "#e8b866",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineInfo", {
+ sp = "#c8b2a2",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticUnderlineHint", {
+ sp = "#c9a8dc",
+ undercurl = true
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextError", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextWarn", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextInfo", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticVirtualTextHint", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingError", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingWarn", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingInfo", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticFloatingHint", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignError", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignWarn", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignInfo", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "DiagnosticSignHint", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceText", {
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceRead", {
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "LspReferenceWrite", {
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "LspInlayHint", {
+ fg = "#8c7c6d",
+ bg = "#231c18"
+ })
+
+
+ -- ── Telescope ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "TelescopeNormal", {
+ fg = "#f0e7dc",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeBorder", {
+ fg = "#806754",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeTitle", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptNormal", {
+ fg = "#f0e7dc",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptBorder", {
+ fg = "#806754",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePromptTitle", {
+ fg = "#c96a45",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeSelection", {
+ fg = "#f0e7dc",
+ bg = "#373027"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeMultiSelection", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeMatching", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePreviewNormal", {
+ fg = "#f0e7dc",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopePreviewBorder", {
+ fg = "#806754",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeResultsNormal", {
+ fg = "#f0e7dc",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "TelescopeResultsBorder", {
+ fg = "#806754",
+ bg = "#12100e"
+ })
+
+
+ -- ── NvimTree ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "NvimTreeNormal", {
+ fg = "#f0e7dc",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeRootFolder", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitDirty", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitNew", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeGitDeleted", {
+ fg = "#e98272"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeOpenedFile", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeSpecialFile", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeSymlink", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeFolderName", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeFolderIcon", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeEmptyFolderName", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeOpenedFolderName", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeImageFile", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeIndentMarker", {
+ fg = "#594d46"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeWinSeparator", {
+ fg = "#806754",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "NvimTreeCursorLine", {
+ bg = "#231c18"
+ })
+
+
+ -- ── WhichKey ─────────────────────────────────────────
+ vim.api.nvim_set_hl(0, "WhichKey", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyGroup", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyDesc", {
+ fg = "#f0e7dc"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeySeperator", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeySeparator", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyFloat", {
+ fg = "#f0e7dc",
+ bg = "#231c18"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyBorder", {
+ fg = "#806754"
+ })
+
+ vim.api.nvim_set_hl(0, "WhichKeyValue", {
+ fg = "#d98aa9"
+ })
+
+
+ -- ── Lazy / Noice / Cmp ───────────────────────────────
+ vim.api.nvim_set_hl(0, "LazyNormal", {
+ fg = "#f0e7dc",
+ bg = "#12100e"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonPlugin", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonStart", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonSource", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonRt", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyReasonCmd", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyValue", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyCommit", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "LazySpecial", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyDir", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyH1", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "LazyButton", {
+ fg = "#f0e7dc",
+ bg = "#2e241f"
+ })
+
+ vim.api.nvim_set_hl(0, "LazyButtonActive", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "NoiceMsg", {
+ fg = "#f0e7dc"
+ })
+
+ vim.api.nvim_set_hl(0, "NoiceCursor", {
+ fg = "#12100e",
+ bg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "MiniCompletionActiveParameter", {
+ underline = true
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbr", {
+ fg = "#f0e7dc"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbrMatch", {
+ fg = "#d99555",
+ bold = true
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemAbbrMatchFuzzy", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKind", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemMenu", {
+ fg = "#b8a99a"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindSnippet", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindKeyword", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindText", {
+ fg = "#f0e7dc"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindMethod", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFunction", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindConstructor", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindField", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindVariable", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindClass", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindInterface", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindModule", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindProperty", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindUnit", {
+ fg = "#b8bf84"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindValue", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEnum", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEnumMember", {
+ fg = "#d98aa9"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindKeyword", {
+ fg = "#d99555"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindColor", {
+ fg = "#c9a8dc"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFile", {
+ fg = "#f0e7dc"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindReference", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindFolder", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindEvent", {
+ fg = "#e8b866"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindOperator", {
+ fg = "#c96a45"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindTypeParameter", {
+ fg = "#c8b2a2"
+ })
+
+ vim.api.nvim_set_hl(0, "CmpItemKindCopied", {
+ fg = "#b8bf84"
+ })
+
diff --git a/themes/dreamcoder/obsidian-dreamcoder-dark.css b/themes/dreamcoder/obsidian-dreamcoder-dark.css
new file mode 100644
index 0000000..f21e8c4
--- /dev/null
+++ b/themes/dreamcoder/obsidian-dreamcoder-dark.css
@@ -0,0 +1,143 @@
+/* ========================================================
+ Dreamcoder Ember Noir — Obsidian CSS snippet
+ ========================================================
+ Place in your vault's .obsidian/snippets/ folder.
+ Enable in Settings → Appearance → CSS snippets. */
+
+.theme-dark {
+ /* Base — raw palette colors for backgrounds and surfaces */
+ --background-primary: #12100e;
+ --background-primary-alt: #211c18;
+ --background-secondary: #211c18;
+ --background-secondary-alt: #2e241f;
+ --background-modifier-border: #594d46;
+ --background-modifier-border-hover: #806754;
+ --background-modifier-form-field: #211c18;
+ --background-modifier-success: #3e3c2b;
+ --background-modifier-error: #4a2c26;
+ --background-modifier-message: #2e241f;
+
+ /* Text — guarded against background for accessibility */
+ --text-normal: #e8dfd0;
+ --text-muted: #b8a99a;
+ --text-faint: #9a8b7c;
+ --text-accent: #d99555;
+ --text-accent-hover: #c96a45;
+ --text-error: #e98272;
+ --text-warning: #e8b866;
+ --text-success: #b8bf84;
+ --text-selection: #2c1c15;
+ --text-on-accent: #12100e;
+
+ /* Interactive */
+ --interactive-normal: #2e241f;
+ --interactive-hover: #3e3129;
+ --interactive-accent: #d99555;
+ --interactive-accent-hover: #c96a45;
+ --interactive-success: #b8bf84;
+
+ /* Scrollbar */
+ --scrollbar-bg: transparent;
+ --scrollbar-thumb-bg: #594d46;
+ --scrollbar-active-thumb-bg: #806754;
+
+ /* Code — syntax highlighting colors */
+ --code-normal: #e8dfd0;
+ --code-comment: #8c7c6d;
+ --code-punctuation: #b8a99a;
+ --code-keyword: #d99555;
+ --code-operator: #c96a45;
+ --code-function: #c96a45;
+ --code-string: #b8bf84;
+ --code-number: #d98aa9;
+ --code-tag: #d99555;
+ --code-important: #e98272;
+ --code-background: #211c18;
+
+ /* Heading */
+ --h1-color: #d99555;
+ --h2-color: #d99555;
+ --h3-color: #c96a45;
+ --h4-color: #c8b2a2;
+ --h5-color: #b8a99a;
+ --h6-color: #9a8b7c;
+
+ /* Link */
+ --link-color: #d99555;
+ --link-color-hover: #c96a45;
+ --link-external-color: #c8b2a2;
+ --link-external-color-hover: #c96a45;
+
+ /* Checkbox */
+ --checkbox-color: #d99555;
+ --checkbox-color-hover: #c96a45;
+ --checkbox-border-color: #594d46;
+ --checkbox-mark-color: #12100e;
+
+ /* Table */
+ --table-header-background: #2e241f;
+ --table-header-background-hover: #3e3129;
+ --table-row-background-hover: #211c18;
+ --table-border-color: #594d46;
+
+ /* Graph */
+ --graph-line: #594d46;
+ --graph-node: #b8a99a;
+ --graph-node-focused: #d99555;
+ --graph-node-tag: #c8b2a2;
+ --graph-node-attachment: #b8bf84;
+}
+
+/* Headings */
+.markdown-rendered h1 { color: var(--h1-color); }
+.markdown-rendered h2 { color: var(--h2-color); }
+.markdown-rendered h3 { color: var(--h3-color); }
+.markdown-rendered h4 { color: var(--h4-color); }
+.markdown-rendered h5 { color: var(--h5-color); }
+.markdown-rendered h6 { color: var(--h6-color); }
+
+/* Tags */
+.tag {
+ background-color: #342619;
+ color: #d99555;
+ border-radius: 4px;
+ padding: 0 6px;
+}
+
+/* Blockquotes */
+blockquote {
+ border-color: #d99555 !important;
+}
+
+/* Search highlights */
+.search-result-file-matched-text,
+.is-selected .search-result-file-matched-text,
+mark {
+ background-color: #3f2d1d !important;
+ color: #12100e !important;
+}
+
+/* Active line in edit mode */
+.cm-active {
+ background-color: #211c18 !important;
+}
+
+/* Selection */
+::selection {
+ background-color: #2e1e16 !important;
+}
+
+/* Tooltip */
+.tooltip {
+ background-color: #2e241f !important;
+ color: #e8dfd0 !important;
+}
+
+/* Menu */
+.menu {
+ background-color: #211c18 !important;
+}
+
+.menu-item:hover {
+ background-color: #2e241f !important;
+}
diff --git a/themes/dreamcoder/obsidian-dreamcoder-dusk.css b/themes/dreamcoder/obsidian-dreamcoder-dusk.css
new file mode 100644
index 0000000..eeb1e85
--- /dev/null
+++ b/themes/dreamcoder/obsidian-dreamcoder-dusk.css
@@ -0,0 +1,143 @@
+/* ========================================================
+ Dreamcoder Dusk — Obsidian CSS snippet
+ ========================================================
+ Place in your vault's .obsidian/snippets/ folder.
+ Enable in Settings → Appearance → CSS snippets. */
+
+.theme-light {
+ /* Base — raw palette colors for backgrounds and surfaces */
+ --background-primary: #ebe4d6;
+ --background-primary-alt: #f1eadf;
+ --background-secondary: #f1eadf;
+ --background-secondary-alt: #d8cbb8;
+ --background-modifier-border: #a7947a;
+ --background-modifier-border-hover: #665845;
+ --background-modifier-form-field: #f1eadf;
+ --background-modifier-success: #c2c6b1;
+ --background-modifier-error: #ceb7aa;
+ --background-modifier-message: #d8cbb8;
+
+ /* Text — guarded against background for accessibility */
+ --text-normal: #1a1713;
+ --text-muted: #4c443a;
+ --text-faint: #5a4f43;
+ --text-accent: #8a5520;
+ --text-accent-hover: #96411e;
+ --text-error: #773126;
+ --text-warning: #604000;
+ --text-success: #466b41;
+ --text-selection: #827e74;
+ --text-on-accent: #ebe4d6;
+
+ /* Interactive */
+ --interactive-normal: #d8cbb8;
+ --interactive-hover: #c6b6a0;
+ --interactive-accent: #8a5520;
+ --interactive-accent-hover: #96411e;
+ --interactive-success: #466b41;
+
+ /* Scrollbar */
+ --scrollbar-bg: transparent;
+ --scrollbar-thumb-bg: #a7947a;
+ --scrollbar-active-thumb-bg: #665845;
+
+ /* Code — syntax highlighting colors */
+ --code-normal: #1a1713;
+ --code-comment: #615548;
+ --code-punctuation: #4c443a;
+ --code-keyword: #8a5520;
+ --code-operator: #96411e;
+ --code-function: #96411e;
+ --code-string: #466b41;
+ --code-number: #784762;
+ --code-tag: #8a5520;
+ --code-important: #773126;
+ --code-background: #f1eadf;
+
+ /* Heading */
+ --h1-color: #8a5520;
+ --h2-color: #8a5520;
+ --h3-color: #96411e;
+ --h4-color: #104b67;
+ --h5-color: #4c443a;
+ --h6-color: #5a4f43;
+
+ /* Link */
+ --link-color: #8a5520;
+ --link-color-hover: #96411e;
+ --link-external-color: #104b67;
+ --link-external-color-hover: #96411e;
+
+ /* Checkbox */
+ --checkbox-color: #8a5520;
+ --checkbox-color-hover: #96411e;
+ --checkbox-border-color: #a7947a;
+ --checkbox-mark-color: #ebe4d6;
+
+ /* Table */
+ --table-header-background: #d8cbb8;
+ --table-header-background-hover: #c6b6a0;
+ --table-row-background-hover: #f1eadf;
+ --table-border-color: #a7947a;
+
+ /* Graph */
+ --graph-line: #a7947a;
+ --graph-node: #4c443a;
+ --graph-node-focused: #8a5520;
+ --graph-node-tag: #104b67;
+ --graph-node-attachment: #466b41;
+}
+
+/* Headings */
+.markdown-rendered h1 { color: var(--h1-color); }
+.markdown-rendered h2 { color: var(--h2-color); }
+.markdown-rendered h3 { color: var(--h3-color); }
+.markdown-rendered h4 { color: var(--h4-color); }
+.markdown-rendered h5 { color: var(--h5-color); }
+.markdown-rendered h6 { color: var(--h6-color); }
+
+/* Tags */
+.tag {
+ background-color: #dccfbb;
+ color: #8a5520;
+ border-radius: 4px;
+ padding: 0 6px;
+}
+
+/* Blockquotes */
+blockquote {
+ border-color: #8a5520 !important;
+}
+
+/* Search highlights */
+.search-result-file-matched-text,
+.is-selected .search-result-file-matched-text,
+mark {
+ background-color: #d8c7b2 !important;
+ color: #ebe4d6 !important;
+}
+
+/* Active line in edit mode */
+.cm-active {
+ background-color: #f1eadf !important;
+}
+
+/* Selection */
+::selection {
+ background-color: #78736b !important;
+}
+
+/* Tooltip */
+.tooltip {
+ background-color: #d8cbb8 !important;
+ color: #1a1713 !important;
+}
+
+/* Menu */
+.menu {
+ background-color: #f1eadf !important;
+}
+
+.menu-item:hover {
+ background-color: #d8cbb8 !important;
+}
diff --git a/themes/dreamcoder/obsidian-dreamcoder-light.css b/themes/dreamcoder/obsidian-dreamcoder-light.css
new file mode 100644
index 0000000..d28deab
--- /dev/null
+++ b/themes/dreamcoder/obsidian-dreamcoder-light.css
@@ -0,0 +1,143 @@
+/* ========================================================
+ Dreamcoder Light — Obsidian CSS snippet
+ ========================================================
+ Place in your vault's .obsidian/snippets/ folder.
+ Enable in Settings → Appearance → CSS snippets. */
+
+.theme-light {
+ /* Base — raw palette colors for backgrounds and surfaces */
+ --background-primary: #f3eadc;
+ --background-primary-alt: #fff7ea;
+ --background-secondary: #fff7ea;
+ --background-secondary-alt: #decbb1;
+ --background-modifier-border: #8a7358;
+ --background-modifier-border-hover: #66513b;
+ --background-modifier-form-field: #fff7ea;
+ --background-modifier-success: #c6cab2;
+ --background-modifier-error: #d7bbae;
+ --background-modifier-message: #decbb1;
+
+ /* Text — guarded against background for accessibility */
+ --text-normal: #17120d;
+ --text-muted: #3d3228;
+ --text-faint: #554635;
+ --text-accent: #824f16;
+ --text-accent-hover: #a7471c;
+ --text-error: #842f24;
+ --text-warning: #654300;
+ --text-success: #3f6b35;
+ --text-selection: #857e74;
+ --text-on-accent: #f3eadc;
+
+ /* Interactive */
+ --interactive-normal: #decbb1;
+ --interactive-hover: #c8ad89;
+ --interactive-accent: #824f16;
+ --interactive-accent-hover: #a7471c;
+ --interactive-success: #3f6b35;
+
+ /* Scrollbar */
+ --scrollbar-bg: transparent;
+ --scrollbar-thumb-bg: #8a7358;
+ --scrollbar-active-thumb-bg: #66513b;
+
+ /* Code — syntax highlighting colors */
+ --code-normal: #17120d;
+ --code-comment: #66523f;
+ --code-punctuation: #3d3228;
+ --code-keyword: #824f16;
+ --code-operator: #a7471c;
+ --code-function: #a7471c;
+ --code-string: #3f6b35;
+ --code-number: #7d3e64;
+ --code-tag: #824f16;
+ --code-important: #842f24;
+ --code-background: #fff7ea;
+
+ /* Heading */
+ --h1-color: #824f16;
+ --h2-color: #824f16;
+ --h3-color: #a7471c;
+ --h4-color: #15516e;
+ --h5-color: #3d3228;
+ --h6-color: #554635;
+
+ /* Link */
+ --link-color: #824f16;
+ --link-color-hover: #a7471c;
+ --link-external-color: #15516e;
+ --link-external-color-hover: #a7471c;
+
+ /* Checkbox */
+ --checkbox-color: #824f16;
+ --checkbox-color-hover: #a7471c;
+ --checkbox-border-color: #8a7358;
+ --checkbox-mark-color: #f3eadc;
+
+ /* Table */
+ --table-header-background: #decbb1;
+ --table-header-background-hover: #c8ad89;
+ --table-row-background-hover: #fff7ea;
+ --table-border-color: #8a7358;
+
+ /* Graph */
+ --graph-line: #8a7358;
+ --graph-node: #3d3228;
+ --graph-node-focused: #824f16;
+ --graph-node-tag: #15516e;
+ --graph-node-attachment: #3f6b35;
+}
+
+/* Headings */
+.markdown-rendered h1 { color: var(--h1-color); }
+.markdown-rendered h2 { color: var(--h2-color); }
+.markdown-rendered h3 { color: var(--h3-color); }
+.markdown-rendered h4 { color: var(--h4-color); }
+.markdown-rendered h5 { color: var(--h5-color); }
+.markdown-rendered h6 { color: var(--h6-color); }
+
+/* Tags */
+.tag {
+ background-color: #e2d3be;
+ color: #824f16;
+ border-radius: 4px;
+ padding: 0 6px;
+}
+
+/* Blockquotes */
+blockquote {
+ border-color: #824f16 !important;
+}
+
+/* Search highlights */
+.search-result-file-matched-text,
+.is-selected .search-result-file-matched-text,
+mark {
+ background-color: #dccbb4 !important;
+ color: #f3eadc !important;
+}
+
+/* Active line in edit mode */
+.cm-active {
+ background-color: #fff7ea !important;
+}
+
+/* Selection */
+::selection {
+ background-color: #7a736a !important;
+}
+
+/* Tooltip */
+.tooltip {
+ background-color: #decbb1 !important;
+ color: #17120d !important;
+}
+
+/* Menu */
+.menu {
+ background-color: #fff7ea !important;
+}
+
+.menu-item:hover {
+ background-color: #decbb1 !important;
+}
diff --git a/themes/dreamcoder/obsidian-dreamcoder.css b/themes/dreamcoder/obsidian-dreamcoder.css
new file mode 100644
index 0000000..1362044
--- /dev/null
+++ b/themes/dreamcoder/obsidian-dreamcoder.css
@@ -0,0 +1,143 @@
+/* ========================================================
+ Dreamcoder Ember Noir — Obsidian CSS snippet
+ ========================================================
+ Place in your vault's .obsidian/snippets/ folder.
+ Enable in Settings → Appearance → CSS snippets. */
+
+.theme-dark {
+ /* Base — raw palette colors for backgrounds and surfaces */
+ --background-primary: #14110e;
+ --background-primary-alt: #231c18;
+ --background-secondary: #231c18;
+ --background-secondary-alt: #2e241f;
+ --background-modifier-border: #594d46;
+ --background-modifier-border-hover: #806754;
+ --background-modifier-form-field: #231c18;
+ --background-modifier-success: #3d3c2c;
+ --background-modifier-error: #492d27;
+ --background-modifier-message: #2e241f;
+
+ /* Text — guarded against background for accessibility */
+ --text-normal: #f0e7dc;
+ --text-muted: #c7b9aa;
+ --text-faint: #aa927c;
+ --text-accent: #cdae7d;
+ --text-accent-hover: #ce836c;
+ --text-error: #e98272;
+ --text-warning: #e8b866;
+ --text-success: #b8bf84;
+ --text-selection: #26201a;
+ --text-on-accent: #14110e;
+
+ /* Interactive */
+ --interactive-normal: #2e241f;
+ --interactive-hover: #3e3129;
+ --interactive-accent: #cdae7d;
+ --interactive-accent-hover: #ce836c;
+ --interactive-success: #b8bf84;
+
+ /* Scrollbar */
+ --scrollbar-bg: transparent;
+ --scrollbar-thumb-bg: #594d46;
+ --scrollbar-active-thumb-bg: #806754;
+
+ /* Code — syntax highlighting colors */
+ --code-normal: #f0e7dc;
+ --code-comment: #9c826d;
+ --code-punctuation: #c7b9aa;
+ --code-keyword: #cdae7d;
+ --code-operator: #ce836c;
+ --code-function: #ce836c;
+ --code-string: #b8bf84;
+ --code-number: #d98aa9;
+ --code-tag: #cdae7d;
+ --code-important: #e98272;
+ --code-background: #231c18;
+
+ /* Heading */
+ --h1-color: #cdae7d;
+ --h2-color: #cdae7d;
+ --h3-color: #ce836c;
+ --h4-color: #c7b2a2;
+ --h5-color: #c7b9aa;
+ --h6-color: #aa927c;
+
+ /* Link */
+ --link-color: #cdae7d;
+ --link-color-hover: #ce836c;
+ --link-external-color: #c7b2a2;
+ --link-external-color-hover: #ce836c;
+
+ /* Checkbox */
+ --checkbox-color: #cdae7d;
+ --checkbox-color-hover: #ce836c;
+ --checkbox-border-color: #594d46;
+ --checkbox-mark-color: #14110e;
+
+ /* Table */
+ --table-header-background: #2e241f;
+ --table-header-background-hover: #3e3129;
+ --table-row-background-hover: #231c18;
+ --table-border-color: #594d46;
+
+ /* Graph */
+ --graph-line: #594d46;
+ --graph-node: #c7b9aa;
+ --graph-node-focused: #cdae7d;
+ --graph-node-tag: #c7b2a2;
+ --graph-node-attachment: #b8bf84;
+}
+
+/* Headings */
+.markdown-rendered h1 { color: var(--h1-color); }
+.markdown-rendered h2 { color: var(--h2-color); }
+.markdown-rendered h3 { color: var(--h3-color); }
+.markdown-rendered h4 { color: var(--h4-color); }
+.markdown-rendered h5 { color: var(--h5-color); }
+.markdown-rendered h6 { color: var(--h6-color); }
+
+/* Tags */
+.tag {
+ background-color: #30291f;
+ color: #cdae7d;
+ border-radius: 4px;
+ padding: 0 6px;
+}
+
+/* Blockquotes */
+blockquote {
+ border-color: #cdae7d !important;
+}
+
+/* Search highlights */
+.search-result-file-matched-text,
+.is-selected .search-result-file-matched-text,
+mark {
+ background-color: #393024 !important;
+ color: #14110e !important;
+}
+
+/* Active line in edit mode */
+.cm-active {
+ background-color: #231c18 !important;
+}
+
+/* Selection */
+::selection {
+ background-color: #27221c !important;
+}
+
+/* Tooltip */
+.tooltip {
+ background-color: #2e241f !important;
+ color: #f0e7dc !important;
+}
+
+/* Menu */
+.menu {
+ background-color: #231c18 !important;
+}
+
+.menu-item:hover {
+ background-color: #2e241f !important;
+}
diff --git a/themes/dreamcoder/rofi-dark.rasi b/themes/dreamcoder/rofi-dark.rasi
index 7ff6474..a725e5d 100644
--- a/themes/dreamcoder/rofi-dark.rasi
+++ b/themes/dreamcoder/rofi-dark.rasi
@@ -1,32 +1,38 @@
-/* Dreamcoder color layer for Rofi. */
+/* Dreamcoder color layer for Rofi OLED. */
* {
- background: rgba(16, 18, 22, 0.76);
- background-alt: #1c222b;
- foreground: #e8e1d7;
- muted: #b8b0a5;
- selected: rgba(217, 173, 103, 0.14);
- active: #d9ad67;
- urgent: #d78373;
- border-color: #d9ad67;
+ background: rgba(18, 16, 14, 0.76);
+ background-alt: #211c18;
+ foreground: #e8dfd0;
+ muted: #b8a99a;
+ selected: rgba(217, 149, 85, 0.24);
+ active: #d99555;
+ border-ui: #806754;
+ focus: #d99555;
+ urgent: #e98272;
+ border-color: #806754;
}
window {
background-color: @background;
border: 1px;
- border-color: @border-color;
+ border-color: @border-ui;
border-radius: 18px;
}
entry {
background-color: @selected;
text-color: @foreground;
+ border: 1px;
+ border-color: @focus;
border-radius: 12px;
}
element selected {
background-color: @selected;
text-color: @foreground;
+ border: 0 0 0 3px;
+ border-color: @focus;
}
element-text { text-color: @muted; }
-element selected element-text { text-color: @foreground; }
+element selected element-text { text-color: @foreground; }
\ No newline at end of file
diff --git a/themes/dreamcoder/rofi-dusk.rasi b/themes/dreamcoder/rofi-dusk.rasi
new file mode 100644
index 0000000..2220139
--- /dev/null
+++ b/themes/dreamcoder/rofi-dusk.rasi
@@ -0,0 +1,38 @@
+/* Dreamcoder color layer for Rofi. */
+* {
+ background: rgba(235, 228, 214, 0.88);
+ background-alt: #f1eadf;
+ foreground: #1a1713;
+ muted: #4c443a;
+ selected: rgba(138, 85, 32, 0.22);
+ active: #8a5520;
+ border-ui: #665845;
+ focus: #216a73;
+ urgent: #773126;
+ border-color: #665845;
+}
+
+window {
+ background-color: @background;
+ border: 1px;
+ border-color: @border-ui;
+ border-radius: 18px;
+}
+
+entry {
+ background-color: @selected;
+ text-color: @foreground;
+ border: 1px;
+ border-color: @focus;
+ border-radius: 12px;
+}
+
+element selected {
+ background-color: @selected;
+ text-color: @foreground;
+ border: 0 0 0 3px;
+ border-color: @focus;
+}
+
+element-text { text-color: @muted; }
+element selected element-text { text-color: @foreground; }
diff --git a/themes/dreamcoder/rofi-light.rasi b/themes/dreamcoder/rofi-light.rasi
index 93c04a5..7467489 100644
--- a/themes/dreamcoder/rofi-light.rasi
+++ b/themes/dreamcoder/rofi-light.rasi
@@ -1,31 +1,37 @@
/* Dreamcoder color layer for Rofi. */
* {
- background: rgba(246, 241, 232, 0.90);
- background-alt: #fbf8f1;
- foreground: #15130f;
- muted: #4a463f;
- selected: rgba(133, 87, 25, 0.15);
- active: #855719;
- urgent: #9b4b40;
- border-color: #855719;
+ background: rgba(243, 234, 220, 0.96);
+ background-alt: #fff7ea;
+ foreground: #17120d;
+ muted: #3d3228;
+ selected: rgba(130, 79, 22, 0.34);
+ active: #824f16;
+ border-ui: #66513b;
+ focus: #0f6570;
+ urgent: #842f24;
+ border-color: #66513b;
}
window {
background-color: @background;
border: 1px;
- border-color: @border-color;
+ border-color: @border-ui;
border-radius: 18px;
}
entry {
background-color: @selected;
text-color: @foreground;
+ border: 1px;
+ border-color: @focus;
border-radius: 12px;
}
element selected {
background-color: @selected;
text-color: @foreground;
+ border: 0 0 0 3px;
+ border-color: @focus;
}
element-text { text-color: @muted; }
diff --git a/themes/dreamcoder/tokens.json b/themes/dreamcoder/tokens.json
index 88f6980..6605a29 100644
--- a/themes/dreamcoder/tokens.json
+++ b/themes/dreamcoder/tokens.json
@@ -2,86 +2,129 @@
"$schema": "./tokens.schema.json",
"name": "Dreamcoder OS",
"version": 1,
- "description": "Canonical Dreamcoder visual tokens. Cocoa/lúcuma warmth with visual-health contrast guardrails.",
+ "description": "Canonical Dreamcoder visual tokens. Premium Cocoa/L\u00facuma light, Ember Noir dark, and Dusk transition with visual-health guardrails.",
"modes": {
- "dark": {
- "name": "Dreamcoder Dark",
- "bg": "#101216",
- "bg_soft": "#161922",
- "surface0": "#1c222b",
- "surface1": "#282e38",
- "surface2": "#353b45",
- "text": "#e8e1d7",
- "muted": "#b8b0a5",
- "subtle": "#928a80",
- "comment": "#8c857c",
- "border": "#3d4350",
- "border_hi": "#c8bda9",
- "accent": "#d9ad67",
- "accent_2": "#b87a48",
- "diagnostic": "#92c7cd",
- "sage": "#a5b89c",
- "lavender": "#c4b3df",
- "mauve": "#d2a3c3",
- "error": "#d78373",
- "warning": "#ddb36b",
- "selection": "#32291f",
- "panel_rgba": "rgba(16, 18, 22, 0.76)",
- "module_rgba": "rgba(232, 225, 215, 0.07)",
- "active_rgba": "rgba(217, 173, 103, 0.14)",
- "inactive_border": "rgba(3d4350b3)",
+"dark": {
+ "name": "Dreamcoder Ember Noir OLED",
+ "bg": "#12100e",
+ "bg_soft": "#1a1511",
+ "surface0": "#211c18",
+ "surface1": "#2e241f",
+ "surface2": "#3e3129",
+ "text": "#e8dfd0",
+ "muted": "#b8a99a",
+ "subtle": "#9a8b7c",
+ "comment": "#8c7c6d",
+ "border": "#594d46",
+ "border_ui": "#806754",
+ "border_hi": "#c8b195",
+ "focus": "#d99555",
+ "accent": "#d99555",
+ "accent_2": "#c96a45",
+ "diagnostic": "#c8b2a2",
+ "selection": "#373027",
+ "panel_rgba": "rgba(18, 16, 14, 0.76)",
+ "module_rgba": "rgba(232, 223, 208, 0.08)",
+ "active_rgba": "rgba(217, 149, 85, 0.24)",
+ "inactive_border": "rgba(89, 77, 70, 0.78)",
"details": "darker",
- "prompt_bg": "#19120c",
- "prompt_surface0": "#2a1d13",
- "prompt_surface1": "#402c18",
- "prompt_surface2": "#5a3a1f",
- "prompt_text": "#f4e9dd",
- "prompt_muted": "#d5c3b5",
- "prompt_accent": "#e8aa67",
- "prompt_accent_2": "#be7d42"
+ "prompt_bg": "#19110c",
+ "prompt_surface0": "#2c1c14",
+ "prompt_surface1": "#52301c",
+ "prompt_surface2": "#744524",
+ "prompt_text": "#fff0df",
+ "prompt_muted": "#dcc3aa",
+ "prompt_accent": "#e6a15c",
+ "prompt_accent_2": "#d66f50"
},
"light": {
"name": "Dreamcoder Light",
- "bg": "#f6f1e8",
- "bg_soft": "#ece4d8",
- "surface0": "#fbf8f1",
- "surface1": "#ded3c4",
- "surface2": "#c2b39f",
- "text": "#15130f",
- "muted": "#4a463f",
- "subtle": "#6b6458",
- "comment": "#6a6258",
- "border": "#b7a78f",
- "border_hi": "#75644f",
- "accent": "#855719",
- "accent_2": "#7f4a27",
- "diagnostic": "#1e6871",
- "sage": "#496f45",
- "lavender": "#665593",
- "mauve": "#844c71",
- "error": "#9b4b40",
- "warning": "#765019",
- "selection": "#dcc49e",
- "panel_rgba": "rgba(246, 241, 232, 0.90)",
- "module_rgba": "rgba(21, 19, 15, 0.075)",
- "active_rgba": "rgba(133, 87, 25, 0.15)",
- "inactive_border": "rgba(b7a78fdf)",
+ "bg": "#f3eadc",
+ "bg_soft": "#e6d7c4",
+ "surface0": "#fff7ea",
+ "surface1": "#decbb1",
+ "surface2": "#c8ad89",
+ "text": "#17120d",
+ "muted": "#3d3228",
+ "subtle": "#554635",
+ "comment": "#66523f",
+ "border": "#8a7358",
+ "border_ui": "#66513b",
+ "border_hi": "#3e2f20",
+ "focus": "#0f6570",
+ "accent": "#824f16",
+ "accent_2": "#a7471c",
+ "diagnostic": "#15516e",
+ "sage": "#3f6b35",
+ "lavender": "#57478b",
+ "mauve": "#7d3e64",
+ "error": "#842f24",
+ "warning": "#654300",
+ "selection": "#17120d",
+ "panel_rgba": "rgba(243, 234, 220, 0.96)",
+ "module_rgba": "rgba(222, 203, 177, 0.80)",
+ "active_rgba": "rgba(130, 79, 22, 0.34)",
+ "inactive_border": "rgba(8a7358ee)",
"details": "lighter",
- "prompt_bg": "#f6f1e8",
- "prompt_surface0": "#fbefd9",
- "prompt_surface1": "#dcc094",
- "prompt_surface2": "#bd905a",
- "prompt_text": "#241911",
- "prompt_muted": "#63503e",
- "prompt_accent": "#985d2b",
- "prompt_accent_2": "#7a5028"
+ "prompt_bg": "#f3eadc",
+ "prompt_surface0": "#fff0d8",
+ "prompt_surface1": "#d6ac72",
+ "prompt_surface2": "#a96d31",
+ "prompt_text": "#20150c",
+ "prompt_muted": "#53402e",
+ "prompt_accent": "#8a5520",
+ "prompt_accent_2": "#a7471c"
+ },
+ "dusk": {
+ "name": "Dreamcoder Dusk",
+ "bg": "#ebe4d6",
+ "bg_soft": "#dfd5c4",
+ "surface0": "#f1eadf",
+ "surface1": "#d8cbb8",
+ "surface2": "#c6b6a0",
+ "text": "#1a1713",
+ "muted": "#4c443a",
+ "subtle": "#5a4f43",
+ "comment": "#615548",
+ "border": "#a7947a",
+ "border_ui": "#665845",
+ "border_hi": "#4a3f32",
+ "focus": "#216a73",
+ "accent": "#8a5520",
+ "accent_2": "#96411e",
+ "diagnostic": "#104b67",
+ "sage": "#466b41",
+ "lavender": "#5b4e86",
+ "mauve": "#784762",
+ "error": "#773126",
+ "warning": "#604000",
+ "selection": "#1a1713",
+ "panel_rgba": "rgba(235, 228, 214, 0.88)",
+ "module_rgba": "rgba(26, 23, 19, 0.08)",
+ "active_rgba": "rgba(138, 85, 32, 0.22)",
+ "inactive_border": "rgba(a7947adf)",
+ "details": "lighter",
+ "prompt_bg": "#ebe4d6",
+ "prompt_surface0": "#f2e6d4",
+ "prompt_surface1": "#d3bc98",
+ "prompt_surface2": "#b88958",
+ "prompt_text": "#261c14",
+ "prompt_muted": "#574939",
+ "prompt_accent": "#965f25",
+ "prompt_accent_2": "#96411e"
}
},
"guardrails": {
"avoid_pure_black_white": true,
"minimum_text_contrast": 4.5,
"preferred_main_text_contrast": 7.0,
- "light_hours_default": "07:00-18:00",
+ "minimum_apca_body": 75,
+ "minimum_apca_body_dark": 50,
+ "minimum_apca_quiet": 44,
+ "minimum_apca_ui": 60,
+ "minimum_apca_ui_dark": 28,
+ "light_hours_default": "07:00-16:00",
+ "dusk_hours_default": "16:00-18:00",
"canonical_opencode_theme": "dreamcoder"
}
}
diff --git a/themes/dreamcoder/tokens.schema.json b/themes/dreamcoder/tokens.schema.json
index 090e0ac..eac190e 100644
--- a/themes/dreamcoder/tokens.schema.json
+++ b/themes/dreamcoder/tokens.schema.json
@@ -2,45 +2,137 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Dreamcoder OS design tokens",
"type": "object",
- "required": ["name", "version", "modes", "guardrails"],
+ "required": [
+ "name",
+ "version",
+ "modes",
+ "guardrails"
+ ],
"properties": {
- "name": { "const": "Dreamcoder OS" },
- "version": { "type": "integer", "minimum": 1 },
- "description": { "type": "string" },
+ "name": {
+ "const": "Dreamcoder OS"
+ },
+ "version": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "description": {
+ "type": "string"
+ },
"modes": {
"type": "object",
- "required": ["dark", "light"],
- "additionalProperties": { "$ref": "#/$defs/palette" }
+ "required": [
+ "dark",
+ "light"
+ ],
+ "additionalProperties": {
+ "$ref": "#/$defs/palette"
+ }
},
"guardrails": {
"type": "object",
- "required": ["canonical_opencode_theme", "minimum_text_contrast", "preferred_main_text_contrast"],
+ "required": [
+ "canonical_opencode_theme",
+ "minimum_text_contrast",
+ "preferred_main_text_contrast"
+ ],
"properties": {
- "canonical_opencode_theme": { "const": "dreamcoder" },
- "avoid_pure_black_white": { "type": "boolean" },
- "minimum_text_contrast": { "type": "number", "minimum": 4.5 },
- "preferred_main_text_contrast": { "type": "number", "minimum": 7 },
- "light_hours_default": { "type": "string" }
+ "canonical_opencode_theme": {
+ "const": "dreamcoder"
+ },
+ "avoid_pure_black_white": {
+ "type": "boolean"
+ },
+ "minimum_text_contrast": {
+ "type": "number",
+ "minimum": 4.5
+ },
+ "preferred_main_text_contrast": {
+ "type": "number",
+ "minimum": 7
+ },
+ "light_hours_default": {
+ "type": "string"
+ },
+ "dusk_hours_default": {
+ "type": "string"
+ },
+ "minimum_apca_body": {
+ "type": "number",
+ "minimum": 60
+ },
+ "minimum_apca_quiet": {
+ "type": "number",
+ "minimum": 45
+ },
+ "minimum_apca_ui": {
+ "type": "number",
+ "minimum": 45
+ }
}
}
},
"$defs": {
- "hex": { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" },
+ "hex": {
+ "type": "string",
+ "pattern": "^#[0-9a-fA-F]{6}$"
+ },
"palette": {
"type": "object",
- "required": ["bg", "text", "muted", "comment", "accent", "accent_2", "diagnostic", "sage", "error", "warning"],
- "additionalProperties": { "type": "string" },
+ "required": [
+ "bg",
+ "text",
+ "muted",
+ "comment",
+ "accent",
+ "accent_2",
+ "diagnostic",
+ "sage",
+ "error",
+ "warning",
+ "border_ui",
+ "focus"
+ ],
+ "additionalProperties": {
+ "type": "string"
+ },
"properties": {
- "bg": { "$ref": "#/$defs/hex" },
- "text": { "$ref": "#/$defs/hex" },
- "muted": { "$ref": "#/$defs/hex" },
- "comment": { "$ref": "#/$defs/hex" },
- "accent": { "$ref": "#/$defs/hex" },
- "accent_2": { "$ref": "#/$defs/hex" },
- "diagnostic": { "$ref": "#/$defs/hex" },
- "sage": { "$ref": "#/$defs/hex" },
- "error": { "$ref": "#/$defs/hex" },
- "warning": { "$ref": "#/$defs/hex" }
+ "bg": {
+ "$ref": "#/$defs/hex"
+ },
+ "text": {
+ "$ref": "#/$defs/hex"
+ },
+ "muted": {
+ "$ref": "#/$defs/hex"
+ },
+ "comment": {
+ "$ref": "#/$defs/hex"
+ },
+ "accent": {
+ "$ref": "#/$defs/hex"
+ },
+ "accent_2": {
+ "$ref": "#/$defs/hex"
+ },
+ "diagnostic": {
+ "$ref": "#/$defs/hex"
+ },
+ "sage": {
+ "$ref": "#/$defs/hex"
+ },
+ "error": {
+ "$ref": "#/$defs/hex"
+ },
+ "warning": {
+ "$ref": "#/$defs/hex"
+ },
+ "border_ui": {
+ "$ref": "#/$defs/hex"
+ },
+ "focus": {
+ "$ref": "#/$defs/hex"
+ }
}
}
}
diff --git a/themes/dreamcoder/waybar-dark.css b/themes/dreamcoder/waybar-dark.css
index d83f670..74e5fb7 100644
--- a/themes/dreamcoder/waybar-dark.css
+++ b/themes/dreamcoder/waybar-dark.css
@@ -1,19 +1,21 @@
-/* Dreamcoder color layer for Waybar. */
-@define-color bg #101216;
-@define-color bg-soft #161922;
-@define-color surface #1c222b;
-@define-color text #e8e1d7;
-@define-color muted #b8b0a5;
-@define-color border #3d4350;
-@define-color accent #d9ad67;
-@define-color accent-2 #b87a48;
-@define-color diagnostic #92c7cd;
-@define-color error #d78373;
-@define-color success #a5b89c;
-@define-color warning #ddb36b;
+/* Dreamcoder color layer for Waybar OLED. */
+@define-color bg #12100e;
+@define-color bg-soft #1a1511;
+@define-color surface #211c18;
+@define-color text #e8dfd0;
+@define-color muted #b8a99a;
+@define-color border #594d46;
+@define-color border-ui #806754;
+@define-color focus #d99555;
+@define-color accent #d99555;
+@define-color accent-2 #c96a45;
+@define-color diagnostic #c8b2a2;
+@define-color error #e98272;
+@define-color success #b8bf84;
+@define-color warning #e8b866;
window#waybar {
- background: rgba(16, 18, 22, 0.76);
+ background: rgba(18, 16, 14, 0.76);
color: @text;
border-bottom: 1px solid alpha(@accent, 0.26);
}
@@ -21,11 +23,13 @@ window#waybar {
#workspaces button {
color: @muted;
background: transparent;
+ border: 1px solid transparent;
}
#workspaces button.active {
color: @accent;
- background: rgba(217, 173, 103, 0.14);
+ background: rgba(217, 149, 85, 0.24);
+ border-color: alpha(@focus, 0.82);
}
#clock,
@@ -35,11 +39,11 @@ window#waybar {
#cpu,
#memory,
#tray {
- background: rgba(232, 225, 215, 0.07);
+ background: rgba(232, 223, 208, 0.08);
color: @text;
- border: 1px solid alpha(@border, 0.45);
+ border: 1px solid alpha(@border-ui, 0.78);
}
#battery.warning { color: @warning; }
#battery.critical { color: @error; }
-#network.disconnected { color: @error; }
+#network.disconnected { color: @error; }
\ No newline at end of file
diff --git a/themes/dreamcoder/waybar-dusk.css b/themes/dreamcoder/waybar-dusk.css
new file mode 100644
index 0000000..3105273
--- /dev/null
+++ b/themes/dreamcoder/waybar-dusk.css
@@ -0,0 +1,49 @@
+/* Dreamcoder color layer for Waybar. */
+@define-color bg #ebe4d6;
+@define-color bg-soft #dfd5c4;
+@define-color surface #f1eadf;
+@define-color text #1a1713;
+@define-color muted #4c443a;
+@define-color border #a7947a;
+@define-color border-ui #665845;
+@define-color focus #216a73;
+@define-color accent #8a5520;
+@define-color accent-2 #96411e;
+@define-color diagnostic #104b67;
+@define-color error #773126;
+@define-color success #466b41;
+@define-color warning #604000;
+
+window#waybar {
+ background: rgba(235, 228, 214, 0.88);
+ color: @text;
+ border-bottom: 1px solid alpha(@accent, 0.26);
+}
+
+#workspaces button {
+ color: @muted;
+ background: transparent;
+ border: 1px solid transparent;
+}
+
+#workspaces button.active {
+ color: @accent;
+ background: rgba(138, 85, 32, 0.22);
+ border-color: alpha(@focus, 0.82);
+}
+
+#clock,
+#battery,
+#network,
+#pulseaudio,
+#cpu,
+#memory,
+#tray {
+ background: rgba(26, 23, 19, 0.08);
+ color: @text;
+ border: 1px solid alpha(@border-ui, 0.78);
+}
+
+#battery.warning { color: @warning; }
+#battery.critical { color: @error; }
+#network.disconnected { color: @error; }
diff --git a/themes/dreamcoder/waybar-light.css b/themes/dreamcoder/waybar-light.css
index 59257f0..c1aaa4b 100644
--- a/themes/dreamcoder/waybar-light.css
+++ b/themes/dreamcoder/waybar-light.css
@@ -1,19 +1,21 @@
/* Dreamcoder color layer for Waybar. */
-@define-color bg #f6f1e8;
-@define-color bg-soft #ece4d8;
-@define-color surface #fbf8f1;
-@define-color text #15130f;
-@define-color muted #4a463f;
-@define-color border #b7a78f;
-@define-color accent #855719;
-@define-color accent-2 #7f4a27;
-@define-color diagnostic #1e6871;
-@define-color error #9b4b40;
-@define-color success #496f45;
-@define-color warning #765019;
+@define-color bg #f3eadc;
+@define-color bg-soft #e6d7c4;
+@define-color surface #fff7ea;
+@define-color text #17120d;
+@define-color muted #3d3228;
+@define-color border #8a7358;
+@define-color border-ui #66513b;
+@define-color focus #0f6570;
+@define-color accent #824f16;
+@define-color accent-2 #a7471c;
+@define-color diagnostic #15516e;
+@define-color error #842f24;
+@define-color success #3f6b35;
+@define-color warning #654300;
window#waybar {
- background: rgba(246, 241, 232, 0.90);
+ background: rgba(243, 234, 220, 0.96);
color: @text;
border-bottom: 1px solid alpha(@accent, 0.26);
}
@@ -21,11 +23,13 @@ window#waybar {
#workspaces button {
color: @muted;
background: transparent;
+ border: 1px solid transparent;
}
#workspaces button.active {
color: @accent;
- background: rgba(133, 87, 25, 0.15);
+ background: rgba(130, 79, 22, 0.34);
+ border-color: alpha(@focus, 0.82);
}
#clock,
@@ -35,9 +39,9 @@ window#waybar {
#cpu,
#memory,
#tray {
- background: rgba(21, 19, 15, 0.075);
+ background: rgba(222, 203, 177, 0.80);
color: @text;
- border: 1px solid alpha(@border, 0.45);
+ border: 1px solid alpha(@border-ui, 0.78);
}
#battery.warning { color: @warning; }
diff --git a/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder-dark.zsh b/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder-dark.zsh
new file mode 100644
index 0000000..a4764e8
--- /dev/null
+++ b/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder-dark.zsh
@@ -0,0 +1,56 @@
+# ========================================================
+# Dreamcoder Ember Noir — Zsh-syntax-highlighting theme
+# Source this from .zshrc AFTER zsh-syntax-highlighting plugin.
+# ========================================================
+
+typeset -A ZSH_HIGHLIGHT_STYLES
+
+# Main
+ZSH_HIGHLIGHT_STYLES[default]='fg=#e8dfd0'
+ZSH_HIGHLIGHT_STYLES[unknown-token]='fg=#e98272'
+ZSH_HIGHLIGHT_STYLES[reserved-word]='fg=#d99555,bold'
+ZSH_HIGHLIGHT_STYLES[alias]='fg=#c96a45'
+ZSH_HIGHLIGHT_STYLES[suffix-alias]='fg=#c96a45'
+ZSH_HIGHLIGHT_STYLES[builtin]='fg=#d99555'
+ZSH_HIGHLIGHT_STYLES[function]='fg=#c96a45'
+ZSH_HIGHLIGHT_STYLES[command]='fg=#d99555'
+ZSH_HIGHLIGHT_STYLES[precommand]='fg=#d99555,italic'
+ZSH_HIGHLIGHT_STYLES[commandseparator]='fg=#b8a99a'
+ZSH_HIGHLIGHT_STYLES[hashed-command]='fg=#d99555'
+
+# Paths
+ZSH_HIGHLIGHT_STYLES[path]='fg=#c8b2a2'
+ZSH_HIGHLIGHT_STYLES[path_pathseparator]='fg=#d99555'
+ZSH_HIGHLIGHT_STYLES[path_prefix]='fg=#c8b2a2,underline'
+ZSH_HIGHLIGHT_STYLES[path_approx]='fg=#e8b866,underline'
+
+# Globbing
+ZSH_HIGHLIGHT_STYLES[globbing]='fg=#c9a8dc'
+ZSH_HIGHLIGHT_STYLES[history-expansion]='fg=#c9a8dc'
+
+# Quoting & Brackets
+ZSH_HIGHLIGHT_STYLES[single-hyphen-option]='fg=#c8b2a2'
+ZSH_HIGHLIGHT_STYLES[double-hyphen-option]='fg=#c8b2a2'
+ZSH_HIGHLIGHT_STYLES[back-quoted-argument]='fg=#b8bf84'
+ZSH_HIGHLIGHT_STYLES[single-quoted-argument]='fg=#b8bf84'
+ZSH_HIGHLIGHT_STYLES[double-quoted-argument]='fg=#b8bf84'
+ZSH_HIGHLIGHT_STYLES[dollar-quoted-argument]='fg=#b8bf84'
+ZSH_HIGHLIGHT_STYLES[rc-quote]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[dollar-double-quoted-argument]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[back-double-quoted-argument]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[back-dollar-quoted-argument]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[assign]='fg=#e8dfd0'
+ZSH_HIGHLIGHT_STYLES[redirection]='fg=#c96a45'
+ZSH_HIGHLIGHT_STYLES[comment]='fg=#9c826d,italic'
+ZSH_HIGHLIGHT_STYLES[variable]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[mathvar]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[null]='fg=#b8a99a'
+
+# Brackets
+ZSH_HIGHLIGHT_STYLES[bracket-level-1]='fg=#d99555'
+ZSH_HIGHLIGHT_STYLES[bracket-level-2]='fg=#c8b2a2'
+ZSH_HIGHLIGHT_STYLES[bracket-level-3]='fg=#b8bf84'
+ZSH_HIGHLIGHT_STYLES[bracket-level-4]='fg=#c9a8dc'
+
+# Cursor
+ZSH_HIGHLIGHT_STYLES[cursor-matchingbracket]='fg=#12100e,bg=#d99555'
diff --git a/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder-dusk.zsh b/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder-dusk.zsh
new file mode 100644
index 0000000..0dc7afb
--- /dev/null
+++ b/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder-dusk.zsh
@@ -0,0 +1,56 @@
+# ========================================================
+# Dreamcoder Dusk — Zsh-syntax-highlighting theme
+# Source this from .zshrc AFTER zsh-syntax-highlighting plugin.
+# ========================================================
+
+typeset -A ZSH_HIGHLIGHT_STYLES
+
+# Main
+ZSH_HIGHLIGHT_STYLES[default]='fg=#1a1713'
+ZSH_HIGHLIGHT_STYLES[unknown-token]='fg=#773126'
+ZSH_HIGHLIGHT_STYLES[reserved-word]='fg=#8a5520,bold'
+ZSH_HIGHLIGHT_STYLES[alias]='fg=#96411e'
+ZSH_HIGHLIGHT_STYLES[suffix-alias]='fg=#96411e'
+ZSH_HIGHLIGHT_STYLES[builtin]='fg=#8a5520'
+ZSH_HIGHLIGHT_STYLES[function]='fg=#96411e'
+ZSH_HIGHLIGHT_STYLES[command]='fg=#8a5520'
+ZSH_HIGHLIGHT_STYLES[precommand]='fg=#8a5520,italic'
+ZSH_HIGHLIGHT_STYLES[commandseparator]='fg=#4c443a'
+ZSH_HIGHLIGHT_STYLES[hashed-command]='fg=#8a5520'
+
+# Paths
+ZSH_HIGHLIGHT_STYLES[path]='fg=#104b67'
+ZSH_HIGHLIGHT_STYLES[path_pathseparator]='fg=#8a5520'
+ZSH_HIGHLIGHT_STYLES[path_prefix]='fg=#104b67,underline'
+ZSH_HIGHLIGHT_STYLES[path_approx]='fg=#604000,underline'
+
+# Globbing
+ZSH_HIGHLIGHT_STYLES[globbing]='fg=#5b4e86'
+ZSH_HIGHLIGHT_STYLES[history-expansion]='fg=#5b4e86'
+
+# Quoting & Brackets
+ZSH_HIGHLIGHT_STYLES[single-hyphen-option]='fg=#104b67'
+ZSH_HIGHLIGHT_STYLES[double-hyphen-option]='fg=#104b67'
+ZSH_HIGHLIGHT_STYLES[back-quoted-argument]='fg=#466b41'
+ZSH_HIGHLIGHT_STYLES[single-quoted-argument]='fg=#466b41'
+ZSH_HIGHLIGHT_STYLES[double-quoted-argument]='fg=#466b41'
+ZSH_HIGHLIGHT_STYLES[dollar-quoted-argument]='fg=#466b41'
+ZSH_HIGHLIGHT_STYLES[rc-quote]='fg=#784762'
+ZSH_HIGHLIGHT_STYLES[dollar-double-quoted-argument]='fg=#784762'
+ZSH_HIGHLIGHT_STYLES[back-double-quoted-argument]='fg=#784762'
+ZSH_HIGHLIGHT_STYLES[back-dollar-quoted-argument]='fg=#784762'
+ZSH_HIGHLIGHT_STYLES[assign]='fg=#1a1713'
+ZSH_HIGHLIGHT_STYLES[redirection]='fg=#96411e'
+ZSH_HIGHLIGHT_STYLES[comment]='fg=#615548,italic'
+ZSH_HIGHLIGHT_STYLES[variable]='fg=#784762'
+ZSH_HIGHLIGHT_STYLES[mathvar]='fg=#784762'
+ZSH_HIGHLIGHT_STYLES[null]='fg=#4c443a'
+
+# Brackets
+ZSH_HIGHLIGHT_STYLES[bracket-level-1]='fg=#8a5520'
+ZSH_HIGHLIGHT_STYLES[bracket-level-2]='fg=#104b67'
+ZSH_HIGHLIGHT_STYLES[bracket-level-3]='fg=#466b41'
+ZSH_HIGHLIGHT_STYLES[bracket-level-4]='fg=#5b4e86'
+
+# Cursor
+ZSH_HIGHLIGHT_STYLES[cursor-matchingbracket]='fg=#ebe4d6,bg=#8a5520'
diff --git a/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder-light.zsh b/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder-light.zsh
new file mode 100644
index 0000000..bb70f10
--- /dev/null
+++ b/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder-light.zsh
@@ -0,0 +1,56 @@
+# ========================================================
+# Dreamcoder Light — Zsh-syntax-highlighting theme
+# Source this from .zshrc AFTER zsh-syntax-highlighting plugin.
+# ========================================================
+
+typeset -A ZSH_HIGHLIGHT_STYLES
+
+# Main
+ZSH_HIGHLIGHT_STYLES[default]='fg=#17120d'
+ZSH_HIGHLIGHT_STYLES[unknown-token]='fg=#842f24'
+ZSH_HIGHLIGHT_STYLES[reserved-word]='fg=#824f16,bold'
+ZSH_HIGHLIGHT_STYLES[alias]='fg=#a7471c'
+ZSH_HIGHLIGHT_STYLES[suffix-alias]='fg=#a7471c'
+ZSH_HIGHLIGHT_STYLES[builtin]='fg=#824f16'
+ZSH_HIGHLIGHT_STYLES[function]='fg=#a7471c'
+ZSH_HIGHLIGHT_STYLES[command]='fg=#824f16'
+ZSH_HIGHLIGHT_STYLES[precommand]='fg=#824f16,italic'
+ZSH_HIGHLIGHT_STYLES[commandseparator]='fg=#3d3228'
+ZSH_HIGHLIGHT_STYLES[hashed-command]='fg=#824f16'
+
+# Paths
+ZSH_HIGHLIGHT_STYLES[path]='fg=#15516e'
+ZSH_HIGHLIGHT_STYLES[path_pathseparator]='fg=#824f16'
+ZSH_HIGHLIGHT_STYLES[path_prefix]='fg=#15516e,underline'
+ZSH_HIGHLIGHT_STYLES[path_approx]='fg=#654300,underline'
+
+# Globbing
+ZSH_HIGHLIGHT_STYLES[globbing]='fg=#57478b'
+ZSH_HIGHLIGHT_STYLES[history-expansion]='fg=#57478b'
+
+# Quoting & Brackets
+ZSH_HIGHLIGHT_STYLES[single-hyphen-option]='fg=#15516e'
+ZSH_HIGHLIGHT_STYLES[double-hyphen-option]='fg=#15516e'
+ZSH_HIGHLIGHT_STYLES[back-quoted-argument]='fg=#3f6b35'
+ZSH_HIGHLIGHT_STYLES[single-quoted-argument]='fg=#3f6b35'
+ZSH_HIGHLIGHT_STYLES[double-quoted-argument]='fg=#3f6b35'
+ZSH_HIGHLIGHT_STYLES[dollar-quoted-argument]='fg=#3f6b35'
+ZSH_HIGHLIGHT_STYLES[rc-quote]='fg=#7d3e64'
+ZSH_HIGHLIGHT_STYLES[dollar-double-quoted-argument]='fg=#7d3e64'
+ZSH_HIGHLIGHT_STYLES[back-double-quoted-argument]='fg=#7d3e64'
+ZSH_HIGHLIGHT_STYLES[back-dollar-quoted-argument]='fg=#7d3e64'
+ZSH_HIGHLIGHT_STYLES[assign]='fg=#17120d'
+ZSH_HIGHLIGHT_STYLES[redirection]='fg=#a7471c'
+ZSH_HIGHLIGHT_STYLES[comment]='fg=#66523f,italic'
+ZSH_HIGHLIGHT_STYLES[variable]='fg=#7d3e64'
+ZSH_HIGHLIGHT_STYLES[mathvar]='fg=#7d3e64'
+ZSH_HIGHLIGHT_STYLES[null]='fg=#3d3228'
+
+# Brackets
+ZSH_HIGHLIGHT_STYLES[bracket-level-1]='fg=#824f16'
+ZSH_HIGHLIGHT_STYLES[bracket-level-2]='fg=#15516e'
+ZSH_HIGHLIGHT_STYLES[bracket-level-3]='fg=#3f6b35'
+ZSH_HIGHLIGHT_STYLES[bracket-level-4]='fg=#57478b'
+
+# Cursor
+ZSH_HIGHLIGHT_STYLES[cursor-matchingbracket]='fg=#f3eadc,bg=#824f16'
diff --git a/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder.zsh b/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder.zsh
new file mode 100644
index 0000000..0e6b8ac
--- /dev/null
+++ b/themes/dreamcoder/zsh-syntax-highlighting-dreamcoder.zsh
@@ -0,0 +1,56 @@
+# ========================================================
+# Dreamcoder Ember Noir — Zsh-syntax-highlighting theme
+# Source this from .zshrc AFTER zsh-syntax-highlighting plugin.
+# ========================================================
+
+typeset -A ZSH_HIGHLIGHT_STYLES
+
+# Main
+ZSH_HIGHLIGHT_STYLES[default]='fg=#f0e7dc'
+ZSH_HIGHLIGHT_STYLES[unknown-token]='fg=#e98272'
+ZSH_HIGHLIGHT_STYLES[reserved-word]='fg=#cdae7d,bold'
+ZSH_HIGHLIGHT_STYLES[alias]='fg=#ce836c'
+ZSH_HIGHLIGHT_STYLES[suffix-alias]='fg=#ce836c'
+ZSH_HIGHLIGHT_STYLES[builtin]='fg=#cdae7d'
+ZSH_HIGHLIGHT_STYLES[function]='fg=#ce836c'
+ZSH_HIGHLIGHT_STYLES[command]='fg=#cdae7d'
+ZSH_HIGHLIGHT_STYLES[precommand]='fg=#cdae7d,italic'
+ZSH_HIGHLIGHT_STYLES[commandseparator]='fg=#c7b9aa'
+ZSH_HIGHLIGHT_STYLES[hashed-command]='fg=#cdae7d'
+
+# Paths
+ZSH_HIGHLIGHT_STYLES[path]='fg=#c7b2a2'
+ZSH_HIGHLIGHT_STYLES[path_pathseparator]='fg=#cdae7d'
+ZSH_HIGHLIGHT_STYLES[path_prefix]='fg=#c7b2a2,underline'
+ZSH_HIGHLIGHT_STYLES[path_approx]='fg=#e8b866,underline'
+
+# Globbing
+ZSH_HIGHLIGHT_STYLES[globbing]='fg=#c9a8dc'
+ZSH_HIGHLIGHT_STYLES[history-expansion]='fg=#c9a8dc'
+
+# Quoting & Brackets
+ZSH_HIGHLIGHT_STYLES[single-hyphen-option]='fg=#c7b2a2'
+ZSH_HIGHLIGHT_STYLES[double-hyphen-option]='fg=#c7b2a2'
+ZSH_HIGHLIGHT_STYLES[back-quoted-argument]='fg=#b8bf84'
+ZSH_HIGHLIGHT_STYLES[single-quoted-argument]='fg=#b8bf84'
+ZSH_HIGHLIGHT_STYLES[double-quoted-argument]='fg=#b8bf84'
+ZSH_HIGHLIGHT_STYLES[dollar-quoted-argument]='fg=#b8bf84'
+ZSH_HIGHLIGHT_STYLES[rc-quote]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[dollar-double-quoted-argument]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[back-double-quoted-argument]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[back-dollar-quoted-argument]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[assign]='fg=#f0e7dc'
+ZSH_HIGHLIGHT_STYLES[redirection]='fg=#ce836c'
+ZSH_HIGHLIGHT_STYLES[comment]='fg=#9c826d,italic'
+ZSH_HIGHLIGHT_STYLES[variable]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[mathvar]='fg=#d98aa9'
+ZSH_HIGHLIGHT_STYLES[null]='fg=#c7b9aa'
+
+# Brackets
+ZSH_HIGHLIGHT_STYLES[bracket-level-1]='fg=#cdae7d'
+ZSH_HIGHLIGHT_STYLES[bracket-level-2]='fg=#c7b2a2'
+ZSH_HIGHLIGHT_STYLES[bracket-level-3]='fg=#b8bf84'
+ZSH_HIGHLIGHT_STYLES[bracket-level-4]='fg=#c9a8dc'
+
+# Cursor
+ZSH_HIGHLIGHT_STYLES[cursor-matchingbracket]='fg=#14110e,bg=#cdae7d'