@brimveyn/aimux 1.5.0 → 1.6.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/README.md +14 -2
- package/package.json +5 -3
- package/src/app-runtime/side-effects.ts +41 -6
- package/src/app-runtime/use-renderer-bindings.ts +1 -1
- package/src/app.tsx +17 -5
- package/src/config.ts +29 -4
- package/src/diff-parser/clean-last-newline.ts +5 -0
- package/src/diff-parser/constants.ts +13 -0
- package/src/diff-parser/index.ts +12 -0
- package/src/diff-parser/parse-line-type.ts +29 -0
- package/src/diff-parser/parse-patch-files.ts +369 -0
- package/src/diff-parser/types.ts +75 -0
- package/src/index.tsx +2 -2
- package/src/input/modes/bridge.ts +8 -0
- package/src/input/modes/transitions.ts +2 -1
- package/src/input/modes/types.ts +4 -1
- package/src/pty/terminal-snapshot.ts +4 -4
- package/src/state/git-tree.ts +220 -0
- package/src/state/reducers/git-mode-state.ts +232 -35
- package/src/state/reducers/git-panel-state.ts +29 -3
- package/src/state/reducers/modal-state.ts +43 -10
- package/src/state/store.ts +5 -1
- package/src/state/types.ts +41 -5
- package/src/state/workspace-save.ts +2 -0
- package/src/ui/components/create-session-modal.tsx +23 -7
- package/src/ui/components/diff-renderer/build-rows.ts +349 -0
- package/src/ui/components/diff-renderer/filetype.ts +29 -0
- package/src/ui/components/diff-renderer/fold-strip.tsx +84 -0
- package/src/ui/components/diff-renderer/highlight.ts +48 -0
- package/src/ui/components/diff-renderer/index.ts +1 -0
- package/src/ui/components/diff-renderer/pierre-diff.tsx +170 -0
- package/src/ui/components/diff-renderer/split-view.tsx +207 -0
- package/src/ui/components/diff-renderer/stacked-view.tsx +166 -0
- package/src/ui/components/git-commit-modal.tsx +14 -2
- package/src/ui/components/git-pane-widget.tsx +5 -0
- package/src/ui/components/git-panel.tsx +176 -79
- package/src/ui/components/git-view.tsx +89 -87
- package/src/ui/components/help-modal.tsx +43 -11
- package/src/ui/components/input-field.tsx +2 -2
- package/src/ui/components/list-item.tsx +9 -1
- package/src/ui/components/modal-filter-bar.tsx +1 -1
- package/src/ui/components/modal-keybinds-overlay.tsx +2 -2
- package/src/ui/components/modal-shell.tsx +3 -3
- package/src/ui/components/new-tab-modal.tsx +15 -3
- package/src/ui/components/pending-chord-overlay.tsx +3 -3
- package/src/ui/components/session-bar.tsx +10 -5
- package/src/ui/components/session-picker-modal.tsx +26 -9
- package/src/ui/components/sidebar.tsx +22 -10
- package/src/ui/components/snippet-editor-modal.tsx +16 -2
- package/src/ui/components/snippet-picker-modal.tsx +11 -3
- package/src/ui/components/split-layout.tsx +1 -1
- package/src/ui/components/status-bar.tsx +12 -9
- package/src/ui/components/surface.tsx +5 -5
- package/src/ui/components/tab-item.tsx +22 -16
- package/src/ui/components/terminal-pane.tsx +25 -15
- package/src/ui/components/theme-picker-modal.tsx +111 -16
- package/src/ui/components/update-available-modal.tsx +11 -1
- package/src/ui/filter-themes.ts +10 -0
- package/src/ui/house-themes.ts +313 -0
- package/src/ui/keymap-context.ts +10 -2
- package/src/ui/root.tsx +26 -6
- package/src/ui/shiki.ts +49 -0
- package/src/ui/status-bar-model.ts +19 -1
- package/src/ui/theme.ts +22 -10
- package/src/ui/themes.generated.ts +59794 -0
- package/src/ui/themes.ts +68 -274
- package/src/ui/syntax.ts +0 -102
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { THEME_IDS, type ThemeId, THEMES } from './themes'
|
|
2
|
+
|
|
3
|
+
export function filterThemeIds(filter: string | null): ThemeId[] {
|
|
4
|
+
if (!filter) return THEME_IDS
|
|
5
|
+
const needle = filter.toLowerCase()
|
|
6
|
+
return THEME_IDS.filter((id) => {
|
|
7
|
+
const name = THEMES[id]?.name.toLowerCase() ?? ''
|
|
8
|
+
return id.toLowerCase().includes(needle) || name.includes(needle)
|
|
9
|
+
})
|
|
10
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// Hand-curated aimux house themes, full Shiki theme shape. Colors follow the
|
|
2
|
+
// VSCode workbench color key namespace; `settings` is a TextMate token rule
|
|
3
|
+
// array shared by the in-repo diff highlighter and `shiki.loadTheme`.
|
|
4
|
+
/* eslint-disable perfectionist/sort-objects */
|
|
5
|
+
|
|
6
|
+
import type { Theme, ThemeColorMap } from './themes'
|
|
7
|
+
|
|
8
|
+
// --- aimux --------------------------------------------------------------
|
|
9
|
+
//
|
|
10
|
+
// Teal accents on a deep blue-black background. Designed to look at home
|
|
11
|
+
// inside the aimux chrome.
|
|
12
|
+
const AIMUX_BG = '#11151b'
|
|
13
|
+
const AIMUX_FG = '#edf4ff'
|
|
14
|
+
|
|
15
|
+
const AIMUX_COLORS: ThemeColorMap = {
|
|
16
|
+
'contrastBorder': '#3a4c60',
|
|
17
|
+
'descriptionForeground': '#6b829a',
|
|
18
|
+
'diffEditor.insertedLineBackground': '#1e3d2b',
|
|
19
|
+
'diffEditor.removedLineBackground': '#3b1e27',
|
|
20
|
+
'editor.background': AIMUX_BG,
|
|
21
|
+
'editor.foreground': AIMUX_FG,
|
|
22
|
+
'editor.lineHighlightBackground': '#1a2330',
|
|
23
|
+
'editor.selectionBackground': '#1f3344',
|
|
24
|
+
'editorCursor.foreground': '#7cd1b8',
|
|
25
|
+
'editorError.foreground': '#f38ba8',
|
|
26
|
+
'editorGroup.border': '#2d3f52',
|
|
27
|
+
'editorGroupHeader.tabsBackground': '#16202b',
|
|
28
|
+
'editorLineNumber.foreground': '#3e506a',
|
|
29
|
+
'editorWarning.foreground': '#f6c177',
|
|
30
|
+
'editorWidget.background': '#0b1016',
|
|
31
|
+
'errorForeground': '#f38ba8',
|
|
32
|
+
'focusBorder': '#7cd1b8',
|
|
33
|
+
'foreground': AIMUX_FG,
|
|
34
|
+
'gitDecoration.addedResourceForeground': '#8bd5ca',
|
|
35
|
+
'gitDecoration.deletedResourceForeground': '#f38ba8',
|
|
36
|
+
'gitDecoration.modifiedResourceForeground': '#f6c177',
|
|
37
|
+
'gitDecoration.untrackedResourceForeground': '#8bd5ca',
|
|
38
|
+
'input.background': '#1c2734',
|
|
39
|
+
'input.border': '#2d3f52',
|
|
40
|
+
'input.foreground': AIMUX_FG,
|
|
41
|
+
'list.activeSelectionBackground': '#1f3344',
|
|
42
|
+
'list.activeSelectionForeground': AIMUX_FG,
|
|
43
|
+
'list.hoverBackground': '#1a2330',
|
|
44
|
+
'panel.background': '#16202b',
|
|
45
|
+
'panel.border': '#2d3f52',
|
|
46
|
+
'sideBar.background': '#16202b',
|
|
47
|
+
'sideBar.foreground': AIMUX_FG,
|
|
48
|
+
'sideBarSectionHeader.background': '#1c2734',
|
|
49
|
+
'sideBarSectionHeader.foreground': '#b8c5d6',
|
|
50
|
+
'terminal.ansiBlue': '#7cb3ff',
|
|
51
|
+
'terminal.ansiCyan': '#7cd1b8',
|
|
52
|
+
'terminal.ansiGreen': '#8bd5ca',
|
|
53
|
+
'terminal.ansiMagenta': '#c4a7e7',
|
|
54
|
+
'terminal.ansiRed': '#f38ba8',
|
|
55
|
+
'terminal.ansiYellow': '#f6c177',
|
|
56
|
+
'textLink.activeForeground': '#7cd1b8',
|
|
57
|
+
'textLink.foreground': '#7cd1b8',
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// --- dracula-at-night --------------------------------------------------
|
|
61
|
+
const DAN_BG = '#0e1419'
|
|
62
|
+
const DAN_FG = '#f8f8f2'
|
|
63
|
+
|
|
64
|
+
const DAN_COLORS: ThemeColorMap = {
|
|
65
|
+
'contrastBorder': '#44475a',
|
|
66
|
+
'descriptionForeground': '#6272a4',
|
|
67
|
+
'diffEditor.insertedLineBackground': '#162b1d',
|
|
68
|
+
'diffEditor.removedLineBackground': '#2a1820',
|
|
69
|
+
'editor.background': DAN_BG,
|
|
70
|
+
'editor.foreground': DAN_FG,
|
|
71
|
+
'editor.lineHighlightBackground': '#1a2129',
|
|
72
|
+
'editor.selectionBackground': '#222a33',
|
|
73
|
+
'editorCursor.foreground': '#f8f8f0',
|
|
74
|
+
'editorError.foreground': '#ff5555',
|
|
75
|
+
'editorGroup.border': '#2a3440',
|
|
76
|
+
'editorGroupHeader.tabsBackground': '#131920',
|
|
77
|
+
'editorLineNumber.foreground': '#495466',
|
|
78
|
+
'editorWarning.foreground': '#f1fa8c',
|
|
79
|
+
'editorWidget.background': '#090d11',
|
|
80
|
+
'errorForeground': '#ff5555',
|
|
81
|
+
'focusBorder': '#bd93f9',
|
|
82
|
+
'foreground': DAN_FG,
|
|
83
|
+
'gitDecoration.addedResourceForeground': '#50fa7b',
|
|
84
|
+
'gitDecoration.deletedResourceForeground': '#ff5555',
|
|
85
|
+
'gitDecoration.modifiedResourceForeground': '#f1fa8c',
|
|
86
|
+
'gitDecoration.untrackedResourceForeground': '#50fa7b',
|
|
87
|
+
'input.background': '#1a2129',
|
|
88
|
+
'input.border': '#2a3440',
|
|
89
|
+
'input.foreground': DAN_FG,
|
|
90
|
+
'list.activeSelectionBackground': '#222a33',
|
|
91
|
+
'list.activeSelectionForeground': DAN_FG,
|
|
92
|
+
'list.hoverBackground': '#1a2129',
|
|
93
|
+
'panel.background': '#131920',
|
|
94
|
+
'panel.border': '#2a3440',
|
|
95
|
+
'sideBar.background': '#131920',
|
|
96
|
+
'sideBar.foreground': DAN_FG,
|
|
97
|
+
'sideBarSectionHeader.background': '#1a2129',
|
|
98
|
+
'sideBarSectionHeader.foreground': '#bdc3cf',
|
|
99
|
+
'terminal.ansiBlue': '#6272a4',
|
|
100
|
+
'terminal.ansiCyan': '#8be9fd',
|
|
101
|
+
'terminal.ansiGreen': '#50fa7b',
|
|
102
|
+
'terminal.ansiMagenta': '#ff79c6',
|
|
103
|
+
'terminal.ansiRed': '#ff5555',
|
|
104
|
+
'terminal.ansiYellow': '#f1fa8c',
|
|
105
|
+
'textLink.activeForeground': '#ff79c6',
|
|
106
|
+
'textLink.foreground': '#bd93f9',
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// --- Shared TextMate rule helpers --------------------------------------
|
|
110
|
+
|
|
111
|
+
const AIMUX_SETTINGS = [
|
|
112
|
+
{ settings: { background: AIMUX_BG, foreground: AIMUX_FG } },
|
|
113
|
+
{
|
|
114
|
+
scope: ['comment', 'punctuation.definition.comment'],
|
|
115
|
+
settings: { fontStyle: 'italic', foreground: '#6b829a' },
|
|
116
|
+
},
|
|
117
|
+
{ scope: ['comment.documentation'], settings: { foreground: '#8fa6bf', fontStyle: 'italic' } },
|
|
118
|
+
{ scope: ['string', 'string.quoted'], settings: { foreground: '#f6c177' } },
|
|
119
|
+
{ scope: ['string.template'], settings: { foreground: '#f6c177' } },
|
|
120
|
+
{
|
|
121
|
+
scope: ['string.regexp', 'string.escape', 'constant.character.escape'],
|
|
122
|
+
settings: { foreground: '#c4a7e7' },
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
scope: ['constant.numeric', 'constant.language', 'constant.language.boolean'],
|
|
126
|
+
settings: { foreground: '#f38ba8' },
|
|
127
|
+
},
|
|
128
|
+
{ scope: ['constant.character', 'constant.other'], settings: { foreground: '#f38ba8' } },
|
|
129
|
+
{
|
|
130
|
+
scope: ['keyword', 'keyword.control', 'keyword.operator.new', 'keyword.other'],
|
|
131
|
+
settings: { fontStyle: 'bold', foreground: '#c4a7e7' },
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
scope: ['storage', 'storage.type', 'storage.modifier'],
|
|
135
|
+
settings: { fontStyle: 'bold', foreground: '#c4a7e7' },
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
scope: ['keyword.operator', 'punctuation', 'meta.brace', 'punctuation.separator'],
|
|
139
|
+
settings: { foreground: '#6b829a' },
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
scope: [
|
|
143
|
+
'entity.name.function',
|
|
144
|
+
'support.function',
|
|
145
|
+
'meta.function-call',
|
|
146
|
+
'entity.name.function.member',
|
|
147
|
+
],
|
|
148
|
+
settings: { foreground: '#8bd5ca' },
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
scope: [
|
|
152
|
+
'entity.name.type',
|
|
153
|
+
'support.type',
|
|
154
|
+
'support.class',
|
|
155
|
+
'entity.name.class',
|
|
156
|
+
'entity.name.interface',
|
|
157
|
+
'entity.name.namespace',
|
|
158
|
+
],
|
|
159
|
+
settings: { fontStyle: 'italic', foreground: '#7cd1b8' },
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
scope: ['entity.name.type.enum', 'entity.name.type.alias'],
|
|
163
|
+
settings: { foreground: '#7cd1b8' },
|
|
164
|
+
},
|
|
165
|
+
{ scope: ['variable', 'meta.variable'], settings: { foreground: AIMUX_FG } },
|
|
166
|
+
{
|
|
167
|
+
scope: ['variable.parameter', 'meta.function.parameters'],
|
|
168
|
+
settings: { fontStyle: 'italic', foreground: AIMUX_FG },
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
scope: ['variable.other.property', 'variable.other.member', 'meta.object-literal.key'],
|
|
172
|
+
settings: { foreground: '#7cd1b8' },
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
scope: ['variable.language', 'variable.language.this'],
|
|
176
|
+
settings: { foreground: '#f38ba8', fontStyle: 'italic' },
|
|
177
|
+
},
|
|
178
|
+
{ scope: ['entity.name.tag', 'meta.tag'], settings: { foreground: '#f38ba8' } },
|
|
179
|
+
{
|
|
180
|
+
scope: ['entity.other.attribute-name'],
|
|
181
|
+
settings: { fontStyle: 'italic', foreground: '#f6c177' },
|
|
182
|
+
},
|
|
183
|
+
{ scope: ['support.constant'], settings: { foreground: '#f38ba8' } },
|
|
184
|
+
{ scope: ['support.variable'], settings: { foreground: AIMUX_FG } },
|
|
185
|
+
// Markdown
|
|
186
|
+
{ scope: ['markup.heading'], settings: { fontStyle: 'bold', foreground: '#7cd1b8' } },
|
|
187
|
+
{ scope: ['markup.bold'], settings: { fontStyle: 'bold', foreground: AIMUX_FG } },
|
|
188
|
+
{ scope: ['markup.italic'], settings: { fontStyle: 'italic', foreground: AIMUX_FG } },
|
|
189
|
+
{ scope: ['markup.underline.link', 'markup.inline.raw'], settings: { foreground: '#8bd5ca' } },
|
|
190
|
+
{ scope: ['markup.list'], settings: { foreground: '#c4a7e7' } },
|
|
191
|
+
{ scope: ['markup.quote'], settings: { foreground: '#6b829a', fontStyle: 'italic' } },
|
|
192
|
+
{ scope: ['markup.raw'], settings: { foreground: '#f6c177' } },
|
|
193
|
+
// JSX / TSX
|
|
194
|
+
{
|
|
195
|
+
scope: ['entity.name.tag.jsx', 'support.class.component'],
|
|
196
|
+
settings: { foreground: '#7cd1b8' },
|
|
197
|
+
},
|
|
198
|
+
{ scope: ['meta.jsx.children'], settings: { foreground: AIMUX_FG } },
|
|
199
|
+
// Diff
|
|
200
|
+
{ scope: ['markup.inserted', 'meta.diff.header.to-file'], settings: { foreground: '#8bd5ca' } },
|
|
201
|
+
{ scope: ['markup.deleted', 'meta.diff.header.from-file'], settings: { foreground: '#f38ba8' } },
|
|
202
|
+
{ scope: ['markup.changed'], settings: { foreground: '#f6c177' } },
|
|
203
|
+
{ scope: ['meta.diff.range'], settings: { foreground: '#c4a7e7' } },
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
const DAN_SETTINGS = [
|
|
207
|
+
{ settings: { background: DAN_BG, foreground: DAN_FG } },
|
|
208
|
+
{
|
|
209
|
+
scope: ['comment', 'punctuation.definition.comment'],
|
|
210
|
+
settings: { fontStyle: 'italic', foreground: '#6272a4' },
|
|
211
|
+
},
|
|
212
|
+
{ scope: ['comment.documentation'], settings: { fontStyle: 'italic', foreground: '#8a96b4' } },
|
|
213
|
+
{ scope: ['string', 'string.quoted'], settings: { foreground: '#f1fa8c' } },
|
|
214
|
+
{ scope: ['string.template'], settings: { foreground: '#f1fa8c' } },
|
|
215
|
+
{
|
|
216
|
+
scope: ['string.regexp', 'string.escape', 'constant.character.escape'],
|
|
217
|
+
settings: { foreground: '#ff79c6' },
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
scope: ['constant.numeric', 'constant.language', 'constant.language.boolean'],
|
|
221
|
+
settings: { foreground: '#bd93f9' },
|
|
222
|
+
},
|
|
223
|
+
{ scope: ['constant.character', 'constant.other'], settings: { foreground: '#bd93f9' } },
|
|
224
|
+
{
|
|
225
|
+
scope: ['keyword', 'keyword.control', 'keyword.other'],
|
|
226
|
+
settings: { fontStyle: 'bold', foreground: '#ff79c6' },
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
scope: ['storage', 'storage.type', 'storage.modifier'],
|
|
230
|
+
settings: { fontStyle: 'bold italic', foreground: '#ff79c6' },
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
scope: ['keyword.operator', 'punctuation', 'meta.brace', 'punctuation.separator'],
|
|
234
|
+
settings: { foreground: '#f8f8f2' },
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
scope: ['entity.name.function', 'support.function', 'meta.function-call'],
|
|
238
|
+
settings: { foreground: '#50fa7b' },
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
scope: [
|
|
242
|
+
'entity.name.type',
|
|
243
|
+
'support.type',
|
|
244
|
+
'support.class',
|
|
245
|
+
'entity.name.class',
|
|
246
|
+
'entity.name.interface',
|
|
247
|
+
],
|
|
248
|
+
settings: { fontStyle: 'italic', foreground: '#8be9fd' },
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
scope: ['entity.name.type.enum', 'entity.name.type.alias'],
|
|
252
|
+
settings: { foreground: '#8be9fd' },
|
|
253
|
+
},
|
|
254
|
+
{ scope: ['variable', 'meta.variable'], settings: { foreground: DAN_FG } },
|
|
255
|
+
{
|
|
256
|
+
scope: ['variable.parameter', 'meta.function.parameters'],
|
|
257
|
+
settings: { fontStyle: 'italic', foreground: '#ffb86c' },
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
scope: ['variable.other.property', 'variable.other.member', 'meta.object-literal.key'],
|
|
261
|
+
settings: { foreground: '#8be9fd' },
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
scope: ['variable.language', 'variable.language.this'],
|
|
265
|
+
settings: { foreground: '#bd93f9', fontStyle: 'italic' },
|
|
266
|
+
},
|
|
267
|
+
{ scope: ['entity.name.tag', 'meta.tag'], settings: { foreground: '#ff79c6' } },
|
|
268
|
+
{
|
|
269
|
+
scope: ['entity.other.attribute-name'],
|
|
270
|
+
settings: { fontStyle: 'italic', foreground: '#50fa7b' },
|
|
271
|
+
},
|
|
272
|
+
{ scope: ['support.constant'], settings: { foreground: '#bd93f9' } },
|
|
273
|
+
// Markdown
|
|
274
|
+
{ scope: ['markup.heading'], settings: { fontStyle: 'bold', foreground: '#bd93f9' } },
|
|
275
|
+
{ scope: ['markup.bold'], settings: { fontStyle: 'bold', foreground: '#ffb86c' } },
|
|
276
|
+
{ scope: ['markup.italic'], settings: { fontStyle: 'italic', foreground: '#f1fa8c' } },
|
|
277
|
+
{ scope: ['markup.underline.link', 'markup.inline.raw'], settings: { foreground: '#8be9fd' } },
|
|
278
|
+
{ scope: ['markup.list'], settings: { foreground: '#ff79c6' } },
|
|
279
|
+
{ scope: ['markup.quote'], settings: { foreground: '#6272a4', fontStyle: 'italic' } },
|
|
280
|
+
{ scope: ['markup.raw'], settings: { foreground: '#f1fa8c' } },
|
|
281
|
+
// JSX / TSX
|
|
282
|
+
{
|
|
283
|
+
scope: ['entity.name.tag.jsx', 'support.class.component'],
|
|
284
|
+
settings: { foreground: '#8be9fd' },
|
|
285
|
+
},
|
|
286
|
+
// Diff
|
|
287
|
+
{ scope: ['markup.inserted'], settings: { foreground: '#50fa7b' } },
|
|
288
|
+
{ scope: ['markup.deleted'], settings: { foreground: '#ff5555' } },
|
|
289
|
+
{ scope: ['markup.changed'], settings: { foreground: '#f1fa8c' } },
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
export const HOUSE_THEMES: Record<string, Theme> = {
|
|
293
|
+
'aimux': {
|
|
294
|
+
bg: AIMUX_BG,
|
|
295
|
+
colors: AIMUX_COLORS,
|
|
296
|
+
displayName: 'Aimux',
|
|
297
|
+
fg: AIMUX_FG,
|
|
298
|
+
name: 'aimux',
|
|
299
|
+
settings: AIMUX_SETTINGS,
|
|
300
|
+
type: 'dark',
|
|
301
|
+
},
|
|
302
|
+
'dracula-at-night': {
|
|
303
|
+
bg: DAN_BG,
|
|
304
|
+
colors: DAN_COLORS,
|
|
305
|
+
displayName: 'Dracula At Night',
|
|
306
|
+
fg: DAN_FG,
|
|
307
|
+
name: 'dracula-at-night',
|
|
308
|
+
settings: DAN_SETTINGS,
|
|
309
|
+
type: 'dark',
|
|
310
|
+
},
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export const HOUSE_THEME_IDS: string[] = Object.keys(HOUSE_THEMES)
|
package/src/ui/keymap-context.ts
CHANGED
|
@@ -18,17 +18,25 @@ export function useKeymap(): ResolvedKeymapConfig {
|
|
|
18
18
|
const HELP_JOINER = ' · '
|
|
19
19
|
const MAX_HELP_ENTRIES = 5
|
|
20
20
|
|
|
21
|
+
interface BuildHintOptions {
|
|
22
|
+
excludeDescriptions?: readonly string[]
|
|
23
|
+
}
|
|
24
|
+
|
|
21
25
|
/** Build a single-line help string for a mode (used in modal headers and status bar). */
|
|
22
26
|
export function buildHintText(
|
|
23
27
|
config: ResolvedKeymapConfig,
|
|
24
28
|
modeId: ModeId,
|
|
25
|
-
limit: number = MAX_HELP_ENTRIES
|
|
29
|
+
limit: number = MAX_HELP_ENTRIES,
|
|
30
|
+
options: BuildHintOptions = {}
|
|
26
31
|
): string {
|
|
27
32
|
const bindings = describeBindings(config, modeId, {
|
|
28
33
|
dedupeByDescription: true,
|
|
29
34
|
withDescriptionOnly: true,
|
|
30
35
|
})
|
|
31
36
|
if (bindings.length === 0) return ''
|
|
32
|
-
const
|
|
37
|
+
const excluded = new Set(options.excludeDescriptions ?? [])
|
|
38
|
+
const kept =
|
|
39
|
+
excluded.size > 0 ? bindings.filter((b) => !excluded.has(b.description ?? '')) : bindings
|
|
40
|
+
const trimmed = kept.slice(0, limit)
|
|
33
41
|
return trimmed.map((b) => `${b.keysDisplay} ${b.description ?? ''}`.trim()).join(HELP_JOINER)
|
|
34
42
|
}
|
package/src/ui/root.tsx
CHANGED
|
@@ -134,7 +134,11 @@ function renderModal(
|
|
|
134
134
|
)
|
|
135
135
|
case 'theme-picker':
|
|
136
136
|
return (
|
|
137
|
-
<ThemePickerModal
|
|
137
|
+
<ThemePickerModal
|
|
138
|
+
selectedIndex={modal.selectedIndex}
|
|
139
|
+
currentThemeId={options.themeId}
|
|
140
|
+
filter={modal.editBuffer}
|
|
141
|
+
/>
|
|
138
142
|
)
|
|
139
143
|
case 'update-available':
|
|
140
144
|
return (
|
|
@@ -145,7 +149,13 @@ function renderModal(
|
|
|
145
149
|
/>
|
|
146
150
|
)
|
|
147
151
|
case 'help':
|
|
148
|
-
return
|
|
152
|
+
return (
|
|
153
|
+
<HelpModal
|
|
154
|
+
filter={modal.editBuffer}
|
|
155
|
+
selectedIndex={modal.selectedIndex}
|
|
156
|
+
scope={modal.scope}
|
|
157
|
+
/>
|
|
158
|
+
)
|
|
149
159
|
case 'git-commit': {
|
|
150
160
|
const titleText =
|
|
151
161
|
modal.activeField === 'title' ? (modal.editBuffer ?? '') : modal.contentBuffer
|
|
@@ -230,8 +240,13 @@ export function RootView({
|
|
|
230
240
|
const inGitMode = focusMode === 'git' || modal.type === 'git-commit'
|
|
231
241
|
if (inGitMode) {
|
|
232
242
|
return (
|
|
233
|
-
<box
|
|
234
|
-
|
|
243
|
+
<box
|
|
244
|
+
flexDirection="column"
|
|
245
|
+
width="100%"
|
|
246
|
+
height="100%"
|
|
247
|
+
backgroundColor={theme.colors['editor.background']}
|
|
248
|
+
>
|
|
249
|
+
<GitView themeId={themeId} />
|
|
235
250
|
<StatusBar />
|
|
236
251
|
<PendingChordOverlay />
|
|
237
252
|
{renderModal(modal, {
|
|
@@ -249,7 +264,12 @@ export function RootView({
|
|
|
249
264
|
}
|
|
250
265
|
|
|
251
266
|
return (
|
|
252
|
-
<box
|
|
267
|
+
<box
|
|
268
|
+
flexDirection="column"
|
|
269
|
+
width="100%"
|
|
270
|
+
height="100%"
|
|
271
|
+
backgroundColor={theme.colors['editor.background']}
|
|
272
|
+
>
|
|
253
273
|
{sessionBarPosition === 'top' && <SessionBar />}
|
|
254
274
|
<box flexDirection="row" gap={0} padding={0} flexGrow={1}>
|
|
255
275
|
<Sidebar onTabActivate={onPaneActivate} />
|
|
@@ -330,7 +350,7 @@ function GitPaneInPaneMode({ ratio }: { ratio: number }) {
|
|
|
330
350
|
flexDirection="column"
|
|
331
351
|
width={width}
|
|
332
352
|
flexShrink={0}
|
|
333
|
-
backgroundColor={theme.
|
|
353
|
+
backgroundColor={theme.colors['sideBar.background']}
|
|
334
354
|
overflow="hidden"
|
|
335
355
|
>
|
|
336
356
|
<GitPaneWidget pollingEnabled />
|
package/src/ui/shiki.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type BundledLanguage, type BundledTheme, createHighlighter, type Highlighter } from 'shiki'
|
|
2
|
+
|
|
3
|
+
import { THEMES } from './themes'
|
|
4
|
+
|
|
5
|
+
// Shiki's highlighter is created with at least one real shiki-bundled theme so
|
|
6
|
+
// its worker can warm up. House and user themes load on demand.
|
|
7
|
+
const WARM_THEME: BundledTheme = 'catppuccin-mocha'
|
|
8
|
+
|
|
9
|
+
let highlighterPromise: Promise<Highlighter> | null = null
|
|
10
|
+
|
|
11
|
+
export async function getShikiHighlighter(): Promise<Highlighter> {
|
|
12
|
+
if (!highlighterPromise) {
|
|
13
|
+
highlighterPromise = createHighlighter({ langs: [], themes: [WARM_THEME] })
|
|
14
|
+
}
|
|
15
|
+
return highlighterPromise
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const loadedLangs = new Set<string>()
|
|
19
|
+
|
|
20
|
+
export async function ensureShikiLang(h: Highlighter, lang: string): Promise<boolean> {
|
|
21
|
+
if (loadedLangs.has(lang)) return true
|
|
22
|
+
try {
|
|
23
|
+
await h.loadLanguage(lang as BundledLanguage)
|
|
24
|
+
loadedLangs.add(lang)
|
|
25
|
+
return true
|
|
26
|
+
} catch {
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const loadedThemes = new Set<string>([WARM_THEME])
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Load the theme keyed by `id` into the shiki highlighter. The same object
|
|
35
|
+
* powers the UI palette and the code highlighter — no synthesis, no mapping.
|
|
36
|
+
*/
|
|
37
|
+
export async function ensureShikiTheme(h: Highlighter, id: string): Promise<boolean> {
|
|
38
|
+
if (loadedThemes.has(id)) return true
|
|
39
|
+
const entry = THEMES[id]
|
|
40
|
+
if (!entry) return false
|
|
41
|
+
try {
|
|
42
|
+
// eslint-disable-next-line typescript/no-explicit-any
|
|
43
|
+
await h.loadTheme(entry as any)
|
|
44
|
+
loadedThemes.add(id)
|
|
45
|
+
return true
|
|
46
|
+
} catch {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -2,16 +2,19 @@ import type { ModeId, ResolvedKeymapConfig } from '@brimveyn/aimux-config'
|
|
|
2
2
|
|
|
3
3
|
import type { AppState, TabSession } from '../state/types'
|
|
4
4
|
|
|
5
|
+
import { describeBindings } from '../input/keymap/describe-bindings'
|
|
5
6
|
import { buildHintText } from './keymap-context'
|
|
6
7
|
import { abbreviatePath } from './path-format'
|
|
7
8
|
|
|
8
9
|
export interface StatusBarModel {
|
|
9
10
|
left: string
|
|
10
11
|
right: string
|
|
12
|
+
help: string
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
const MAX_TAB_LABEL_LENGTH = 24
|
|
14
16
|
const HINT_LIMIT = 6
|
|
17
|
+
const HELP_DESCRIPTION = 'Help'
|
|
15
18
|
|
|
16
19
|
function truncateLabel(label: string): string {
|
|
17
20
|
if (label.length <= MAX_TAB_LABEL_LENGTH) {
|
|
@@ -30,7 +33,17 @@ function getActiveTabLabel(tab?: TabSession): string {
|
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
function hintForMode(config: ResolvedKeymapConfig, modeId: ModeId): string {
|
|
33
|
-
return buildHintText(config, modeId, HINT_LIMIT)
|
|
36
|
+
return buildHintText(config, modeId, HINT_LIMIT, { excludeDescriptions: [HELP_DESCRIPTION] })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function helpHintForMode(config: ResolvedKeymapConfig, modeId: ModeId): string {
|
|
40
|
+
const bindings = describeBindings(config, modeId, {
|
|
41
|
+
dedupeByDescription: true,
|
|
42
|
+
withDescriptionOnly: true,
|
|
43
|
+
})
|
|
44
|
+
const helpBinding = bindings.find((b) => b.description === HELP_DESCRIPTION)
|
|
45
|
+
if (!helpBinding) return ''
|
|
46
|
+
return `${helpBinding.keysDisplay} ${helpBinding.description ?? ''}`.trim()
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
export function getStatusBarModel(
|
|
@@ -52,24 +65,28 @@ export function getStatusBarModel(
|
|
|
52
65
|
switch (state.focusMode) {
|
|
53
66
|
case 'terminal-input':
|
|
54
67
|
return {
|
|
68
|
+
help: '',
|
|
55
69
|
left: `${getActiveTabLabel(activeTab)} ${sessionIcon} ${sessionLabel}`,
|
|
56
70
|
right: hintForMode(config, 'terminal-input'),
|
|
57
71
|
}
|
|
58
72
|
case 'modal': {
|
|
59
73
|
const modalMode = deriveModalModeId(state.modal.type)
|
|
60
74
|
return {
|
|
75
|
+
help: '',
|
|
61
76
|
left: `${sessionIcon} ${sessionLabel}`,
|
|
62
77
|
right: modalMode ? hintForMode(config, modalMode) : '',
|
|
63
78
|
}
|
|
64
79
|
}
|
|
65
80
|
case 'git':
|
|
66
81
|
return {
|
|
82
|
+
help: helpHintForMode(config, 'git-mode'),
|
|
67
83
|
left: `${sessionIcon} ${sessionLabel}`,
|
|
68
84
|
right: hintForMode(config, 'git-mode'),
|
|
69
85
|
}
|
|
70
86
|
case 'command-edit': {
|
|
71
87
|
const commandEditMode = deriveCommandEditModeId(state.modal.type)
|
|
72
88
|
return {
|
|
89
|
+
help: '',
|
|
73
90
|
left: `${sessionIcon} ${sessionLabel}`,
|
|
74
91
|
right: commandEditMode ? hintForMode(config, commandEditMode) : '',
|
|
75
92
|
}
|
|
@@ -77,6 +94,7 @@ export function getStatusBarModel(
|
|
|
77
94
|
case 'navigation':
|
|
78
95
|
default:
|
|
79
96
|
return {
|
|
97
|
+
help: helpHintForMode(config, 'navigation'),
|
|
80
98
|
left: `${sessionIcon} ${sessionLabel} ${getActiveTabLabel(activeTab)}`,
|
|
81
99
|
right: hintForMode(config, 'navigation'),
|
|
82
100
|
}
|
package/src/ui/theme.ts
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Theme, type ThemeId, THEMES } from './themes'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const DEFAULT = THEMES['aimux']
|
|
4
|
+
if (!DEFAULT) throw new Error('default theme missing')
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
// Mutable theme singleton. Components access `theme.colors['editor.foreground']`
|
|
7
|
+
// directly. On `applyTheme(id)` the colors map is replaced in place so live
|
|
8
|
+
// imports keep pointing at the current palette.
|
|
9
|
+
export const theme: Theme = {
|
|
10
|
+
bg: DEFAULT.bg,
|
|
11
|
+
colors: { ...DEFAULT.colors },
|
|
12
|
+
displayName: DEFAULT.displayName,
|
|
13
|
+
fg: DEFAULT.fg,
|
|
14
|
+
name: DEFAULT.name,
|
|
15
|
+
settings: DEFAULT.settings,
|
|
16
|
+
type: DEFAULT.type,
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
export function applyTheme(id: ThemeId): void {
|
|
14
20
|
const entry = THEMES[id]
|
|
15
21
|
if (!entry) return
|
|
16
|
-
Object.
|
|
17
|
-
|
|
22
|
+
for (const key of Object.keys(theme.colors)) delete theme.colors[key]
|
|
23
|
+
Object.assign(theme.colors, entry.colors)
|
|
24
|
+
theme.name = entry.name
|
|
25
|
+
theme.displayName = entry.displayName
|
|
26
|
+
theme.type = entry.type
|
|
27
|
+
theme.fg = entry.fg
|
|
28
|
+
theme.bg = entry.bg
|
|
29
|
+
theme.settings = entry.settings
|
|
18
30
|
}
|