@duckmind/dm-darwin-x64 0.35.4 → 0.35.5
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/dm +0 -0
- package/extensions/.dm-extensions.json +1 -27
- package/package.json +1 -1
- package/extensions/dm-alps/LICENSE +0 -21
- package/extensions/dm-alps/README.md +0 -22
- package/extensions/dm-alps/index.ts +0 -172
- package/extensions/dm-alps/package.json +0 -49
- package/extensions/dm-alps/src/commands.ts +0 -208
- package/extensions/dm-alps/src/features/animations/debug.ts +0 -160
- package/extensions/dm-alps/src/features/animations/index.ts +0 -33
- package/extensions/dm-alps/src/features/animations/patch.ts +0 -112
- package/extensions/dm-alps/src/features/animations/preview.ts +0 -117
- package/extensions/dm-alps/src/features/animations/registry.ts +0 -593
- package/extensions/dm-alps/src/features/animations/runtime.ts +0 -574
- package/extensions/dm-alps/src/features/animations/settings.ts +0 -69
- package/extensions/dm-alps/src/features/bottom-input/cluster.ts +0 -2
- package/extensions/dm-alps/src/features/bottom-input/compositor.ts +0 -2
- package/extensions/dm-alps/src/features/bottom-input/editor.ts +0 -148
- package/extensions/dm-alps/src/features/bottom-input/frame.ts +0 -224
- package/extensions/dm-alps/src/features/bottom-input/icons.ts +0 -36
- package/extensions/dm-alps/src/features/bottom-input/index.ts +0 -8
- package/extensions/dm-alps/src/features/bottom-input/runtime.ts +0 -1197
- package/extensions/dm-alps/src/features/bottom-input/shortcuts.ts +0 -286
- package/extensions/dm-alps/src/features/bottom-input/status.ts +0 -663
- package/extensions/dm-alps/src/features/bottom-status/index.ts +0 -2
- package/extensions/dm-alps/src/features/chrome-frame/chrome.ts +0 -222
- package/extensions/dm-alps/src/features/chrome-frame/debug.ts +0 -212
- package/extensions/dm-alps/src/features/chrome-frame/image.ts +0 -11
- package/extensions/dm-alps/src/features/chrome-frame/index.ts +0 -4
- package/extensions/dm-alps/src/features/chrome-frame/osc.ts +0 -111
- package/extensions/dm-alps/src/features/chrome-frame/patch.ts +0 -769
- package/extensions/dm-alps/src/features/chrome-frame/preview.ts +0 -67
- package/extensions/dm-alps/src/features/chrome-frame/styles.ts +0 -105
- package/extensions/dm-alps/src/features/fixed-bottom-editor/cluster.ts +0 -161
- package/extensions/dm-alps/src/features/fixed-bottom-editor/compositor.ts +0 -1149
- package/extensions/dm-alps/src/features/fixed-bottom-editor/index.ts +0 -3
- package/extensions/dm-alps/src/features/fixed-bottom-editor/runtime.ts +0 -3
- package/extensions/dm-alps/src/settings-store.ts +0 -194
- package/extensions/dm-alps/src/settings-ui.ts +0 -653
- package/extensions/dm-alps/src/settings.ts +0 -102
- package/extensions/dm-alps/src/terminal-sanitizer.ts +0 -91
- package/extensions/dm-alps/themes/LICENSE.synthwave-84 +0 -21
- package/extensions/dm-alps/themes/alps.json +0 -93
|
@@ -1,653 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { Container, Key, matchesKey, SettingsList, truncateToWidth, visibleWidth, type Component, type SettingItem, type SettingsListTheme } from "@mariozechner/pi-tui";
|
|
3
|
-
import { type PatchState, disablePatch, enablePatch, getGlobalPatchState } from "./features/chrome-frame/index.ts";
|
|
4
|
-
import type { AlpsDmSettings, FixedBottomEditorStatus } from "./settings.ts";
|
|
5
|
-
import { ANIMATION_FPS_VALUES, ANIMATION_WIDTH_VALUES } from "./features/animations/settings.ts";
|
|
6
|
-
import { getAnimationsForCategory } from "./features/animations/registry.ts";
|
|
7
|
-
import { AnimationsPreviewComponent } from "./features/animations/preview.ts";
|
|
8
|
-
import type { ThemeLike } from "./features/chrome-frame/styles.ts";
|
|
9
|
-
import {
|
|
10
|
-
DEFAULT_BOTTOM_INPUT_SHORTCUTS,
|
|
11
|
-
SHORTCUT_KEYS,
|
|
12
|
-
SHORTCUT_LABELS,
|
|
13
|
-
shortcutFromRawInput,
|
|
14
|
-
validateShortcutChange,
|
|
15
|
-
type BottomInputShortcutKey,
|
|
16
|
-
} from "./features/bottom-input/shortcuts.ts";
|
|
17
|
-
|
|
18
|
-
export type SettingsPanelOps = {
|
|
19
|
-
getState?: () => PatchState;
|
|
20
|
-
enableChromeFrame?: () => PatchState;
|
|
21
|
-
disableChromeFrame?: () => PatchState;
|
|
22
|
-
setFixedBottomEditorEnabled?: (enabled: boolean) => FixedBottomEditorStatus | void;
|
|
23
|
-
setBeautifiedInputEnabled?: (enabled: boolean) => FixedBottomEditorStatus | void;
|
|
24
|
-
onSettingsChanged?: (settings: AlpsDmSettings) => void;
|
|
25
|
-
requestRender?: () => void;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const MAIN_MAX_VISIBLE = 10;
|
|
29
|
-
const ANIMATIONS_MAX_VISIBLE = 8;
|
|
30
|
-
const SHORTCUT_MAX_VISIBLE = 8;
|
|
31
|
-
const ON = "ON";
|
|
32
|
-
const OFF = "OFF";
|
|
33
|
-
const CONFIGURE = "configure";
|
|
34
|
-
|
|
35
|
-
const SHORTCUT_DESCRIPTIONS: Record<BottomInputShortcutKey, string> = {
|
|
36
|
-
stashEditor: "Stash or restore the current input",
|
|
37
|
-
copyEditor: "Copy the current input text",
|
|
38
|
-
cutEditor: "Cut the current input text",
|
|
39
|
-
scrollChatUp: "Scroll chat up",
|
|
40
|
-
scrollChatDown: "Scroll chat down",
|
|
41
|
-
editorStart: "Move cursor to input start",
|
|
42
|
-
editorEnd: "Move cursor to input end",
|
|
43
|
-
jumpPreviousUserMessage: "Jump to previous user message",
|
|
44
|
-
jumpNextUserMessage: "Jump to next user message",
|
|
45
|
-
jumpPreviousAssistantMessage: "Jump to previous assistant message",
|
|
46
|
-
jumpNextAssistantMessage: "Jump to next assistant message",
|
|
47
|
-
jumpChatBottom: "Return to chat bottom",
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
type MainSettingId =
|
|
51
|
-
| "chromeFrame.enabled"
|
|
52
|
-
| "chromeFrame.assistantFrame"
|
|
53
|
-
| "chromeFrame.toolCompactMode"
|
|
54
|
-
| "chromeFrame.compactEditTool"
|
|
55
|
-
| "fixedBottomEditor.enabled"
|
|
56
|
-
| "beautifiedInput.enabled"
|
|
57
|
-
| "animations"
|
|
58
|
-
| "shortcuts";
|
|
59
|
-
|
|
60
|
-
type AnimationsSettingId =
|
|
61
|
-
| "animations.enabled"
|
|
62
|
-
| "animations.randomMode"
|
|
63
|
-
| "animations.thinking"
|
|
64
|
-
| "animations.working"
|
|
65
|
-
| "animations.tool"
|
|
66
|
-
| "animations.width"
|
|
67
|
-
| "animations.fps"
|
|
68
|
-
| "animations.preview";
|
|
69
|
-
|
|
70
|
-
function booleanLabel(value: boolean): string {
|
|
71
|
-
return value ? ON : OFF;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function booleanValue(value: string): boolean {
|
|
75
|
-
return value === ON;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function isShortcutKey(value: unknown): value is BottomInputShortcutKey {
|
|
79
|
-
return typeof value === "string" && (SHORTCUT_KEYS as readonly string[]).includes(value);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function createNativeSettingsListTheme(theme: ThemeLike): SettingsListTheme {
|
|
83
|
-
return {
|
|
84
|
-
label: (text, selected) => selected ? theme.fg("accent", text) : text,
|
|
85
|
-
value: (text, selected) => selected ? theme.fg("accent", text) : theme.fg("muted", text),
|
|
86
|
-
description: (text) => theme.fg("dim", text),
|
|
87
|
-
cursor: theme.fg("accent", "→ "),
|
|
88
|
-
hint: (text) => theme.fg("dim", text),
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function withSettingsListHint(base: SettingsListTheme, hint: string): SettingsListTheme {
|
|
93
|
-
return {
|
|
94
|
-
...base,
|
|
95
|
-
hint: (text) => base.hint(text.includes("Enter") || text.includes("Type to search") ? ` ${hint}` : text),
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function createSettingsListTheme(theme: ThemeLike, hint: string): SettingsListTheme {
|
|
100
|
-
return withSettingsListHint(createNativeSettingsListTheme(theme), hint);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function safeFg(theme: ThemeLike, token: string, text: string, fallback = "text"): string {
|
|
104
|
-
try {
|
|
105
|
-
return theme.fg(token, text);
|
|
106
|
-
} catch {
|
|
107
|
-
try {
|
|
108
|
-
return theme.fg(fallback, text);
|
|
109
|
-
} catch {
|
|
110
|
-
return text;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function getSettingsListInternals(list: SettingsList): { items?: SettingItem[]; filteredItems?: SettingItem[]; selectedIndex?: number; submenuComponent?: Component | null; closeSubmenu?: () => void } {
|
|
116
|
-
return list as any;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function resetSettingsListSelection(list: SettingsList): void {
|
|
120
|
-
const internals = getSettingsListInternals(list);
|
|
121
|
-
internals.selectedIndex = 0;
|
|
122
|
-
if (Array.isArray(internals.items)) internals.filteredItems = internals.items;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function getSelectedSettingsListItem(list: SettingsList): SettingItem | undefined {
|
|
126
|
-
const internals = getSettingsListInternals(list);
|
|
127
|
-
return typeof internals.selectedIndex === "number" ? internals.items?.[internals.selectedIndex] : undefined;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function hasSettingsListSubmenu(list: SettingsList): boolean {
|
|
131
|
-
return Boolean(getSettingsListInternals(list).submenuComponent);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function closeSettingsListSubmenu(list: SettingsList): void {
|
|
135
|
-
const submenu = getSettingsListInternals(list).submenuComponent as any;
|
|
136
|
-
submenu?.dispose?.();
|
|
137
|
-
getSettingsListInternals(list).closeSubmenu?.();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
class FramedSettingsPanel implements Component {
|
|
141
|
-
private readonly content: Component;
|
|
142
|
-
private readonly theme: ThemeLike;
|
|
143
|
-
|
|
144
|
-
constructor(content: Component, theme: ThemeLike) {
|
|
145
|
-
this.content = content;
|
|
146
|
-
this.theme = theme;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
render(width: number): string[] {
|
|
150
|
-
const safeWidth = Math.max(1, Math.floor(width));
|
|
151
|
-
if (safeWidth < 8) return this.content.render(safeWidth);
|
|
152
|
-
const innerWidth = Math.max(1, safeWidth - 4);
|
|
153
|
-
const contentLines = this.content.render(innerWidth);
|
|
154
|
-
return [
|
|
155
|
-
this.border("╭" + "─".repeat(Math.max(0, safeWidth - 2)) + "╮"),
|
|
156
|
-
...contentLines.map((line) => this.contentLine(line, safeWidth)),
|
|
157
|
-
this.border("╰" + "─".repeat(Math.max(0, safeWidth - 2)) + "╯"),
|
|
158
|
-
];
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
invalidate(): void {
|
|
162
|
-
this.content.invalidate?.();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
handleInput(data: string): void {
|
|
166
|
-
this.content.handleInput?.(data);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
private contentLine(line: string, width: number): string {
|
|
170
|
-
const innerWidth = Math.max(0, width - 4);
|
|
171
|
-
const clipped = truncateToWidth(line, innerWidth, "", false);
|
|
172
|
-
const padding = " ".repeat(Math.max(0, innerWidth - visibleWidth(clipped)));
|
|
173
|
-
return this.border("│") + " " + clipped + padding + " " + this.border("│");
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
private border(text: string): string {
|
|
177
|
-
return safeFg(this.theme, "borderAccent", text, "border");
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export class AlpsDmSettingsComponent extends Container {
|
|
182
|
-
private readonly done?: () => void;
|
|
183
|
-
private readonly ops: Required<SettingsPanelOps>;
|
|
184
|
-
private readonly listTheme: SettingsListTheme;
|
|
185
|
-
private readonly settingsList: SettingsList;
|
|
186
|
-
private closed = false;
|
|
187
|
-
|
|
188
|
-
constructor(theme: ThemeLike, done?: () => void, ops: SettingsPanelOps = {}) {
|
|
189
|
-
super();
|
|
190
|
-
this.listTheme = createSettingsListTheme(theme, "↑/↓ select · Enter/Space toggle · Esc/q close");
|
|
191
|
-
this.done = done;
|
|
192
|
-
this.ops = {
|
|
193
|
-
getState: ops.getState ?? getGlobalPatchState,
|
|
194
|
-
enableChromeFrame: ops.enableChromeFrame ?? (() => enablePatch()),
|
|
195
|
-
disableChromeFrame: ops.disableChromeFrame ?? (() => disablePatch()),
|
|
196
|
-
setFixedBottomEditorEnabled: ops.setFixedBottomEditorEnabled ?? (() => undefined),
|
|
197
|
-
setBeautifiedInputEnabled: ops.setBeautifiedInputEnabled ?? (() => undefined),
|
|
198
|
-
onSettingsChanged: ops.onSettingsChanged ?? (() => undefined),
|
|
199
|
-
requestRender: ops.requestRender ?? (() => undefined),
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
this.settingsList = new SettingsList(
|
|
203
|
-
this.createMainItems(),
|
|
204
|
-
MAIN_MAX_VISIBLE,
|
|
205
|
-
this.listTheme,
|
|
206
|
-
(id, newValue) => this.handleMainChange(id as MainSettingId, newValue),
|
|
207
|
-
() => this.close(),
|
|
208
|
-
{ enableSearch: true },
|
|
209
|
-
);
|
|
210
|
-
this.syncAllMainValues();
|
|
211
|
-
resetSettingsListSelection(this.settingsList);
|
|
212
|
-
this.addChild(new FramedSettingsPanel(this.settingsList, theme));
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
getSettingsList(): SettingsList {
|
|
216
|
-
return this.settingsList;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
handleInput(data: string): void {
|
|
220
|
-
if ((data === "q" || data === "Q" || matchesKey(data, Key.ctrl("c"))) && !this.hasActiveSubmenu()) {
|
|
221
|
-
this.close();
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
this.settingsList.handleInput(data);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
private createMainItems(): SettingItem[] {
|
|
228
|
-
const settings = this.ops.getState().config.settings;
|
|
229
|
-
return [
|
|
230
|
-
{
|
|
231
|
-
id: "chromeFrame.enabled",
|
|
232
|
-
label: "Message Frame",
|
|
233
|
-
description: "Control message, tool, and bash frames",
|
|
234
|
-
currentValue: booleanLabel(settings.chromeFrame.enabled),
|
|
235
|
-
values: [ON, OFF],
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
id: "chromeFrame.assistantFrame",
|
|
239
|
-
label: "Assistant Frame",
|
|
240
|
-
description: "Control whether assistant replies are framed",
|
|
241
|
-
currentValue: booleanLabel(settings.chromeFrame.assistantFrame),
|
|
242
|
-
values: [ON, OFF],
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
id: "chromeFrame.toolCompactMode",
|
|
246
|
-
label: "Compact Tools",
|
|
247
|
-
description: "Show only the first useful line for collapsed tools",
|
|
248
|
-
currentValue: booleanLabel(settings.chromeFrame.toolCompactMode),
|
|
249
|
-
values: [ON, OFF],
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
id: "chromeFrame.compactEditTool",
|
|
253
|
-
label: "Compact Edit",
|
|
254
|
-
description: "Allow edit tools to use compact display",
|
|
255
|
-
currentValue: booleanLabel(settings.chromeFrame.compactEditTool),
|
|
256
|
-
values: [ON, OFF],
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
id: "fixedBottomEditor.enabled",
|
|
260
|
-
label: "Fixed Input",
|
|
261
|
-
description: "Control the fixed bottom input runtime",
|
|
262
|
-
currentValue: booleanLabel(settings.fixedBottomEditor.enabled),
|
|
263
|
-
values: [ON, OFF],
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
id: "beautifiedInput.enabled",
|
|
267
|
-
label: "Beautified Input",
|
|
268
|
-
description: "Control input frame and embedded border state",
|
|
269
|
-
currentValue: booleanLabel(settings.beautifiedInput.enabled),
|
|
270
|
-
values: [ON, OFF],
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
id: "animations",
|
|
274
|
-
label: "Animations",
|
|
275
|
-
description: "Configure bottom and hidden-thinking animations",
|
|
276
|
-
currentValue: CONFIGURE,
|
|
277
|
-
submenu: (_currentValue, done) => new AnimationsSettingsSubmenu(this.ops, () => done(), this.listTheme),
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
id: "shortcuts",
|
|
281
|
-
label: "Shortcuts",
|
|
282
|
-
description: "Manage bottom input shortcuts",
|
|
283
|
-
currentValue: CONFIGURE,
|
|
284
|
-
submenu: (_currentValue, done) => new ShortcutSettingsSubmenu(this.ops, () => done(), this.listTheme),
|
|
285
|
-
},
|
|
286
|
-
];
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
private handleMainChange(id: MainSettingId, newValue: string): void {
|
|
290
|
-
const state = this.ops.getState();
|
|
291
|
-
switch (id) {
|
|
292
|
-
case "chromeFrame.enabled":
|
|
293
|
-
if (booleanValue(newValue)) {
|
|
294
|
-
this.ops.enableChromeFrame();
|
|
295
|
-
} else {
|
|
296
|
-
this.ops.disableChromeFrame();
|
|
297
|
-
}
|
|
298
|
-
this.syncMainValue(id, state.config.settings.chromeFrame.enabled);
|
|
299
|
-
return;
|
|
300
|
-
case "chromeFrame.assistantFrame":
|
|
301
|
-
state.config.settings.chromeFrame.assistantFrame = booleanValue(newValue);
|
|
302
|
-
this.ops.onSettingsChanged(state.config.settings);
|
|
303
|
-
return;
|
|
304
|
-
case "chromeFrame.toolCompactMode":
|
|
305
|
-
state.config.settings.chromeFrame.toolCompactMode = booleanValue(newValue);
|
|
306
|
-
this.ops.onSettingsChanged(state.config.settings);
|
|
307
|
-
return;
|
|
308
|
-
case "chromeFrame.compactEditTool":
|
|
309
|
-
state.config.settings.chromeFrame.compactEditTool = booleanValue(newValue);
|
|
310
|
-
this.ops.onSettingsChanged(state.config.settings);
|
|
311
|
-
return;
|
|
312
|
-
case "fixedBottomEditor.enabled": {
|
|
313
|
-
const nextEnabled = booleanValue(newValue);
|
|
314
|
-
state.config.settings.fixedBottomEditor.enabled = nextEnabled;
|
|
315
|
-
this.ops.setFixedBottomEditorEnabled(nextEnabled);
|
|
316
|
-
this.syncMainValue(id, state.config.settings.fixedBottomEditor.enabled);
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
case "beautifiedInput.enabled": {
|
|
320
|
-
state.config.settings.beautifiedInput.enabled = booleanValue(newValue);
|
|
321
|
-
this.ops.setBeautifiedInputEnabled(state.config.settings.beautifiedInput.enabled);
|
|
322
|
-
this.syncMainValue(id, state.config.settings.beautifiedInput.enabled);
|
|
323
|
-
this.syncMainValue("fixedBottomEditor.enabled", state.config.settings.fixedBottomEditor.enabled);
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
private syncAllMainValues(): void {
|
|
330
|
-
const settings = this.ops.getState().config.settings;
|
|
331
|
-
this.syncMainValue("chromeFrame.enabled", settings.chromeFrame.enabled);
|
|
332
|
-
this.syncMainValue("chromeFrame.assistantFrame", settings.chromeFrame.assistantFrame);
|
|
333
|
-
this.syncMainValue("chromeFrame.toolCompactMode", settings.chromeFrame.toolCompactMode);
|
|
334
|
-
this.syncMainValue("chromeFrame.compactEditTool", settings.chromeFrame.compactEditTool);
|
|
335
|
-
this.syncMainValue("fixedBottomEditor.enabled", settings.fixedBottomEditor.enabled);
|
|
336
|
-
this.syncMainValue("beautifiedInput.enabled", settings.beautifiedInput.enabled);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
private syncMainValue(id: MainSettingId, value: boolean): void {
|
|
340
|
-
this.settingsList.updateValue(id, booleanLabel(value));
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
private hasActiveSubmenu(): boolean {
|
|
344
|
-
return hasSettingsListSubmenu(this.settingsList);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
private close(): void {
|
|
348
|
-
if (this.closed) return;
|
|
349
|
-
this.closed = true;
|
|
350
|
-
this.done?.();
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
class AnimationsSettingsSubmenu extends Container {
|
|
355
|
-
private readonly ops: Required<SettingsPanelOps>;
|
|
356
|
-
private readonly onCancel: () => void;
|
|
357
|
-
private readonly listTheme: SettingsListTheme;
|
|
358
|
-
private readonly settingsList: SettingsList;
|
|
359
|
-
|
|
360
|
-
constructor(ops: Required<SettingsPanelOps>, onCancel: () => void, listTheme: SettingsListTheme) {
|
|
361
|
-
super();
|
|
362
|
-
this.ops = ops;
|
|
363
|
-
this.onCancel = onCancel;
|
|
364
|
-
this.listTheme = listTheme;
|
|
365
|
-
this.settingsList = new SettingsList(
|
|
366
|
-
this.createAnimationItems(),
|
|
367
|
-
ANIMATIONS_MAX_VISIBLE,
|
|
368
|
-
withSettingsListHint(listTheme, "↑/↓ select · Enter/Space change · Esc back"),
|
|
369
|
-
(id, newValue) => this.handleChange(id as AnimationsSettingId, newValue),
|
|
370
|
-
onCancel,
|
|
371
|
-
);
|
|
372
|
-
this.addChild(this.settingsList);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
handleInput(data: string): void {
|
|
376
|
-
if ((data === "q" || data === "Q") && !hasSettingsListSubmenu(this.settingsList)) {
|
|
377
|
-
this.onCancel();
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
this.settingsList.handleInput(data);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
private createAnimationItems(): SettingItem[] {
|
|
384
|
-
const animations = this.ops.getState().config.settings.animations;
|
|
385
|
-
const thinkingValues = getAnimationsForCategory("thinking").map((animation) => animation.name);
|
|
386
|
-
const workingValues = getAnimationsForCategory("working").map((animation) => animation.name);
|
|
387
|
-
return [
|
|
388
|
-
{
|
|
389
|
-
id: "animations.enabled",
|
|
390
|
-
label: "Enabled",
|
|
391
|
-
description: "Enable bottom and hidden-thinking animation replacement",
|
|
392
|
-
currentValue: booleanLabel(animations.enabled),
|
|
393
|
-
values: [ON, OFF],
|
|
394
|
-
},
|
|
395
|
-
{
|
|
396
|
-
id: "animations.randomMode",
|
|
397
|
-
label: "Random Mode",
|
|
398
|
-
description: "Choose a random animation per category each time",
|
|
399
|
-
currentValue: booleanLabel(animations.randomMode),
|
|
400
|
-
values: [ON, OFF],
|
|
401
|
-
},
|
|
402
|
-
{
|
|
403
|
-
id: "animations.thinking",
|
|
404
|
-
label: "Thinking",
|
|
405
|
-
description: "Replace DM native Thinking... animation",
|
|
406
|
-
currentValue: animations.thinking,
|
|
407
|
-
values: thinkingValues,
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
id: "animations.working",
|
|
411
|
-
label: "Working",
|
|
412
|
-
description: "Replace bottom Working... animation",
|
|
413
|
-
currentValue: animations.working,
|
|
414
|
-
values: workingValues,
|
|
415
|
-
},
|
|
416
|
-
{
|
|
417
|
-
id: "animations.tool",
|
|
418
|
-
label: "Tool",
|
|
419
|
-
description: "Bottom animation while a tool runs",
|
|
420
|
-
currentValue: animations.tool,
|
|
421
|
-
values: workingValues,
|
|
422
|
-
},
|
|
423
|
-
{
|
|
424
|
-
id: "animations.width",
|
|
425
|
-
label: "Width",
|
|
426
|
-
description: "Animation render width",
|
|
427
|
-
currentValue: String(animations.width),
|
|
428
|
-
values: ANIMATION_WIDTH_VALUES.map(String),
|
|
429
|
-
},
|
|
430
|
-
{
|
|
431
|
-
id: "animations.fps",
|
|
432
|
-
label: "FPS",
|
|
433
|
-
description: "Animation refresh rate",
|
|
434
|
-
currentValue: String(animations.fps),
|
|
435
|
-
values: ANIMATION_FPS_VALUES.map(String),
|
|
436
|
-
},
|
|
437
|
-
{
|
|
438
|
-
id: "animations.preview",
|
|
439
|
-
label: "Preview",
|
|
440
|
-
description: "Preview all built-in animations",
|
|
441
|
-
currentValue: "open",
|
|
442
|
-
submenu: (_currentValue, done) => new AnimationsPreviewComponent({
|
|
443
|
-
settings: animations,
|
|
444
|
-
theme: this.listThemeToTheme(),
|
|
445
|
-
onClose: done,
|
|
446
|
-
requestRender: this.ops.requestRender,
|
|
447
|
-
}),
|
|
448
|
-
},
|
|
449
|
-
];
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
private listThemeToTheme(): ThemeLike {
|
|
453
|
-
return {
|
|
454
|
-
fg: (token: string, text: string) => token === "dim" ? this.listTheme.description(text) : this.listTheme.value(text, false),
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
private handleChange(id: AnimationsSettingId, newValue: string): void {
|
|
459
|
-
if (id === "animations.preview") return;
|
|
460
|
-
const settings = this.ops.getState().config.settings;
|
|
461
|
-
switch (id) {
|
|
462
|
-
case "animations.enabled":
|
|
463
|
-
settings.animations.enabled = booleanValue(newValue);
|
|
464
|
-
break;
|
|
465
|
-
case "animations.randomMode":
|
|
466
|
-
settings.animations.randomMode = booleanValue(newValue);
|
|
467
|
-
break;
|
|
468
|
-
case "animations.thinking":
|
|
469
|
-
settings.animations.thinking = newValue;
|
|
470
|
-
break;
|
|
471
|
-
case "animations.working":
|
|
472
|
-
settings.animations.working = newValue;
|
|
473
|
-
break;
|
|
474
|
-
case "animations.tool":
|
|
475
|
-
settings.animations.tool = newValue;
|
|
476
|
-
break;
|
|
477
|
-
case "animations.width":
|
|
478
|
-
settings.animations.width = newValue === "full" || newValue === "default" ? newValue : Number(newValue);
|
|
479
|
-
break;
|
|
480
|
-
case "animations.fps":
|
|
481
|
-
settings.animations.fps = Number(newValue);
|
|
482
|
-
break;
|
|
483
|
-
}
|
|
484
|
-
this.ops.onSettingsChanged(settings);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
class ShortcutStatusText implements Component {
|
|
489
|
-
private text = "";
|
|
490
|
-
|
|
491
|
-
setText(text: string): void {
|
|
492
|
-
this.text = text;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
render(_width: number): string[] {
|
|
496
|
-
return this.text ? ["", ` ${this.text}`] : [];
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
invalidate(): void {}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
class ShortcutSettingsSubmenu extends Container {
|
|
503
|
-
private readonly ops: Required<SettingsPanelOps>;
|
|
504
|
-
private readonly onCancel: () => void;
|
|
505
|
-
private readonly message: ShortcutStatusText;
|
|
506
|
-
private readonly settingsList: SettingsList;
|
|
507
|
-
|
|
508
|
-
constructor(ops: Required<SettingsPanelOps>, onCancel: () => void, listTheme: SettingsListTheme) {
|
|
509
|
-
super();
|
|
510
|
-
this.ops = ops;
|
|
511
|
-
this.onCancel = onCancel;
|
|
512
|
-
this.message = new ShortcutStatusText();
|
|
513
|
-
this.settingsList = new SettingsList(
|
|
514
|
-
this.createShortcutItems(),
|
|
515
|
-
SHORTCUT_MAX_VISIBLE,
|
|
516
|
-
withSettingsListHint(listTheme, "↑/↓ select · Enter capture · Backspace default · Esc back"),
|
|
517
|
-
(id) => this.startCapture(id as BottomInputShortcutKey),
|
|
518
|
-
onCancel,
|
|
519
|
-
);
|
|
520
|
-
this.addChild(this.settingsList);
|
|
521
|
-
this.addChild(this.message);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
handleInput(data: string): void {
|
|
525
|
-
if (this.hasActiveCapture()) {
|
|
526
|
-
if (matchesKey(data, Key.escape)) {
|
|
527
|
-
this.settingsList.handleInput(data);
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
if (matchesKey(data, Key.backspace)) {
|
|
531
|
-
if (this.restoreSelectedShortcutDefault()) {
|
|
532
|
-
this.closeActiveCapture();
|
|
533
|
-
}
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
const key = this.getActiveCaptureKey();
|
|
537
|
-
const shortcut = shortcutFromRawInput(data);
|
|
538
|
-
if (!key || !shortcut) {
|
|
539
|
-
this.showMessage("Unrecognized shortcut");
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
if (this.applyShortcut(key, shortcut, "Saved")) {
|
|
543
|
-
this.closeActiveCapture();
|
|
544
|
-
}
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
if (matchesKey(data, Key.backspace)) {
|
|
548
|
-
this.restoreSelectedShortcutDefault();
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
if (data === "q" || data === "Q") {
|
|
552
|
-
this.onCancel();
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
this.settingsList.handleInput(data);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
private createShortcutItems(): SettingItem[] {
|
|
559
|
-
const shortcuts = this.ops.getState().config.settings.shortcuts;
|
|
560
|
-
return SHORTCUT_KEYS.map((key) => ({
|
|
561
|
-
id: key,
|
|
562
|
-
label: SHORTCUT_LABELS[key],
|
|
563
|
-
description: SHORTCUT_DESCRIPTIONS[key],
|
|
564
|
-
currentValue: shortcuts[key],
|
|
565
|
-
submenu: (_currentValue, done) => {
|
|
566
|
-
this.startCapture(key);
|
|
567
|
-
return new ShortcutCaptureComponent(key, (message) => this.showMessage(message), done);
|
|
568
|
-
},
|
|
569
|
-
}));
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
private startCapture(key: BottomInputShortcutKey): void {
|
|
573
|
-
this.showMessage(`Editing: ${SHORTCUT_LABELS[key]} · Esc cancel · Backspace default`);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
private restoreSelectedShortcutDefault(): boolean {
|
|
577
|
-
const key = this.getSelectedShortcutKey();
|
|
578
|
-
if (!key) return false;
|
|
579
|
-
return this.restoreShortcutDefault(key);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
private restoreShortcutDefault(key: BottomInputShortcutKey): boolean {
|
|
583
|
-
return this.applyShortcut(key, DEFAULT_BOTTOM_INPUT_SHORTCUTS[key], "Restored default");
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
private applyShortcut(key: BottomInputShortcutKey, shortcut: string, successMessage: string): boolean {
|
|
587
|
-
const settings = this.ops.getState().config.settings;
|
|
588
|
-
const validation = validateShortcutChange(settings.shortcuts, key, shortcut);
|
|
589
|
-
if (!validation.ok) {
|
|
590
|
-
this.showMessage(validation.reason);
|
|
591
|
-
return false;
|
|
592
|
-
}
|
|
593
|
-
settings.shortcuts[key] = validation.shortcut;
|
|
594
|
-
this.settingsList.updateValue(key, validation.shortcut);
|
|
595
|
-
this.ops.onSettingsChanged(settings);
|
|
596
|
-
this.showMessage(successMessage);
|
|
597
|
-
return true;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
private hasActiveCapture(): boolean {
|
|
601
|
-
return hasSettingsListSubmenu(this.settingsList);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
private getActiveCaptureKey(): BottomInputShortcutKey | undefined {
|
|
605
|
-
return this.getSelectedShortcutKey();
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
private closeActiveCapture(): void {
|
|
609
|
-
closeSettingsListSubmenu(this.settingsList);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
private getSelectedShortcutKey(): BottomInputShortcutKey | undefined {
|
|
613
|
-
const item = getSelectedSettingsListItem(this.settingsList);
|
|
614
|
-
return isShortcutKey(item?.id) ? item.id : undefined;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
private showMessage(text: string): void {
|
|
618
|
-
this.message.setText(text);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
class ShortcutCaptureComponent implements Component {
|
|
623
|
-
private readonly shortcutKey: BottomInputShortcutKey;
|
|
624
|
-
private readonly onMessage: (message: string) => void;
|
|
625
|
-
private readonly onDone: () => void;
|
|
626
|
-
|
|
627
|
-
constructor(shortcutKey: BottomInputShortcutKey, onMessage: (message: string) => void, onDone: () => void) {
|
|
628
|
-
this.shortcutKey = shortcutKey;
|
|
629
|
-
this.onMessage = onMessage;
|
|
630
|
-
this.onDone = onDone;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
render(_width: number): string[] {
|
|
634
|
-
return [
|
|
635
|
-
`Editing: ${SHORTCUT_LABELS[this.shortcutKey]}`,
|
|
636
|
-
"",
|
|
637
|
-
"Press a new shortcut",
|
|
638
|
-
];
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
invalidate(): void {}
|
|
642
|
-
|
|
643
|
-
handleInput(data: string): void {
|
|
644
|
-
if (matchesKey(data, Key.escape)) {
|
|
645
|
-
this.onMessage("Cancelled");
|
|
646
|
-
this.onDone();
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
export function createSettingsComponent(theme: ThemeLike, done?: () => void, ops: SettingsPanelOps = {}): Component {
|
|
652
|
-
return new AlpsDmSettingsComponent(theme, done, ops);
|
|
653
|
-
}
|