@timurproko/a1 0.2.6-dev.727 → 0.2.6-dev.736
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/app/session-shell/clipboard-services.d.ts +19 -0
- package/dist/app/session-shell/clipboard-services.js +7 -0
- package/dist/app/session-shell/exit-notice.d.ts +9 -5
- package/dist/app/session-shell/exit-notice.js +5 -5
- package/dist/app/session-shell/index.d.ts +4 -2
- package/dist/app/session-shell/index.js +2 -1
- package/dist/app/session-shell/paste-preparation-client.d.ts +3 -0
- package/dist/app/session-shell/paste-preparation-client.js +5 -2
- package/dist/app/session-shell/path-chip-presentation.d.ts +10 -3
- package/dist/app/session-shell/path-chip-presentation.js +48 -6
- package/dist/app/session-shell/prompt-chips.js +30 -4
- package/dist/app/session-shell/quit-outro.d.ts +3 -1
- package/dist/app/session-shell/quit-outro.js +23 -8
- package/dist/app/session-shell/response-copy-transport.d.ts +6 -1
- package/dist/app/session-shell/response-copy-transport.js +8 -2
- package/dist/app/session-shell/session-shell-root.d.ts +29 -11
- package/dist/app/session-shell/session-shell-root.js +62 -53
- package/dist/app/session-shell/session-shell.d.ts +55 -29
- package/dist/app/session-shell/session-shell.js +748 -1132
- package/dist/app/session-shell/shell-host.d.ts +105 -0
- package/dist/app/session-shell/shell-host.js +523 -0
- package/dist/cli/session-context.js +6 -3
- package/dist/composition/owned-ui.d.ts +20 -2
- package/dist/composition/owned-ui.js +107 -56
- package/dist/composition/session-info-reference.d.ts +2 -2
- package/dist/composition/session-info-reference.js +2 -2
- package/dist/contracts/agent-engine/capability-ports.d.ts +1 -85
- package/dist/contracts/agent-engine/capability-ports.js +1 -35
- package/dist/contracts/agent-engine/index.d.ts +1 -7
- package/dist/contracts/agent-engine/index.js +0 -4
- package/dist/contracts/owned-ui/index.d.ts +5 -1
- package/dist/contracts/owned-ui/index.js +1 -0
- package/dist/contracts/owned-ui/model.d.ts +6 -0
- package/dist/contracts/owned-ui/session-backend.d.ts +119 -0
- package/dist/contracts/owned-ui/session-presenters.d.ts +82 -0
- package/dist/contracts/owned-ui/session-presenters.js +1 -0
- package/dist/contracts/owned-ui/session-workflows.d.ts +342 -0
- package/dist/contracts/owned-ui/session-workflows.js +30 -0
- package/dist/features/launch/index.d.ts +1 -1
- package/dist/features/launch/index.js +1 -1
- package/dist/features/launch/session-context.d.ts +1 -0
- package/dist/features/launch/session-context.js +1 -0
- package/dist/features/owned-ui/settings-app.js +35 -15
- package/dist/foundation/lifecycle/index.d.ts +1 -1
- package/dist/foundation/lifecycle/index.js +1 -1
- package/dist/foundation/lifecycle/session-repository-context.d.ts +8 -2
- package/dist/foundation/lifecycle/session-repository-context.js +200 -65
- package/dist/foundation/release/bootstrap.js +1 -2
- package/dist/foundation/release/release-gc.js +1 -1
- package/dist/foundation/release/update.js +0 -4
- package/dist/foundation/storage/control-store.js +0 -12
- package/dist/integrations/pi/components/components.js +0 -16
- package/dist/integrations/pi/components/index.d.ts +3 -3
- package/dist/integrations/pi/components/index.js +2 -2
- package/dist/integrations/pi/components/models-dialog.js +1 -30
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +11 -9
- package/dist/integrations/pi/components/shell-extension-ui.d.ts +3 -3
- package/dist/integrations/pi/components/shell-extension-ui.js +6 -2
- package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -4
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +21 -1
- package/dist/integrations/pi/components/shell-shared-facade.js +18 -2
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +5 -1
- package/dist/integrations/pi/components/upstream/theme/theme.js +20 -2
- package/dist/integrations/pi/engine/adapter.d.ts +44 -24
- package/dist/integrations/pi/engine/adapter.js +130 -22
- package/dist/integrations/pi/engine/command-dispatch.d.ts +2 -5
- package/dist/integrations/pi/engine/extension-ui-binding.d.ts +2 -11
- package/dist/integrations/pi/engine/host-ports.d.ts +20 -0
- package/dist/integrations/pi/engine/host-ports.js +13 -0
- package/dist/integrations/pi/engine/host.d.ts +70 -0
- package/dist/integrations/pi/engine/host.js +147 -0
- package/dist/integrations/pi/engine/index.d.ts +8 -4
- package/dist/integrations/pi/engine/index.js +3 -1
- package/dist/integrations/pi/engine/resource-catalog.d.ts +4 -23
- package/dist/integrations/pi/engine/runtime-integration.d.ts +8 -2
- package/dist/integrations/pi/engine/runtime-integration.js +12 -7
- package/dist/integrations/pi/engine/session-integration.d.ts +3 -8
- package/dist/integrations/pi/engine/session-integration.js +0 -49
- package/dist/integrations/pi/engine/session-runtime.d.ts +18 -3
- package/dist/integrations/pi/engine/session-runtime.js +18 -7
- package/dist/integrations/pi/engine/settings-bridge.d.ts +2 -2
- package/dist/integrations/pi/engine/settings-bridge.js +0 -2
- package/dist/integrations/pi/engine/settings-port.d.ts +2 -0
- package/dist/integrations/pi/engine/settings-port.js +3 -5
- package/dist/integrations/pi/engine/workflows.d.ts +33 -260
- package/dist/integrations/pi/engine/workflows.js +6 -28
- package/dist/integrations/pi/session-presenters/index.d.ts +2 -0
- package/dist/integrations/pi/session-presenters/index.js +1 -0
- package/dist/integrations/pi/session-presenters/presenters.d.ts +16 -0
- package/dist/integrations/pi/session-presenters/presenters.js +341 -0
- package/dist/integrations/pi/tui-runtime/adapter.js +0 -2
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +18 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +84 -3
- package/dist/integrations/pi/tui-runtime/index.d.ts +1 -1
- package/dist/integrations/pi/tui-runtime/index.js +1 -1
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/ui/settings/declarations.d.ts +13 -1
- package/dist/ui/settings/declarations.js +12 -1
- package/dist/ui/settings/migrations.js +7 -0
- package/docs/architecture/boundaries.md +9 -1
- package/docs/architecture/project-structure.md +3 -2
- package/docs/architecture/ui-reference-provenance.md +1 -1
- package/package.json +1 -1
- package/dist/contracts/agent-engine/model.d.ts +0 -78
- package/dist/contracts/agent-engine/model.js +0 -1
- package/dist/contracts/agent-engine/ports.d.ts +0 -18
- package/dist/contracts/agent-engine/serialization.d.ts +0 -7
- package/dist/contracts/agent-engine/serialization.js +0 -58
- package/dist/contracts/agent-engine/validation.d.ts +0 -5
- package/dist/contracts/agent-engine/validation.js +0 -128
- package/dist/integrations/pi/components/upstream/theme/system-theme.d.ts +0 -61
- package/dist/integrations/pi/components/upstream/theme/system-theme.js +0 -709
- /package/dist/contracts/{agent-engine/ports.js → owned-ui/session-backend.js} +0 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { createPiShellModelSelector, createPiShellModelsDialog, createPiShellScopedModelsSelector, createPiShellSessionSelector, createPiShellSettingsSelector, } from "../components/shell-selectors-dialogs.js";
|
|
2
|
+
const MODEL_REFRESH_TIMEOUT_MS = 15_000;
|
|
3
|
+
const MODEL_REFRESH_TIMED_OUT = "Model refresh timed out; showing cached models.";
|
|
4
|
+
/**
|
|
5
|
+
* Builds the selectors and transcript renderers whose inputs are Pi engine objects. This owner is the one place
|
|
6
|
+
* that sees both the engine adapter and the Pi components, so the shell only ever holds the neutral port.
|
|
7
|
+
*/
|
|
8
|
+
export function createPiSessionPresenters(adapter, options = {}) {
|
|
9
|
+
const source = () => adapter.presentationSource();
|
|
10
|
+
let lazySelectors = options.lazySelectors;
|
|
11
|
+
let lazySelectorsPromise;
|
|
12
|
+
// Performance: the tree selector module stays out of the startup graph and loads on first use.
|
|
13
|
+
const loadLazySelectors = () => {
|
|
14
|
+
if (lazySelectors !== undefined)
|
|
15
|
+
return Promise.resolve(lazySelectors);
|
|
16
|
+
return lazySelectorsPromise ??= import("../components/lazy-selectors.js").then(module => {
|
|
17
|
+
lazySelectors = module.piShellLazySelectors;
|
|
18
|
+
return module.piShellLazySelectors;
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
const renderers = {
|
|
22
|
+
getMessageRenderer: customType => opaqueRenderer(source().pinnedMessageRenderer(customType)),
|
|
23
|
+
getToolRenderers: toolName => opaqueRenderer(source().pinnedToolRenderers(toolName)),
|
|
24
|
+
// Compatibility: the component adapter passes Pi's own keybinding config through the neutral port.
|
|
25
|
+
getShortcuts: bindings => source().pinnedShortcutDescriptions(bindings),
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
transcriptRenderers: () => renderers,
|
|
29
|
+
openModelSelector(host, initialSearchInput) {
|
|
30
|
+
const context = source().pinnedModelSelectorContext();
|
|
31
|
+
const close = closeSurface(host);
|
|
32
|
+
const component = createPiShellModelSelector({
|
|
33
|
+
...context,
|
|
34
|
+
runtime: {
|
|
35
|
+
getColumns: () => host.viewport().columns,
|
|
36
|
+
getRows: () => host.viewport().rows,
|
|
37
|
+
requestRender: () => host.requestRender(),
|
|
38
|
+
},
|
|
39
|
+
...(initialSearchInput === undefined ? {} : { initialSearchInput }),
|
|
40
|
+
onSelect: model => {
|
|
41
|
+
close();
|
|
42
|
+
void host.runWorkflow({ command: "model", argument: "", selection: modelReference(model) });
|
|
43
|
+
},
|
|
44
|
+
onSelectAsDefault: model => {
|
|
45
|
+
close();
|
|
46
|
+
void host.runWorkflow({ command: "model", argument: "", selection: modelReference(model), persist: true });
|
|
47
|
+
},
|
|
48
|
+
onCancel: close,
|
|
49
|
+
});
|
|
50
|
+
host.setInputSurface(component);
|
|
51
|
+
host.requestRender();
|
|
52
|
+
},
|
|
53
|
+
async openSessionSelector(host, onExit) {
|
|
54
|
+
const context = source().pinnedSessionSelectorContext();
|
|
55
|
+
const close = closeSurface(host);
|
|
56
|
+
const component = await createPiShellSessionSelector({
|
|
57
|
+
currentSessionsLoader: context.loadCurrentSessions,
|
|
58
|
+
allSessionsLoader: context.loadAllSessions,
|
|
59
|
+
currentSessionFilePath: context.currentSessionFilePath,
|
|
60
|
+
renameSession: context.renameSession,
|
|
61
|
+
requestRender: () => host.requestRender(),
|
|
62
|
+
onSelect: sessionPath => {
|
|
63
|
+
close();
|
|
64
|
+
void host.runWorkflow({ command: "resume", argument: sessionPath });
|
|
65
|
+
},
|
|
66
|
+
onCancel: close,
|
|
67
|
+
onExit: () => {
|
|
68
|
+
close();
|
|
69
|
+
onExit();
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
host.setInputSurface(component);
|
|
73
|
+
host.requestRender();
|
|
74
|
+
},
|
|
75
|
+
async openTreeSelector(host, navigate, initialSelectedId) {
|
|
76
|
+
const context = source().pinnedTreeSelectorContext();
|
|
77
|
+
if (context.tree.length === 0) {
|
|
78
|
+
host.appendWorkflowStatus("No entries in session");
|
|
79
|
+
host.requestRender();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const close = closeSurface(host);
|
|
83
|
+
const releasePresentation = host.beginPresentationHold?.();
|
|
84
|
+
try {
|
|
85
|
+
const selectors = await loadLazySelectors();
|
|
86
|
+
const component = await selectors.createTree({
|
|
87
|
+
tree: context.tree,
|
|
88
|
+
currentLeafId: context.currentLeafId,
|
|
89
|
+
terminalHeight: host.viewport().rows,
|
|
90
|
+
initialFilterMode: context.filterMode,
|
|
91
|
+
...(initialSelectedId === undefined ? {} : { initialSelectedId }),
|
|
92
|
+
onLabelChange: context.appendLabelChange,
|
|
93
|
+
onCopy: text => {
|
|
94
|
+
if (!text) {
|
|
95
|
+
host.appendWorkflowResult({ command: "tree", outcome: "failed", message: "Selected entry has no text to copy" });
|
|
96
|
+
host.requestRender();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const generation = adapter.identity.sessionBindingGeneration;
|
|
100
|
+
const stale = () => host.disposed || generation !== adapter.identity.sessionBindingGeneration;
|
|
101
|
+
void adapter.workflows.copyWorkflowText(text).then(acknowledged => {
|
|
102
|
+
if (stale())
|
|
103
|
+
return;
|
|
104
|
+
host.appendWorkflowStatus(acknowledged ? "Copied selected message to clipboard" : "Submitted selected message to clipboard");
|
|
105
|
+
host.requestRender();
|
|
106
|
+
}).catch(error => {
|
|
107
|
+
if (stale())
|
|
108
|
+
return;
|
|
109
|
+
host.appendWorkflowResult({ command: "tree", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
|
|
110
|
+
host.requestRender();
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
onCancel: close,
|
|
114
|
+
onSelect: entryId => {
|
|
115
|
+
if (entryId === context.currentLeafId) {
|
|
116
|
+
close();
|
|
117
|
+
host.appendWorkflowStatus("Already at this point");
|
|
118
|
+
host.requestRender();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (context.skipSummaryPrompt)
|
|
122
|
+
close();
|
|
123
|
+
navigate(entryId, context.skipSummaryPrompt);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
if (host.disposed)
|
|
127
|
+
return;
|
|
128
|
+
host.setInputSurface(component);
|
|
129
|
+
host.requestRender();
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
releasePresentation?.();
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
openSettingsSelector(host) {
|
|
136
|
+
const snapshot = adapter.settings.snapshot();
|
|
137
|
+
const { currentModel, availableDefaultModels } = source().pinnedSettingsModels();
|
|
138
|
+
const close = closeSurface(host);
|
|
139
|
+
const component = createPiShellSettingsSelector({
|
|
140
|
+
config: {
|
|
141
|
+
...snapshot,
|
|
142
|
+
availableThinkingLevels: [...snapshot.availableThinkingLevels],
|
|
143
|
+
availableThemes: [...snapshot.availableThemes],
|
|
144
|
+
warnings: { ...snapshot.warnings },
|
|
145
|
+
modelThinkingLevels: { ...snapshot.modelThinkingLevels },
|
|
146
|
+
...(currentModel === undefined ? {} : { currentModel: currentModel }),
|
|
147
|
+
availableDefaultModels: availableDefaultModels,
|
|
148
|
+
},
|
|
149
|
+
onChange: (callback, value) => {
|
|
150
|
+
if (callback === "onCancel") {
|
|
151
|
+
close();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (callback === "onTuiModeChange") {
|
|
155
|
+
// Invariant: a surface that cannot change TUI mode (bare A1 is permanently fullscreen) ignores this callback.
|
|
156
|
+
if (host.switchTuiMode === undefined)
|
|
157
|
+
return;
|
|
158
|
+
if (value !== "regular" && value !== "fullscreen")
|
|
159
|
+
return;
|
|
160
|
+
if (!host.switchTuiMode(value)) {
|
|
161
|
+
host.appendWorkflowStatus("Close active overlays before changing TUI mode");
|
|
162
|
+
host.requestRender();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
void source().applyPinnedSettingValue(callback, value).then(result => {
|
|
167
|
+
if (result.outcome === "failed")
|
|
168
|
+
host.appendWorkflowResult(result);
|
|
169
|
+
else if (callback === "onTuiModeChange")
|
|
170
|
+
host.appendWorkflowStatus(`TUI mode: ${value}`);
|
|
171
|
+
host.requestRender();
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
onCancel: close,
|
|
175
|
+
});
|
|
176
|
+
host.setInputSurface(component);
|
|
177
|
+
host.requestRender();
|
|
178
|
+
},
|
|
179
|
+
/** The bare-A1 unified Models dialog: switch on Enter, scope on Space, persist on Ctrl+S, all through the engine. */
|
|
180
|
+
openModelsDialog(host, initialQuery) {
|
|
181
|
+
const catalog = adapter.catalog;
|
|
182
|
+
const context = catalog.modelsContext();
|
|
183
|
+
const available = new Set(context.models.map(model => `${model.provider}/${model.id}`));
|
|
184
|
+
const savedScopeIds = context.persistedScopeIds.filter(id => available.has(id));
|
|
185
|
+
// Invariant: an explicit session scope wins; otherwise the dialog starts from what is persisted, never from "all rows scoped".
|
|
186
|
+
const scopeIds = context.sessionScopeIds.length > 0 ? context.sessionScopeIds : savedScopeIds;
|
|
187
|
+
const refresh = new ModelCatalogRefresh();
|
|
188
|
+
const close = () => {
|
|
189
|
+
refresh.stop();
|
|
190
|
+
host.setInputSurface(null);
|
|
191
|
+
host.requestRender();
|
|
192
|
+
};
|
|
193
|
+
const dialog = createPiShellModelsDialog({
|
|
194
|
+
models: context.models,
|
|
195
|
+
activeModelId: context.activeModelId,
|
|
196
|
+
scopeIds,
|
|
197
|
+
savedScopeIds,
|
|
198
|
+
...(initialQuery === undefined ? {} : { initialQuery }),
|
|
199
|
+
refreshStatus: "Refreshing model catalogs…",
|
|
200
|
+
requestRender: () => host.requestRender(),
|
|
201
|
+
onSelect: modelId => {
|
|
202
|
+
void host.runWorkflow({ command: "models", argument: "", selection: modelId }).then(result => {
|
|
203
|
+
if (!refresh.stopped && result.outcome === "completed")
|
|
204
|
+
close();
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
onScopeChange: ids => {
|
|
208
|
+
catalog.setSessionModelScope(ids);
|
|
209
|
+
host.requestRender();
|
|
210
|
+
},
|
|
211
|
+
onSave: ids => {
|
|
212
|
+
try {
|
|
213
|
+
catalog.persistModelScope(ids);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
host.appendWorkflowResult({ command: "models", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
|
|
217
|
+
host.requestRender();
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
host.appendWorkflowStatus("Model selection saved to settings");
|
|
221
|
+
host.requestRender();
|
|
222
|
+
},
|
|
223
|
+
onCancel: close,
|
|
224
|
+
});
|
|
225
|
+
const component = {
|
|
226
|
+
...dialog,
|
|
227
|
+
dispose: () => {
|
|
228
|
+
refresh.stop();
|
|
229
|
+
dialog.dispose?.();
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
host.setInputSurface(component);
|
|
233
|
+
host.requestRender();
|
|
234
|
+
refresh.run(host, component, signal => catalog.refreshModels(signal), refreshed => component.updateModels(refreshed.models));
|
|
235
|
+
},
|
|
236
|
+
openScopedModelsSelector(host) {
|
|
237
|
+
const catalog = adapter.catalog;
|
|
238
|
+
const initial = catalog.pinnedScopedModelsContext();
|
|
239
|
+
let currentEnabledIds = initial.enabledModelIds === null ? null : [...initial.enabledModelIds];
|
|
240
|
+
let selectionChanged = false;
|
|
241
|
+
const refresh = new ModelCatalogRefresh();
|
|
242
|
+
const close = () => {
|
|
243
|
+
refresh.stop();
|
|
244
|
+
host.setInputSurface(null);
|
|
245
|
+
host.requestRender();
|
|
246
|
+
};
|
|
247
|
+
const selector = createPiShellScopedModelsSelector({
|
|
248
|
+
models: initial.models,
|
|
249
|
+
enabledModelIds: currentEnabledIds,
|
|
250
|
+
refreshStatus: "Refreshing model catalogs…",
|
|
251
|
+
onChange: enabledIds => {
|
|
252
|
+
selectionChanged = true;
|
|
253
|
+
currentEnabledIds = enabledIds === null ? null : [...enabledIds];
|
|
254
|
+
catalog.updateScopedModels(currentEnabledIds);
|
|
255
|
+
host.requestRender();
|
|
256
|
+
},
|
|
257
|
+
onPersist: enabledIds => {
|
|
258
|
+
currentEnabledIds = enabledIds === null ? null : [...enabledIds];
|
|
259
|
+
catalog.persistScopedModels(currentEnabledIds);
|
|
260
|
+
host.appendWorkflowStatus("Model selection saved to settings");
|
|
261
|
+
host.requestRender();
|
|
262
|
+
},
|
|
263
|
+
onCancel: close,
|
|
264
|
+
});
|
|
265
|
+
const component = {
|
|
266
|
+
...selector,
|
|
267
|
+
dispose: () => {
|
|
268
|
+
refresh.stop();
|
|
269
|
+
selector.dispose?.();
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
host.setInputSurface(component);
|
|
273
|
+
host.requestRender();
|
|
274
|
+
refresh.run(host, component, signal => catalog.refreshScopedModels(signal), refreshed => {
|
|
275
|
+
if (!selectionChanged) {
|
|
276
|
+
currentEnabledIds = refreshed.enabledModelIds === null ? null : [...refreshed.enabledModelIds];
|
|
277
|
+
component.updateModels(refreshed.models, currentEnabledIds);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
component.updateModels(refreshed.models);
|
|
281
|
+
catalog.updateScopedModels(currentEnabledIds);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* One model-catalog refresh owned by an open dialog: it aborts after fifteen seconds or when the dialog
|
|
289
|
+
* closes, and a result that arrives after the dialog closed is dropped.
|
|
290
|
+
*/
|
|
291
|
+
class ModelCatalogRefresh {
|
|
292
|
+
#controller = new AbortController();
|
|
293
|
+
#timeout;
|
|
294
|
+
#stopped = false;
|
|
295
|
+
#timedOut = false;
|
|
296
|
+
constructor() {
|
|
297
|
+
this.#timeout = setTimeout(() => {
|
|
298
|
+
this.#timedOut = true;
|
|
299
|
+
this.#controller.abort();
|
|
300
|
+
}, MODEL_REFRESH_TIMEOUT_MS);
|
|
301
|
+
}
|
|
302
|
+
get stopped() { return this.#stopped; }
|
|
303
|
+
stop() {
|
|
304
|
+
this.#stopped = true;
|
|
305
|
+
clearTimeout(this.#timeout);
|
|
306
|
+
this.#controller.abort();
|
|
307
|
+
}
|
|
308
|
+
run(host, dialog, refresh, apply) {
|
|
309
|
+
void refresh(this.#controller.signal).then(refreshed => {
|
|
310
|
+
if (this.#stopped)
|
|
311
|
+
return;
|
|
312
|
+
apply(refreshed);
|
|
313
|
+
dialog.setRefreshStatus(this.#timedOut ? MODEL_REFRESH_TIMED_OUT : refreshed.status, this.#timedOut ? "warning" : refreshed.statusKind);
|
|
314
|
+
host.requestRender();
|
|
315
|
+
}).catch(error => {
|
|
316
|
+
if (this.#stopped)
|
|
317
|
+
return;
|
|
318
|
+
dialog.setRefreshStatus(this.#timedOut
|
|
319
|
+
? MODEL_REFRESH_TIMED_OUT
|
|
320
|
+
: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
321
|
+
host.requestRender();
|
|
322
|
+
}).finally(() => clearTimeout(this.#timeout));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function closeSurface(host) {
|
|
326
|
+
return () => {
|
|
327
|
+
host.setInputSurface(null);
|
|
328
|
+
host.requestRender();
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function opaqueRenderer(value) {
|
|
332
|
+
return (typeof value === "object" && value !== null) || typeof value === "function" ? value : undefined;
|
|
333
|
+
}
|
|
334
|
+
function modelReference(model) {
|
|
335
|
+
if (typeof model !== "object" || model === null)
|
|
336
|
+
return "";
|
|
337
|
+
const value = model;
|
|
338
|
+
const provider = typeof value.provider === "string" ? value.provider : "";
|
|
339
|
+
const id = typeof value.id === "string" ? value.id : typeof value.modelId === "string" ? value.modelId : "";
|
|
340
|
+
return provider && id ? `${provider}/${id}` : "";
|
|
341
|
+
}
|
|
@@ -95,7 +95,6 @@ export class PiTuiRuntimeAdapter {
|
|
|
95
95
|
#terminal;
|
|
96
96
|
#tuiTerminal;
|
|
97
97
|
#tui;
|
|
98
|
-
#root;
|
|
99
98
|
#layoutRoot;
|
|
100
99
|
#tuiOptions;
|
|
101
100
|
#logDirectory;
|
|
@@ -120,7 +119,6 @@ export class PiTuiRuntimeAdapter {
|
|
|
120
119
|
#presentationFrozen = false;
|
|
121
120
|
#presentationHolds = new Set();
|
|
122
121
|
constructor(options) {
|
|
123
|
-
this.#root = options.root;
|
|
124
122
|
this.#overlayGeometry = options.onOverlayGeometry === undefined ? undefined : new OverlayGeometryTracker(options.onOverlayGeometry);
|
|
125
123
|
this.#terminal = options.terminal ?? new ProcessTerminal();
|
|
126
124
|
this.#consumeUnhandledMouse = options.consumeUnhandledMouse ?? false;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { PiTuiTerminalPort } from "./contracts.js";
|
|
2
2
|
export declare const PINNED_PI_TUI_DAMAGE_GRAMMAR = "@earendil-works/pi-tui@1.1.0:tui-alt-screen-one-write-v1";
|
|
3
3
|
export interface PiTuiDamageFrameDescriptor {
|
|
4
|
+
/**
|
|
5
|
+
* Frame ids are compared only within one presentation epoch; absent means the current epoch. A
|
|
6
|
+
* presenter switch starts a new epoch, because each presenter numbers its own frames.
|
|
7
|
+
*/
|
|
8
|
+
readonly epoch?: number;
|
|
4
9
|
readonly frameId: number;
|
|
5
10
|
readonly width: number;
|
|
6
11
|
readonly height: number;
|
|
@@ -63,6 +68,15 @@ export declare class DamageAwareTerminalAdapter implements PiTuiTerminalPort {
|
|
|
63
68
|
get kittyProtocolActive(): boolean;
|
|
64
69
|
get lastDecision(): PiTuiDamageDecision;
|
|
65
70
|
get hyperlinkCleanupPending(): boolean;
|
|
71
|
+
get canvasBackgroundAnsi(): string | null;
|
|
72
|
+
get presentationEpoch(): number;
|
|
73
|
+
/**
|
|
74
|
+
* Starts a new presentation epoch: the remembered cells and the armed frame are discarded and frame ids
|
|
75
|
+
* restart, so the next frame is painted in full rather than reconciled against another presenter's.
|
|
76
|
+
*/
|
|
77
|
+
invalidatePresentation(): void;
|
|
78
|
+
/** Invalidates remembered terminal cells before the shell forces one complete canvas repaint. */
|
|
79
|
+
setCanvasBackground(ansi: string | null): boolean;
|
|
66
80
|
/**
|
|
67
81
|
* The rows as last forwarded to the terminal, top to bottom, with styling
|
|
68
82
|
* intact. A row the adapter has not seen since its last invalidation is empty.
|
|
@@ -86,3 +100,7 @@ export declare class DamageAwareTerminalAdapter implements PiTuiTerminalPort {
|
|
|
86
100
|
setTitle(title: string): void;
|
|
87
101
|
setProgress(active: boolean): void;
|
|
88
102
|
}
|
|
103
|
+
/** Makes SGR background resets return to A1's canvas while preserving explicit backgrounds. */
|
|
104
|
+
export declare function rebaseTerminalDefaultBackground(content: string, backgroundAnsi: string): string;
|
|
105
|
+
/** Paints only synchronized fullscreen rows; clipboard/title/progress controls remain byte-identical. */
|
|
106
|
+
export declare function paintTerminalCanvasFrame(data: string, backgroundAnsi: string): string;
|
|
@@ -8,6 +8,7 @@ const ROW_MARKER = /\u001b\[(\d+);1H\u001b\[2K/gu;
|
|
|
8
8
|
const ROW_CONTENT_CURSOR = /\u001b\[\d+;1H/u;
|
|
9
9
|
const CURSOR_SUFFIX = /(?:\u001b\[(\d+);(\d+)H)?\u001b\[\?25[hl]\u001b\[\?2026l$/u;
|
|
10
10
|
const OUT_OF_BAND = /^(?:\u001b\](?:0|2|9|52);[^\u0007\u001b]*(?:\u0007|\u001b\\)|\u001b\[\?25[hl])+$/u;
|
|
11
|
+
const SGR_SEQUENCE = /\u001b\[([0-9:;]*)m/gu;
|
|
11
12
|
/**
|
|
12
13
|
* A fail-closed adapter for one pinned Pi fullscreen write grammar. Semantic
|
|
13
14
|
* viewport metadata authorizes a shift; bytes are inspected only to validate
|
|
@@ -22,10 +23,12 @@ export class DamageAwareTerminalAdapter {
|
|
|
22
23
|
#cleanedRevision = 0;
|
|
23
24
|
#recoveryRevision = 0;
|
|
24
25
|
#epoch = 0;
|
|
26
|
+
#presentationEpoch = 0;
|
|
25
27
|
#armed;
|
|
26
28
|
#lastConsumedFrameId = 0;
|
|
27
29
|
#cacheWidth = 0;
|
|
28
30
|
#cacheHeight = 0;
|
|
31
|
+
#canvasBackgroundAnsi = null;
|
|
29
32
|
#decision = {
|
|
30
33
|
frameId: null,
|
|
31
34
|
transformed: false,
|
|
@@ -41,6 +44,26 @@ export class DamageAwareTerminalAdapter {
|
|
|
41
44
|
get kittyProtocolActive() { return this.inner.kittyProtocolActive; }
|
|
42
45
|
get lastDecision() { return this.#decision; }
|
|
43
46
|
get hyperlinkCleanupPending() { return this.#cleanupRevision > this.#cleanedRevision; }
|
|
47
|
+
get canvasBackgroundAnsi() { return this.#canvasBackgroundAnsi; }
|
|
48
|
+
get presentationEpoch() { return this.#presentationEpoch; }
|
|
49
|
+
/**
|
|
50
|
+
* Starts a new presentation epoch: the remembered cells and the armed frame are discarded and frame ids
|
|
51
|
+
* restart, so the next frame is painted in full rather than reconciled against another presenter's.
|
|
52
|
+
*/
|
|
53
|
+
invalidatePresentation() {
|
|
54
|
+
this.#presentationEpoch += 1;
|
|
55
|
+
this.#armed = undefined;
|
|
56
|
+
this.#lastConsumedFrameId = 0;
|
|
57
|
+
this.#invalidatePresentation();
|
|
58
|
+
}
|
|
59
|
+
/** Invalidates remembered terminal cells before the shell forces one complete canvas repaint. */
|
|
60
|
+
setCanvasBackground(ansi) {
|
|
61
|
+
if (this.#canvasBackgroundAnsi === ansi)
|
|
62
|
+
return false;
|
|
63
|
+
this.#canvasBackgroundAnsi = ansi;
|
|
64
|
+
this.#invalidatePresentation();
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
44
67
|
/**
|
|
45
68
|
* The rows as last forwarded to the terminal, top to bottom, with styling
|
|
46
69
|
* intact. A row the adapter has not seen since its last invalidation is empty.
|
|
@@ -97,7 +120,7 @@ export class DamageAwareTerminalAdapter {
|
|
|
97
120
|
const armed = this.#armed;
|
|
98
121
|
this.#armed = undefined;
|
|
99
122
|
const parsed = parsePinnedFullscreenWrite(data);
|
|
100
|
-
if (armed !== undefined && armed.descriptor
|
|
123
|
+
if (armed !== undefined && this.#isStale(armed.descriptor)) {
|
|
101
124
|
// Concurrency: a recognized obsolete frame must never repaint newer cells,
|
|
102
125
|
// acknowledge cleanup, or become the reference for a subsequent differential.
|
|
103
126
|
this.#decision = this.#decide(armed, parsed);
|
|
@@ -140,7 +163,10 @@ export class DamageAwareTerminalAdapter {
|
|
|
140
163
|
output = buildDamageWrite(parsed, armed.descriptor, new Set(decision.paintedRows), this.#rows);
|
|
141
164
|
}
|
|
142
165
|
}
|
|
143
|
-
this
|
|
166
|
+
const forwarded = this.#canvasBackgroundAnsi === null
|
|
167
|
+
? output
|
|
168
|
+
: paintTerminalCanvasFrame(output, this.#canvasBackgroundAnsi);
|
|
169
|
+
this.inner.write(forwarded);
|
|
144
170
|
if (epoch !== this.#epoch
|
|
145
171
|
|| (armed !== undefined && this.#lastConsumedFrameId !== armed.descriptor.frameId))
|
|
146
172
|
return;
|
|
@@ -175,7 +201,7 @@ export class DamageAwareTerminalAdapter {
|
|
|
175
201
|
shiftRows: descriptor.verticalShiftRows,
|
|
176
202
|
paintedRows: [],
|
|
177
203
|
};
|
|
178
|
-
if (
|
|
204
|
+
if (this.#isStale(descriptor))
|
|
179
205
|
return { ...base, reason: "stale-frame" };
|
|
180
206
|
this.#lastConsumedFrameId = descriptor.frameId;
|
|
181
207
|
if (this.hyperlinkCleanupPending)
|
|
@@ -251,6 +277,10 @@ export class DamageAwareTerminalAdapter {
|
|
|
251
277
|
paintedRows: [...painted].sort((left, right) => left - right),
|
|
252
278
|
};
|
|
253
279
|
}
|
|
280
|
+
#isStale(descriptor) {
|
|
281
|
+
return (descriptor.epoch ?? this.#presentationEpoch) !== this.#presentationEpoch
|
|
282
|
+
|| descriptor.frameId <= this.#lastConsumedFrameId;
|
|
283
|
+
}
|
|
254
284
|
#rememberFrame(descriptor, parsed, raw) {
|
|
255
285
|
if (raw.includes("\u001b[2J") || raw.includes("\u001b[?1049") || raw.includes("\u001b_G") || raw.includes("\u001bPq")) {
|
|
256
286
|
this.#invalidateRows();
|
|
@@ -363,6 +393,57 @@ export class DamageAwareTerminalAdapter {
|
|
|
363
393
|
this.#cacheHeight = 0;
|
|
364
394
|
}
|
|
365
395
|
}
|
|
396
|
+
/** Makes SGR background resets return to A1's canvas while preserving explicit backgrounds. */
|
|
397
|
+
export function rebaseTerminalDefaultBackground(content, backgroundAnsi) {
|
|
398
|
+
return content.replace(SGR_SEQUENCE, (sequence, parameters) => (sgrLeavesDefaultBackground(parameters) ? `${sequence}${backgroundAnsi}` : sequence));
|
|
399
|
+
}
|
|
400
|
+
/** Paints only synchronized fullscreen rows; clipboard/title/progress controls remain byte-identical. */
|
|
401
|
+
export function paintTerminalCanvasFrame(data, backgroundAnsi) {
|
|
402
|
+
if (!data.startsWith(BEGIN_SYNCHRONIZED_OUTPUT) || !data.endsWith(END_SYNCHRONIZED_OUTPUT)
|
|
403
|
+
|| !ROW_MARKER.test(data)) {
|
|
404
|
+
ROW_MARKER.lastIndex = 0;
|
|
405
|
+
return data;
|
|
406
|
+
}
|
|
407
|
+
ROW_MARKER.lastIndex = 0;
|
|
408
|
+
const rows = data.replace(ROW_MARKER, (_marker, row) => (`\u001b[${row};1H${backgroundAnsi}\u001b[2K${backgroundAnsi}`));
|
|
409
|
+
ROW_MARKER.lastIndex = 0;
|
|
410
|
+
const rebased = rebaseTerminalDefaultBackground(rows, backgroundAnsi);
|
|
411
|
+
return `${rebased.slice(0, -END_SYNCHRONIZED_OUTPUT.length)}\u001b[49m${END_SYNCHRONIZED_OUTPUT}`;
|
|
412
|
+
}
|
|
413
|
+
function sgrLeavesDefaultBackground(parameters) {
|
|
414
|
+
if (parameters.length === 0)
|
|
415
|
+
return true;
|
|
416
|
+
const values = parameters.split(";");
|
|
417
|
+
let defaultBackground;
|
|
418
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
419
|
+
const token = values[index];
|
|
420
|
+
const code = Number.parseInt(token.split(":", 1)[0] || "0", 10);
|
|
421
|
+
if (!Number.isFinite(code))
|
|
422
|
+
continue;
|
|
423
|
+
if (code === 0 || code === 49) {
|
|
424
|
+
defaultBackground = true;
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
if (code >= 40 && code <= 47 || code >= 100 && code <= 107) {
|
|
428
|
+
defaultBackground = false;
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (code !== 38 && code !== 48 && code !== 58)
|
|
432
|
+
continue;
|
|
433
|
+
if (code === 48)
|
|
434
|
+
defaultBackground = false;
|
|
435
|
+
if (token.includes(":"))
|
|
436
|
+
continue;
|
|
437
|
+
const mode = Number.parseInt(values[index + 1] ?? "", 10);
|
|
438
|
+
if (mode === 5)
|
|
439
|
+
index += 2;
|
|
440
|
+
else if (mode === 2)
|
|
441
|
+
index += 4;
|
|
442
|
+
else
|
|
443
|
+
index += 1;
|
|
444
|
+
}
|
|
445
|
+
return defaultBackground === true;
|
|
446
|
+
}
|
|
366
447
|
function parsePinnedFullscreenWrite(data) {
|
|
367
448
|
if (!data.startsWith(BEGIN_SYNCHRONIZED_OUTPUT) || !data.endsWith(END_SYNCHRONIZED_OUTPUT))
|
|
368
449
|
return null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { PiTuiComponentPort, PiTuiInputDiagnosticsEvent, PiTuiInputDiagnosticsPhase, PiTuiInputListener, PiTuiInputListenerResult, PiTuiLayoutEntry, PiTuiLayoutNode, PiTuiOverlayAnchor, PiTuiOverlayHandle, PiTuiOverlayMargin, PiTuiOverlayOptions, PiTuiOverlayUnfocusOptions, PiTuiPointerSurface, PiTuiPreInputListener, PiTuiRuntimeAdapterOptions, PiTuiRuntimeState, PiTuiScrollState, PiTuiSizeValue, PiTuiStopOptions, PiTuiTerminalPort, PiTuiViewport, } from "./contracts.js";
|
|
2
2
|
export { samePointerSurfaces } from "./overlay-geometry.js";
|
|
3
|
-
export { DamageAwareTerminalAdapter, PINNED_PI_TUI_DAMAGE_GRAMMAR } from "./damage-aware-terminal.js";
|
|
3
|
+
export { DamageAwareTerminalAdapter, PINNED_PI_TUI_DAMAGE_GRAMMAR, paintTerminalCanvasFrame, rebaseTerminalDefaultBackground, } from "./damage-aware-terminal.js";
|
|
4
4
|
export type { DamageAwareTerminalOptions, PiTuiDamageDecision, PiTuiDamageDecisionReason, PiTuiDamageFrameDescriptor, PiTuiDamageFrameSafety, } from "./damage-aware-terminal.js";
|
|
5
5
|
export { InputPresentationCoordinator, classifyPiTuiInput } from "./input-presentation-coordinator.js";
|
|
6
6
|
export type { InputPresentationCoordinatorOptions, PiTuiInputCoordinationDecision, PiTuiInputCoordinationScheduler, PiTuiInputCoordinationTrace, PiTuiInputSurfaceKind, } from "./input-presentation-coordinator.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { samePointerSurfaces } from "./overlay-geometry.js";
|
|
2
|
-
export { DamageAwareTerminalAdapter, PINNED_PI_TUI_DAMAGE_GRAMMAR } from "./damage-aware-terminal.js";
|
|
2
|
+
export { DamageAwareTerminalAdapter, PINNED_PI_TUI_DAMAGE_GRAMMAR, paintTerminalCanvasFrame, rebaseTerminalDefaultBackground, } from "./damage-aware-terminal.js";
|
|
3
3
|
export { InputPresentationCoordinator, classifyPiTuiInput } from "./input-presentation-coordinator.js";
|
|
4
4
|
export { PiTuiRuntimeAdapter, PiTuiRuntimeError } from "./adapter.js";
|
|
5
5
|
export { createPiPresentationRuntime, createPiTerminalBridge } from "./presentation-adapter.js";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "darwin",
|
|
6
6
|
"architecture": "arm64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-10-
|
|
8
|
+
"builtAt": "2026-10-10T09:15:37.918Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "51b0b2dc1c9ae577f2586c4217066929278726c2c15308d9144eb6f538e118ff",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "linux",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-10-
|
|
8
|
+
"builtAt": "2026-10-10T09:15:10.349Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "ef62679e83bc1fad3d38f9f21675d9256f08d20e67fd4791571032115ca1b0e9",
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"platform": "win32",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-10-
|
|
8
|
+
"builtAt": "2026-10-10T09:16:06.991Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian.exe",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "c6e2cb23d012d779478fd1bff750f17687a4c5466bad7621b1b3c200a0557101",
|
|
12
12
|
"size": 288256
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export declare const OWNED_UI_SETTINGS_VERSION = 13;
|
|
2
2
|
export type OwnedUiSettingValue = string | number | boolean;
|
|
3
3
|
export type OwnedUiSettingApplication = "live" | "restart";
|
|
4
4
|
export interface OwnedUiSettingDeclaration {
|
|
@@ -44,6 +44,18 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
44
44
|
defaultValue: "purple";
|
|
45
45
|
allowedValues: readonly ["purple", "blue", "cyan", "green", "orange", "pink"];
|
|
46
46
|
}>;
|
|
47
|
+
readonly backgroundStyle: Readonly<{
|
|
48
|
+
id: "backgroundStyle";
|
|
49
|
+
label: "Background";
|
|
50
|
+
section: Readonly<{
|
|
51
|
+
id: "appearance";
|
|
52
|
+
title: "Appearance";
|
|
53
|
+
}>;
|
|
54
|
+
description: "Fullscreen canvas background: terminal transparency, a dark accent tint, or fixed neutral dark.";
|
|
55
|
+
application: "live";
|
|
56
|
+
defaultValue: "transparent";
|
|
57
|
+
allowedValues: readonly ["transparent", "accent", "dark"];
|
|
58
|
+
}>;
|
|
47
59
|
readonly quitAnimation: Readonly<{
|
|
48
60
|
id: "quitAnimation";
|
|
49
61
|
label: "Quit animation";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export const OWNED_UI_SETTINGS_VERSION = 13;
|
|
2
2
|
const UI_ACCENT_COLOR_CHOICES = Object.freeze([
|
|
3
3
|
"purple", "blue", "cyan", "green", "orange", "pink",
|
|
4
4
|
]);
|
|
@@ -32,6 +32,17 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
|
32
32
|
defaultValue: "purple",
|
|
33
33
|
allowedValues: UI_ACCENT_COLOR_CHOICES,
|
|
34
34
|
}),
|
|
35
|
+
backgroundStyle: Object.freeze({
|
|
36
|
+
id: "backgroundStyle",
|
|
37
|
+
label: "Background",
|
|
38
|
+
section: APPEARANCE_SECTION,
|
|
39
|
+
description: "Fullscreen canvas background: terminal transparency, a dark accent tint, or fixed neutral dark.",
|
|
40
|
+
application: "live",
|
|
41
|
+
defaultValue: "transparent",
|
|
42
|
+
allowedValues: Object.freeze([
|
|
43
|
+
"transparent", "accent", "dark",
|
|
44
|
+
]),
|
|
45
|
+
}),
|
|
35
46
|
quitAnimation: Object.freeze({
|
|
36
47
|
id: "quitAnimation",
|
|
37
48
|
label: "Quit animation",
|
|
@@ -97,6 +97,13 @@ export const OWNED_UI_SETTINGS_MIGRATIONS = Object.freeze([
|
|
|
97
97
|
: { ...values };
|
|
98
98
|
},
|
|
99
99
|
}),
|
|
100
|
+
Object.freeze({
|
|
101
|
+
to: 13,
|
|
102
|
+
description: "Introduce the transparent fullscreen background default.",
|
|
103
|
+
migrate(values) {
|
|
104
|
+
return { ...values };
|
|
105
|
+
},
|
|
106
|
+
}),
|
|
100
107
|
]);
|
|
101
108
|
export function assertOwnedUiSettingsMigrations(migrations, currentVersion = OWNED_UI_SETTINGS_VERSION) {
|
|
102
109
|
const firstProduced = currentVersion - migrations.length + 1;
|