@konitif/workbench-runtime 0.284.1
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/LICENSE.md +22 -0
- package/README.md +60 -0
- package/dist/boot/createDemoWorkbenchBootGraph.d.ts +9 -0
- package/dist/boot/createDemoWorkbenchBootGraph.js +180 -0
- package/dist/boot/createLegacyWorkbenchBootGraph.d.ts +34 -0
- package/dist/boot/createLegacyWorkbenchBootGraph.js +321 -0
- package/dist/boot/createWorkbenchBootSession.d.ts +23 -0
- package/dist/boot/createWorkbenchBootSession.js +90 -0
- package/dist/boot/index.d.ts +3 -0
- package/dist/boot/index.js +3 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/runtime.d.ts +7 -0
- package/dist/runtime.js +2 -0
- package/dist/workbench/browserDetachedWindowHost.d.ts +3 -0
- package/dist/workbench/browserDetachedWindowHost.js +69 -0
- package/dist/workbench/browserWorkbenchHostEvents.d.ts +3 -0
- package/dist/workbench/browserWorkbenchHostEvents.js +35 -0
- package/dist/workbench/createWorkbenchStore.d.ts +129 -0
- package/dist/workbench/createWorkbenchStore.js +50 -0
- package/dist/workbench/createWorkbenchStoreRuntime.d.ts +154 -0
- package/dist/workbench/createWorkbenchStoreRuntime.js +242 -0
- package/dist/workbench/createWorkspaceModeController.d.ts +36 -0
- package/dist/workbench/createWorkspaceModeController.js +168 -0
- package/dist/workbench/workbenchDetachedWindowHost.d.ts +16 -0
- package/dist/workbench/workbenchDetachedWindowHost.js +1 -0
- package/dist/workbench/workbenchFocusPersistence.d.ts +15 -0
- package/dist/workbench/workbenchFocusPersistence.js +44 -0
- package/dist/workbench/workbenchHistoryActions.d.ts +21 -0
- package/dist/workbench/workbenchHistoryActions.js +45 -0
- package/dist/workbench/workbenchHistoryRuntime.d.ts +16 -0
- package/dist/workbench/workbenchHistoryRuntime.js +42 -0
- package/dist/workbench/workbenchHostEvents.d.ts +13 -0
- package/dist/workbench/workbenchHostEvents.js +1 -0
- package/dist/workbench/workbenchLayoutActions.d.ts +84 -0
- package/dist/workbench/workbenchLayoutActions.js +191 -0
- package/dist/workbench/workbenchPersistenceRuntime.d.ts +39 -0
- package/dist/workbench/workbenchPersistenceRuntime.js +168 -0
- package/dist/workbench/workbenchShellActions.d.ts +24 -0
- package/dist/workbench/workbenchShellActions.js +63 -0
- package/dist/workbench/workbenchShellPersistence.d.ts +18 -0
- package/dist/workbench/workbenchShellPersistence.js +124 -0
- package/dist/workbench/workbenchStateFactory.d.ts +22 -0
- package/dist/workbench/workbenchStateFactory.js +47 -0
- package/dist/workbench/workbenchToolRuntimeActions.d.ts +23 -0
- package/dist/workbench/workbenchToolRuntimeActions.js +98 -0
- package/dist/workbench/workbenchToolRuntimeUiStore.d.ts +17 -0
- package/dist/workbench/workbenchToolRuntimeUiStore.js +85 -0
- package/dist/workbench/workbenchWindowRuntime.d.ts +19 -0
- package/dist/workbench/workbenchWindowRuntime.js +85 -0
- package/dist/workbench/workbenchWorkspaceActions.d.ts +30 -0
- package/dist/workbench/workbenchWorkspaceActions.js +55 -0
- package/dist/workbench/workspaceHistoryController.d.ts +28 -0
- package/dist/workbench/workspaceHistoryController.js +126 -0
- package/dist/workbench/workspaceSyncController.d.ts +20 -0
- package/dist/workbench/workspaceSyncController.js +105 -0
- package/package.json +49 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { derived, get, writable } from 'svelte/store';
|
|
2
|
+
import { createWorkbenchShellActions } from "./workbenchShellActions.js";
|
|
3
|
+
import { createWorkbenchToolRuntimeActions } from "./workbenchToolRuntimeActions.js";
|
|
4
|
+
import { createWorkbenchLayoutActions } from "./workbenchLayoutActions.js";
|
|
5
|
+
import { createWorkbenchHistoryActions } from "./workbenchHistoryActions.js";
|
|
6
|
+
import { createWorkbenchWorkspaceActions } from "./workbenchWorkspaceActions.js";
|
|
7
|
+
import { createWorkbenchWindowRuntime } from "./workbenchWindowRuntime.js";
|
|
8
|
+
import { createWorkbenchToolRuntimeUiStore } from "./workbenchToolRuntimeUiStore.js";
|
|
9
|
+
import { createWorkbenchPersistenceRuntime } from "./workbenchPersistenceRuntime.js";
|
|
10
|
+
import { createWorkbenchStateFactory } from "./workbenchStateFactory.js";
|
|
11
|
+
import { createWorkbenchHistoryRuntime } from "./workbenchHistoryRuntime.js";
|
|
12
|
+
import { createWorkspaceSessionState, dismissLayoutInteraction } from '@konitif/workbench/workspace-contracts';
|
|
13
|
+
export function createWorkbenchStoreRuntime(options) {
|
|
14
|
+
for (const key of ['workspacePersistence', 'shellPersistence', 'focusPersistence', 'workspaceSync', 'hostEvents', 'detachedWindows']) {
|
|
15
|
+
if (options[key] === undefined || (key !== 'hostEvents' && key !== 'detachedWindows' && options[key] === null)) {
|
|
16
|
+
throw new TypeError(`Missing required Workbench port: ${key}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
let disposed = false;
|
|
20
|
+
const persistence = options.workspacePersistence;
|
|
21
|
+
const shellPersistenceKey = options.shellPersistenceKey ?? `${options.persistenceKey}.shell`;
|
|
22
|
+
const focusPersistenceKey = options.focusPersistenceKey ?? `${options.persistenceKey}.focus`;
|
|
23
|
+
const persistenceDebounceMs = Math.max(0, options.persistenceDebounceMs ?? 120);
|
|
24
|
+
function isPersistenceEnabled() {
|
|
25
|
+
return options.persistenceEnabled?.() ?? true;
|
|
26
|
+
}
|
|
27
|
+
const shellPersistence = options.shellPersistence;
|
|
28
|
+
const workspaceSync = options.workspaceSync;
|
|
29
|
+
const focusPersistence = options.focusPersistence;
|
|
30
|
+
const stateFactory = createWorkbenchStateFactory({
|
|
31
|
+
initialToolId: options.initialToolId,
|
|
32
|
+
toolCatalog: options.toolCatalog,
|
|
33
|
+
shellWidgetCatalog: options.shellWidgetCatalog,
|
|
34
|
+
initialWorkspaceMode: options.initialWorkspaceMode,
|
|
35
|
+
initialStackHeaderVisible: options.initialStackHeaderVisible,
|
|
36
|
+
defaultOpenShellRegions: options.defaultOpenShellRegions
|
|
37
|
+
});
|
|
38
|
+
const { createInitialWorkspace, createShellFallback, createWorkbenchState } = stateFactory;
|
|
39
|
+
const initialWorkspace = isPersistenceEnabled()
|
|
40
|
+
? (persistence.load() ?? createWorkbenchState().workspace)
|
|
41
|
+
: createWorkbenchState().workspace;
|
|
42
|
+
const initialShell = shellPersistence.load(createShellFallback());
|
|
43
|
+
const fallbackSession = createWorkspaceSessionState(initialWorkspace);
|
|
44
|
+
const initialFocus = focusPersistence.load(initialWorkspace, fallbackSession.focus);
|
|
45
|
+
const internalWorkspaceStore = writable(createWorkbenchState(createWorkspaceSessionState(initialWorkspace, initialFocus), initialShell));
|
|
46
|
+
const toolRuntimeUi = createWorkbenchToolRuntimeUiStore(() => get(internalWorkspaceStore).workspace);
|
|
47
|
+
const historyRuntime = createWorkbenchHistoryRuntime();
|
|
48
|
+
const persistenceCoordinator = createWorkbenchPersistenceRuntime({
|
|
49
|
+
persistenceKey: options.persistenceKey,
|
|
50
|
+
shellPersistenceKey,
|
|
51
|
+
focusPersistenceKey,
|
|
52
|
+
persistenceDebounceMs,
|
|
53
|
+
shellWidgetCatalog: options.shellWidgetCatalog,
|
|
54
|
+
defaultOpenShellRegions: options.defaultOpenShellRegions,
|
|
55
|
+
initialWorkspace,
|
|
56
|
+
initialShell,
|
|
57
|
+
initialFocus,
|
|
58
|
+
workspacePersistence: persistence,
|
|
59
|
+
shellPersistence,
|
|
60
|
+
focusPersistence,
|
|
61
|
+
workspaceSync,
|
|
62
|
+
hostEvents: options.hostEvents,
|
|
63
|
+
isPersistenceEnabled,
|
|
64
|
+
getWorkbenchState: () => get(internalWorkspaceStore),
|
|
65
|
+
createWorkbenchState,
|
|
66
|
+
setWorkbenchState
|
|
67
|
+
});
|
|
68
|
+
function flushWorkspacePersistence() {
|
|
69
|
+
persistenceCoordinator.flush();
|
|
70
|
+
}
|
|
71
|
+
function updateWorkbenchState(updater, historyScope = 'both') {
|
|
72
|
+
if (disposed)
|
|
73
|
+
return;
|
|
74
|
+
internalWorkspaceStore.update((state) => {
|
|
75
|
+
const updatedState = updater(state);
|
|
76
|
+
const provenance = state.workspace.presetProvenance;
|
|
77
|
+
const presetContentChanged = provenance?.status === 'exact' &&
|
|
78
|
+
(updatedState.workspace !== state.workspace || updatedState.shell !== state.shell);
|
|
79
|
+
const nextState = presetContentChanged
|
|
80
|
+
? {
|
|
81
|
+
...updatedState,
|
|
82
|
+
workspace: {
|
|
83
|
+
...updatedState.workspace,
|
|
84
|
+
presetProvenance: {
|
|
85
|
+
...provenance,
|
|
86
|
+
status: 'customized'
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
: updatedState;
|
|
91
|
+
historyRuntime.recordTransition(state, nextState, historyScope);
|
|
92
|
+
return nextState;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function setWorkbenchState(nextState, historyScope = 'both') {
|
|
96
|
+
if (disposed)
|
|
97
|
+
return;
|
|
98
|
+
internalWorkspaceStore.update((state) => {
|
|
99
|
+
historyRuntime.recordTransition(state, nextState, historyScope);
|
|
100
|
+
return nextState;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function restoreHistorySnapshot(snapshot) {
|
|
104
|
+
return createWorkbenchState(createWorkspaceSessionState(snapshot.workspace, snapshot.focus), snapshot.shell, dismissLayoutInteraction());
|
|
105
|
+
}
|
|
106
|
+
function resolveWorkspaceCommandHistoryScope(command) {
|
|
107
|
+
switch (command.type) {
|
|
108
|
+
case 'open-tool':
|
|
109
|
+
case 'open-tool-in-panel':
|
|
110
|
+
case 'open-tool-in-new-tab':
|
|
111
|
+
case 'split-panel-horizontal':
|
|
112
|
+
case 'split-panel-vertical':
|
|
113
|
+
case 'split-panel-to-side':
|
|
114
|
+
case 'join-panel-area':
|
|
115
|
+
case 'swap-panel-area':
|
|
116
|
+
case 'close-active-panel':
|
|
117
|
+
case 'close-panel':
|
|
118
|
+
case 'detach-panel-to-window':
|
|
119
|
+
case 'focus-panel':
|
|
120
|
+
case 'activate-tab':
|
|
121
|
+
case 'set-stack-header-visibility':
|
|
122
|
+
case 'set-panel-fullscreen-toggle-visibility':
|
|
123
|
+
case 'run-tool-shell-command':
|
|
124
|
+
return 'app';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const toolRuntimeActions = createWorkbenchToolRuntimeActions({
|
|
128
|
+
toolCatalog: options.toolCatalog,
|
|
129
|
+
workspaceHistory: historyRuntime.controller,
|
|
130
|
+
getWorkbenchState: () => get(internalWorkspaceStore),
|
|
131
|
+
createHistorySnapshot: historyRuntime.createSnapshot,
|
|
132
|
+
restoreHistorySnapshot,
|
|
133
|
+
createWorkbenchState,
|
|
134
|
+
updateWorkbenchState,
|
|
135
|
+
setWorkbenchState,
|
|
136
|
+
syncHistoryStatus: historyRuntime.syncStatus,
|
|
137
|
+
setToolPanelLoadingState(toolInstanceId, nextLoading) {
|
|
138
|
+
if (disposed)
|
|
139
|
+
return false;
|
|
140
|
+
return toolRuntimeUi.setToolPanelLoadingState(toolInstanceId, nextLoading);
|
|
141
|
+
},
|
|
142
|
+
setToolResourceLoadingState(toolInstanceId, nextLoading) {
|
|
143
|
+
if (disposed)
|
|
144
|
+
return false;
|
|
145
|
+
return toolRuntimeUi.setToolResourceLoadingState(toolInstanceId, nextLoading);
|
|
146
|
+
},
|
|
147
|
+
setToolShellStatusState(toolInstanceId, nextStatus) {
|
|
148
|
+
if (disposed)
|
|
149
|
+
return false;
|
|
150
|
+
return toolRuntimeUi.setToolShellStatus(toolInstanceId, nextStatus);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
const shellActions = createWorkbenchShellActions({
|
|
154
|
+
updateWorkbenchState,
|
|
155
|
+
flushPersistence: flushWorkspacePersistence
|
|
156
|
+
});
|
|
157
|
+
const layoutActions = createWorkbenchLayoutActions({
|
|
158
|
+
toolCatalog: options.toolCatalog,
|
|
159
|
+
getWorkbenchState: () => get(internalWorkspaceStore),
|
|
160
|
+
createWorkbenchState,
|
|
161
|
+
updateWorkbenchState
|
|
162
|
+
});
|
|
163
|
+
const genericWorkspaceActions = createWorkbenchWorkspaceActions({
|
|
164
|
+
toolCatalog: options.toolCatalog,
|
|
165
|
+
initialWorkspaceMode: options.initialWorkspaceMode,
|
|
166
|
+
createInitialWorkspace,
|
|
167
|
+
getWorkbenchState: () => get(internalWorkspaceStore),
|
|
168
|
+
createWorkbenchState,
|
|
169
|
+
updateWorkbenchState,
|
|
170
|
+
setWorkbenchState,
|
|
171
|
+
resolveWorkspaceCommandHistoryScope
|
|
172
|
+
});
|
|
173
|
+
const historyActions = createWorkbenchHistoryActions({
|
|
174
|
+
workspaceHistory: historyRuntime.controller,
|
|
175
|
+
getWorkbenchState: () => get(internalWorkspaceStore),
|
|
176
|
+
createHistorySnapshot: historyRuntime.createSnapshot,
|
|
177
|
+
restoreHistorySnapshot,
|
|
178
|
+
setWorkbenchState,
|
|
179
|
+
syncHistoryStatus: historyRuntime.syncStatus
|
|
180
|
+
});
|
|
181
|
+
const windowActions = createWorkbenchWindowRuntime({
|
|
182
|
+
detachedWindows: options.detachedWindows,
|
|
183
|
+
createWorkbenchState,
|
|
184
|
+
updateWorkbenchState,
|
|
185
|
+
flushWorkspacePersistence
|
|
186
|
+
});
|
|
187
|
+
const unsubscribeState = internalWorkspaceStore.subscribe((state) => {
|
|
188
|
+
persistenceCoordinator.handleStateChange(state);
|
|
189
|
+
toolRuntimeUi.prune(state.workspace.toolInstances);
|
|
190
|
+
});
|
|
191
|
+
const workspaceStore = {
|
|
192
|
+
subscribe: derived([internalWorkspaceStore, toolRuntimeUi.store], ([$state, $toolRuntimeUi]) => ({
|
|
193
|
+
...$state,
|
|
194
|
+
toolRuntimeUi: $toolRuntimeUi
|
|
195
|
+
})).subscribe
|
|
196
|
+
};
|
|
197
|
+
const workspaceHistoryStore = {
|
|
198
|
+
subscribe: historyRuntime.statusStore.subscribe
|
|
199
|
+
};
|
|
200
|
+
const workspaceActions = {
|
|
201
|
+
...genericWorkspaceActions,
|
|
202
|
+
...layoutActions,
|
|
203
|
+
...windowActions,
|
|
204
|
+
...shellActions,
|
|
205
|
+
...historyActions,
|
|
206
|
+
dispatchCommand(command) {
|
|
207
|
+
genericWorkspaceActions.dispatchCommand(command);
|
|
208
|
+
// The active tab is user-visible shell state. Persist it at the interaction boundary so a
|
|
209
|
+
// reload cannot reopen the default tab while the ordinary debounce is still pending.
|
|
210
|
+
if (command.type === 'activate-tab' || command.type === 'focus-panel')
|
|
211
|
+
flushWorkspacePersistence();
|
|
212
|
+
},
|
|
213
|
+
toolRuntime: toolRuntimeActions
|
|
214
|
+
};
|
|
215
|
+
function resetForTests() {
|
|
216
|
+
if (disposed)
|
|
217
|
+
return;
|
|
218
|
+
windowActions.disposeDetachedWindowTracking();
|
|
219
|
+
historyRuntime.reset();
|
|
220
|
+
internalWorkspaceStore.set(createWorkbenchState());
|
|
221
|
+
toolRuntimeUi.reset();
|
|
222
|
+
flushWorkspacePersistence();
|
|
223
|
+
historyRuntime.syncStatus();
|
|
224
|
+
}
|
|
225
|
+
function dispose() {
|
|
226
|
+
if (disposed)
|
|
227
|
+
return;
|
|
228
|
+
disposed = true;
|
|
229
|
+
unsubscribeState();
|
|
230
|
+
windowActions.disposeDetachedWindowTracking();
|
|
231
|
+
// Providers belong to the caller; release only this runtime's subscriptions.
|
|
232
|
+
persistenceCoordinator.dispose();
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
dispose,
|
|
236
|
+
flushPersistence: flushWorkspacePersistence,
|
|
237
|
+
workspaceStore,
|
|
238
|
+
workspaceHistoryStore,
|
|
239
|
+
workspaceActions,
|
|
240
|
+
__resetWorkspaceStoreForTests: resetForTests
|
|
241
|
+
};
|
|
242
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Readable } from 'svelte/store';
|
|
2
|
+
import type { WorkspaceCommand } from '@konitif/workbench';
|
|
3
|
+
import type { createWorkbenchStore } from "./createWorkbenchStore.js";
|
|
4
|
+
type WorkbenchStore = ReturnType<typeof createWorkbenchStore>;
|
|
5
|
+
type WorkspaceActions = WorkbenchStore['workspaceActions'];
|
|
6
|
+
type WorkspaceState = Parameters<WorkbenchStore['workspaceStore']['subscribe']>[0] extends (value: infer State) => void ? State : never;
|
|
7
|
+
type WorkspaceHistoryState = Parameters<WorkbenchStore['workspaceHistoryStore']['subscribe']>[0] extends (value: infer State) => void ? State : never;
|
|
8
|
+
type WorkspaceActionName = keyof WorkspaceActions;
|
|
9
|
+
export interface WorkspaceModeControllerOptions<Mode extends string> {
|
|
10
|
+
initialMode: Mode;
|
|
11
|
+
modes?: readonly Mode[];
|
|
12
|
+
stores?: Record<Mode, WorkbenchStore>;
|
|
13
|
+
getStore?: (mode: Mode) => WorkbenchStore;
|
|
14
|
+
isWorkspaceCommandBlocked?: (mode: Mode, command: WorkspaceCommand) => boolean;
|
|
15
|
+
isWorkspaceActionBlocked?: (mode: Mode, action: WorkspaceActionName) => boolean;
|
|
16
|
+
resolveBlockedWorkspaceAction?: (context: {
|
|
17
|
+
mode: Mode;
|
|
18
|
+
action: WorkspaceActionName;
|
|
19
|
+
store: WorkbenchStore;
|
|
20
|
+
}) => ((...args: unknown[]) => unknown) | null | undefined;
|
|
21
|
+
onModeActivated?: (mode: Mode, store: WorkbenchStore) => void;
|
|
22
|
+
onWorkspaceReset?: (mode: Mode, store: WorkbenchStore) => void;
|
|
23
|
+
}
|
|
24
|
+
export interface WorkspaceModeController<Mode extends string> {
|
|
25
|
+
activeMode: Readable<Mode>;
|
|
26
|
+
workspaceStore: Readable<WorkspaceState>;
|
|
27
|
+
workspaceHistoryStore: Readable<WorkspaceHistoryState>;
|
|
28
|
+
workspaceActions: WorkspaceActions;
|
|
29
|
+
setActiveMode: (mode: Mode) => void;
|
|
30
|
+
suspendMode: (mode: Mode) => void;
|
|
31
|
+
resumeMode: (mode: Mode) => void;
|
|
32
|
+
getWorkspaceActionsForMode: (mode: Mode) => WorkspaceActions;
|
|
33
|
+
getWorkspaceStateForMode: (mode: Mode) => WorkspaceState;
|
|
34
|
+
}
|
|
35
|
+
export declare function createWorkspaceModeController<Mode extends string>(options: WorkspaceModeControllerOptions<Mode>): WorkspaceModeController<Mode>;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { get, writable } from 'svelte/store';
|
|
2
|
+
export function createWorkspaceModeController(options) {
|
|
3
|
+
const activeModeStore = writable(options.initialMode);
|
|
4
|
+
const suspendedModes = new Set();
|
|
5
|
+
const refreshReaders = new Set();
|
|
6
|
+
function getStoreForMode(mode) {
|
|
7
|
+
if (suspendedModes.has(mode))
|
|
8
|
+
throw new Error(`Workbench mode "${mode}" is suspended.`);
|
|
9
|
+
const store = options.getStore?.(mode) ?? options.stores?.[mode];
|
|
10
|
+
if (!store) {
|
|
11
|
+
throw new Error(`No workbench store registered for mode "${mode}".`);
|
|
12
|
+
}
|
|
13
|
+
return store;
|
|
14
|
+
}
|
|
15
|
+
function createActiveReadable(selectStore) {
|
|
16
|
+
return {
|
|
17
|
+
subscribe(run, invalidate) {
|
|
18
|
+
if (suspendedModes.has(get(activeModeStore))) {
|
|
19
|
+
throw new Error(`Workbench mode "${get(activeModeStore)}" is suspended.`);
|
|
20
|
+
}
|
|
21
|
+
let activeUnsubscribe = null;
|
|
22
|
+
const refresh = () => {
|
|
23
|
+
activeUnsubscribe?.();
|
|
24
|
+
activeUnsubscribe = null;
|
|
25
|
+
const mode = get(activeModeStore);
|
|
26
|
+
if (suspendedModes.has(mode))
|
|
27
|
+
return;
|
|
28
|
+
activeUnsubscribe = selectStore(getStoreForMode(mode)).subscribe(run, invalidate);
|
|
29
|
+
};
|
|
30
|
+
refreshReaders.add(refresh);
|
|
31
|
+
const modeUnsubscribe = activeModeStore.subscribe(refresh);
|
|
32
|
+
return () => {
|
|
33
|
+
activeUnsubscribe?.();
|
|
34
|
+
modeUnsubscribe();
|
|
35
|
+
refreshReaders.delete(refresh);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function createActionProxy(resolveTarget) {
|
|
41
|
+
return new Proxy({}, {
|
|
42
|
+
ownKeys() {
|
|
43
|
+
return Reflect.ownKeys(resolveTarget());
|
|
44
|
+
},
|
|
45
|
+
getOwnPropertyDescriptor(_target, property) {
|
|
46
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(resolveTarget(), property);
|
|
47
|
+
return descriptor
|
|
48
|
+
? {
|
|
49
|
+
...descriptor,
|
|
50
|
+
configurable: true,
|
|
51
|
+
enumerable: true
|
|
52
|
+
}
|
|
53
|
+
: undefined;
|
|
54
|
+
},
|
|
55
|
+
has(_target, property) {
|
|
56
|
+
return property in resolveTarget();
|
|
57
|
+
},
|
|
58
|
+
get(_target, property) {
|
|
59
|
+
const value = Reflect.get(resolveTarget(), property);
|
|
60
|
+
if (typeof value === 'function') {
|
|
61
|
+
return (...args) => Reflect.get(resolveTarget(), property)(...args);
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function createWorkspaceActionsForMode(mode) {
|
|
68
|
+
const toolRuntimeActions = createActionProxy(() => getStoreForMode(mode).workspaceActions.toolRuntime);
|
|
69
|
+
return new Proxy({ toolRuntime: toolRuntimeActions }, {
|
|
70
|
+
get(target, property) {
|
|
71
|
+
const store = getStoreForMode(mode);
|
|
72
|
+
if (property === 'toolRuntime') {
|
|
73
|
+
return target.toolRuntime;
|
|
74
|
+
}
|
|
75
|
+
if (property === 'dispatchCommand') {
|
|
76
|
+
return (command) => {
|
|
77
|
+
if (options.isWorkspaceCommandBlocked?.(mode, command)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
getStoreForMode(mode).workspaceActions.dispatchCommand(command);
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (property === 'resetWorkspace') {
|
|
84
|
+
return () => {
|
|
85
|
+
const current = getStoreForMode(mode);
|
|
86
|
+
current.workspaceActions.resetWorkspace();
|
|
87
|
+
options.onWorkspaceReset?.(mode, current);
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (options.isWorkspaceActionBlocked?.(mode, property)) {
|
|
91
|
+
const blockedAction = options.resolveBlockedWorkspaceAction?.({
|
|
92
|
+
mode,
|
|
93
|
+
action: property,
|
|
94
|
+
store
|
|
95
|
+
});
|
|
96
|
+
return blockedAction ?? (() => undefined);
|
|
97
|
+
}
|
|
98
|
+
const value = Reflect.get(store.workspaceActions, property);
|
|
99
|
+
if (typeof value === 'function') {
|
|
100
|
+
return (...args) => Reflect.get(getStoreForMode(mode).workspaceActions, property)(...args);
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const workspaceActionsByMode = Object.fromEntries(resolveWorkspaceModes().map((mode) => [
|
|
107
|
+
mode,
|
|
108
|
+
createWorkspaceActionsForMode(mode)
|
|
109
|
+
]));
|
|
110
|
+
const activeToolRuntimeActions = createActionProxy(() => workspaceActionsByMode[get(activeModeStore)].toolRuntime);
|
|
111
|
+
const activeWorkspaceActions = new Proxy({ toolRuntime: activeToolRuntimeActions }, {
|
|
112
|
+
get(target, property) {
|
|
113
|
+
if (property === 'toolRuntime') {
|
|
114
|
+
return target.toolRuntime;
|
|
115
|
+
}
|
|
116
|
+
const value = Reflect.get(workspaceActionsByMode[get(activeModeStore)], property);
|
|
117
|
+
if (typeof value === 'function') {
|
|
118
|
+
return (...args) => Reflect.get(workspaceActionsByMode[get(activeModeStore)], property)(...args);
|
|
119
|
+
}
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
activeMode: {
|
|
125
|
+
subscribe: activeModeStore.subscribe
|
|
126
|
+
},
|
|
127
|
+
workspaceStore: createActiveReadable((store) => store.workspaceStore),
|
|
128
|
+
workspaceHistoryStore: createActiveReadable((store) => store.workspaceHistoryStore),
|
|
129
|
+
workspaceActions: activeWorkspaceActions,
|
|
130
|
+
suspendMode(mode) {
|
|
131
|
+
if (suspendedModes.has(mode))
|
|
132
|
+
return;
|
|
133
|
+
suspendedModes.add(mode);
|
|
134
|
+
if (get(activeModeStore) === mode)
|
|
135
|
+
for (const refresh of [...refreshReaders])
|
|
136
|
+
refresh();
|
|
137
|
+
},
|
|
138
|
+
resumeMode(mode) {
|
|
139
|
+
if (!suspendedModes.delete(mode))
|
|
140
|
+
return;
|
|
141
|
+
if (get(activeModeStore) === mode)
|
|
142
|
+
for (const refresh of [...refreshReaders])
|
|
143
|
+
refresh();
|
|
144
|
+
},
|
|
145
|
+
setActiveMode(mode) {
|
|
146
|
+
const store = getStoreForMode(mode);
|
|
147
|
+
if (get(activeModeStore) !== mode) {
|
|
148
|
+
activeModeStore.set(mode);
|
|
149
|
+
}
|
|
150
|
+
options.onModeActivated?.(mode, store);
|
|
151
|
+
},
|
|
152
|
+
getWorkspaceActionsForMode(mode) {
|
|
153
|
+
return workspaceActionsByMode[mode];
|
|
154
|
+
},
|
|
155
|
+
getWorkspaceStateForMode(mode) {
|
|
156
|
+
return get(getStoreForMode(mode).workspaceStore);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
function resolveWorkspaceModes() {
|
|
160
|
+
if (options.modes) {
|
|
161
|
+
return options.modes;
|
|
162
|
+
}
|
|
163
|
+
if (options.stores) {
|
|
164
|
+
return Object.keys(options.stores);
|
|
165
|
+
}
|
|
166
|
+
return [options.initialMode];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Native handles, URLs and polling belong to the host, not to workspace state. */
|
|
2
|
+
export type WorkbenchDetachedWindowOpening = {
|
|
3
|
+
status: 'blocked';
|
|
4
|
+
}
|
|
5
|
+
/** Logical detachment without a native window or closure observation. */
|
|
6
|
+
| {
|
|
7
|
+
status: 'unmanaged';
|
|
8
|
+
} | {
|
|
9
|
+
status: 'opened';
|
|
10
|
+
/** May notify synchronously. Unsubscribe never closes the native window. */
|
|
11
|
+
observeClosed(onClosed: () => void): () => void;
|
|
12
|
+
};
|
|
13
|
+
export interface WorkbenchDetachedWindowHost {
|
|
14
|
+
/** A thrown error means opening failed without retaining a native window. */
|
|
15
|
+
open(windowId: string): WorkbenchDetachedWindowOpening;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Workspace, type WorkspaceFocus } from '@konitif/workbench';
|
|
2
|
+
export interface WorkbenchFocusPersistencePort {
|
|
3
|
+
load(workspace: Workspace, fallback: WorkspaceFocus): WorkspaceFocus;
|
|
4
|
+
save(focus: WorkspaceFocus): void;
|
|
5
|
+
serialize(focus: WorkspaceFocus): string;
|
|
6
|
+
normalize(input: unknown, workspace: Workspace, fallback: WorkspaceFocus): WorkspaceFocus;
|
|
7
|
+
}
|
|
8
|
+
interface LocalWorkbenchFocusPersistenceOptions {
|
|
9
|
+
storageKey: string;
|
|
10
|
+
isEnabled?: () => boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function createLocalWorkbenchFocusPersistence(options: LocalWorkbenchFocusPersistenceOptions): WorkbenchFocusPersistencePort;
|
|
13
|
+
export declare function serializeWorkspaceFocus(focus: WorkspaceFocus): string;
|
|
14
|
+
export declare function normalizePersistedWorkspaceFocus(input: unknown, workspace: Workspace, fallback: WorkspaceFocus): WorkspaceFocus;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { findPanelInWorkspace, normalizeWorkspaceFocus } from '@konitif/workbench';
|
|
2
|
+
export function createLocalWorkbenchFocusPersistence(options) {
|
|
3
|
+
return {
|
|
4
|
+
load(workspace, fallback) {
|
|
5
|
+
if (!isEnabled(options) || typeof localStorage === 'undefined') {
|
|
6
|
+
return fallback;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const rawFocus = localStorage.getItem(options.storageKey);
|
|
10
|
+
return rawFocus ? normalizePersistedWorkspaceFocus(JSON.parse(rawFocus), workspace, fallback) : fallback;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return fallback;
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
save(focus) {
|
|
17
|
+
if (!isEnabled(options) || typeof localStorage === 'undefined') {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
localStorage.setItem(options.storageKey, serializeWorkspaceFocus(focus));
|
|
21
|
+
},
|
|
22
|
+
serialize: serializeWorkspaceFocus,
|
|
23
|
+
normalize: normalizePersistedWorkspaceFocus
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function serializeWorkspaceFocus(focus) {
|
|
27
|
+
return JSON.stringify(focus);
|
|
28
|
+
}
|
|
29
|
+
export function normalizePersistedWorkspaceFocus(input, workspace, fallback) {
|
|
30
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
31
|
+
return normalizeWorkspaceFocus(workspace, fallback);
|
|
32
|
+
}
|
|
33
|
+
const activePanelId = input.activePanelId;
|
|
34
|
+
const requestedPanelId = typeof activePanelId === 'string' ? activePanelId : fallback.activePanelId;
|
|
35
|
+
const validPanelId = requestedPanelId && findPanelInWorkspace(workspace, requestedPanelId)
|
|
36
|
+
? requestedPanelId
|
|
37
|
+
: fallback.activePanelId;
|
|
38
|
+
return normalizeWorkspaceFocus(workspace, {
|
|
39
|
+
activePanelId: validPanelId
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function isEnabled(options) {
|
|
43
|
+
return options.isEnabled?.() ?? true;
|
|
44
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { WorkspaceHistoryController } from "./workspaceHistoryController.js";
|
|
2
|
+
export interface CreateWorkbenchHistoryActionsInput<TState, TSnapshot> {
|
|
3
|
+
workspaceHistory: WorkspaceHistoryController<TSnapshot>;
|
|
4
|
+
getWorkbenchState(): TState;
|
|
5
|
+
createHistorySnapshot(state: TState): TSnapshot;
|
|
6
|
+
restoreHistorySnapshot(snapshot: TSnapshot): TState;
|
|
7
|
+
setWorkbenchState(nextState: TState, historyScope?: 'none'): void;
|
|
8
|
+
syncHistoryStatus(): void;
|
|
9
|
+
}
|
|
10
|
+
export declare function createWorkbenchHistoryActions<TState, TSnapshot>(input: CreateWorkbenchHistoryActionsInput<TState, TSnapshot>): {
|
|
11
|
+
beginAppHistoryTransaction: () => void;
|
|
12
|
+
commitAppHistoryTransaction: () => void;
|
|
13
|
+
cancelAppHistoryTransaction: () => void;
|
|
14
|
+
beginCoreHistoryTransaction: () => void;
|
|
15
|
+
commitCoreHistoryTransaction: () => void;
|
|
16
|
+
cancelCoreHistoryTransaction: () => void;
|
|
17
|
+
undoAppHistory: () => void;
|
|
18
|
+
redoAppHistory: () => void;
|
|
19
|
+
undoCoreHistory: () => void;
|
|
20
|
+
redoCoreHistory: () => void;
|
|
21
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function createWorkbenchHistoryActions(input) {
|
|
2
|
+
function beginTransaction(channel) {
|
|
3
|
+
input.workspaceHistory.beginTransaction(channel, input.createHistorySnapshot(input.getWorkbenchState()));
|
|
4
|
+
}
|
|
5
|
+
function commitTransaction(channel) {
|
|
6
|
+
input.workspaceHistory.commitTransaction(channel, input.createHistorySnapshot(input.getWorkbenchState()));
|
|
7
|
+
input.syncHistoryStatus();
|
|
8
|
+
}
|
|
9
|
+
function cancelTransaction(channel) {
|
|
10
|
+
const snapshot = input.workspaceHistory.cancelTransaction(channel);
|
|
11
|
+
if (!snapshot) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
input.setWorkbenchState(input.restoreHistorySnapshot(snapshot), 'none');
|
|
15
|
+
input.syncHistoryStatus();
|
|
16
|
+
}
|
|
17
|
+
function undoHistory(channel) {
|
|
18
|
+
const snapshot = input.workspaceHistory.undo(channel, input.createHistorySnapshot(input.getWorkbenchState()));
|
|
19
|
+
if (!snapshot) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
input.setWorkbenchState(input.restoreHistorySnapshot(snapshot), 'none');
|
|
23
|
+
input.syncHistoryStatus();
|
|
24
|
+
}
|
|
25
|
+
function redoHistory(channel) {
|
|
26
|
+
const snapshot = input.workspaceHistory.redo(channel, input.createHistorySnapshot(input.getWorkbenchState()));
|
|
27
|
+
if (!snapshot) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
input.setWorkbenchState(input.restoreHistorySnapshot(snapshot), 'none');
|
|
31
|
+
input.syncHistoryStatus();
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
beginAppHistoryTransaction: () => beginTransaction('app'),
|
|
35
|
+
commitAppHistoryTransaction: () => commitTransaction('app'),
|
|
36
|
+
cancelAppHistoryTransaction: () => cancelTransaction('app'),
|
|
37
|
+
beginCoreHistoryTransaction: () => beginTransaction('core'),
|
|
38
|
+
commitCoreHistoryTransaction: () => commitTransaction('core'),
|
|
39
|
+
cancelCoreHistoryTransaction: () => cancelTransaction('core'),
|
|
40
|
+
undoAppHistory: () => undoHistory('app'),
|
|
41
|
+
redoAppHistory: () => redoHistory('app'),
|
|
42
|
+
undoCoreHistory: () => undoHistory('core'),
|
|
43
|
+
redoCoreHistory: () => redoHistory('core')
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ShellState, Workspace, WorkspaceFocus } from '@konitif/workbench/workspace-contracts';
|
|
2
|
+
import { type WorkbenchHistoryScope, type WorkbenchHistoryStatus } from "./workspaceHistoryController.js";
|
|
3
|
+
export interface WorkbenchHistoryState {
|
|
4
|
+
workspace: Workspace;
|
|
5
|
+
focus: WorkspaceFocus;
|
|
6
|
+
shell: ShellState;
|
|
7
|
+
}
|
|
8
|
+
export type WorkbenchSnapshot<TState extends WorkbenchHistoryState> = Pick<TState, 'workspace' | 'focus' | 'shell'>;
|
|
9
|
+
export declare function createWorkbenchHistoryRuntime<TState extends WorkbenchHistoryState>(): {
|
|
10
|
+
controller: import("./workspaceHistoryController.js").WorkspaceHistoryController<WorkbenchSnapshot<TState>>;
|
|
11
|
+
statusStore: import("svelte/store").Writable<WorkbenchHistoryStatus>;
|
|
12
|
+
createSnapshot: (state: TState) => WorkbenchSnapshot<TState>;
|
|
13
|
+
recordTransition: (previousState: TState, nextState: TState, historyScope: WorkbenchHistoryScope) => void;
|
|
14
|
+
reset: () => void;
|
|
15
|
+
syncStatus: () => void;
|
|
16
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { writable } from 'svelte/store';
|
|
2
|
+
import { createWorkspaceHistoryController } from "./workspaceHistoryController.js";
|
|
3
|
+
export function createWorkbenchHistoryRuntime() {
|
|
4
|
+
const statusStore = writable({
|
|
5
|
+
app: { canUndo: false, canRedo: false },
|
|
6
|
+
core: { canUndo: false, canRedo: false }
|
|
7
|
+
});
|
|
8
|
+
const controller = createWorkspaceHistoryController({
|
|
9
|
+
isChanged(left, right) {
|
|
10
|
+
return left.workspace !== right.workspace || left.focus !== right.focus || left.shell !== right.shell;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
function createSnapshot(state) {
|
|
14
|
+
return {
|
|
15
|
+
workspace: state.workspace,
|
|
16
|
+
focus: state.focus,
|
|
17
|
+
shell: state.shell
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function syncStatus() {
|
|
21
|
+
statusStore.set(controller.getStatus());
|
|
22
|
+
}
|
|
23
|
+
function recordTransition(previousState, nextState, historyScope) {
|
|
24
|
+
if (previousState === nextState || historyScope === 'none') {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
controller.recordTransition(createSnapshot(previousState), createSnapshot(nextState), historyScope);
|
|
28
|
+
syncStatus();
|
|
29
|
+
}
|
|
30
|
+
function reset() {
|
|
31
|
+
controller.reset();
|
|
32
|
+
syncStatus();
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
controller,
|
|
36
|
+
statusStore,
|
|
37
|
+
createSnapshot,
|
|
38
|
+
recordTransition,
|
|
39
|
+
reset,
|
|
40
|
+
syncStatus
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Notifications only; the persistence coordinator owns admission and effects. */
|
|
2
|
+
export interface WorkbenchHostStorageChange {
|
|
3
|
+
key: string | null;
|
|
4
|
+
newValue: string | null;
|
|
5
|
+
}
|
|
6
|
+
export interface WorkbenchHostEventHandlers {
|
|
7
|
+
onPersistenceBoundary(): void;
|
|
8
|
+
onStorageChange(event: WorkbenchHostStorageChange): void;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkbenchHostEvents {
|
|
11
|
+
/** Own only this subscription. Return an idempotent unsubscribe function. */
|
|
12
|
+
subscribe(handlers: WorkbenchHostEventHandlers): () => void;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|