@tooee/themes 0.2.3 → 0.3.0
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/{CloseButton.d.ts → close-button.d.ts} +2 -2
- package/dist/close-button.d.ts.map +1 -0
- package/dist/{CloseButton.js → close-button.js} +5 -4
- package/dist/close-button.js.map +1 -0
- package/dist/context.d.ts +7 -7
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +28 -26
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +5 -5
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +80 -74
- package/dist/loader.js.map +1 -1
- package/dist/syntax-rules.d.ts +1 -1
- package/dist/syntax-rules.d.ts.map +1 -1
- package/dist/syntax-rules.js +17 -17
- package/dist/syntax-rules.js.map +1 -1
- package/dist/{ThemePicker.d.ts → theme-picker.d.ts} +2 -2
- package/dist/theme-picker.d.ts.map +1 -0
- package/dist/{ThemePicker.js → theme-picker.js} +37 -33
- package/dist/theme-picker.js.map +1 -0
- package/dist/types.d.ts +7 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +52 -43
- package/dist/types.js.map +1 -1
- package/package.json +4 -4
- package/src/{CloseButton.tsx → close-button.tsx} +21 -15
- package/src/context.tsx +83 -74
- package/src/index.ts +10 -10
- package/src/loader.ts +109 -95
- package/src/syntax-rules.ts +21 -21
- package/src/theme-picker.tsx +189 -0
- package/src/types.ts +129 -110
- package/dist/CloseButton.d.ts.map +0 -1
- package/dist/CloseButton.js.map +0 -1
- package/dist/ThemePicker.d.ts.map +0 -1
- package/dist/ThemePicker.js.map +0 -1
- package/src/ThemePicker.tsx +0 -177
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { useState, useMemo, useCallback } from "react";
|
|
2
|
+
import { useCommand } from "@tooee/commands";
|
|
3
|
+
import { fuzzyMatch } from "@tooee/fuzzy";
|
|
4
|
+
import { useTheme } from "./context.js";
|
|
5
|
+
import { CloseButton } from "./close-button.js";
|
|
6
|
+
|
|
7
|
+
export interface ThemePickerEntry {
|
|
8
|
+
id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ThemePickerProps {
|
|
13
|
+
entries: ThemePickerEntry[];
|
|
14
|
+
currentTheme: string;
|
|
15
|
+
onSelect: (name: string) => void;
|
|
16
|
+
onClose: () => void;
|
|
17
|
+
onNavigate: (name: string) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const ThemePicker = function ThemePicker({
|
|
21
|
+
entries,
|
|
22
|
+
currentTheme,
|
|
23
|
+
onSelect,
|
|
24
|
+
onClose,
|
|
25
|
+
onNavigate,
|
|
26
|
+
}: ThemePickerProps): React.ReactNode {
|
|
27
|
+
const { theme } = useTheme();
|
|
28
|
+
const [filter, setFilter] = useState("");
|
|
29
|
+
const [activeIndex, setActiveIndex] = useState(() => {
|
|
30
|
+
const idx = entries.findIndex((e) => e.id === currentTheme);
|
|
31
|
+
return Math.max(idx, 0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const filtered = useMemo(() => {
|
|
35
|
+
if (!filter) {
|
|
36
|
+
return entries;
|
|
37
|
+
}
|
|
38
|
+
const results: { entry: ThemePickerEntry; score: number }[] = [];
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
const score = fuzzyMatch(filter, entry.title);
|
|
41
|
+
if (score !== null) {
|
|
42
|
+
results.push({ entry, score });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
results.sort((a, b) => b.score - a.score);
|
|
46
|
+
return results.map((r) => r.entry);
|
|
47
|
+
}, [entries, filter]);
|
|
48
|
+
|
|
49
|
+
const handleSelect = useCallback(() => {
|
|
50
|
+
const item = filtered[activeIndex];
|
|
51
|
+
if (item !== undefined) {
|
|
52
|
+
onSelect(item.id);
|
|
53
|
+
}
|
|
54
|
+
}, [filtered, activeIndex, onSelect]);
|
|
55
|
+
|
|
56
|
+
const navigateTo = useCallback(
|
|
57
|
+
(index: number) => {
|
|
58
|
+
setActiveIndex(index);
|
|
59
|
+
const item = filtered[index];
|
|
60
|
+
if (item !== undefined) {
|
|
61
|
+
onNavigate(item.id);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[filtered, onNavigate],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const moveUp = useCallback(() => {
|
|
68
|
+
navigateTo(Math.max(0, activeIndex - 1));
|
|
69
|
+
}, [activeIndex, navigateTo]);
|
|
70
|
+
|
|
71
|
+
const moveDown = useCallback(() => {
|
|
72
|
+
navigateTo(Math.min(filtered.length - 1, activeIndex + 1));
|
|
73
|
+
}, [activeIndex, filtered.length, navigateTo]);
|
|
74
|
+
|
|
75
|
+
useCommand({
|
|
76
|
+
handler: onClose,
|
|
77
|
+
hidden: true,
|
|
78
|
+
hotkey: "Escape",
|
|
79
|
+
id: "theme-picker:close",
|
|
80
|
+
modes: ["insert", "cursor"],
|
|
81
|
+
title: "Close theme picker",
|
|
82
|
+
});
|
|
83
|
+
useCommand({
|
|
84
|
+
handler: handleSelect,
|
|
85
|
+
hidden: true,
|
|
86
|
+
hotkey: "Enter",
|
|
87
|
+
id: "theme-picker:select",
|
|
88
|
+
modes: ["insert", "cursor"],
|
|
89
|
+
title: "Select theme",
|
|
90
|
+
});
|
|
91
|
+
useCommand({
|
|
92
|
+
handler: moveUp,
|
|
93
|
+
hidden: true,
|
|
94
|
+
hotkey: "up",
|
|
95
|
+
id: "theme-picker:move-up",
|
|
96
|
+
modes: ["insert", "cursor"],
|
|
97
|
+
title: "Move up",
|
|
98
|
+
});
|
|
99
|
+
useCommand({
|
|
100
|
+
handler: moveDown,
|
|
101
|
+
hidden: true,
|
|
102
|
+
hotkey: "down",
|
|
103
|
+
id: "theme-picker:move-down",
|
|
104
|
+
modes: ["insert", "cursor"],
|
|
105
|
+
title: "Move down",
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<box
|
|
110
|
+
position="absolute"
|
|
111
|
+
left="20%"
|
|
112
|
+
right="20%"
|
|
113
|
+
top={2}
|
|
114
|
+
maxHeight="60%"
|
|
115
|
+
flexDirection="column"
|
|
116
|
+
backgroundColor={theme.backgroundPanel}
|
|
117
|
+
border
|
|
118
|
+
borderColor={theme.border}
|
|
119
|
+
>
|
|
120
|
+
{/* Filter row */}
|
|
121
|
+
<box flexDirection="row" paddingLeft={1} paddingRight={1} height={1}>
|
|
122
|
+
<text content="🎨 " fg={theme.accent} />
|
|
123
|
+
<input
|
|
124
|
+
focused
|
|
125
|
+
placeholder="Filter themes..."
|
|
126
|
+
onSubmit={handleSelect}
|
|
127
|
+
onInput={(value: string) => {
|
|
128
|
+
setFilter(value);
|
|
129
|
+
setActiveIndex(0);
|
|
130
|
+
// Preview first match
|
|
131
|
+
if (value) {
|
|
132
|
+
const results: { entry: ThemePickerEntry; score: number }[] = [];
|
|
133
|
+
for (const entry of entries) {
|
|
134
|
+
const score = fuzzyMatch(value, entry.title);
|
|
135
|
+
if (score !== null) {
|
|
136
|
+
results.push({ entry, score });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
results.sort((a, b) => b.score - a.score);
|
|
140
|
+
if (results.length > 0) {
|
|
141
|
+
onNavigate(results[0].entry.id);
|
|
142
|
+
}
|
|
143
|
+
} else if (entries.length > 0) {
|
|
144
|
+
onNavigate(entries[0].id);
|
|
145
|
+
}
|
|
146
|
+
}}
|
|
147
|
+
backgroundColor="transparent"
|
|
148
|
+
focusedBackgroundColor="transparent"
|
|
149
|
+
textColor={theme.text}
|
|
150
|
+
placeholderColor={theme.textMuted}
|
|
151
|
+
cursorColor={theme.accent}
|
|
152
|
+
style={{ flexGrow: 1 }}
|
|
153
|
+
/>
|
|
154
|
+
<text content={` ${filtered.length}`} fg={theme.textMuted} />
|
|
155
|
+
<CloseButton onClose={onClose} />
|
|
156
|
+
</box>
|
|
157
|
+
|
|
158
|
+
{/* Separator */}
|
|
159
|
+
<box height={1} width="100%" backgroundColor={theme.border} />
|
|
160
|
+
|
|
161
|
+
{/* Theme list */}
|
|
162
|
+
<scrollbox focused={false} style={{ flexGrow: 1 }}>
|
|
163
|
+
{filtered.map(
|
|
164
|
+
(entry, i): React.ReactNode => (
|
|
165
|
+
<box
|
|
166
|
+
key={entry.id}
|
|
167
|
+
flexDirection="row"
|
|
168
|
+
paddingLeft={1}
|
|
169
|
+
paddingRight={1}
|
|
170
|
+
height={1}
|
|
171
|
+
backgroundColor={i === activeIndex ? theme.backgroundElement : undefined}
|
|
172
|
+
onMouseDown={(event) => {
|
|
173
|
+
// Left-click applies the theme — same code path as Enter.
|
|
174
|
+
if (event.button !== 0) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
event.preventDefault();
|
|
178
|
+
event.stopPropagation();
|
|
179
|
+
onSelect(entry.id);
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
<text content={entry.title} fg={theme.text} style={{ flexGrow: 1 }} />
|
|
183
|
+
</box>
|
|
184
|
+
),
|
|
185
|
+
)}
|
|
186
|
+
</scrollbox>
|
|
187
|
+
</box>
|
|
188
|
+
);
|
|
189
|
+
};
|
package/src/types.ts
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
// Theme JSON format (OpenCode-compatible)
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
interface Variant {
|
|
6
|
+
dark: string;
|
|
7
|
+
light: string;
|
|
8
|
+
}
|
|
9
|
+
type ColorValue = string | Variant;
|
|
9
10
|
|
|
10
11
|
export interface ThemeJSON {
|
|
11
|
-
$schema?: string
|
|
12
|
-
defs?: Record<string,
|
|
13
|
-
theme: Record<string, ColorValue
|
|
12
|
+
$schema?: string;
|
|
13
|
+
defs?: Record<string, string>;
|
|
14
|
+
theme: Record<string, ColorValue>;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
@@ -19,62 +20,62 @@ export interface ThemeJSON {
|
|
|
19
20
|
|
|
20
21
|
export interface ResolvedTheme {
|
|
21
22
|
// UI
|
|
22
|
-
primary: string
|
|
23
|
-
secondary: string
|
|
24
|
-
accent: string
|
|
25
|
-
error: string
|
|
26
|
-
warning: string
|
|
27
|
-
success: string
|
|
28
|
-
info: string
|
|
29
|
-
text: string
|
|
30
|
-
textMuted: string
|
|
31
|
-
background: string
|
|
32
|
-
backgroundPanel: string
|
|
33
|
-
backgroundElement: string
|
|
34
|
-
border: string
|
|
35
|
-
borderActive: string
|
|
36
|
-
borderSubtle: string
|
|
23
|
+
primary: string;
|
|
24
|
+
secondary: string;
|
|
25
|
+
accent: string;
|
|
26
|
+
error: string;
|
|
27
|
+
warning: string;
|
|
28
|
+
success: string;
|
|
29
|
+
info: string;
|
|
30
|
+
text: string;
|
|
31
|
+
textMuted: string;
|
|
32
|
+
background: string;
|
|
33
|
+
backgroundPanel: string;
|
|
34
|
+
backgroundElement: string;
|
|
35
|
+
border: string;
|
|
36
|
+
borderActive: string;
|
|
37
|
+
borderSubtle: string;
|
|
37
38
|
// Diff
|
|
38
|
-
diffAdded: string
|
|
39
|
-
diffRemoved: string
|
|
40
|
-
diffContext: string
|
|
41
|
-
diffHunkHeader: string
|
|
42
|
-
diffHighlightAdded: string
|
|
43
|
-
diffHighlightRemoved: string
|
|
44
|
-
diffAddedBg: string
|
|
45
|
-
diffRemovedBg: string
|
|
46
|
-
diffContextBg: string
|
|
47
|
-
diffLineNumber: string
|
|
48
|
-
diffAddedLineNumberBg: string
|
|
49
|
-
diffRemovedLineNumberBg: string
|
|
39
|
+
diffAdded: string;
|
|
40
|
+
diffRemoved: string;
|
|
41
|
+
diffContext: string;
|
|
42
|
+
diffHunkHeader: string;
|
|
43
|
+
diffHighlightAdded: string;
|
|
44
|
+
diffHighlightRemoved: string;
|
|
45
|
+
diffAddedBg: string;
|
|
46
|
+
diffRemovedBg: string;
|
|
47
|
+
diffContextBg: string;
|
|
48
|
+
diffLineNumber: string;
|
|
49
|
+
diffAddedLineNumberBg: string;
|
|
50
|
+
diffRemovedLineNumberBg: string;
|
|
50
51
|
// Markdown
|
|
51
|
-
markdownText: string
|
|
52
|
-
markdownHeading: string
|
|
53
|
-
markdownLink: string
|
|
54
|
-
markdownLinkText: string
|
|
55
|
-
markdownCode: string
|
|
56
|
-
markdownBlockQuote: string
|
|
57
|
-
markdownEmph: string
|
|
58
|
-
markdownStrong: string
|
|
59
|
-
markdownHorizontalRule: string
|
|
60
|
-
markdownListItem: string
|
|
61
|
-
markdownListEnumeration: string
|
|
62
|
-
markdownImage: string
|
|
63
|
-
markdownImageText: string
|
|
64
|
-
markdownCodeBlock: string
|
|
52
|
+
markdownText: string;
|
|
53
|
+
markdownHeading: string;
|
|
54
|
+
markdownLink: string;
|
|
55
|
+
markdownLinkText: string;
|
|
56
|
+
markdownCode: string;
|
|
57
|
+
markdownBlockQuote: string;
|
|
58
|
+
markdownEmph: string;
|
|
59
|
+
markdownStrong: string;
|
|
60
|
+
markdownHorizontalRule: string;
|
|
61
|
+
markdownListItem: string;
|
|
62
|
+
markdownListEnumeration: string;
|
|
63
|
+
markdownImage: string;
|
|
64
|
+
markdownImageText: string;
|
|
65
|
+
markdownCodeBlock: string;
|
|
65
66
|
// Cursor/Selection
|
|
66
|
-
cursorLine: string
|
|
67
|
-
selection: string
|
|
67
|
+
cursorLine: string;
|
|
68
|
+
selection: string;
|
|
68
69
|
// Syntax
|
|
69
|
-
syntaxComment: string
|
|
70
|
-
syntaxKeyword: string
|
|
71
|
-
syntaxFunction: string
|
|
72
|
-
syntaxVariable: string
|
|
73
|
-
syntaxString: string
|
|
74
|
-
syntaxNumber: string
|
|
75
|
-
syntaxType: string
|
|
76
|
-
syntaxOperator: string
|
|
77
|
-
syntaxPunctuation: string
|
|
70
|
+
syntaxComment: string;
|
|
71
|
+
syntaxKeyword: string;
|
|
72
|
+
syntaxFunction: string;
|
|
73
|
+
syntaxVariable: string;
|
|
74
|
+
syntaxString: string;
|
|
75
|
+
syntaxNumber: string;
|
|
76
|
+
syntaxType: string;
|
|
77
|
+
syntaxOperator: string;
|
|
78
|
+
syntaxPunctuation: string;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
// All keys of ResolvedTheme for iteration
|
|
@@ -131,91 +132,109 @@ export const RESOLVED_KEYS: (keyof ResolvedTheme)[] = [
|
|
|
131
132
|
"syntaxType",
|
|
132
133
|
"syntaxOperator",
|
|
133
134
|
"syntaxPunctuation",
|
|
134
|
-
]
|
|
135
|
+
];
|
|
135
136
|
|
|
136
137
|
// Fallbacks used when a theme key is missing
|
|
137
138
|
export const FALLBACKS: Record<string, string> = {
|
|
138
|
-
primary: "#808080",
|
|
139
|
-
secondary: "#808080",
|
|
140
139
|
accent: "#808080",
|
|
141
|
-
error: "#808080",
|
|
142
|
-
warning: "#808080",
|
|
143
|
-
success: "#808080",
|
|
144
|
-
info: "#808080",
|
|
145
|
-
text: "#d4d4d4",
|
|
146
|
-
textMuted: "#808080",
|
|
147
140
|
background: "#1e1e1e",
|
|
148
|
-
backgroundPanel: "#1e1e1e",
|
|
149
141
|
backgroundElement: "#1e1e1e",
|
|
150
|
-
|
|
151
|
-
selection: "#1e1e1e",
|
|
142
|
+
backgroundPanel: "#1e1e1e",
|
|
152
143
|
border: "#808080",
|
|
153
144
|
borderActive: "#808080",
|
|
154
145
|
borderSubtle: "#808080",
|
|
146
|
+
cursorLine: "#1e1e1e",
|
|
155
147
|
diffAdded: "#4fd6be",
|
|
156
|
-
|
|
148
|
+
diffAddedBg: "#1e3a1e",
|
|
149
|
+
diffAddedLineNumberBg: "#1e3a1e",
|
|
157
150
|
diffContext: "#808080",
|
|
158
|
-
|
|
151
|
+
diffContextBg: "#1e1e1e",
|
|
159
152
|
diffHighlightAdded: "#4fd6be",
|
|
160
153
|
diffHighlightRemoved: "#c53b53",
|
|
161
|
-
|
|
162
|
-
diffRemovedBg: "#3a1e1e",
|
|
163
|
-
diffContextBg: "#1e1e1e",
|
|
154
|
+
diffHunkHeader: "#808080",
|
|
164
155
|
diffLineNumber: "#808080",
|
|
165
|
-
|
|
156
|
+
diffRemoved: "#c53b53",
|
|
157
|
+
diffRemovedBg: "#3a1e1e",
|
|
166
158
|
diffRemovedLineNumberBg: "#3a1e1e",
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
markdownLink: "#808080",
|
|
170
|
-
markdownLinkText: "#808080",
|
|
171
|
-
markdownCode: "#808080",
|
|
159
|
+
error: "#808080",
|
|
160
|
+
info: "#808080",
|
|
172
161
|
markdownBlockQuote: "#808080",
|
|
162
|
+
markdownCode: "#808080",
|
|
163
|
+
markdownCodeBlock: "#d4d4d4",
|
|
173
164
|
markdownEmph: "#808080",
|
|
174
|
-
|
|
165
|
+
markdownHeading: "#808080",
|
|
175
166
|
markdownHorizontalRule: "#808080",
|
|
176
|
-
markdownListItem: "#808080",
|
|
177
|
-
markdownListEnumeration: "#808080",
|
|
178
167
|
markdownImage: "#808080",
|
|
179
168
|
markdownImageText: "#808080",
|
|
180
|
-
|
|
169
|
+
markdownLink: "#808080",
|
|
170
|
+
markdownLinkText: "#808080",
|
|
171
|
+
markdownListEnumeration: "#808080",
|
|
172
|
+
markdownListItem: "#808080",
|
|
173
|
+
markdownStrong: "#808080",
|
|
174
|
+
markdownText: "#d4d4d4",
|
|
175
|
+
primary: "#808080",
|
|
176
|
+
secondary: "#808080",
|
|
177
|
+
selection: "#1e1e1e",
|
|
178
|
+
success: "#808080",
|
|
181
179
|
syntaxComment: "#808080",
|
|
182
|
-
syntaxKeyword: "#808080",
|
|
183
180
|
syntaxFunction: "#808080",
|
|
184
|
-
|
|
185
|
-
syntaxString: "#808080",
|
|
181
|
+
syntaxKeyword: "#808080",
|
|
186
182
|
syntaxNumber: "#808080",
|
|
187
|
-
syntaxType: "#808080",
|
|
188
183
|
syntaxOperator: "#808080",
|
|
189
184
|
syntaxPunctuation: "#808080",
|
|
190
|
-
|
|
185
|
+
syntaxString: "#808080",
|
|
186
|
+
syntaxType: "#808080",
|
|
187
|
+
syntaxVariable: "#808080",
|
|
188
|
+
text: "#d4d4d4",
|
|
189
|
+
textMuted: "#808080",
|
|
190
|
+
warning: "#808080",
|
|
191
|
+
};
|
|
191
192
|
|
|
192
193
|
// ---------------------------------------------------------------------------
|
|
193
194
|
// Resolution
|
|
194
195
|
// ---------------------------------------------------------------------------
|
|
195
196
|
|
|
196
|
-
export
|
|
197
|
-
|
|
197
|
+
export const resolveTheme = function resolveTheme(
|
|
198
|
+
json: ThemeJSON,
|
|
199
|
+
mode: "dark" | "light",
|
|
200
|
+
): ResolvedTheme {
|
|
201
|
+
const defs = json.defs ?? {};
|
|
198
202
|
|
|
199
|
-
function resolveColor(c: ColorValue, seen
|
|
203
|
+
const resolveColor = function resolveColor(c: ColorValue, seen = new Set<string>()): string {
|
|
200
204
|
if (typeof c === "string") {
|
|
201
|
-
if (c === "transparent" || c === "none")
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
if (c === "transparent" || c === "none") {
|
|
206
|
+
return "#00000000";
|
|
207
|
+
}
|
|
208
|
+
if (c.startsWith("#")) {
|
|
209
|
+
return c;
|
|
210
|
+
}
|
|
211
|
+
if (seen.has(c)) {
|
|
212
|
+
return "#808080";
|
|
213
|
+
}
|
|
214
|
+
if (defs[c] !== undefined && defs[c] !== null) {
|
|
215
|
+
return resolveColor(defs[c], new Set(seen).add(c));
|
|
216
|
+
}
|
|
217
|
+
if (json.theme[c] !== undefined) {
|
|
218
|
+
return resolveColor(json.theme[c], new Set(seen).add(c));
|
|
219
|
+
}
|
|
220
|
+
return "#808080";
|
|
208
221
|
}
|
|
209
|
-
return resolveColor(c[mode], seen)
|
|
210
|
-
}
|
|
222
|
+
return resolveColor(c[mode], seen);
|
|
223
|
+
};
|
|
211
224
|
|
|
212
|
-
const result = {} as Record<string, string
|
|
225
|
+
const result = {} as Record<string, string>;
|
|
213
226
|
for (const key of RESOLVED_KEYS) {
|
|
214
|
-
const val = json.theme[key]
|
|
215
|
-
result[key] = val
|
|
227
|
+
const val = json.theme[key];
|
|
228
|
+
result[key] = val === undefined ? (FALLBACKS[key] ?? "#808080") : resolveColor(val);
|
|
216
229
|
}
|
|
217
230
|
// Dynamic fallbacks that reference other resolved keys
|
|
218
|
-
if (json.theme
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
231
|
+
if (json.theme.cursorLine === undefined) {
|
|
232
|
+
result.cursorLine = result.backgroundElement;
|
|
233
|
+
}
|
|
234
|
+
if (json.theme.selection === undefined) {
|
|
235
|
+
result.selection = result.backgroundPanel;
|
|
236
|
+
}
|
|
237
|
+
// Deferred(lint-sweep): replace with a typed exhaustive key map for compile-time completeness
|
|
238
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- RESOLVED_KEYS drives every required key and defaults missing values
|
|
239
|
+
return result as unknown as ResolvedTheme;
|
|
240
|
+
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CloseButton.d.ts","sourceRoot":"","sources":["../src/CloseButton.tsx"],"names":[],"mappings":"AAIA,UAAU,gBAAgB;IACxB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,KAAW,EAAE,KAAK,EAAE,EAAE,gBAAgB,6BAmB5E"}
|
package/dist/CloseButton.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CloseButton.js","sourceRoot":"","sources":["../src/CloseButton.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAEnC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAWvC;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,OAAO,EAAE,KAAK,GAAG,GAAG,EAAE,KAAK,EAAoB;IAC3E,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAA;IAE5B,MAAM,eAAe,GAAG,WAAW,CACjC,CAAC,KAAiB,EAAE,EAAE;QACpB,8CAA8C;QAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAM;QAC9B,KAAK,CAAC,cAAc,EAAE,CAAA;QACtB,KAAK,CAAC,eAAe,EAAE,CAAA;QACvB,OAAO,EAAE,CAAA;IACX,CAAC,EACD,CAAC,OAAO,CAAC,CACV,CAAA;IAED,OAAO,CACL,cAAK,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,eAAe,YAC9D,eAAM,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,IAAI,KAAK,CAAC,SAAS,GAAI,GAClD,CACP,CAAA;AACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ThemePicker.d.ts","sourceRoot":"","sources":["../src/ThemePicker.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CACd;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC;AAED,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,UAAU,GACX,EAAE,gBAAgB,6BAuJlB"}
|
package/dist/ThemePicker.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ThemePicker.js","sourceRoot":"","sources":["../src/ThemePicker.tsx"],"names":[],"mappings":"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"}
|
package/src/ThemePicker.tsx
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { useState, useMemo, useCallback } from "react"
|
|
2
|
-
import { useCommand } from "@tooee/commands"
|
|
3
|
-
import { fuzzyMatch } from "@tooee/fuzzy"
|
|
4
|
-
import { useTheme } from "./context.js"
|
|
5
|
-
import { CloseButton } from "./CloseButton.js"
|
|
6
|
-
|
|
7
|
-
export interface ThemePickerEntry {
|
|
8
|
-
id: string
|
|
9
|
-
title: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface ThemePickerProps {
|
|
13
|
-
entries: ThemePickerEntry[]
|
|
14
|
-
currentTheme: string
|
|
15
|
-
onSelect: (name: string) => void
|
|
16
|
-
onClose: () => void
|
|
17
|
-
onNavigate: (name: string) => void
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function ThemePicker({
|
|
21
|
-
entries,
|
|
22
|
-
currentTheme,
|
|
23
|
-
onSelect,
|
|
24
|
-
onClose,
|
|
25
|
-
onNavigate,
|
|
26
|
-
}: ThemePickerProps) {
|
|
27
|
-
const { theme } = useTheme()
|
|
28
|
-
const [filter, setFilter] = useState("")
|
|
29
|
-
const [activeIndex, setActiveIndex] = useState(() => {
|
|
30
|
-
const idx = entries.findIndex((e) => e.id === currentTheme)
|
|
31
|
-
return idx >= 0 ? idx : 0
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
const filtered = useMemo(() => {
|
|
35
|
-
if (!filter) return entries
|
|
36
|
-
const results: { entry: ThemePickerEntry; score: number }[] = []
|
|
37
|
-
for (const entry of entries) {
|
|
38
|
-
const score = fuzzyMatch(filter, entry.title)
|
|
39
|
-
if (score !== null) results.push({ entry, score })
|
|
40
|
-
}
|
|
41
|
-
results.sort((a, b) => b.score - a.score)
|
|
42
|
-
return results.map((r) => r.entry)
|
|
43
|
-
}, [entries, filter])
|
|
44
|
-
|
|
45
|
-
const handleSelect = useCallback(() => {
|
|
46
|
-
const item = filtered[activeIndex]
|
|
47
|
-
if (item) {
|
|
48
|
-
onSelect(item.id)
|
|
49
|
-
}
|
|
50
|
-
}, [filtered, activeIndex, onSelect])
|
|
51
|
-
|
|
52
|
-
const navigateTo = useCallback(
|
|
53
|
-
(index: number) => {
|
|
54
|
-
setActiveIndex(index)
|
|
55
|
-
const item = filtered[index]
|
|
56
|
-
if (item) {
|
|
57
|
-
onNavigate(item.id)
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
[filtered, onNavigate],
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
const moveUp = useCallback(() => {
|
|
64
|
-
navigateTo(Math.max(0, activeIndex - 1))
|
|
65
|
-
}, [activeIndex, navigateTo])
|
|
66
|
-
|
|
67
|
-
const moveDown = useCallback(() => {
|
|
68
|
-
navigateTo(Math.min(filtered.length - 1, activeIndex + 1))
|
|
69
|
-
}, [activeIndex, filtered.length, navigateTo])
|
|
70
|
-
|
|
71
|
-
useCommand({
|
|
72
|
-
id: "theme-picker:close",
|
|
73
|
-
title: "Close theme picker",
|
|
74
|
-
hotkey: "Escape",
|
|
75
|
-
modes: ["insert", "cursor"],
|
|
76
|
-
hidden: true,
|
|
77
|
-
handler: onClose,
|
|
78
|
-
})
|
|
79
|
-
useCommand({
|
|
80
|
-
id: "theme-picker:select",
|
|
81
|
-
title: "Select theme",
|
|
82
|
-
hotkey: "Enter",
|
|
83
|
-
modes: ["insert", "cursor"],
|
|
84
|
-
hidden: true,
|
|
85
|
-
handler: handleSelect,
|
|
86
|
-
})
|
|
87
|
-
useCommand({
|
|
88
|
-
id: "theme-picker:move-up",
|
|
89
|
-
title: "Move up",
|
|
90
|
-
hotkey: "up",
|
|
91
|
-
modes: ["insert", "cursor"],
|
|
92
|
-
hidden: true,
|
|
93
|
-
handler: moveUp,
|
|
94
|
-
})
|
|
95
|
-
useCommand({
|
|
96
|
-
id: "theme-picker:move-down",
|
|
97
|
-
title: "Move down",
|
|
98
|
-
hotkey: "down",
|
|
99
|
-
modes: ["insert", "cursor"],
|
|
100
|
-
hidden: true,
|
|
101
|
-
handler: moveDown,
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<box
|
|
106
|
-
position="absolute"
|
|
107
|
-
left="20%"
|
|
108
|
-
right="20%"
|
|
109
|
-
top={2}
|
|
110
|
-
maxHeight="60%"
|
|
111
|
-
flexDirection="column"
|
|
112
|
-
backgroundColor={theme.backgroundPanel}
|
|
113
|
-
border
|
|
114
|
-
borderColor={theme.border}
|
|
115
|
-
>
|
|
116
|
-
{/* Filter row */}
|
|
117
|
-
<box flexDirection="row" paddingLeft={1} paddingRight={1} height={1}>
|
|
118
|
-
<text content="🎨 " fg={theme.accent} />
|
|
119
|
-
<input
|
|
120
|
-
focused
|
|
121
|
-
placeholder="Filter themes..."
|
|
122
|
-
onSubmit={handleSelect}
|
|
123
|
-
onInput={(value: string) => {
|
|
124
|
-
setFilter(value)
|
|
125
|
-
setActiveIndex(0)
|
|
126
|
-
// Preview first match
|
|
127
|
-
if (!value) {
|
|
128
|
-
if (entries.length > 0) onNavigate(entries[0]!.id)
|
|
129
|
-
} else {
|
|
130
|
-
const results: { entry: ThemePickerEntry; score: number }[] = []
|
|
131
|
-
for (const entry of entries) {
|
|
132
|
-
const score = fuzzyMatch(value, entry.title)
|
|
133
|
-
if (score !== null) results.push({ entry, score })
|
|
134
|
-
}
|
|
135
|
-
results.sort((a, b) => b.score - a.score)
|
|
136
|
-
if (results.length > 0) onNavigate(results[0]!.entry.id)
|
|
137
|
-
}
|
|
138
|
-
}}
|
|
139
|
-
backgroundColor="transparent"
|
|
140
|
-
focusedBackgroundColor="transparent"
|
|
141
|
-
textColor={theme.text}
|
|
142
|
-
placeholderColor={theme.textMuted}
|
|
143
|
-
cursorColor={theme.accent}
|
|
144
|
-
style={{ flexGrow: 1 }}
|
|
145
|
-
/>
|
|
146
|
-
<text content={` ${filtered.length}`} fg={theme.textMuted} />
|
|
147
|
-
<CloseButton onClose={onClose} />
|
|
148
|
-
</box>
|
|
149
|
-
|
|
150
|
-
{/* Separator */}
|
|
151
|
-
<box height={1} width="100%" backgroundColor={theme.border} />
|
|
152
|
-
|
|
153
|
-
{/* Theme list */}
|
|
154
|
-
<scrollbox focused={false} style={{ flexGrow: 1 }}>
|
|
155
|
-
{filtered.map((entry, i) => (
|
|
156
|
-
<box
|
|
157
|
-
key={entry.id}
|
|
158
|
-
flexDirection="row"
|
|
159
|
-
paddingLeft={1}
|
|
160
|
-
paddingRight={1}
|
|
161
|
-
height={1}
|
|
162
|
-
backgroundColor={i === activeIndex ? theme.backgroundElement : undefined}
|
|
163
|
-
onMouseDown={(event) => {
|
|
164
|
-
// Left-click applies the theme — same code path as Enter.
|
|
165
|
-
if (event.button !== 0) return
|
|
166
|
-
event.preventDefault()
|
|
167
|
-
event.stopPropagation()
|
|
168
|
-
onSelect(entry.id)
|
|
169
|
-
}}
|
|
170
|
-
>
|
|
171
|
-
<text content={entry.title} fg={theme.text} style={{ flexGrow: 1 }} />
|
|
172
|
-
</box>
|
|
173
|
-
))}
|
|
174
|
-
</scrollbox>
|
|
175
|
-
</box>
|
|
176
|
-
)
|
|
177
|
-
}
|