@tooee/themes 0.1.11 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ThemePicker.d.ts.map +1 -1
- package/dist/ThemePicker.js +2 -21
- package/dist/ThemePicker.js.map +1 -1
- package/dist/context.d.ts +38 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +70 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +15 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +136 -0
- package/dist/loader.js.map +1 -0
- package/dist/syntax-rules.d.ts +4 -0
- package/dist/syntax-rules.d.ts.map +1 -0
- package/dist/syntax-rules.js +207 -0
- package/dist/syntax-rules.js.map +1 -0
- package/dist/{theme.d.ts → types.d.ts} +3 -45
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +145 -0
- package/dist/types.js.map +1 -0
- package/package.json +17 -16
- package/src/ThemePicker.tsx +2 -23
- package/src/context.tsx +142 -0
- package/src/index.ts +10 -18
- package/src/loader.ts +155 -0
- package/src/syntax-rules.ts +210 -0
- package/src/types.ts +219 -0
- package/dist/theme.d.ts.map +0 -1
- package/dist/theme.js +0 -552
- package/dist/theme.js.map +0 -1
- package/src/theme.tsx +0 -716
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { type ReactNode } from "react";
|
|
2
|
-
import { SyntaxStyle } from "@opentui/core";
|
|
3
1
|
type HexColor = `#${string}`;
|
|
4
2
|
type RefName = string;
|
|
5
3
|
type Variant = {
|
|
@@ -66,48 +64,8 @@ export interface ResolvedTheme {
|
|
|
66
64
|
syntaxOperator: string;
|
|
67
65
|
syntaxPunctuation: string;
|
|
68
66
|
}
|
|
67
|
+
export declare const RESOLVED_KEYS: (keyof ResolvedTheme)[];
|
|
68
|
+
export declare const FALLBACKS: Record<string, string>;
|
|
69
69
|
export declare function resolveTheme(json: ThemeJSON, mode: "dark" | "light"): ResolvedTheme;
|
|
70
|
-
export declare function buildSyntaxStyle(resolved: ResolvedTheme): SyntaxStyle;
|
|
71
|
-
export declare function loadThemes(): Map<string, ThemeJSON>;
|
|
72
|
-
export declare function getThemeNames(): string[];
|
|
73
|
-
export interface Theme {
|
|
74
|
-
name: string;
|
|
75
|
-
mode: "dark" | "light";
|
|
76
|
-
colors: ResolvedTheme;
|
|
77
|
-
syntax: SyntaxStyle;
|
|
78
|
-
}
|
|
79
|
-
export declare const defaultTheme: Theme;
|
|
80
|
-
interface ThemeContextValue {
|
|
81
|
-
theme: ResolvedTheme;
|
|
82
|
-
syntax: SyntaxStyle;
|
|
83
|
-
name: string;
|
|
84
|
-
mode: "dark" | "light";
|
|
85
|
-
}
|
|
86
|
-
export interface ThemeProviderProps {
|
|
87
|
-
/** Theme name (e.g. "tokyonight", "catppuccin", "dracula") */
|
|
88
|
-
name?: string;
|
|
89
|
-
/** Color mode */
|
|
90
|
-
mode?: "dark" | "light";
|
|
91
|
-
/** Full Theme object (overrides name/mode if provided) */
|
|
92
|
-
theme?: Theme;
|
|
93
|
-
children: ReactNode;
|
|
94
|
-
}
|
|
95
|
-
export declare function ThemeProvider({ name, mode, theme: themeProp, children }: ThemeProviderProps): ReactNode;
|
|
96
|
-
export declare function useTheme(): ThemeContextValue;
|
|
97
|
-
interface ThemeSwitcherContextValue extends ThemeContextValue {
|
|
98
|
-
nextTheme: () => void;
|
|
99
|
-
prevTheme: () => void;
|
|
100
|
-
setTheme: (name: string, opts?: {
|
|
101
|
-
persist?: boolean;
|
|
102
|
-
}) => void;
|
|
103
|
-
allThemes: string[];
|
|
104
|
-
}
|
|
105
|
-
export interface ThemeSwitcherProviderProps {
|
|
106
|
-
initialTheme?: string;
|
|
107
|
-
initialMode?: "dark" | "light";
|
|
108
|
-
children: ReactNode;
|
|
109
|
-
}
|
|
110
|
-
export declare function ThemeSwitcherProvider({ initialTheme, initialMode, children, }: ThemeSwitcherProviderProps): ReactNode;
|
|
111
|
-
export declare function useThemeSwitcher(): ThemeSwitcherContextValue;
|
|
112
70
|
export {};
|
|
113
|
-
//# sourceMappingURL=
|
|
71
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,KAAK,QAAQ,GAAG,IAAI,MAAM,EAAE,CAAA;AAC5B,KAAK,OAAO,GAAG,MAAM,CAAA;AACrB,KAAK,OAAO,GAAG;IAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAA;CAAE,CAAA;AACtE,KAAK,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAA;AAE9C,MAAM,WAAW,SAAS;IACxB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAA;IACzC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CAClC;AAMD,MAAM,WAAW,aAAa;IAE5B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IAEpB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,uBAAuB,EAAE,MAAM,CAAA;IAE/B,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;IACpB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,sBAAsB,EAAE,MAAM,CAAA;IAC9B,gBAAgB,EAAE,MAAM,CAAA;IACxB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,MAAM,CAAA;IAEzB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IAEjB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAGD,eAAO,MAAM,aAAa,EAAE,CAAC,MAAM,aAAa,CAAC,EAqDhD,CAAA;AAGD,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAqD5C,CAAA;AAMD,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,aAAa,CAuBnF"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Theme JSON format (OpenCode-compatible)
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// All keys of ResolvedTheme for iteration
|
|
5
|
+
export const RESOLVED_KEYS = [
|
|
6
|
+
"primary",
|
|
7
|
+
"secondary",
|
|
8
|
+
"accent",
|
|
9
|
+
"error",
|
|
10
|
+
"warning",
|
|
11
|
+
"success",
|
|
12
|
+
"info",
|
|
13
|
+
"text",
|
|
14
|
+
"textMuted",
|
|
15
|
+
"background",
|
|
16
|
+
"backgroundPanel",
|
|
17
|
+
"backgroundElement",
|
|
18
|
+
"border",
|
|
19
|
+
"borderActive",
|
|
20
|
+
"borderSubtle",
|
|
21
|
+
"cursorLine",
|
|
22
|
+
"selection",
|
|
23
|
+
"diffAdded",
|
|
24
|
+
"diffRemoved",
|
|
25
|
+
"diffContext",
|
|
26
|
+
"diffHunkHeader",
|
|
27
|
+
"diffHighlightAdded",
|
|
28
|
+
"diffHighlightRemoved",
|
|
29
|
+
"diffAddedBg",
|
|
30
|
+
"diffRemovedBg",
|
|
31
|
+
"diffContextBg",
|
|
32
|
+
"diffLineNumber",
|
|
33
|
+
"diffAddedLineNumberBg",
|
|
34
|
+
"diffRemovedLineNumberBg",
|
|
35
|
+
"markdownText",
|
|
36
|
+
"markdownHeading",
|
|
37
|
+
"markdownLink",
|
|
38
|
+
"markdownLinkText",
|
|
39
|
+
"markdownCode",
|
|
40
|
+
"markdownBlockQuote",
|
|
41
|
+
"markdownEmph",
|
|
42
|
+
"markdownStrong",
|
|
43
|
+
"markdownHorizontalRule",
|
|
44
|
+
"markdownListItem",
|
|
45
|
+
"markdownListEnumeration",
|
|
46
|
+
"markdownImage",
|
|
47
|
+
"markdownImageText",
|
|
48
|
+
"markdownCodeBlock",
|
|
49
|
+
"syntaxComment",
|
|
50
|
+
"syntaxKeyword",
|
|
51
|
+
"syntaxFunction",
|
|
52
|
+
"syntaxVariable",
|
|
53
|
+
"syntaxString",
|
|
54
|
+
"syntaxNumber",
|
|
55
|
+
"syntaxType",
|
|
56
|
+
"syntaxOperator",
|
|
57
|
+
"syntaxPunctuation",
|
|
58
|
+
];
|
|
59
|
+
// Fallbacks used when a theme key is missing
|
|
60
|
+
export const FALLBACKS = {
|
|
61
|
+
primary: "#808080",
|
|
62
|
+
secondary: "#808080",
|
|
63
|
+
accent: "#808080",
|
|
64
|
+
error: "#808080",
|
|
65
|
+
warning: "#808080",
|
|
66
|
+
success: "#808080",
|
|
67
|
+
info: "#808080",
|
|
68
|
+
text: "#d4d4d4",
|
|
69
|
+
textMuted: "#808080",
|
|
70
|
+
background: "#1e1e1e",
|
|
71
|
+
backgroundPanel: "#1e1e1e",
|
|
72
|
+
backgroundElement: "#1e1e1e",
|
|
73
|
+
cursorLine: "#1e1e1e",
|
|
74
|
+
selection: "#1e1e1e",
|
|
75
|
+
border: "#808080",
|
|
76
|
+
borderActive: "#808080",
|
|
77
|
+
borderSubtle: "#808080",
|
|
78
|
+
diffAdded: "#4fd6be",
|
|
79
|
+
diffRemoved: "#c53b53",
|
|
80
|
+
diffContext: "#808080",
|
|
81
|
+
diffHunkHeader: "#808080",
|
|
82
|
+
diffHighlightAdded: "#4fd6be",
|
|
83
|
+
diffHighlightRemoved: "#c53b53",
|
|
84
|
+
diffAddedBg: "#1e3a1e",
|
|
85
|
+
diffRemovedBg: "#3a1e1e",
|
|
86
|
+
diffContextBg: "#1e1e1e",
|
|
87
|
+
diffLineNumber: "#808080",
|
|
88
|
+
diffAddedLineNumberBg: "#1e3a1e",
|
|
89
|
+
diffRemovedLineNumberBg: "#3a1e1e",
|
|
90
|
+
markdownText: "#d4d4d4",
|
|
91
|
+
markdownHeading: "#808080",
|
|
92
|
+
markdownLink: "#808080",
|
|
93
|
+
markdownLinkText: "#808080",
|
|
94
|
+
markdownCode: "#808080",
|
|
95
|
+
markdownBlockQuote: "#808080",
|
|
96
|
+
markdownEmph: "#808080",
|
|
97
|
+
markdownStrong: "#808080",
|
|
98
|
+
markdownHorizontalRule: "#808080",
|
|
99
|
+
markdownListItem: "#808080",
|
|
100
|
+
markdownListEnumeration: "#808080",
|
|
101
|
+
markdownImage: "#808080",
|
|
102
|
+
markdownImageText: "#808080",
|
|
103
|
+
markdownCodeBlock: "#d4d4d4",
|
|
104
|
+
syntaxComment: "#808080",
|
|
105
|
+
syntaxKeyword: "#808080",
|
|
106
|
+
syntaxFunction: "#808080",
|
|
107
|
+
syntaxVariable: "#808080",
|
|
108
|
+
syntaxString: "#808080",
|
|
109
|
+
syntaxNumber: "#808080",
|
|
110
|
+
syntaxType: "#808080",
|
|
111
|
+
syntaxOperator: "#808080",
|
|
112
|
+
syntaxPunctuation: "#808080",
|
|
113
|
+
};
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Resolution
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
export function resolveTheme(json, mode) {
|
|
118
|
+
const defs = json.defs ?? {};
|
|
119
|
+
function resolveColor(c) {
|
|
120
|
+
if (typeof c === "string") {
|
|
121
|
+
if (c === "transparent" || c === "none")
|
|
122
|
+
return "#00000000";
|
|
123
|
+
if (c.startsWith("#"))
|
|
124
|
+
return c;
|
|
125
|
+
if (defs[c] != null)
|
|
126
|
+
return resolveColor(defs[c]);
|
|
127
|
+
if (json.theme[c] !== undefined)
|
|
128
|
+
return resolveColor(json.theme[c]);
|
|
129
|
+
return "#808080";
|
|
130
|
+
}
|
|
131
|
+
return resolveColor(c[mode]);
|
|
132
|
+
}
|
|
133
|
+
const result = {};
|
|
134
|
+
for (const key of RESOLVED_KEYS) {
|
|
135
|
+
const val = json.theme[key];
|
|
136
|
+
result[key] = val !== undefined ? resolveColor(val) : (FALLBACKS[key] ?? "#808080");
|
|
137
|
+
}
|
|
138
|
+
// Dynamic fallbacks that reference other resolved keys
|
|
139
|
+
if (json.theme["cursorLine"] === undefined)
|
|
140
|
+
result.cursorLine = result.backgroundElement;
|
|
141
|
+
if (json.theme["selection"] === undefined)
|
|
142
|
+
result.selection = result.backgroundPanel;
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,0CAA0C;AAC1C,8EAA8E;AA6E9E,0CAA0C;AAC1C,MAAM,CAAC,MAAM,aAAa,GAA4B;IACpD,SAAS;IACT,WAAW;IACX,QAAQ;IACR,OAAO;IACP,SAAS;IACT,SAAS;IACT,MAAM;IACN,MAAM;IACN,WAAW;IACX,YAAY;IACZ,iBAAiB;IACjB,mBAAmB;IACnB,QAAQ;IACR,cAAc;IACd,cAAc;IACd,YAAY;IACZ,WAAW;IACX,WAAW;IACX,aAAa;IACb,aAAa;IACb,gBAAgB;IAChB,oBAAoB;IACpB,sBAAsB;IACtB,aAAa;IACb,eAAe;IACf,eAAe;IACf,gBAAgB;IAChB,uBAAuB;IACvB,yBAAyB;IACzB,cAAc;IACd,iBAAiB;IACjB,cAAc;IACd,kBAAkB;IAClB,cAAc;IACd,oBAAoB;IACpB,cAAc;IACd,gBAAgB;IAChB,wBAAwB;IACxB,kBAAkB;IAClB,yBAAyB;IACzB,eAAe;IACf,mBAAmB;IACnB,mBAAmB;IACnB,eAAe;IACf,eAAe;IACf,gBAAgB;IAChB,gBAAgB;IAChB,cAAc;IACd,cAAc;IACd,YAAY;IACZ,gBAAgB;IAChB,mBAAmB;CACpB,CAAA;AAED,6CAA6C;AAC7C,MAAM,CAAC,MAAM,SAAS,GAA2B;IAC/C,OAAO,EAAE,SAAS;IAClB,SAAS,EAAE,SAAS;IACpB,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,SAAS;IACf,SAAS,EAAE,SAAS;IACpB,UAAU,EAAE,SAAS;IACrB,eAAe,EAAE,SAAS;IAC1B,iBAAiB,EAAE,SAAS;IAC5B,UAAU,EAAE,SAAS;IACrB,SAAS,EAAE,SAAS;IACpB,MAAM,EAAE,SAAS;IACjB,YAAY,EAAE,SAAS;IACvB,YAAY,EAAE,SAAS;IACvB,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,SAAS;IACtB,WAAW,EAAE,SAAS;IACtB,cAAc,EAAE,SAAS;IACzB,kBAAkB,EAAE,SAAS;IAC7B,oBAAoB,EAAE,SAAS;IAC/B,WAAW,EAAE,SAAS;IACtB,aAAa,EAAE,SAAS;IACxB,aAAa,EAAE,SAAS;IACxB,cAAc,EAAE,SAAS;IACzB,qBAAqB,EAAE,SAAS;IAChC,uBAAuB,EAAE,SAAS;IAClC,YAAY,EAAE,SAAS;IACvB,eAAe,EAAE,SAAS;IAC1B,YAAY,EAAE,SAAS;IACvB,gBAAgB,EAAE,SAAS;IAC3B,YAAY,EAAE,SAAS;IACvB,kBAAkB,EAAE,SAAS;IAC7B,YAAY,EAAE,SAAS;IACvB,cAAc,EAAE,SAAS;IACzB,sBAAsB,EAAE,SAAS;IACjC,gBAAgB,EAAE,SAAS;IAC3B,uBAAuB,EAAE,SAAS;IAClC,aAAa,EAAE,SAAS;IACxB,iBAAiB,EAAE,SAAS;IAC5B,iBAAiB,EAAE,SAAS;IAC5B,aAAa,EAAE,SAAS;IACxB,aAAa,EAAE,SAAS;IACxB,cAAc,EAAE,SAAS;IACzB,cAAc,EAAE,SAAS;IACzB,YAAY,EAAE,SAAS;IACvB,YAAY,EAAE,SAAS;IACvB,UAAU,EAAE,SAAS;IACrB,cAAc,EAAE,SAAS;IACzB,iBAAiB,EAAE,SAAS;CAC7B,CAAA;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,MAAM,UAAU,YAAY,CAAC,IAAe,EAAE,IAAsB;IAClE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAA;IAE5B,SAAS,YAAY,CAAC,CAAa;QACjC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,KAAK,MAAM;gBAAE,OAAO,WAAW,CAAA;YAC3D,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,CAAA;YAC/B,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI;gBAAE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,CAAe,CAAC,CAAA;YAC/D,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS;gBAAE,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAe,CAAC,CAAA;YACjF,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,OAAO,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,MAAM,GAAG,EAA4B,CAAA;IAC3C,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC3B,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,CAAA;IACrF,CAAC;IACD,uDAAuD;IACvD,IAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,SAAS;QAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,iBAAiB,CAAA;IACxF,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,SAAS;QAAE,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,eAAe,CAAA;IACpF,OAAO,MAAkC,CAAA;AAC3C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tooee/themes",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "Theme management with 34 bundled themes for Tooee",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"colors",
|
|
8
|
+
"opentui",
|
|
9
|
+
"terminal",
|
|
10
|
+
"themes",
|
|
11
|
+
"tui"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/gingerhendrix/tooee",
|
|
14
|
+
"bugs": "https://github.com/gingerhendrix/tooee/issues",
|
|
5
15
|
"license": "MIT",
|
|
6
16
|
"author": "Gareth Andrew",
|
|
7
17
|
"repository": {
|
|
@@ -9,15 +19,10 @@
|
|
|
9
19
|
"url": "https://github.com/gingerhendrix/tooee.git",
|
|
10
20
|
"directory": "packages/themes"
|
|
11
21
|
},
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"terminal",
|
|
17
|
-
"cli",
|
|
18
|
-
"opentui",
|
|
19
|
-
"themes",
|
|
20
|
-
"colors"
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src",
|
|
25
|
+
"themes"
|
|
21
26
|
],
|
|
22
27
|
"type": "module",
|
|
23
28
|
"exports": {
|
|
@@ -28,16 +33,12 @@
|
|
|
28
33
|
}
|
|
29
34
|
}
|
|
30
35
|
},
|
|
31
|
-
"files": [
|
|
32
|
-
"dist",
|
|
33
|
-
"src",
|
|
34
|
-
"themes"
|
|
35
|
-
],
|
|
36
36
|
"scripts": {
|
|
37
37
|
"typecheck": "tsc --noEmit"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@tooee/config": "0.1.
|
|
40
|
+
"@tooee/config": "0.1.14",
|
|
41
|
+
"@tooee/fuzzy": "0.1.14"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"@opentui/core": "^0.1.86",
|
package/src/ThemePicker.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState, useMemo, useCallback } from "react"
|
|
2
2
|
import { useKeyboard } from "@opentui/react"
|
|
3
|
-
import {
|
|
3
|
+
import { fuzzyMatch } from "@tooee/fuzzy"
|
|
4
|
+
import { useTheme } from "./context.js"
|
|
4
5
|
|
|
5
6
|
export interface ThemePickerEntry {
|
|
6
7
|
id: string
|
|
@@ -15,28 +16,6 @@ interface ThemePickerProps {
|
|
|
15
16
|
onNavigate: (name: string) => void
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
function fuzzyMatch(query: string, text: string): number | null {
|
|
19
|
-
const lowerQuery = query.toLowerCase()
|
|
20
|
-
const lowerText = text.toLowerCase()
|
|
21
|
-
|
|
22
|
-
let qi = 0
|
|
23
|
-
let score = 0
|
|
24
|
-
let lastMatchIndex = -2
|
|
25
|
-
|
|
26
|
-
for (let ti = 0; ti < lowerText.length && qi < lowerQuery.length; ti++) {
|
|
27
|
-
if (lowerText[ti] === lowerQuery[qi]) {
|
|
28
|
-
if (ti === 0) score += 3
|
|
29
|
-
else if (" -./".includes(lowerText[ti - 1]!)) score += 2
|
|
30
|
-
else if (ti === lastMatchIndex + 1) score += 1
|
|
31
|
-
|
|
32
|
-
lastMatchIndex = ti
|
|
33
|
-
qi++
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return qi === lowerQuery.length ? score : null
|
|
38
|
-
}
|
|
39
|
-
|
|
40
19
|
export function ThemePicker({
|
|
41
20
|
entries,
|
|
42
21
|
currentTheme,
|
package/src/context.tsx
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { createContext, useContext, useState, useCallback, type ReactNode } from "react"
|
|
2
|
+
import { type SyntaxStyle } from "@opentui/core"
|
|
3
|
+
import { writeGlobalConfig } from "@tooee/config"
|
|
4
|
+
import type { ResolvedTheme } from "./types.js"
|
|
5
|
+
import {
|
|
6
|
+
type Theme,
|
|
7
|
+
buildTheme,
|
|
8
|
+
getThemeNames,
|
|
9
|
+
defaultTheme,
|
|
10
|
+
DEFAULT_THEME_NAME,
|
|
11
|
+
DEFAULT_MODE,
|
|
12
|
+
} from "./loader.js"
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Context
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
interface ThemeContextValue {
|
|
19
|
+
theme: ResolvedTheme
|
|
20
|
+
syntax: SyntaxStyle
|
|
21
|
+
name: string
|
|
22
|
+
mode: "dark" | "light"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ThemeContext = createContext<ThemeContextValue>({
|
|
26
|
+
theme: defaultTheme.colors,
|
|
27
|
+
syntax: defaultTheme.syntax,
|
|
28
|
+
name: defaultTheme.name,
|
|
29
|
+
mode: defaultTheme.mode,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
export interface ThemeProviderProps {
|
|
33
|
+
/** Theme name (e.g. "tokyonight", "catppuccin", "dracula") */
|
|
34
|
+
name?: string
|
|
35
|
+
/** Color mode */
|
|
36
|
+
mode?: "dark" | "light"
|
|
37
|
+
/** Full Theme object (overrides name/mode if provided) */
|
|
38
|
+
theme?: Theme
|
|
39
|
+
children: ReactNode
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function ThemeProvider({ name, mode, theme: themeProp, children }: ThemeProviderProps) {
|
|
43
|
+
const resolved = themeProp
|
|
44
|
+
? {
|
|
45
|
+
theme: themeProp.colors,
|
|
46
|
+
syntax: themeProp.syntax,
|
|
47
|
+
name: themeProp.name,
|
|
48
|
+
mode: themeProp.mode,
|
|
49
|
+
}
|
|
50
|
+
: (() => {
|
|
51
|
+
const t = buildTheme(name ?? DEFAULT_THEME_NAME, mode ?? DEFAULT_MODE)
|
|
52
|
+
return { theme: t.colors, syntax: t.syntax, name: t.name, mode: t.mode }
|
|
53
|
+
})()
|
|
54
|
+
|
|
55
|
+
return <ThemeContext.Provider value={resolved}>{children}</ThemeContext.Provider>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function useTheme(): ThemeContextValue {
|
|
59
|
+
return useContext(ThemeContext)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// ThemeSwitcherProvider + useThemeSwitcher
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
interface ThemeSwitcherContextValue extends ThemeContextValue {
|
|
67
|
+
nextTheme: () => void
|
|
68
|
+
prevTheme: () => void
|
|
69
|
+
setTheme: (name: string, opts?: { persist?: boolean }) => void
|
|
70
|
+
allThemes: string[]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const ThemeSwitcherContext = createContext<ThemeSwitcherContextValue | null>(null)
|
|
74
|
+
|
|
75
|
+
export interface ThemeSwitcherProviderProps {
|
|
76
|
+
initialTheme?: string
|
|
77
|
+
initialMode?: "dark" | "light"
|
|
78
|
+
children: ReactNode
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function ThemeSwitcherProvider({
|
|
82
|
+
initialTheme,
|
|
83
|
+
initialMode,
|
|
84
|
+
children,
|
|
85
|
+
}: ThemeSwitcherProviderProps) {
|
|
86
|
+
const allThemes = getThemeNames()
|
|
87
|
+
const [themeName, setThemeName] = useState(initialTheme ?? DEFAULT_THEME_NAME)
|
|
88
|
+
const [mode, _setMode] = useState<"dark" | "light">(initialMode ?? DEFAULT_MODE)
|
|
89
|
+
|
|
90
|
+
const theme = buildTheme(themeName, mode)
|
|
91
|
+
|
|
92
|
+
const nextTheme = useCallback(() => {
|
|
93
|
+
const idx = allThemes.indexOf(themeName)
|
|
94
|
+
const next = allThemes[(idx + 1) % allThemes.length]
|
|
95
|
+
setThemeName(next)
|
|
96
|
+
writeGlobalConfig({ theme: { name: next, mode } })
|
|
97
|
+
}, [allThemes, mode, themeName])
|
|
98
|
+
|
|
99
|
+
const prevTheme = useCallback(() => {
|
|
100
|
+
const idx = allThemes.indexOf(themeName)
|
|
101
|
+
const prev = allThemes[(idx - 1 + allThemes.length) % allThemes.length]
|
|
102
|
+
setThemeName(prev)
|
|
103
|
+
writeGlobalConfig({ theme: { name: prev, mode } })
|
|
104
|
+
}, [allThemes, mode, themeName])
|
|
105
|
+
|
|
106
|
+
const setThemeByName = useCallback(
|
|
107
|
+
(name: string, opts?: { persist?: boolean }) => {
|
|
108
|
+
setThemeName(name)
|
|
109
|
+
if (opts?.persist) {
|
|
110
|
+
writeGlobalConfig({ theme: { name, mode } })
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
[mode],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const value: ThemeSwitcherContextValue = {
|
|
117
|
+
theme: theme.colors,
|
|
118
|
+
syntax: theme.syntax,
|
|
119
|
+
name: theme.name,
|
|
120
|
+
mode,
|
|
121
|
+
nextTheme,
|
|
122
|
+
prevTheme,
|
|
123
|
+
setTheme: setThemeByName,
|
|
124
|
+
allThemes,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<ThemeSwitcherContext.Provider value={value}>
|
|
129
|
+
<ThemeContext.Provider
|
|
130
|
+
value={{ theme: theme.colors, syntax: theme.syntax, name: theme.name, mode }}
|
|
131
|
+
>
|
|
132
|
+
{children}
|
|
133
|
+
</ThemeContext.Provider>
|
|
134
|
+
</ThemeSwitcherContext.Provider>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function useThemeSwitcher(): ThemeSwitcherContextValue {
|
|
139
|
+
const ctx = useContext(ThemeSwitcherContext)
|
|
140
|
+
if (!ctx) throw new Error("useThemeSwitcher must be used within ThemeSwitcherProvider")
|
|
141
|
+
return ctx
|
|
142
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from "./theme.js"
|
|
12
|
-
export type {
|
|
13
|
-
Theme,
|
|
14
|
-
ThemeJSON,
|
|
15
|
-
ResolvedTheme,
|
|
16
|
-
ThemeProviderProps,
|
|
17
|
-
ThemeSwitcherProviderProps,
|
|
18
|
-
} from "./theme.js"
|
|
1
|
+
export { resolveTheme } from "./types.js"
|
|
2
|
+
export type { ThemeJSON, ResolvedTheme } from "./types.js"
|
|
3
|
+
|
|
4
|
+
export { buildSyntaxStyle } from "./syntax-rules.js"
|
|
5
|
+
|
|
6
|
+
export { loadThemes, getThemeNames, defaultTheme } from "./loader.js"
|
|
7
|
+
export type { Theme } from "./loader.js"
|
|
8
|
+
|
|
9
|
+
export { ThemeProvider, ThemeSwitcherProvider, useTheme, useThemeSwitcher } from "./context.js"
|
|
10
|
+
export type { ThemeProviderProps, ThemeSwitcherProviderProps } from "./context.js"
|
|
19
11
|
|
|
20
12
|
export { ThemePicker } from "./ThemePicker.js"
|
|
21
13
|
export type { ThemePickerEntry } from "./ThemePicker.js"
|
package/src/loader.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { type SyntaxStyle } from "@opentui/core"
|
|
2
|
+
import { readFileSync, readdirSync, existsSync } from "fs"
|
|
3
|
+
import { join, basename, dirname } from "path"
|
|
4
|
+
import { type ThemeJSON, type ResolvedTheme, resolveTheme } from "./types.js"
|
|
5
|
+
import { buildSyntaxStyle } from "./syntax-rules.js"
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Theme loading
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
export interface Theme {
|
|
12
|
+
name: string
|
|
13
|
+
mode: "dark" | "light"
|
|
14
|
+
colors: ResolvedTheme
|
|
15
|
+
syntax: SyntaxStyle
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Cache of loaded theme JSONs by name */
|
|
19
|
+
const themeJsonCache = new Map<string, ThemeJSON>()
|
|
20
|
+
|
|
21
|
+
function loadJsonThemesFromDir(dir: string, target: Map<string, ThemeJSON>) {
|
|
22
|
+
try {
|
|
23
|
+
if (!existsSync(dir)) return
|
|
24
|
+
for (const file of readdirSync(dir)) {
|
|
25
|
+
if (!file.endsWith(".json")) continue
|
|
26
|
+
const name = basename(file, ".json")
|
|
27
|
+
try {
|
|
28
|
+
const content = readFileSync(join(dir, file), "utf-8")
|
|
29
|
+
target.set(name, JSON.parse(content) as ThemeJSON)
|
|
30
|
+
} catch {
|
|
31
|
+
// skip invalid files
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// dir not readable
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Load all bundled themes from packages/themes/themes/ */
|
|
40
|
+
function loadBundledThemes(): Map<string, ThemeJSON> {
|
|
41
|
+
if (themeJsonCache.size > 0) return themeJsonCache
|
|
42
|
+
|
|
43
|
+
// Bundled themes
|
|
44
|
+
const bundledDir = join(dirname(new URL(import.meta.url).pathname), "..", "themes")
|
|
45
|
+
loadJsonThemesFromDir(bundledDir, themeJsonCache)
|
|
46
|
+
|
|
47
|
+
// XDG config: ~/.config/tooee/themes/
|
|
48
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join(process.env.HOME ?? "", ".config")
|
|
49
|
+
loadJsonThemesFromDir(join(xdgConfig, "tooee", "themes"), themeJsonCache)
|
|
50
|
+
|
|
51
|
+
// Project-local: search upward for .tooee/themes/
|
|
52
|
+
let dir = process.cwd()
|
|
53
|
+
const seen = new Set<string>()
|
|
54
|
+
while (dir && !seen.has(dir)) {
|
|
55
|
+
seen.add(dir)
|
|
56
|
+
loadJsonThemesFromDir(join(dir, ".tooee", "themes"), themeJsonCache)
|
|
57
|
+
const parent = dirname(dir)
|
|
58
|
+
if (parent === dir) break
|
|
59
|
+
dir = parent
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return themeJsonCache
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function loadThemes(): Map<string, ThemeJSON> {
|
|
66
|
+
return loadBundledThemes()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getThemeNames(): string[] {
|
|
70
|
+
return Array.from(loadThemes().keys()).sort()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Default theme
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
export const DEFAULT_THEME_NAME = "tokyonight"
|
|
78
|
+
export const DEFAULT_MODE: "dark" | "light" = "dark"
|
|
79
|
+
|
|
80
|
+
export function buildTheme(name: string, mode: "dark" | "light"): Theme {
|
|
81
|
+
const themes = loadThemes()
|
|
82
|
+
const json = themes.get(name)
|
|
83
|
+
if (!json) {
|
|
84
|
+
// Fall back to tokyonight, then first available, then hardcoded
|
|
85
|
+
const fallbackJson = themes.get(DEFAULT_THEME_NAME) ?? themes.values().next().value
|
|
86
|
+
if (fallbackJson) {
|
|
87
|
+
const resolved = resolveTheme(fallbackJson, mode)
|
|
88
|
+
return { name, mode, colors: resolved, syntax: buildSyntaxStyle(resolved) }
|
|
89
|
+
}
|
|
90
|
+
// Absolute fallback — hardcoded Tokyo Night colors
|
|
91
|
+
return hardcodedDefaultTheme
|
|
92
|
+
}
|
|
93
|
+
const resolved = resolveTheme(json, mode)
|
|
94
|
+
return { name, mode, colors: resolved, syntax: buildSyntaxStyle(resolved) }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const hardcodedDefaultTheme: Theme = (() => {
|
|
98
|
+
const colors: ResolvedTheme = {
|
|
99
|
+
primary: "#7aa2f7",
|
|
100
|
+
secondary: "#bb9af7",
|
|
101
|
+
accent: "#7dcfff",
|
|
102
|
+
error: "#f7768e",
|
|
103
|
+
warning: "#e0af68",
|
|
104
|
+
success: "#9ece6a",
|
|
105
|
+
info: "#7aa2f7",
|
|
106
|
+
text: "#c0caf5",
|
|
107
|
+
textMuted: "#565f89",
|
|
108
|
+
background: "#1a1b26",
|
|
109
|
+
backgroundPanel: "#1e2030",
|
|
110
|
+
backgroundElement: "#222436",
|
|
111
|
+
cursorLine: "#222436",
|
|
112
|
+
selection: "#1e2030",
|
|
113
|
+
border: "#565f89",
|
|
114
|
+
borderActive: "#737aa2",
|
|
115
|
+
borderSubtle: "#414868",
|
|
116
|
+
diffAdded: "#4fd6be",
|
|
117
|
+
diffRemoved: "#c53b53",
|
|
118
|
+
diffContext: "#828bb8",
|
|
119
|
+
diffHunkHeader: "#828bb8",
|
|
120
|
+
diffHighlightAdded: "#b8db87",
|
|
121
|
+
diffHighlightRemoved: "#e26a75",
|
|
122
|
+
diffAddedBg: "#20303b",
|
|
123
|
+
diffRemovedBg: "#37222c",
|
|
124
|
+
diffContextBg: "#1e2030",
|
|
125
|
+
diffLineNumber: "#222436",
|
|
126
|
+
diffAddedLineNumberBg: "#1b2b34",
|
|
127
|
+
diffRemovedLineNumberBg: "#2d1f26",
|
|
128
|
+
markdownText: "#c0caf5",
|
|
129
|
+
markdownHeading: "#bb9af7",
|
|
130
|
+
markdownLink: "#7aa2f7",
|
|
131
|
+
markdownLinkText: "#7dcfff",
|
|
132
|
+
markdownCode: "#9ece6a",
|
|
133
|
+
markdownBlockQuote: "#e0af68",
|
|
134
|
+
markdownEmph: "#e0af68",
|
|
135
|
+
markdownStrong: "#ff966c",
|
|
136
|
+
markdownHorizontalRule: "#565f89",
|
|
137
|
+
markdownListItem: "#7aa2f7",
|
|
138
|
+
markdownListEnumeration: "#7dcfff",
|
|
139
|
+
markdownImage: "#7aa2f7",
|
|
140
|
+
markdownImageText: "#7dcfff",
|
|
141
|
+
markdownCodeBlock: "#c0caf5",
|
|
142
|
+
syntaxComment: "#565f89",
|
|
143
|
+
syntaxKeyword: "#bb9af7",
|
|
144
|
+
syntaxFunction: "#7aa2f7",
|
|
145
|
+
syntaxVariable: "#c0caf5",
|
|
146
|
+
syntaxString: "#9ece6a",
|
|
147
|
+
syntaxNumber: "#ff9e64",
|
|
148
|
+
syntaxType: "#2ac3de",
|
|
149
|
+
syntaxOperator: "#89ddff",
|
|
150
|
+
syntaxPunctuation: "#a9b1d6",
|
|
151
|
+
}
|
|
152
|
+
return { name: DEFAULT_THEME_NAME, mode: DEFAULT_MODE, colors, syntax: buildSyntaxStyle(colors) }
|
|
153
|
+
})()
|
|
154
|
+
|
|
155
|
+
export const defaultTheme: Theme = hardcodedDefaultTheme
|