@cerbur/clutch-dsh-worktree 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -32
- package/README.zh.md +39 -25
- package/lib/client/WorktreeContext.d.ts +1 -1
- package/lib/client/WorktreeContext.d.ts.map +1 -1
- package/lib/client/WorktreeContext.js +1 -1
- package/lib/client/WorktreeContext.js.map +1 -1
- package/lib/client/WorktreeHeroContext.d.ts.map +1 -1
- package/lib/client/WorktreeHeroContext.js +1 -1
- package/lib/client/WorktreeHeroContext.js.map +1 -1
- package/lib/client/WorktreeModeAction.d.ts.map +1 -1
- package/lib/client/WorktreeModeAction.js.map +1 -1
- package/lib/client/WorktreeSurface.d.ts +1 -1
- package/lib/client/WorktreeSurface.d.ts.map +1 -1
- package/lib/client/WorktreeSurface.js +245 -53
- package/lib/client/WorktreeSurface.js.map +1 -1
- package/lib/client/dsh-rc1-slot-contract.d.ts +43 -0
- package/lib/client/dsh-rc1-slot-contract.d.ts.map +1 -0
- package/lib/client/dsh-rc1-slot-contract.js +2 -0
- package/lib/client/dsh-rc1-slot-contract.js.map +1 -0
- package/lib/client/dsh-slot-contract.d.ts +43 -0
- package/lib/client/dsh-slot-contract.d.ts.map +1 -0
- package/lib/client/dsh-slot-contract.js +2 -0
- package/lib/client/dsh-slot-contract.js.map +1 -0
- package/lib/client/entry.d.ts +2 -2
- package/lib/client/entry.d.ts.map +1 -1
- package/lib/client/entry.js +144 -26
- package/lib/client/entry.js.map +1 -1
- package/lib/client/locales.d.ts +12 -0
- package/lib/client/locales.d.ts.map +1 -1
- package/lib/client/locales.js +12 -0
- package/lib/client/locales.js.map +1 -1
- package/lib/client/view-mode-store.d.ts +1 -1
- package/lib/client/view-mode-store.d.ts.map +1 -1
- package/lib/client/view-mode-store.js +1 -1
- package/lib/client/view-mode-store.js.map +1 -1
- package/lib/client/view-mode.d.ts +20 -6
- package/lib/client/view-mode.d.ts.map +1 -1
- package/lib/client/view-mode.js +35 -10
- package/lib/client/view-mode.js.map +1 -1
- package/lib/client/virtual-workspace-membership.d.ts +12 -10
- package/lib/client/virtual-workspace-membership.d.ts.map +1 -1
- package/lib/client/virtual-workspace-membership.js +82 -57
- package/lib/client/virtual-workspace-membership.js.map +1 -1
- package/lib/client/worktree-context-store.d.ts +7 -15
- package/lib/client/worktree-context-store.d.ts.map +1 -1
- package/lib/client/worktree-context-store.js +20 -13
- package/lib/client/worktree-context-store.js.map +1 -1
- package/lib/client/worktree-context.js +1 -1
- package/lib/client/worktree-context.js.map +1 -1
- package/lib/client/worktree-error-copy.d.ts.map +1 -1
- package/lib/client/worktree-error-copy.js +8 -0
- package/lib/client/worktree-error-copy.js.map +1 -1
- package/lib/client/worktree-expand-state.d.ts +5 -4
- package/lib/client/worktree-expand-state.d.ts.map +1 -1
- package/lib/client/worktree-expand-state.js.map +1 -1
- package/lib/client/worktree-session-fork.d.ts +18 -3
- package/lib/client/worktree-session-fork.d.ts.map +1 -1
- package/lib/client/worktree-session-fork.js +125 -36
- package/lib/client/worktree-session-fork.js.map +1 -1
- package/lib/client/worktree-session-order.d.ts +2 -2
- package/lib/client/worktree-session-order.d.ts.map +1 -1
- package/lib/client/worktree-session.d.ts.map +1 -1
- package/lib/client/worktree-session.js +3 -1
- package/lib/client/worktree-session.js.map +1 -1
- package/lib/client/worktree-surface-rows.d.ts.map +1 -1
- package/lib/client/worktree-surface-rows.js +8 -1
- package/lib/client/worktree-surface-rows.js.map +1 -1
- package/lib/client/worktree-surface-types.d.ts +19 -1
- package/lib/client/worktree-surface-types.d.ts.map +1 -1
- package/lib/client/worktree-view-read.d.ts +18 -8
- package/lib/client/worktree-view-read.d.ts.map +1 -1
- package/lib/client/worktree-view-read.js +75 -10
- package/lib/client/worktree-view-read.js.map +1 -1
- package/lib/client.js +1404 -178
- package/lib/client.js.map +1 -1
- package/lib/contract/index.d.ts +6 -2
- package/lib/contract/index.d.ts.map +1 -1
- package/lib/contract/index.js +4 -0
- package/lib/contract/index.js.map +1 -1
- package/lib/host/dsh-read-adapter.d.ts +1 -0
- package/lib/host/dsh-read-adapter.d.ts.map +1 -1
- package/lib/host/dsh-read-adapter.js +6 -0
- package/lib/host/dsh-read-adapter.js.map +1 -1
- package/lib/host/service.d.ts +2 -0
- package/lib/host/service.d.ts.map +1 -1
- package/lib/host/service.js +6 -1
- package/lib/host/service.js.map +1 -1
- package/lib/host/worktree-permission-manager.d.ts.map +1 -1
- package/lib/host/worktree-permission-manager.js +3 -1
- package/lib/host/worktree-permission-manager.js.map +1 -1
- package/lib/host/worktree-permission.d.ts +3 -2
- package/lib/host/worktree-permission.d.ts.map +1 -1
- package/lib/host/worktree-permission.js +29 -5
- package/lib/host/worktree-permission.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/manage/manager-context.d.ts +2 -0
- package/lib/manage/manager-context.d.ts.map +1 -1
- package/lib/manage/manager-worktrees.d.ts +5 -0
- package/lib/manage/manager-worktrees.d.ts.map +1 -1
- package/lib/manage/manager-worktrees.js +130 -39
- package/lib/manage/manager-worktrees.js.map +1 -1
- package/lib/manage/manager.d.ts +20 -0
- package/lib/manage/manager.d.ts.map +1 -1
- package/lib/manage/manager.js +93 -13
- package/lib/manage/manager.js.map +1 -1
- package/lib/manage/types.d.ts +9 -1
- package/lib/manage/types.d.ts.map +1 -1
- package/lib/provider/git.d.ts +37 -9
- package/lib/provider/git.d.ts.map +1 -1
- package/lib/provider/git.js +166 -61
- package/lib/provider/git.js.map +1 -1
- package/lib/provider/index.d.ts +5 -1
- package/lib/provider/index.d.ts.map +1 -1
- package/lib/provider/index.js +4 -0
- package/lib/provider/index.js.map +1 -1
- package/lib/provider/mutation-lock.d.ts +37 -0
- package/lib/provider/mutation-lock.d.ts.map +1 -0
- package/lib/provider/mutation-lock.js +270 -0
- package/lib/provider/mutation-lock.js.map +1 -0
- package/lib/provider/mutation-token.d.ts +8 -0
- package/lib/provider/mutation-token.d.ts.map +1 -0
- package/lib/provider/mutation-token.js +20 -0
- package/lib/provider/mutation-token.js.map +1 -0
- package/lib/provider/repository-fingerprint.d.ts +8 -0
- package/lib/provider/repository-fingerprint.d.ts.map +1 -0
- package/lib/provider/repository-fingerprint.js +13 -0
- package/lib/provider/repository-fingerprint.js.map +1 -0
- package/lib/provider/sidecar-persistence.d.ts +7 -3
- package/lib/provider/sidecar-persistence.d.ts.map +1 -1
- package/lib/provider/sidecar-persistence.js +118 -44
- package/lib/provider/sidecar-persistence.js.map +1 -1
- package/lib/provider/sidecar-schema.d.ts +1 -1
- package/lib/provider/sidecar-schema.d.ts.map +1 -1
- package/lib/provider/sidecar-schema.js +140 -8
- package/lib/provider/sidecar-schema.js.map +1 -1
- package/lib/provider/sidecar.d.ts +3 -1
- package/lib/provider/sidecar.d.ts.map +1 -1
- package/lib/provider/sidecar.js +7 -2
- package/lib/provider/sidecar.js.map +1 -1
- package/lib/provider/subprocess.d.ts +37 -0
- package/lib/provider/subprocess.d.ts.map +1 -0
- package/lib/provider/subprocess.js +327 -0
- package/lib/provider/subprocess.js.map +1 -0
- package/lib/provider/transaction.d.ts +65 -0
- package/lib/provider/transaction.d.ts.map +1 -0
- package/lib/provider/transaction.js +1011 -0
- package/lib/provider/transaction.js.map +1 -0
- package/lib/provider/types.d.ts +98 -7
- package/lib/provider/types.d.ts.map +1 -1
- package/lib/provider/types.js +1 -1
- package/lib/provider/types.js.map +1 -1
- package/lib/typert.host.js +29 -25
- package/lib/typert.remote-client.d.ts +2 -2
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +29 -25
- package/package.json +48 -32
package/lib/client.js
CHANGED
|
@@ -4,10 +4,755 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
-
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
8
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
8
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
9
|
let react = require("react");
|
|
10
|
+
//#region ../../node_modules/.pnpm/zustand@4.4.7_@types+react@18.3.31_immer@10.2.0_react@18.3.1/node_modules/zustand/esm/vanilla.mjs
|
|
11
|
+
const createStoreImpl = (createState) => {
|
|
12
|
+
let state;
|
|
13
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
14
|
+
const setState = (partial, replace) => {
|
|
15
|
+
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
16
|
+
if (!Object.is(nextState, state)) {
|
|
17
|
+
const previousState = state;
|
|
18
|
+
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
19
|
+
listeners.forEach((listener) => listener(state, previousState));
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const getState = () => state;
|
|
23
|
+
const subscribe = (listener) => {
|
|
24
|
+
listeners.add(listener);
|
|
25
|
+
return () => listeners.delete(listener);
|
|
26
|
+
};
|
|
27
|
+
const destroy = () => {
|
|
28
|
+
listeners.clear();
|
|
29
|
+
};
|
|
30
|
+
const api = {
|
|
31
|
+
setState,
|
|
32
|
+
getState,
|
|
33
|
+
subscribe,
|
|
34
|
+
destroy
|
|
35
|
+
};
|
|
36
|
+
state = createState(setState, getState, api);
|
|
37
|
+
return api;
|
|
38
|
+
};
|
|
39
|
+
const createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region ../../node_modules/.pnpm/zustand@4.4.7_@types+react@18.3.31_immer@10.2.0_react@18.3.1/node_modules/zustand/esm/middleware.mjs
|
|
42
|
+
const subscribeWithSelectorImpl = (fn) => (set, get, api) => {
|
|
43
|
+
const origSubscribe = api.subscribe;
|
|
44
|
+
api.subscribe = (selector, optListener, options) => {
|
|
45
|
+
let listener = selector;
|
|
46
|
+
if (optListener) {
|
|
47
|
+
const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is;
|
|
48
|
+
let currentSlice = selector(api.getState());
|
|
49
|
+
listener = (state) => {
|
|
50
|
+
const nextSlice = selector(state);
|
|
51
|
+
if (!equalityFn(currentSlice, nextSlice)) {
|
|
52
|
+
const previousSlice = currentSlice;
|
|
53
|
+
optListener(currentSlice = nextSlice, previousSlice);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
if (options == null ? void 0 : options.fireImmediately) optListener(currentSlice, currentSlice);
|
|
57
|
+
}
|
|
58
|
+
return origSubscribe(listener);
|
|
59
|
+
};
|
|
60
|
+
return fn(set, get, api);
|
|
61
|
+
};
|
|
62
|
+
const subscribeWithSelector = subscribeWithSelectorImpl;
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region ../../node_modules/.pnpm/immer@10.2.0/node_modules/immer/dist/immer.mjs
|
|
65
|
+
var NOTHING = Symbol.for("immer-nothing");
|
|
66
|
+
var DRAFTABLE = Symbol.for("immer-draftable");
|
|
67
|
+
var DRAFT_STATE = Symbol.for("immer-state");
|
|
68
|
+
function die(error, ...args) {
|
|
69
|
+
throw new Error(`[Immer] minified error nr: ${error}. Full error at: https://bit.ly/3cXEKWf`);
|
|
70
|
+
}
|
|
71
|
+
var getPrototypeOf = Object.getPrototypeOf;
|
|
72
|
+
function isDraft(value) {
|
|
73
|
+
return !!value && !!value[DRAFT_STATE];
|
|
74
|
+
}
|
|
75
|
+
function isDraftable(value) {
|
|
76
|
+
if (!value) return false;
|
|
77
|
+
return isPlainObject(value) || Array.isArray(value) || !!value[DRAFTABLE] || !!value.constructor?.[DRAFTABLE] || isMap(value) || isSet(value);
|
|
78
|
+
}
|
|
79
|
+
var objectCtorString = Object.prototype.constructor.toString();
|
|
80
|
+
var cachedCtorStrings = /* @__PURE__ */ new WeakMap();
|
|
81
|
+
function isPlainObject(value) {
|
|
82
|
+
if (!value || typeof value !== "object") return false;
|
|
83
|
+
const proto = Object.getPrototypeOf(value);
|
|
84
|
+
if (proto === null || proto === Object.prototype) return true;
|
|
85
|
+
const Ctor = Object.hasOwnProperty.call(proto, "constructor") && proto.constructor;
|
|
86
|
+
if (Ctor === Object) return true;
|
|
87
|
+
if (typeof Ctor !== "function") return false;
|
|
88
|
+
let ctorString = cachedCtorStrings.get(Ctor);
|
|
89
|
+
if (ctorString === void 0) {
|
|
90
|
+
ctorString = Function.toString.call(Ctor);
|
|
91
|
+
cachedCtorStrings.set(Ctor, ctorString);
|
|
92
|
+
}
|
|
93
|
+
return ctorString === objectCtorString;
|
|
94
|
+
}
|
|
95
|
+
function each(obj, iter, strict = true) {
|
|
96
|
+
if (getArchtype(obj) === 0) (strict ? Reflect.ownKeys(obj) : Object.keys(obj)).forEach((key) => {
|
|
97
|
+
iter(key, obj[key], obj);
|
|
98
|
+
});
|
|
99
|
+
else obj.forEach((entry, index) => iter(index, entry, obj));
|
|
100
|
+
}
|
|
101
|
+
function getArchtype(thing) {
|
|
102
|
+
const state = thing[DRAFT_STATE];
|
|
103
|
+
return state ? state.type_ : Array.isArray(thing) ? 1 : isMap(thing) ? 2 : isSet(thing) ? 3 : 0;
|
|
104
|
+
}
|
|
105
|
+
function has(thing, prop) {
|
|
106
|
+
return getArchtype(thing) === 2 ? thing.has(prop) : Object.prototype.hasOwnProperty.call(thing, prop);
|
|
107
|
+
}
|
|
108
|
+
function set(thing, propOrOldValue, value) {
|
|
109
|
+
const t = getArchtype(thing);
|
|
110
|
+
if (t === 2) thing.set(propOrOldValue, value);
|
|
111
|
+
else if (t === 3) thing.add(value);
|
|
112
|
+
else thing[propOrOldValue] = value;
|
|
113
|
+
}
|
|
114
|
+
function is(x, y) {
|
|
115
|
+
if (x === y) return x !== 0 || 1 / x === 1 / y;
|
|
116
|
+
else return x !== x && y !== y;
|
|
117
|
+
}
|
|
118
|
+
function isMap(target) {
|
|
119
|
+
return target instanceof Map;
|
|
120
|
+
}
|
|
121
|
+
function isSet(target) {
|
|
122
|
+
return target instanceof Set;
|
|
123
|
+
}
|
|
124
|
+
function latest(state) {
|
|
125
|
+
return state.copy_ || state.base_;
|
|
126
|
+
}
|
|
127
|
+
function shallowCopy(base, strict) {
|
|
128
|
+
if (isMap(base)) return new Map(base);
|
|
129
|
+
if (isSet(base)) return new Set(base);
|
|
130
|
+
if (Array.isArray(base)) return Array.prototype.slice.call(base);
|
|
131
|
+
const isPlain = isPlainObject(base);
|
|
132
|
+
if (strict === true || strict === "class_only" && !isPlain) {
|
|
133
|
+
const descriptors = Object.getOwnPropertyDescriptors(base);
|
|
134
|
+
delete descriptors[DRAFT_STATE];
|
|
135
|
+
let keys = Reflect.ownKeys(descriptors);
|
|
136
|
+
for (let i = 0; i < keys.length; i++) {
|
|
137
|
+
const key = keys[i];
|
|
138
|
+
const desc = descriptors[key];
|
|
139
|
+
if (desc.writable === false) {
|
|
140
|
+
desc.writable = true;
|
|
141
|
+
desc.configurable = true;
|
|
142
|
+
}
|
|
143
|
+
if (desc.get || desc.set) descriptors[key] = {
|
|
144
|
+
configurable: true,
|
|
145
|
+
writable: true,
|
|
146
|
+
enumerable: desc.enumerable,
|
|
147
|
+
value: base[key]
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return Object.create(getPrototypeOf(base), descriptors);
|
|
151
|
+
} else {
|
|
152
|
+
const proto = getPrototypeOf(base);
|
|
153
|
+
if (proto !== null && isPlain) return { ...base };
|
|
154
|
+
const obj = Object.create(proto);
|
|
155
|
+
return Object.assign(obj, base);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function freeze(obj, deep = false) {
|
|
159
|
+
if (isFrozen(obj) || isDraft(obj) || !isDraftable(obj)) return obj;
|
|
160
|
+
if (getArchtype(obj) > 1) Object.defineProperties(obj, {
|
|
161
|
+
set: dontMutateMethodOverride,
|
|
162
|
+
add: dontMutateMethodOverride,
|
|
163
|
+
clear: dontMutateMethodOverride,
|
|
164
|
+
delete: dontMutateMethodOverride
|
|
165
|
+
});
|
|
166
|
+
Object.freeze(obj);
|
|
167
|
+
if (deep) Object.values(obj).forEach((value) => freeze(value, true));
|
|
168
|
+
return obj;
|
|
169
|
+
}
|
|
170
|
+
function dontMutateFrozenCollections() {
|
|
171
|
+
die(2);
|
|
172
|
+
}
|
|
173
|
+
var dontMutateMethodOverride = { value: dontMutateFrozenCollections };
|
|
174
|
+
function isFrozen(obj) {
|
|
175
|
+
if (obj === null || typeof obj !== "object") return true;
|
|
176
|
+
return Object.isFrozen(obj);
|
|
177
|
+
}
|
|
178
|
+
var plugins = {};
|
|
179
|
+
function getPlugin(pluginKey) {
|
|
180
|
+
const plugin = plugins[pluginKey];
|
|
181
|
+
if (!plugin) die(0, pluginKey);
|
|
182
|
+
return plugin;
|
|
183
|
+
}
|
|
184
|
+
var currentScope;
|
|
185
|
+
function getCurrentScope() {
|
|
186
|
+
return currentScope;
|
|
187
|
+
}
|
|
188
|
+
function createScope(parent_, immer_) {
|
|
189
|
+
return {
|
|
190
|
+
drafts_: [],
|
|
191
|
+
parent_,
|
|
192
|
+
immer_,
|
|
193
|
+
canAutoFreeze_: true,
|
|
194
|
+
unfinalizedDrafts_: 0
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function usePatchesInScope(scope, patchListener) {
|
|
198
|
+
if (patchListener) {
|
|
199
|
+
getPlugin("Patches");
|
|
200
|
+
scope.patches_ = [];
|
|
201
|
+
scope.inversePatches_ = [];
|
|
202
|
+
scope.patchListener_ = patchListener;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function revokeScope(scope) {
|
|
206
|
+
leaveScope(scope);
|
|
207
|
+
scope.drafts_.forEach(revokeDraft);
|
|
208
|
+
scope.drafts_ = null;
|
|
209
|
+
}
|
|
210
|
+
function leaveScope(scope) {
|
|
211
|
+
if (scope === currentScope) currentScope = scope.parent_;
|
|
212
|
+
}
|
|
213
|
+
function enterScope(immer2) {
|
|
214
|
+
return currentScope = createScope(currentScope, immer2);
|
|
215
|
+
}
|
|
216
|
+
function revokeDraft(draft) {
|
|
217
|
+
const state = draft[DRAFT_STATE];
|
|
218
|
+
if (state.type_ === 0 || state.type_ === 1) state.revoke_();
|
|
219
|
+
else state.revoked_ = true;
|
|
220
|
+
}
|
|
221
|
+
function processResult(result, scope) {
|
|
222
|
+
scope.unfinalizedDrafts_ = scope.drafts_.length;
|
|
223
|
+
const baseDraft = scope.drafts_[0];
|
|
224
|
+
if (result !== void 0 && result !== baseDraft) {
|
|
225
|
+
if (baseDraft[DRAFT_STATE].modified_) {
|
|
226
|
+
revokeScope(scope);
|
|
227
|
+
die(4);
|
|
228
|
+
}
|
|
229
|
+
if (isDraftable(result)) {
|
|
230
|
+
result = finalize(scope, result);
|
|
231
|
+
if (!scope.parent_) maybeFreeze(scope, result);
|
|
232
|
+
}
|
|
233
|
+
if (scope.patches_) getPlugin("Patches").generateReplacementPatches_(baseDraft[DRAFT_STATE].base_, result, scope.patches_, scope.inversePatches_);
|
|
234
|
+
} else result = finalize(scope, baseDraft, []);
|
|
235
|
+
revokeScope(scope);
|
|
236
|
+
if (scope.patches_) scope.patchListener_(scope.patches_, scope.inversePatches_);
|
|
237
|
+
return result !== NOTHING ? result : void 0;
|
|
238
|
+
}
|
|
239
|
+
function finalize(rootScope, value, path) {
|
|
240
|
+
if (isFrozen(value)) return value;
|
|
241
|
+
const useStrictIteration = rootScope.immer_.shouldUseStrictIteration();
|
|
242
|
+
const state = value[DRAFT_STATE];
|
|
243
|
+
if (!state) {
|
|
244
|
+
each(value, (key, childValue) => finalizeProperty(rootScope, state, value, key, childValue, path), useStrictIteration);
|
|
245
|
+
return value;
|
|
246
|
+
}
|
|
247
|
+
if (state.scope_ !== rootScope) return value;
|
|
248
|
+
if (!state.modified_) {
|
|
249
|
+
maybeFreeze(rootScope, state.base_, true);
|
|
250
|
+
return state.base_;
|
|
251
|
+
}
|
|
252
|
+
if (!state.finalized_) {
|
|
253
|
+
state.finalized_ = true;
|
|
254
|
+
state.scope_.unfinalizedDrafts_--;
|
|
255
|
+
const result = state.copy_;
|
|
256
|
+
let resultEach = result;
|
|
257
|
+
let isSet2 = false;
|
|
258
|
+
if (state.type_ === 3) {
|
|
259
|
+
resultEach = new Set(result);
|
|
260
|
+
result.clear();
|
|
261
|
+
isSet2 = true;
|
|
262
|
+
}
|
|
263
|
+
each(resultEach, (key, childValue) => finalizeProperty(rootScope, state, result, key, childValue, path, isSet2), useStrictIteration);
|
|
264
|
+
maybeFreeze(rootScope, result, false);
|
|
265
|
+
if (path && rootScope.patches_) getPlugin("Patches").generatePatches_(state, path, rootScope.patches_, rootScope.inversePatches_);
|
|
266
|
+
}
|
|
267
|
+
return state.copy_;
|
|
268
|
+
}
|
|
269
|
+
function finalizeProperty(rootScope, parentState, targetObject, prop, childValue, rootPath, targetIsSet) {
|
|
270
|
+
if (childValue == null) return;
|
|
271
|
+
if (typeof childValue !== "object" && !targetIsSet) return;
|
|
272
|
+
const childIsFrozen = isFrozen(childValue);
|
|
273
|
+
if (childIsFrozen && !targetIsSet) return;
|
|
274
|
+
if (isDraft(childValue)) {
|
|
275
|
+
const res = finalize(rootScope, childValue, rootPath && parentState && parentState.type_ !== 3 && !has(parentState.assigned_, prop) ? rootPath.concat(prop) : void 0);
|
|
276
|
+
set(targetObject, prop, res);
|
|
277
|
+
if (isDraft(res)) rootScope.canAutoFreeze_ = false;
|
|
278
|
+
else return;
|
|
279
|
+
} else if (targetIsSet) targetObject.add(childValue);
|
|
280
|
+
if (isDraftable(childValue) && !childIsFrozen) {
|
|
281
|
+
if (!rootScope.immer_.autoFreeze_ && rootScope.unfinalizedDrafts_ < 1) return;
|
|
282
|
+
if (parentState && parentState.base_ && parentState.base_[prop] === childValue && childIsFrozen) return;
|
|
283
|
+
finalize(rootScope, childValue);
|
|
284
|
+
if ((!parentState || !parentState.scope_.parent_) && typeof prop !== "symbol" && (isMap(targetObject) ? targetObject.has(prop) : Object.prototype.propertyIsEnumerable.call(targetObject, prop))) maybeFreeze(rootScope, childValue);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function maybeFreeze(scope, value, deep = false) {
|
|
288
|
+
if (!scope.parent_ && scope.immer_.autoFreeze_ && scope.canAutoFreeze_) freeze(value, deep);
|
|
289
|
+
}
|
|
290
|
+
function createProxyProxy(base, parent) {
|
|
291
|
+
const isArray = Array.isArray(base);
|
|
292
|
+
const state = {
|
|
293
|
+
type_: isArray ? 1 : 0,
|
|
294
|
+
scope_: parent ? parent.scope_ : getCurrentScope(),
|
|
295
|
+
modified_: false,
|
|
296
|
+
finalized_: false,
|
|
297
|
+
assigned_: {},
|
|
298
|
+
parent_: parent,
|
|
299
|
+
base_: base,
|
|
300
|
+
draft_: null,
|
|
301
|
+
copy_: null,
|
|
302
|
+
revoke_: null,
|
|
303
|
+
isManual_: false
|
|
304
|
+
};
|
|
305
|
+
let target = state;
|
|
306
|
+
let traps = objectTraps;
|
|
307
|
+
if (isArray) {
|
|
308
|
+
target = [state];
|
|
309
|
+
traps = arrayTraps;
|
|
310
|
+
}
|
|
311
|
+
const { revoke, proxy } = Proxy.revocable(target, traps);
|
|
312
|
+
state.draft_ = proxy;
|
|
313
|
+
state.revoke_ = revoke;
|
|
314
|
+
return proxy;
|
|
315
|
+
}
|
|
316
|
+
var objectTraps = {
|
|
317
|
+
get(state, prop) {
|
|
318
|
+
if (prop === DRAFT_STATE) return state;
|
|
319
|
+
const source = latest(state);
|
|
320
|
+
if (!has(source, prop)) return readPropFromProto(state, source, prop);
|
|
321
|
+
const value = source[prop];
|
|
322
|
+
if (state.finalized_ || !isDraftable(value)) return value;
|
|
323
|
+
if (value === peek(state.base_, prop)) {
|
|
324
|
+
prepareCopy(state);
|
|
325
|
+
return state.copy_[prop] = createProxy(value, state);
|
|
326
|
+
}
|
|
327
|
+
return value;
|
|
328
|
+
},
|
|
329
|
+
has(state, prop) {
|
|
330
|
+
return prop in latest(state);
|
|
331
|
+
},
|
|
332
|
+
ownKeys(state) {
|
|
333
|
+
return Reflect.ownKeys(latest(state));
|
|
334
|
+
},
|
|
335
|
+
set(state, prop, value) {
|
|
336
|
+
const desc = getDescriptorFromProto(latest(state), prop);
|
|
337
|
+
if (desc?.set) {
|
|
338
|
+
desc.set.call(state.draft_, value);
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
if (!state.modified_) {
|
|
342
|
+
const current2 = peek(latest(state), prop);
|
|
343
|
+
const currentState = current2?.[DRAFT_STATE];
|
|
344
|
+
if (currentState && currentState.base_ === value) {
|
|
345
|
+
state.copy_[prop] = value;
|
|
346
|
+
state.assigned_[prop] = false;
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
if (is(value, current2) && (value !== void 0 || has(state.base_, prop))) return true;
|
|
350
|
+
prepareCopy(state);
|
|
351
|
+
markChanged(state);
|
|
352
|
+
}
|
|
353
|
+
if (state.copy_[prop] === value && (value !== void 0 || prop in state.copy_) || Number.isNaN(value) && Number.isNaN(state.copy_[prop])) return true;
|
|
354
|
+
state.copy_[prop] = value;
|
|
355
|
+
state.assigned_[prop] = true;
|
|
356
|
+
return true;
|
|
357
|
+
},
|
|
358
|
+
deleteProperty(state, prop) {
|
|
359
|
+
if (peek(state.base_, prop) !== void 0 || prop in state.base_) {
|
|
360
|
+
state.assigned_[prop] = false;
|
|
361
|
+
prepareCopy(state);
|
|
362
|
+
markChanged(state);
|
|
363
|
+
} else delete state.assigned_[prop];
|
|
364
|
+
if (state.copy_) delete state.copy_[prop];
|
|
365
|
+
return true;
|
|
366
|
+
},
|
|
367
|
+
getOwnPropertyDescriptor(state, prop) {
|
|
368
|
+
const owner = latest(state);
|
|
369
|
+
const desc = Reflect.getOwnPropertyDescriptor(owner, prop);
|
|
370
|
+
if (!desc) return desc;
|
|
371
|
+
return {
|
|
372
|
+
writable: true,
|
|
373
|
+
configurable: state.type_ !== 1 || prop !== "length",
|
|
374
|
+
enumerable: desc.enumerable,
|
|
375
|
+
value: owner[prop]
|
|
376
|
+
};
|
|
377
|
+
},
|
|
378
|
+
defineProperty() {
|
|
379
|
+
die(11);
|
|
380
|
+
},
|
|
381
|
+
getPrototypeOf(state) {
|
|
382
|
+
return getPrototypeOf(state.base_);
|
|
383
|
+
},
|
|
384
|
+
setPrototypeOf() {
|
|
385
|
+
die(12);
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
var arrayTraps = {};
|
|
389
|
+
each(objectTraps, (key, fn) => {
|
|
390
|
+
arrayTraps[key] = function() {
|
|
391
|
+
arguments[0] = arguments[0][0];
|
|
392
|
+
return fn.apply(this, arguments);
|
|
393
|
+
};
|
|
394
|
+
});
|
|
395
|
+
arrayTraps.deleteProperty = function(state, prop) {
|
|
396
|
+
return arrayTraps.set.call(this, state, prop, void 0);
|
|
397
|
+
};
|
|
398
|
+
arrayTraps.set = function(state, prop, value) {
|
|
399
|
+
return objectTraps.set.call(this, state[0], prop, value, state[0]);
|
|
400
|
+
};
|
|
401
|
+
function peek(draft, prop) {
|
|
402
|
+
const state = draft[DRAFT_STATE];
|
|
403
|
+
return (state ? latest(state) : draft)[prop];
|
|
404
|
+
}
|
|
405
|
+
function readPropFromProto(state, source, prop) {
|
|
406
|
+
const desc = getDescriptorFromProto(source, prop);
|
|
407
|
+
return desc ? `value` in desc ? desc.value : desc.get?.call(state.draft_) : void 0;
|
|
408
|
+
}
|
|
409
|
+
function getDescriptorFromProto(source, prop) {
|
|
410
|
+
if (!(prop in source)) return void 0;
|
|
411
|
+
let proto = getPrototypeOf(source);
|
|
412
|
+
while (proto) {
|
|
413
|
+
const desc = Object.getOwnPropertyDescriptor(proto, prop);
|
|
414
|
+
if (desc) return desc;
|
|
415
|
+
proto = getPrototypeOf(proto);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function markChanged(state) {
|
|
419
|
+
if (!state.modified_) {
|
|
420
|
+
state.modified_ = true;
|
|
421
|
+
if (state.parent_) markChanged(state.parent_);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function prepareCopy(state) {
|
|
425
|
+
if (!state.copy_) state.copy_ = shallowCopy(state.base_, state.scope_.immer_.useStrictShallowCopy_);
|
|
426
|
+
}
|
|
427
|
+
var Immer2 = class {
|
|
428
|
+
constructor(config) {
|
|
429
|
+
this.autoFreeze_ = true;
|
|
430
|
+
this.useStrictShallowCopy_ = false;
|
|
431
|
+
this.useStrictIteration_ = true;
|
|
432
|
+
/**
|
|
433
|
+
* The `produce` function takes a value and a "recipe function" (whose
|
|
434
|
+
* return value often depends on the base state). The recipe function is
|
|
435
|
+
* free to mutate its first argument however it wants. All mutations are
|
|
436
|
+
* only ever applied to a __copy__ of the base state.
|
|
437
|
+
*
|
|
438
|
+
* Pass only a function to create a "curried producer" which relieves you
|
|
439
|
+
* from passing the recipe function every time.
|
|
440
|
+
*
|
|
441
|
+
* Only plain objects and arrays are made mutable. All other objects are
|
|
442
|
+
* considered uncopyable.
|
|
443
|
+
*
|
|
444
|
+
* Note: This function is __bound__ to its `Immer` instance.
|
|
445
|
+
*
|
|
446
|
+
* @param {any} base - the initial state
|
|
447
|
+
* @param {Function} recipe - function that receives a proxy of the base state as first argument and which can be freely modified
|
|
448
|
+
* @param {Function} patchListener - optional function that will be called with all the patches produced here
|
|
449
|
+
* @returns {any} a new state, or the initial state if nothing was modified
|
|
450
|
+
*/
|
|
451
|
+
this.produce = (base, recipe, patchListener) => {
|
|
452
|
+
if (typeof base === "function" && typeof recipe !== "function") {
|
|
453
|
+
const defaultBase = recipe;
|
|
454
|
+
recipe = base;
|
|
455
|
+
const self = this;
|
|
456
|
+
return function curriedProduce(base2 = defaultBase, ...args) {
|
|
457
|
+
return self.produce(base2, (draft) => recipe.call(this, draft, ...args));
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
if (typeof recipe !== "function") die(6);
|
|
461
|
+
if (patchListener !== void 0 && typeof patchListener !== "function") die(7);
|
|
462
|
+
let result;
|
|
463
|
+
if (isDraftable(base)) {
|
|
464
|
+
const scope = enterScope(this);
|
|
465
|
+
const proxy = createProxy(base, void 0);
|
|
466
|
+
let hasError = true;
|
|
467
|
+
try {
|
|
468
|
+
result = recipe(proxy);
|
|
469
|
+
hasError = false;
|
|
470
|
+
} finally {
|
|
471
|
+
if (hasError) revokeScope(scope);
|
|
472
|
+
else leaveScope(scope);
|
|
473
|
+
}
|
|
474
|
+
usePatchesInScope(scope, patchListener);
|
|
475
|
+
return processResult(result, scope);
|
|
476
|
+
} else if (!base || typeof base !== "object") {
|
|
477
|
+
result = recipe(base);
|
|
478
|
+
if (result === void 0) result = base;
|
|
479
|
+
if (result === NOTHING) result = void 0;
|
|
480
|
+
if (this.autoFreeze_) freeze(result, true);
|
|
481
|
+
if (patchListener) {
|
|
482
|
+
const p = [];
|
|
483
|
+
const ip = [];
|
|
484
|
+
getPlugin("Patches").generateReplacementPatches_(base, result, p, ip);
|
|
485
|
+
patchListener(p, ip);
|
|
486
|
+
}
|
|
487
|
+
return result;
|
|
488
|
+
} else die(1, base);
|
|
489
|
+
};
|
|
490
|
+
this.produceWithPatches = (base, recipe) => {
|
|
491
|
+
if (typeof base === "function") return (state, ...args) => this.produceWithPatches(state, (draft) => base(draft, ...args));
|
|
492
|
+
let patches, inversePatches;
|
|
493
|
+
return [
|
|
494
|
+
this.produce(base, recipe, (p, ip) => {
|
|
495
|
+
patches = p;
|
|
496
|
+
inversePatches = ip;
|
|
497
|
+
}),
|
|
498
|
+
patches,
|
|
499
|
+
inversePatches
|
|
500
|
+
];
|
|
501
|
+
};
|
|
502
|
+
if (typeof config?.autoFreeze === "boolean") this.setAutoFreeze(config.autoFreeze);
|
|
503
|
+
if (typeof config?.useStrictShallowCopy === "boolean") this.setUseStrictShallowCopy(config.useStrictShallowCopy);
|
|
504
|
+
if (typeof config?.useStrictIteration === "boolean") this.setUseStrictIteration(config.useStrictIteration);
|
|
505
|
+
}
|
|
506
|
+
createDraft(base) {
|
|
507
|
+
if (!isDraftable(base)) die(8);
|
|
508
|
+
if (isDraft(base)) base = current(base);
|
|
509
|
+
const scope = enterScope(this);
|
|
510
|
+
const proxy = createProxy(base, void 0);
|
|
511
|
+
proxy[DRAFT_STATE].isManual_ = true;
|
|
512
|
+
leaveScope(scope);
|
|
513
|
+
return proxy;
|
|
514
|
+
}
|
|
515
|
+
finishDraft(draft, patchListener) {
|
|
516
|
+
const state = draft && draft[DRAFT_STATE];
|
|
517
|
+
if (!state || !state.isManual_) die(9);
|
|
518
|
+
const { scope_: scope } = state;
|
|
519
|
+
usePatchesInScope(scope, patchListener);
|
|
520
|
+
return processResult(void 0, scope);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Pass true to automatically freeze all copies created by Immer.
|
|
524
|
+
*
|
|
525
|
+
* By default, auto-freezing is enabled.
|
|
526
|
+
*/
|
|
527
|
+
setAutoFreeze(value) {
|
|
528
|
+
this.autoFreeze_ = value;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Pass true to enable strict shallow copy.
|
|
532
|
+
*
|
|
533
|
+
* By default, immer does not copy the object descriptors such as getter, setter and non-enumrable properties.
|
|
534
|
+
*/
|
|
535
|
+
setUseStrictShallowCopy(value) {
|
|
536
|
+
this.useStrictShallowCopy_ = value;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Pass false to use faster iteration that skips non-enumerable properties
|
|
540
|
+
* but still handles symbols for compatibility.
|
|
541
|
+
*
|
|
542
|
+
* By default, strict iteration is enabled (includes all own properties).
|
|
543
|
+
*/
|
|
544
|
+
setUseStrictIteration(value) {
|
|
545
|
+
this.useStrictIteration_ = value;
|
|
546
|
+
}
|
|
547
|
+
shouldUseStrictIteration() {
|
|
548
|
+
return this.useStrictIteration_;
|
|
549
|
+
}
|
|
550
|
+
applyPatches(base, patches) {
|
|
551
|
+
let i;
|
|
552
|
+
for (i = patches.length - 1; i >= 0; i--) {
|
|
553
|
+
const patch = patches[i];
|
|
554
|
+
if (patch.path.length === 0 && patch.op === "replace") {
|
|
555
|
+
base = patch.value;
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (i > -1) patches = patches.slice(i + 1);
|
|
560
|
+
const applyPatchesImpl = getPlugin("Patches").applyPatches_;
|
|
561
|
+
if (isDraft(base)) return applyPatchesImpl(base, patches);
|
|
562
|
+
return this.produce(base, (draft) => applyPatchesImpl(draft, patches));
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
function createProxy(value, parent) {
|
|
566
|
+
const draft = isMap(value) ? getPlugin("MapSet").proxyMap_(value, parent) : isSet(value) ? getPlugin("MapSet").proxySet_(value, parent) : createProxyProxy(value, parent);
|
|
567
|
+
(parent ? parent.scope_ : getCurrentScope()).drafts_.push(draft);
|
|
568
|
+
return draft;
|
|
569
|
+
}
|
|
570
|
+
function current(value) {
|
|
571
|
+
if (!isDraft(value)) die(10, value);
|
|
572
|
+
return currentImpl(value);
|
|
573
|
+
}
|
|
574
|
+
function currentImpl(value) {
|
|
575
|
+
if (!isDraftable(value) || isFrozen(value)) return value;
|
|
576
|
+
const state = value[DRAFT_STATE];
|
|
577
|
+
let copy;
|
|
578
|
+
let strict = true;
|
|
579
|
+
if (state) {
|
|
580
|
+
if (!state.modified_) return state.base_;
|
|
581
|
+
state.finalized_ = true;
|
|
582
|
+
copy = shallowCopy(value, state.scope_.immer_.useStrictShallowCopy_);
|
|
583
|
+
strict = state.scope_.immer_.shouldUseStrictIteration();
|
|
584
|
+
} else copy = shallowCopy(value, true);
|
|
585
|
+
each(copy, (key, childValue) => {
|
|
586
|
+
set(copy, key, currentImpl(childValue));
|
|
587
|
+
}, strict);
|
|
588
|
+
if (state) state.finalized_ = false;
|
|
589
|
+
return copy;
|
|
590
|
+
}
|
|
591
|
+
var produce = new Immer2().produce;
|
|
592
|
+
//#endregion
|
|
593
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-store@0.1.2-rc.1_@deepseek-ai+cordis@4.0.1_@types+react@18.3.31_react@18.3.1/node_modules/@deepseek-ai/dsh-client-store/lib/index.js
|
|
594
|
+
/**
|
|
595
|
+
* React-free snapshot store engine (zustand vanilla + immer + subscribeWithSelector +
|
|
596
|
+
* rafFlush middleware + opt-in persist + dev freeze) plus the declarative
|
|
597
|
+
* shell over it: {@link defineStore} bakes an init/persist/actions literal
|
|
598
|
+
* into a {@link StoreHandle}, the registration-side store seat of slot
|
|
599
|
+
* terminals. Engine products are bare observables — subscribe/getSnapshot/
|
|
600
|
+
* update/set, NO selector hook. Hook synthesis is ui-renderer's (the one
|
|
601
|
+
* uSES bridge, cached per source at the binding site).
|
|
602
|
+
*/
|
|
603
|
+
/**
|
|
604
|
+
* Notify an observer set without allowing one callback to starve the rest.
|
|
605
|
+
* @param listeners - current observer callbacks; copied before dispatch.
|
|
606
|
+
* @param label - diagnostic owner prefix.
|
|
607
|
+
* @param args - callback arguments.
|
|
608
|
+
*/
|
|
609
|
+
function notifySubscribers(listeners, label, ...args) {
|
|
610
|
+
for (const listener of [...listeners]) try {
|
|
611
|
+
listener(...args);
|
|
612
|
+
} catch (error) {
|
|
613
|
+
console.error(`${label} subscriber failed:`, error);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
/** Batches subscriber notification into one flush per animation frame. */
|
|
617
|
+
function rafBatch(notify) {
|
|
618
|
+
const schedule = typeof requestAnimationFrame === "function" ? (fn) => {
|
|
619
|
+
requestAnimationFrame(() => {
|
|
620
|
+
fn();
|
|
621
|
+
});
|
|
622
|
+
} : (fn) => {
|
|
623
|
+
queueMicrotask(fn);
|
|
624
|
+
};
|
|
625
|
+
let scheduled = false;
|
|
626
|
+
return () => {
|
|
627
|
+
if (scheduled) return;
|
|
628
|
+
scheduled = true;
|
|
629
|
+
schedule(() => {
|
|
630
|
+
scheduled = false;
|
|
631
|
+
notify();
|
|
632
|
+
});
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Create a snapshot store.
|
|
637
|
+
*
|
|
638
|
+
* Flush default is 'sync' (controlled inputs need same-tick echo); frame-driven
|
|
639
|
+
* stores opt into 'raf', where a frame's worth of updates coalesces into one
|
|
640
|
+
* notification. Known raf-mode tradeoff: a component mounting mid-frame reads
|
|
641
|
+
* fresh state while existing subscribers hear it next flush — transient
|
|
642
|
+
* frame-level skew, same nature as the object layer's microtask batching.
|
|
643
|
+
*
|
|
644
|
+
* @param init - initial state.
|
|
645
|
+
* @param opts - flush mode and opt-in persistence (localStorage, keyed by name).
|
|
646
|
+
* @returns the store.
|
|
647
|
+
*/
|
|
648
|
+
function createSnapshotStore(init, opts) {
|
|
649
|
+
const withSelector = subscribeWithSelector(() => init);
|
|
650
|
+
const api = createStore()(withSelector);
|
|
651
|
+
if (opts?.persist) attachPersistence(api, opts.persist.name);
|
|
652
|
+
let subscribe = (fn) => api.subscribe(() => {
|
|
653
|
+
notifySubscribers([fn], "[client-store]");
|
|
654
|
+
});
|
|
655
|
+
if (opts?.flush === "raf") {
|
|
656
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
657
|
+
const flush = rafBatch(() => {
|
|
658
|
+
notifySubscribers(listeners, "[client-store]");
|
|
659
|
+
});
|
|
660
|
+
api.subscribe(flush);
|
|
661
|
+
subscribe = (fn) => {
|
|
662
|
+
listeners.add(fn);
|
|
663
|
+
return () => {
|
|
664
|
+
listeners.delete(fn);
|
|
665
|
+
};
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
return {
|
|
669
|
+
getSnapshot: () => api.getState(),
|
|
670
|
+
subscribe: (fn) => subscribe(fn),
|
|
671
|
+
update: (mutator) => {
|
|
672
|
+
api.setState(produce(api.getState(), (draft) => {
|
|
673
|
+
mutator(draft);
|
|
674
|
+
}), true);
|
|
675
|
+
},
|
|
676
|
+
set: (next) => {
|
|
677
|
+
api.setState(devFreeze(next), true);
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Whole-value JSON persistence to localStorage. Hand-rolled instead of the
|
|
683
|
+
* zustand persist middleware: its write path spreads state into an object
|
|
684
|
+
* (`partialize({ ...get() })`), exploding primitive state (a persisted string
|
|
685
|
+
* draft becomes {0:'h',1:'e',...}) — not fixable via merge/deserialize options
|
|
686
|
+
* because the corruption happens before serialization. Storage failures
|
|
687
|
+
* (quota, private mode) only disable persistence, never break the store.
|
|
688
|
+
*/
|
|
689
|
+
function attachPersistence(api, name) {
|
|
690
|
+
if (typeof localStorage === "undefined") return;
|
|
691
|
+
try {
|
|
692
|
+
const raw = localStorage.getItem(name);
|
|
693
|
+
if (raw !== null) api.setState(devFreeze(JSON.parse(raw)), true);
|
|
694
|
+
} catch (error) {
|
|
695
|
+
console.error(`snapshot store '${name}' rehydration failed:`, error);
|
|
696
|
+
}
|
|
697
|
+
api.subscribe((state) => {
|
|
698
|
+
try {
|
|
699
|
+
localStorage.setItem(name, JSON.stringify(state));
|
|
700
|
+
} catch (error) {
|
|
701
|
+
console.error(`snapshot store '${name}' persistence failed:`, error);
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
/** Deep-freeze draftable wholesale-set state outside production: set() bypasses immer's freeze. */
|
|
706
|
+
function devFreeze(value) {
|
|
707
|
+
return freeze(value, true);
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Declare a store: initial state, optional persistence, and the full write
|
|
711
|
+
* set as pure draft mutators. The returned handle is the registration
|
|
712
|
+
* currency of the store seat — its identity keys instance sharing. Satisfies
|
|
713
|
+
* ui-slots' DefineStore contract (the handle/instance are the engine-extended
|
|
714
|
+
* subtypes).
|
|
715
|
+
*
|
|
716
|
+
* The `A & ActionsDecl<T>` actions position is load-bearing: T resolves from
|
|
717
|
+
* `init` in the first inference round, and the intersection then contextually
|
|
718
|
+
* types each mutator's draft parameter (context-sensitive functions defer),
|
|
719
|
+
* so call sites write `(d, x: X) => { ... }` with no draft annotation. If a
|
|
720
|
+
* future TS version breaks this single-literal inference, the design's
|
|
721
|
+
* documented fallback is currying (`defineStore(init).actions({...})`).
|
|
722
|
+
* @param decl - init lambda (fresh state per instance), optional persist key, actions table.
|
|
723
|
+
* @returns the store handle.
|
|
724
|
+
*/
|
|
725
|
+
function defineStore(decl) {
|
|
726
|
+
return {
|
|
727
|
+
spec: decl,
|
|
728
|
+
create(scopeKey) {
|
|
729
|
+
const persistKey = decl.persist === void 0 ? void 0 : scopeKey === void 0 ? decl.persist : `${decl.persist}.${scopeKey}`;
|
|
730
|
+
const store = createSnapshotStore(decl.init(), persistKey !== void 0 ? { persist: { name: persistKey } } : void 0);
|
|
731
|
+
const actions = {};
|
|
732
|
+
for (const key of Object.keys(decl.actions)) {
|
|
733
|
+
const mutate = decl.actions[key];
|
|
734
|
+
actions[key] = (...params) => {
|
|
735
|
+
store.update((draft) => {
|
|
736
|
+
mutate(draft, ...params);
|
|
737
|
+
});
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
return {
|
|
741
|
+
actions,
|
|
742
|
+
getSnapshot: () => store.getSnapshot(),
|
|
743
|
+
subscribe: (fn) => store.subscribe(fn),
|
|
744
|
+
store,
|
|
745
|
+
clearPersisted: () => {
|
|
746
|
+
if (persistKey === void 0 || typeof localStorage === "undefined") return;
|
|
747
|
+
try {
|
|
748
|
+
localStorage.removeItem(persistKey);
|
|
749
|
+
} catch {}
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
//#endregion
|
|
11
756
|
//#region lib/client/locales.js
|
|
12
757
|
const WORKTREE_NS = "worktree";
|
|
13
758
|
const zh = {
|
|
@@ -36,6 +781,7 @@ window.__ModuleLoader__.load({
|
|
|
36
781
|
"worktree.mainWithBranch": "本地({branch})",
|
|
37
782
|
"worktree.detached": "已分离 Worktree",
|
|
38
783
|
"worktree.repair": "Worktree 需要修复",
|
|
784
|
+
"worktree.recovery": "Worktree 需要恢复",
|
|
39
785
|
"worktree.ready": "活动 Worktree 已就绪",
|
|
40
786
|
"worktree.expand": "展开 {name}",
|
|
41
787
|
"worktree.collapse": "收起 {name}",
|
|
@@ -72,6 +818,7 @@ window.__ModuleLoader__.load({
|
|
|
72
818
|
"session.rename": "重命名会话",
|
|
73
819
|
"session.fork": "Fork 会话",
|
|
74
820
|
"session.archive": "归档会话",
|
|
821
|
+
"session.copyId": "复制 Session ID",
|
|
75
822
|
"session.expandMore": "展开其余 {count} 个会话",
|
|
76
823
|
"session.collapse": "收起",
|
|
77
824
|
"session.name": "会话名称",
|
|
@@ -125,6 +872,10 @@ window.__ModuleLoader__.load({
|
|
|
125
872
|
"error.worktreeRecordMissing": "Worktree 注册未返回 Worktree 记录,请重试请求。",
|
|
126
873
|
"error.worktreeImportInvalid": "这个 Worktree 无法导入,请选择同一 Workspace 中有效的绝对路径。",
|
|
127
874
|
"error.worktreeAlreadyManaged": "这个 Worktree 已由插件管理。",
|
|
875
|
+
"error.worktreeMutationBusy": "另一个 DSH 实例正在修改 Worktree 状态,请稍后重试。",
|
|
876
|
+
"error.worktreeStateConflict": "Worktree 状态已经变化,请刷新后重试。",
|
|
877
|
+
"error.worktreeRecoveryRequired": "Worktree 状态需要恢复;请先完成恢复后再重试破坏性操作。",
|
|
878
|
+
"error.worktreeIdentityChanged": "Worktree 或 repository 身份已经变化,需要恢复后再操作。",
|
|
128
879
|
"error.worktreeRegistrationSessionUnavailable": "Worktree 已注册,但 Session 创建不可用,请重新连接后重试。",
|
|
129
880
|
"error.workspaceRenameUnavailable": "工作区重命名不可用,请重新连接后重试。",
|
|
130
881
|
"error.workspaceDeleteUnavailable": "工作区删除不可用,请重新连接后重试。",
|
|
@@ -167,6 +918,7 @@ window.__ModuleLoader__.load({
|
|
|
167
918
|
"worktree.mainWithBranch": "Local ({branch})",
|
|
168
919
|
"worktree.detached": "Detached Worktree",
|
|
169
920
|
"worktree.repair": "Worktree needs repair",
|
|
921
|
+
"worktree.recovery": "Worktree requires recovery",
|
|
170
922
|
"worktree.ready": "Active Worktree ready",
|
|
171
923
|
"worktree.expand": "Expand {name}",
|
|
172
924
|
"worktree.collapse": "Collapse {name}",
|
|
@@ -203,6 +955,7 @@ window.__ModuleLoader__.load({
|
|
|
203
955
|
"session.rename": "Rename session",
|
|
204
956
|
"session.fork": "Fork session",
|
|
205
957
|
"session.archive": "Archive session",
|
|
958
|
+
"session.copyId": "Copy session ID",
|
|
206
959
|
"session.expandMore": "Expand {count} more",
|
|
207
960
|
"session.collapse": "Collapse",
|
|
208
961
|
"session.name": "Session name",
|
|
@@ -256,6 +1009,10 @@ window.__ModuleLoader__.load({
|
|
|
256
1009
|
"error.worktreeRecordMissing": "Worktree registration returned no Worktree record; retry the request.",
|
|
257
1010
|
"error.worktreeImportInvalid": "This Worktree cannot be imported. Choose a valid absolute path in this Workspace.",
|
|
258
1011
|
"error.worktreeAlreadyManaged": "This Worktree is already managed by the plugin.",
|
|
1012
|
+
"error.worktreeMutationBusy": "Another DSH instance is mutating Worktree state; retry shortly.",
|
|
1013
|
+
"error.worktreeStateConflict": "The Worktree state changed; refresh and retry the request.",
|
|
1014
|
+
"error.worktreeRecoveryRequired": "Worktree state requires recovery; resolve it before retrying destructive actions.",
|
|
1015
|
+
"error.worktreeIdentityChanged": "The Worktree or repository identity changed; recovery is required before acting.",
|
|
259
1016
|
"error.worktreeRegistrationSessionUnavailable": "The Worktree was registered, but Session creation is unavailable; retry after reconnecting.",
|
|
260
1017
|
"error.workspaceRenameUnavailable": "Workspace rename is unavailable; retry after reconnecting.",
|
|
261
1018
|
"error.workspaceDeleteUnavailable": "Workspace delete is unavailable; retry after reconnecting.",
|
|
@@ -414,7 +1171,7 @@ window.__ModuleLoader__.load({
|
|
|
414
1171
|
};
|
|
415
1172
|
}
|
|
416
1173
|
//#endregion
|
|
417
|
-
//#region \0clutch-dsh-worktree-css:/
|
|
1174
|
+
//#region \0clutch-dsh-worktree-css:/Users/yuancheng/.dsh/clutch-dsh-worktree/worktree/wt_6995a053-1215-47ed-b5a3-53a7525317f1/packages/clutch-dsh-worktree/src/client/worktree-context.css.mjs
|
|
418
1175
|
const css$1 = ".-cerbur-clutch-dsh-worktree-headerContext {\n display: inline-flex;\n min-width: 0;\n height: 22px;\n align-items: center;\n gap: 4px;\n max-width: 180px;\n overflow: hidden;\n border-radius: 6px;\n padding: 0 2px 0 0;\n background: var(--dsw-alias-fill-tsp-secondary);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 22px;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-contextIcon {\n flex: none;\n opacity: 0.7;\n}\n\n.-cerbur-clutch-dsh-worktree-contextLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-contextHoverTitle {\n max-width: 360px;\n color: var(--dsw-static-neutral-bluish-00);\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: anywhere;\n white-space: normal;\n}\n\n.-cerbur-clutch-dsh-worktree-heroContextHost {\n position: absolute;\n z-index: 1;\n display: block;\n width: max-content;\n max-width: 260px;\n min-height: 22px;\n}\n\n.-cerbur-clutch-dsh-worktree-heroContext {\n display: inline-flex;\n box-sizing: border-box;\n max-width: 260px;\n min-height: 22px;\n align-items: center;\n overflow: hidden;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n padding: 0 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 20px;\n pointer-events: auto;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n";
|
|
419
1176
|
const styleId$1 = "@cerbur/clutch-dsh-worktree/worktree-context.css";
|
|
420
1177
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree-context.css\"]") === null) {
|
|
@@ -455,7 +1212,9 @@ window.__ModuleLoader__.load({
|
|
|
455
1212
|
className: worktree_context_css_default.contextHoverTitle,
|
|
456
1213
|
children: value.label
|
|
457
1214
|
}),
|
|
458
|
-
openDelayMs: 500
|
|
1215
|
+
openDelayMs: 500,
|
|
1216
|
+
copyLabel: t("copy"),
|
|
1217
|
+
copiedLabel: t("hover.copied")
|
|
459
1218
|
});
|
|
460
1219
|
}
|
|
461
1220
|
/** Render the current branch or active Worktree branch beside the Session title. */
|
|
@@ -468,7 +1227,7 @@ window.__ModuleLoader__.load({
|
|
|
468
1227
|
});
|
|
469
1228
|
}
|
|
470
1229
|
//#endregion
|
|
471
|
-
//#region \0clutch-dsh-worktree-css:/
|
|
1230
|
+
//#region \0clutch-dsh-worktree-css:/Users/yuancheng/.dsh/clutch-dsh-worktree/worktree/wt_6995a053-1215-47ed-b5a3-53a7525317f1/packages/clutch-dsh-worktree/src/client/worktree.css.mjs
|
|
472
1231
|
const css = ".-cerbur-clutch-dsh-worktree-action {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: 8px;\n width: calc(100% + 4px);\n height: 42px;\n margin: 4px -2px;\n box-sizing: border-box;\n border: 0;\n border-radius: 12px;\n padding: 0 10px 0 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n overflow: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-action:hover,\n.-cerbur-clutch-dsh-worktree-action[data-active='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-action[data-collapsed='true'] {\n width: 36px;\n height: 36px;\n margin: 8px 0 10px;\n justify-content: center;\n gap: 0;\n padding: 0;\n border-radius: 50%;\n}\n\n.-cerbur-clutch-dsh-worktree-actionLabel {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n height: 22px;\n overflow: hidden;\n font-weight: 400;\n line-height: 22px;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-surface {\n position: absolute;\n top: 0;\n left: 0;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n min-width: 56px;\n overflow: hidden;\n border-right: 1px solid var(--dsw-alias-border-l1);\n background: var(--dsw-specific-sidebar-fill);\n color: var(--dsw-alias-label-primary);\n pointer-events: auto;\n transition: width var(--ds-transition-duration-slow) var(--ds-ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .-cerbur-clutch-dsh-worktree-surface {\n transition: none;\n }\n}\n\n.-cerbur-clutch-dsh-worktree-wideContent {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n}\n\n.-cerbur-clutch-dsh-worktree-surface[data-collapsed='true'] .-cerbur-clutch-dsh-worktree-wideContent {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTabs {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTab {\n border: 0;\n border-bottom: 2px solid transparent;\n padding: 4px 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTab[aria-selected='true'] {\n border-bottom-color: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportCandidates {\n display: grid;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportSelect {\n width: 100%;\n min-width: 0;\n padding: 7px 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportError {\n display: grid;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton {\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: inherit;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-header {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n min-height: 44px;\n padding: 6px 10px;\n box-sizing: border-box;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-title {\n min-width: 0;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton {\n flex: none;\n width: 28px;\n height: 28px;\n font-size: 18px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-modeButton[data-active='true'] {\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspacePicker {\n display: block;\n width: calc(100% - 20px);\n margin: 6px 10px 8px;\n padding: 6px 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceLabel {\n display: block;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-content {\n min-height: 0;\n flex: 1;\n overflow: auto;\n padding: 0 10px 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-content:focus-visible {\n outline: 2px solid var(--dsw-alias-border-l3);\n outline-offset: -2px;\n}\n\n.-cerbur-clutch-dsh-worktree-error {\n margin: 12px 0 0;\n padding: 8px;\n border: 1px solid var(--dsw-alias-state-error-primary);\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-notice {\n margin: 12px 0 0;\n padding: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton,\n.-cerbur-clutch-dsh-worktree-actionButton {\n border: 0;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton,\n.-cerbur-clutch-dsh-worktree-actionButton {\n width: 100%;\n padding: 6px 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-recoveryActions {\n display: grid;\n gap: 6px;\n margin-top: 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton:disabled,\n.-cerbur-clutch-dsh-worktree-actionButton:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.-cerbur-clutch-dsh-worktree-actions {\n display: grid;\n gap: 6px;\n margin-top: 10px;\n padding: 8px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-actionSelect {\n min-width: 0;\n padding: 5px 6px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-section {\n margin: 12px 0 0;\n}\n\n.-cerbur-clutch-dsh-worktree-sectionTitle {\n margin: 0 2px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-empty,\n.-cerbur-clutch-dsh-worktree-message {\n margin: 0;\n padding: 8px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-gitReadiness {\n display: grid;\n gap: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-commandBlock {\n margin: 6px 0 0;\n padding: 8px;\n overflow-x: auto;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-secondary);\n font:\n 11px/16px ui-monospace,\n SFMono-Regular,\n Menlo,\n monospace;\n white-space: pre-wrap;\n}\n\n.-cerbur-clutch-dsh-worktree-message[data-error='true'] {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktree,\n.-cerbur-clutch-dsh-worktree-session {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 6px 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree {\n cursor: default;\n}\n\n.-cerbur-clutch-dsh-worktree-session {\n cursor: pointer;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree:hover,\n.-cerbur-clutch-dsh-worktree-session:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeLabel,\n.-cerbur-clutch-dsh-worktree-sessionLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-status {\n flex: none;\n margin-left: auto;\n padding-left: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchRow {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchIcon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n outline: none;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n width: 100%;\n padding: 6px 8px;\n font-size: 13px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput:focus,\n.-cerbur-clutch-dsh-worktree-actionSelect:focus,\n.-cerbur-clutch-dsh-worktree-worktreeImportSelect:focus {\n border-color: #3b82f6;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 20px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceList {\n min-width: 0;\n min-height: 0;\n display: grid;\n gap: 4px;\n overflow-x: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceGroup {\n min-width: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow,\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n position: relative;\n display: flex;\n align-items: center;\n min-width: 0;\n min-height: 36px;\n box-sizing: border-box;\n border-radius: 8px;\n padding: 3px 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceDisclosure,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeDisclosure,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 28px;\n border: 0;\n border-radius: 6px;\n padding: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 17px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton > svg {\n display: block;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-worktreeIcon {\n flex: none;\n width: 24px;\n color: var(--dsw-alias-label-secondary);\n font-size: 20px;\n line-height: 1;\n text-align: center;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeState {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 12px;\n margin-right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon {\n color: var(--dsw-alias-brand-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceTitle,\n.-cerbur-clutch-dsh-worktree-worktreeLabel {\n min-width: 0;\n overflow: hidden;\n flex: 1;\n padding: 0 6px;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-main-group='true'] .-cerbur-clutch-dsh-worktree-worktreeLabel {\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeHoverTitle {\n color: var(--dsw-static-neutral-bluish-00);\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: anywhere;\n}\n\n/* Native Session hover card: full title, relative time, and current status. */\n.-cerbur-clutch-dsh-worktree-sessionHoverContent {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionHoverTitle {\n color: #FFFFFF;\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: break-word;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionHoverTime {\n color: #CFD3D6;\n font-size: 12px;\n line-height: 16px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionHoverStatus {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #ADB2B8;\n font-size: 12px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot,\n.-cerbur-clutch-dsh-worktree-workspaceActions {\n position: relative;\n display: inline-flex;\n flex: 0 0 64px;\n align-items: center;\n justify-content: flex-end;\n width: 64px;\n height: 28px;\n margin-left: auto;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-menuAction {\n position: absolute;\n top: 0;\n right: 32px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n position: absolute;\n top: 0;\n right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-groupActivity {\n position: absolute;\n top: 0;\n right: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n visibility: hidden;\n pointer-events: none;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-groupActivity {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'][data-menu-open='true'] .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'][data-menu-open='true'] .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-groupActivity {\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'][data-menu-open='true']\n .-cerbur-clutch-dsh-worktree-treeActionSlot\n > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'][data-menu-open='true']\n .-cerbur-clutch-dsh-worktree-treeActionSlot\n > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-menuAction {\n display: inline-flex;\n width: 28px;\n height: 28px;\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeChildren {\n margin-left: 16px;\n padding: 2px 0 5px 12px;\n border-left: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-groupLabel {\n padding: 6px 8px 3px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader {\n display: flex;\n align-items: center;\n min-height: 34px;\n padding-right: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader .-cerbur-clutch-dsh-worktree-groupLabel {\n flex: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeGroup {\n margin-top: 2px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n min-height: 34px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-disclosureButton {\n width: 22px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow {\n position: relative;\n display: flex;\n align-items: center;\n width: calc(100% - 22px);\n margin-left: 22px;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-session-current='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-session-current='true'] .-cerbur-clutch-dsh-worktree-treeSessionContent {\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent {\n display: flex;\n align-items: center;\n flex: 1;\n width: 0;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 4px 4px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent:hover {\n background: transparent;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionTrailing {\n position: relative;\n display: inline-flex;\n flex: 0 0 52px;\n align-items: center;\n justify-content: flex-end;\n width: 52px;\n height: 28px;\n margin-left: auto;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionMeta,\n.-cerbur-clutch-dsh-worktree-rowActions {\n position: absolute;\n inset: 0;\n display: inline-flex;\n align-items: center;\n justify-content: flex-end;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionMeta {\n color: var(--dsw-alias-label-tertiary);\n pointer-events: none;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionStatus {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionTime {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-right: 4px;\n font-size: 12px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton {\n display: block;\n width: calc(100% - 24px);\n margin: 2px 0 4px 24px;\n padding: 4px 8px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 12px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-rowActions {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover .-cerbur-clutch-dsh-worktree-sessionMeta,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-sessionMeta,\n.-cerbur-clutch-dsh-worktree-sessionTrailing:focus-within .-cerbur-clutch-dsh-worktree-sessionMeta {\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover .-cerbur-clutch-dsh-worktree-rowActions,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-rowActions,\n.-cerbur-clutch-dsh-worktree-sessionTrailing:focus-within .-cerbur-clutch-dsh-worktree-rowActions {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-renameInput {\n width: 100%;\n}\n\n.-cerbur-clutch-dsh-worktree-renameError {\n margin: 8px 0 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-emptyNested {\n margin: 0;\n padding: 4px 8px 5px 28px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-modalField {\n display: grid;\n gap: 5px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n";
|
|
473
1232
|
const styleId = "@cerbur/clutch-dsh-worktree/worktree.css";
|
|
474
1233
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree.css\"]") === null) {
|
|
@@ -688,7 +1447,9 @@ window.__ModuleLoader__.load({
|
|
|
688
1447
|
className: worktree_context_css_default.contextHoverTitle,
|
|
689
1448
|
children: label
|
|
690
1449
|
}),
|
|
691
|
-
openDelayMs: 500
|
|
1450
|
+
openDelayMs: 500,
|
|
1451
|
+
copyLabel: t("copy"),
|
|
1452
|
+
copiedLabel: t("hover.copied")
|
|
692
1453
|
})
|
|
693
1454
|
});
|
|
694
1455
|
}
|
|
@@ -924,11 +1685,6 @@ window.__ModuleLoader__.load({
|
|
|
924
1685
|
const bound = new Set(boundSessionIds);
|
|
925
1686
|
return sessionIds.filter((sessionId) => !bound.has(sessionId));
|
|
926
1687
|
}
|
|
927
|
-
/**
|
|
928
|
-
* Apply a browser-only membership delta to a DSH Workspace list snapshot.
|
|
929
|
-
* `previousBindings` are removed first so the same function can also undo a
|
|
930
|
-
* projection after a binding is deleted or the plugin is disposed.
|
|
931
|
-
*/
|
|
932
1688
|
function projectVirtualWorkspaceMembership(snapshot, previousBindings, nextBindings) {
|
|
933
1689
|
const previousSessionIds = new Set(previousBindings.map((binding) => binding.sessionId));
|
|
934
1690
|
const nextByWorkspace = /* @__PURE__ */ new Map();
|
|
@@ -953,6 +1709,34 @@ window.__ModuleLoader__.load({
|
|
|
953
1709
|
items
|
|
954
1710
|
};
|
|
955
1711
|
}
|
|
1712
|
+
function validTimestamp(value) {
|
|
1713
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
1714
|
+
}
|
|
1715
|
+
function createdAtTimestamp(createdAt) {
|
|
1716
|
+
const timestamp = Date.parse(createdAt);
|
|
1717
|
+
return Number.isFinite(timestamp) ? timestamp : void 0;
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Derive the most recent Workspace from rc.1 Session/Workspace metadata.
|
|
1721
|
+
* Host order is retained when timestamps tie, so equal activity never causes
|
|
1722
|
+
* the native Workspace order to oscillate.
|
|
1723
|
+
*/
|
|
1724
|
+
function deriveRecentWorkspaceId(workspaces, sessionsById) {
|
|
1725
|
+
let recentWorkspaceId;
|
|
1726
|
+
let recentTimestamp = Number.NEGATIVE_INFINITY;
|
|
1727
|
+
for (const workspace of workspaces.items) {
|
|
1728
|
+
let workspaceTimestamp;
|
|
1729
|
+
for (const sessionId of workspace.sessionIds) {
|
|
1730
|
+
const updatedAt = sessionsById[sessionId]?.updatedAt;
|
|
1731
|
+
if (validTimestamp(updatedAt)) workspaceTimestamp = Math.max(workspaceTimestamp ?? updatedAt, updatedAt);
|
|
1732
|
+
}
|
|
1733
|
+
workspaceTimestamp ??= createdAtTimestamp(workspace.createdAt);
|
|
1734
|
+
if (workspaceTimestamp === void 0 || workspaceTimestamp <= recentTimestamp) continue;
|
|
1735
|
+
recentWorkspaceId = workspace.workspaceId;
|
|
1736
|
+
recentTimestamp = workspaceTimestamp;
|
|
1737
|
+
}
|
|
1738
|
+
return recentWorkspaceId;
|
|
1739
|
+
}
|
|
956
1740
|
//#endregion
|
|
957
1741
|
//#region lib/client/worktree-error-copy.js
|
|
958
1742
|
function detail(error, name) {
|
|
@@ -986,6 +1770,10 @@ window.__ModuleLoader__.load({
|
|
|
986
1770
|
case "WORKTREE_RECORD_MISSING": return t("error.worktreeRecordMissing");
|
|
987
1771
|
case "WORKTREE_IMPORT_INVALID": return t("error.worktreeImportInvalid");
|
|
988
1772
|
case "WORKTREE_ALREADY_MANAGED": return t("error.worktreeAlreadyManaged");
|
|
1773
|
+
case "WORKTREE_MUTATION_BUSY": return t("error.worktreeMutationBusy");
|
|
1774
|
+
case "WORKTREE_STATE_CONFLICT": return t("error.worktreeStateConflict");
|
|
1775
|
+
case "WORKTREE_RECOVERY_REQUIRED": return t("error.worktreeRecoveryRequired");
|
|
1776
|
+
case "WORKTREE_IDENTITY_CHANGED": return t("error.worktreeIdentityChanged");
|
|
989
1777
|
case "WORKTREE_REGISTRATION_SESSION_UNAVAILABLE": return t("error.worktreeRegistrationSessionUnavailable");
|
|
990
1778
|
case "WORKSPACE_RENAME_UNAVAILABLE": return t("error.workspaceRenameUnavailable");
|
|
991
1779
|
case "WORKSPACE_DELETE_UNAVAILABLE": return t("error.workspaceDeleteUnavailable");
|
|
@@ -1504,6 +2292,15 @@ window.__ModuleLoader__.load({
|
|
|
1504
2292
|
merged[index] = nextView;
|
|
1505
2293
|
return merged;
|
|
1506
2294
|
}
|
|
2295
|
+
/** Merge explicit Workspace updates while preserving current order and ready references. */
|
|
2296
|
+
function mergeWorktreeViews(existing, workspaceIds, updates) {
|
|
2297
|
+
const byWorkspaceId = new Map(existing.map((view) => [view.workspaceId, view]));
|
|
2298
|
+
for (const update of updates) byWorkspaceId.set(update.workspaceId, update);
|
|
2299
|
+
return [...new Set(workspaceIds)].flatMap((workspaceId) => {
|
|
2300
|
+
const view = byWorkspaceId.get(workspaceId);
|
|
2301
|
+
return view === void 0 ? [] : [view];
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
1507
2304
|
/** Reuse the previous ID snapshot when DSH republishes equivalent Workspace items. */
|
|
1508
2305
|
function stableWorkspaceIds(previous, next) {
|
|
1509
2306
|
if (previous.length !== next.length) return next;
|
|
@@ -1583,6 +2380,65 @@ window.__ModuleLoader__.load({
|
|
|
1583
2380
|
error: viewError
|
|
1584
2381
|
};
|
|
1585
2382
|
}
|
|
2383
|
+
/** Share complete Workspace reads without allowing stale generations to repopulate the cache. */
|
|
2384
|
+
function createWorktreeViewReader(manager) {
|
|
2385
|
+
const entries = /* @__PURE__ */ new Map();
|
|
2386
|
+
let disposed = false;
|
|
2387
|
+
const entryFor = (workspaceId) => {
|
|
2388
|
+
const current = entries.get(workspaceId);
|
|
2389
|
+
if (current !== void 0) return current;
|
|
2390
|
+
const created = {
|
|
2391
|
+
generation: 0,
|
|
2392
|
+
stale: false
|
|
2393
|
+
};
|
|
2394
|
+
entries.set(workspaceId, created);
|
|
2395
|
+
return created;
|
|
2396
|
+
};
|
|
2397
|
+
const read = (workspaceId) => {
|
|
2398
|
+
if (disposed) return Promise.reject(/* @__PURE__ */ new Error("Worktree view reader disposed"));
|
|
2399
|
+
const entry = entryFor(workspaceId);
|
|
2400
|
+
if (!entry.stale && entry.view !== void 0) return Promise.resolve(entry.view);
|
|
2401
|
+
if (entry.inFlight?.generation === entry.generation) return entry.inFlight.promise;
|
|
2402
|
+
const generation = entry.generation;
|
|
2403
|
+
const promise = loadWorktreeView(manager, workspaceId).then((data) => {
|
|
2404
|
+
const view = {
|
|
2405
|
+
workspaceId,
|
|
2406
|
+
...data
|
|
2407
|
+
};
|
|
2408
|
+
if (!disposed && entries.get(workspaceId) === entry && entry.generation === generation) {
|
|
2409
|
+
entry.view = view;
|
|
2410
|
+
entry.stale = false;
|
|
2411
|
+
}
|
|
2412
|
+
return view;
|
|
2413
|
+
}).finally(() => {
|
|
2414
|
+
if (entry.inFlight?.promise === promise) entry.inFlight = void 0;
|
|
2415
|
+
});
|
|
2416
|
+
entry.inFlight = {
|
|
2417
|
+
generation,
|
|
2418
|
+
promise
|
|
2419
|
+
};
|
|
2420
|
+
return promise;
|
|
2421
|
+
};
|
|
2422
|
+
return {
|
|
2423
|
+
invalidate(workspaceId) {
|
|
2424
|
+
if (disposed) return;
|
|
2425
|
+
const entry = entryFor(workspaceId);
|
|
2426
|
+
entry.generation += 1;
|
|
2427
|
+
entry.stale = true;
|
|
2428
|
+
entry.view = void 0;
|
|
2429
|
+
},
|
|
2430
|
+
read,
|
|
2431
|
+
readMany(workspaceIds) {
|
|
2432
|
+
const uniqueIds = [...new Set(workspaceIds)];
|
|
2433
|
+
return Promise.all(uniqueIds.map((workspaceId) => read(workspaceId)));
|
|
2434
|
+
},
|
|
2435
|
+
dispose() {
|
|
2436
|
+
if (disposed) return;
|
|
2437
|
+
disposed = true;
|
|
2438
|
+
entries.clear();
|
|
2439
|
+
}
|
|
2440
|
+
};
|
|
2441
|
+
}
|
|
1586
2442
|
/** Read all three Worktree projections needed by the surface in one refresh. */
|
|
1587
2443
|
async function loadWorktreeView(manager, workspaceId) {
|
|
1588
2444
|
const [worktreesResult, branchesResult, bindingsResult] = await Promise.allSettled([
|
|
@@ -1609,28 +2465,16 @@ window.__ModuleLoader__.load({
|
|
|
1609
2465
|
readiness: branchesResult.value.length > 0 ? { status: "ready" } : { status: "noLocalBranch" }
|
|
1610
2466
|
};
|
|
1611
2467
|
}
|
|
1612
|
-
/** Isolate modal-target
|
|
1613
|
-
function createWorktreeModalViewLoader() {
|
|
2468
|
+
/** Isolate modal-target callbacks while sharing the Client Workspace reader. */
|
|
2469
|
+
function createWorktreeModalViewLoader(reader) {
|
|
1614
2470
|
const guard = createWorktreeRefreshGuard();
|
|
1615
2471
|
return {
|
|
1616
2472
|
invalidate: guard.invalidate,
|
|
1617
|
-
load(
|
|
1618
|
-
return guard.run(() =>
|
|
1619
|
-
workspaceId,
|
|
1620
|
-
...data
|
|
1621
|
-
}), (error) => onError(toWorktreeViewError(error)));
|
|
2473
|
+
load(workspaceId, onSuccess, onError) {
|
|
2474
|
+
return guard.run(() => reader.read(workspaceId), onSuccess, (error) => onError(toWorktreeViewError(error)));
|
|
1622
2475
|
}
|
|
1623
2476
|
};
|
|
1624
2477
|
}
|
|
1625
|
-
/** Load one independent projection per DSH Workspace for the flat sidebar hierarchy. */
|
|
1626
|
-
async function loadWorktreeViews(manager, workspaceIds, options = {}) {
|
|
1627
|
-
const views = await Promise.all(workspaceIds.map(async (workspaceId) => ({
|
|
1628
|
-
workspaceId,
|
|
1629
|
-
...await loadWorktreeView(manager, workspaceId)
|
|
1630
|
-
})));
|
|
1631
|
-
if (options.invalidateContext !== false) await options.invalidateWorktreeContext?.();
|
|
1632
|
-
return views;
|
|
1633
|
-
}
|
|
1634
2478
|
/** Hide DSH-archived Sessions from the browser-local Worktree projection. */
|
|
1635
2479
|
function filterArchivedSessionIds(sessionIds, archivedSessionIds) {
|
|
1636
2480
|
return sessionIds.filter((sessionId) => !archivedSessionIds.includes(sessionId));
|
|
@@ -1698,7 +2542,7 @@ window.__ModuleLoader__.load({
|
|
|
1698
2542
|
}, result);
|
|
1699
2543
|
}
|
|
1700
2544
|
function resolveWorktreeSessionAction(input) {
|
|
1701
|
-
if (input.target.status !== "active" || input.target.health === "repair") return {
|
|
2545
|
+
if (input.target.status !== "active" || input.target.health === "repair" || input.target.health === "recovery-needed") return {
|
|
1702
2546
|
kind: "reject",
|
|
1703
2547
|
reason: "worktree-not-available"
|
|
1704
2548
|
};
|
|
@@ -2601,7 +3445,9 @@ window.__ModuleLoader__.load({
|
|
|
2601
3445
|
children: label
|
|
2602
3446
|
}),
|
|
2603
3447
|
openDelayMs: 500,
|
|
2604
|
-
disabled: menu?.open === true
|
|
3448
|
+
disabled: menu?.open === true,
|
|
3449
|
+
copyLabel: t("copy"),
|
|
3450
|
+
copiedLabel: t("hover.copied")
|
|
2605
3451
|
});
|
|
2606
3452
|
}
|
|
2607
3453
|
/** Worktree-mode Session row with the same trailing options menu as native DSH rows. */
|
|
@@ -2626,6 +3472,11 @@ window.__ModuleLoader__.load({
|
|
|
2626
3472
|
label: t("session.archive"),
|
|
2627
3473
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutline20, { size: 16 }),
|
|
2628
3474
|
disabled: onArchive === void 0 || actionPending
|
|
3475
|
+
},
|
|
3476
|
+
{
|
|
3477
|
+
id: "copy-session-id",
|
|
3478
|
+
label: t("session.copyId"),
|
|
3479
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {})
|
|
2629
3480
|
}
|
|
2630
3481
|
];
|
|
2631
3482
|
const statusLabel = presentation === void 0 ? void 0 : sessionStatusLabel(t, presentation.status);
|
|
@@ -2697,6 +3548,7 @@ window.__ModuleLoader__.load({
|
|
|
2697
3548
|
if (id === "rename") onRename?.(sessionId, label);
|
|
2698
3549
|
if (id === "fork") onFork?.(sessionId);
|
|
2699
3550
|
if (id === "archive") onArchive?.(sessionId);
|
|
3551
|
+
if (id === "copy-session-id") (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(sessionId);
|
|
2700
3552
|
},
|
|
2701
3553
|
portal: true,
|
|
2702
3554
|
closeOnPointerLeave: true,
|
|
@@ -2796,7 +3648,8 @@ window.__ModuleLoader__.load({
|
|
|
2796
3648
|
const EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT = () => void 0;
|
|
2797
3649
|
const EMPTY_FORK_RECOVERY_SNAPSHOT = {
|
|
2798
3650
|
revision: 0,
|
|
2799
|
-
pending: []
|
|
3651
|
+
pending: [],
|
|
3652
|
+
affectedWorkspaceIds: []
|
|
2800
3653
|
};
|
|
2801
3654
|
const EMPTY_FORK_RECOVERY_STORE = {
|
|
2802
3655
|
getSnapshot: () => EMPTY_FORK_RECOVERY_SNAPSHOT,
|
|
@@ -2827,7 +3680,7 @@ window.__ModuleLoader__.load({
|
|
|
2827
3680
|
* Peer Worktree navigation surface. It keeps the original DSH Workspace mode
|
|
2828
3681
|
* intact and renders a browser-local Workspace → Worktree → Session projection.
|
|
2829
3682
|
*/
|
|
2830
|
-
function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, expandState, sessionOrder, manager, permission, confirmFullAccess, fullAccessConfirmation, onPermissionResult, onPermissionNotice, permissionNotice, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, forkRecovery, retryForkSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession }) {
|
|
3683
|
+
function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, expandState, sessionOrder, manager, viewReader, permission, confirmFullAccess, fullAccessConfirmation, onPermissionResult, onPermissionNotice, permissionNotice, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, forkRecovery, retryForkSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession }) {
|
|
2831
3684
|
const mode = effectiveViewMode(useStore((state) => state.viewMode), available && manager !== void 0);
|
|
2832
3685
|
const sessions = useSessions((state) => state);
|
|
2833
3686
|
const workspaces = useWorkspaces((state) => state);
|
|
@@ -2835,6 +3688,8 @@ window.__ModuleLoader__.load({
|
|
|
2835
3688
|
const forkRecoverySnapshot = (0, react.useSyncExternalStore)(forkRecoveryStore.subscribe, forkRecoveryStore.getSnapshot, forkRecoveryStore.getSnapshot);
|
|
2836
3689
|
const currentSessionId = sessions.current;
|
|
2837
3690
|
const workspaceIds = useStableWorkspaceIds(workspaces.items);
|
|
3691
|
+
const workspaceIdsRef = (0, react.useRef)(workspaceIds);
|
|
3692
|
+
workspaceIdsRef.current = workspaceIds;
|
|
2838
3693
|
const [readState, setReadState] = (0, react.useState)(EMPTY_READ_STATE);
|
|
2839
3694
|
const expandSnapshot = (0, react.useSyncExternalStore)(expandState.subscribe, expandState.getSnapshot, expandState.getSnapshot);
|
|
2840
3695
|
const permissionNoticeSnapshot = (0, react.useSyncExternalStore)(permissionNotice?.subscribe ?? EMPTY_PERMISSION_NOTICE_SUBSCRIBE, permissionNotice?.getSnapshot ?? EMPTY_PERMISSION_NOTICE_SNAPSHOT, permissionNotice?.getSnapshot ?? EMPTY_PERMISSION_NOTICE_SNAPSHOT);
|
|
@@ -2897,8 +3752,9 @@ window.__ModuleLoader__.load({
|
|
|
2897
3752
|
const [worktreeDrag, setWorktreeDrag] = (0, react.useState)();
|
|
2898
3753
|
const worktreeDropCommitted = (0, react.useRef)(false);
|
|
2899
3754
|
const refreshGuard = (0, react.useRef)(createWorktreeRefreshGuard());
|
|
3755
|
+
const targetRefreshGuards = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
2900
3756
|
const forkRecoveryRevisionRef = (0, react.useRef)(forkRecoverySnapshot.revision);
|
|
2901
|
-
const modalReadLoader = (0, react.useRef)(createWorktreeModalViewLoader());
|
|
3757
|
+
const modalReadLoader = (0, react.useRef)(createWorktreeModalViewLoader(viewReader));
|
|
2902
3758
|
const modalReadViewRef = (0, react.useRef)();
|
|
2903
3759
|
const importCandidatesGuard = (0, react.useRef)(createWorktreeRefreshGuard());
|
|
2904
3760
|
const modalWorkspaceIdRef = (0, react.useRef)(worktreeModalWorkspaceId);
|
|
@@ -2908,37 +3764,112 @@ window.__ModuleLoader__.load({
|
|
|
2908
3764
|
const query = searchQuery.trim().toLocaleLowerCase();
|
|
2909
3765
|
const archivedSessionIds = workspaces.archivedSessionIds ?? [];
|
|
2910
3766
|
const pendingSessionArchived = pendingSessionBinding !== void 0 && archivedSessionIds.includes(pendingSessionBinding.sessionId);
|
|
3767
|
+
const guardFor = (workspaceId) => {
|
|
3768
|
+
const current = targetRefreshGuards.current.get(workspaceId);
|
|
3769
|
+
if (current !== void 0) return current;
|
|
3770
|
+
const created = createWorktreeRefreshGuard();
|
|
3771
|
+
targetRefreshGuards.current.set(workspaceId, created);
|
|
3772
|
+
return created;
|
|
3773
|
+
};
|
|
2911
3774
|
const refresh = (0, react.useCallback)(async (options = {}) => {
|
|
2912
3775
|
if (manager === void 0) {
|
|
2913
3776
|
refreshGuard.current.invalidate();
|
|
3777
|
+
for (const guard of targetRefreshGuards.current.values()) guard.invalidate();
|
|
2914
3778
|
setReadState(EMPTY_READ_STATE);
|
|
2915
3779
|
return;
|
|
2916
3780
|
}
|
|
3781
|
+
const scope = options.scope ?? { kind: "global" };
|
|
2917
3782
|
const preserveCurrent = options.preserveCurrent === true;
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
invalidateContext: options.invalidateContext !== false,
|
|
2924
|
-
invalidateWorktreeContext
|
|
2925
|
-
}), (views) => {
|
|
2926
|
-
setReadState({
|
|
2927
|
-
status: "ready",
|
|
2928
|
-
views: modalReadViewRef.current === void 0 ? views : mergeWorktreeView(views, modalReadViewRef.current)
|
|
3783
|
+
const currentWorkspaceIds = workspaceIdsRef.current;
|
|
3784
|
+
if (scope.kind === "global") {
|
|
3785
|
+
if (!preserveCurrent) setReadState({
|
|
3786
|
+
status: "loading",
|
|
3787
|
+
views: []
|
|
2929
3788
|
});
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
views
|
|
2935
|
-
|
|
3789
|
+
const selectedWorkspaceIds = [...currentWorkspaceIds];
|
|
3790
|
+
for (const workspaceId of selectedWorkspaceIds) viewReader.invalidate(workspaceId);
|
|
3791
|
+
const contextRefresh = options.invalidateContext === false ? void 0 : invalidateWorktreeContext?.();
|
|
3792
|
+
await refreshGuard.current.run(async () => {
|
|
3793
|
+
const [views] = await Promise.all([viewReader.readMany(selectedWorkspaceIds), contextRefresh ?? Promise.resolve()]);
|
|
3794
|
+
return views;
|
|
3795
|
+
}, (views) => {
|
|
3796
|
+
setReadState((current) => {
|
|
3797
|
+
const merged = mergeWorktreeViews(current.views, workspaceIdsRef.current, views);
|
|
3798
|
+
return {
|
|
3799
|
+
status: "ready",
|
|
3800
|
+
views: modalReadViewRef.current === void 0 ? merged : mergeWorktreeView(merged, modalReadViewRef.current)
|
|
3801
|
+
};
|
|
3802
|
+
});
|
|
3803
|
+
}, (error) => {
|
|
3804
|
+
if (preserveCurrent) throw error;
|
|
3805
|
+
setReadState({
|
|
3806
|
+
status: "error",
|
|
3807
|
+
views: modalReadViewRef.current === void 0 ? [] : [modalReadViewRef.current],
|
|
3808
|
+
error: toWorktreeViewError(error)
|
|
3809
|
+
});
|
|
2936
3810
|
});
|
|
2937
|
-
|
|
3811
|
+
return;
|
|
3812
|
+
}
|
|
3813
|
+
const requestedWorkspaceIds = scope.kind === "workspace" ? [scope.workspaceId] : scope.workspaceIds;
|
|
3814
|
+
const selectedWorkspaceIds = [...new Set(requestedWorkspaceIds)].filter((workspaceId) => currentWorkspaceIds.includes(workspaceId));
|
|
3815
|
+
if (selectedWorkspaceIds.length === 0) return;
|
|
3816
|
+
await Promise.allSettled(selectedWorkspaceIds.map(async (workspaceId) => {
|
|
3817
|
+
const guard = guardFor(workspaceId);
|
|
3818
|
+
viewReader.invalidate(workspaceId);
|
|
3819
|
+
const contextRefresh = options.invalidateContext === false ? void 0 : invalidateWorktreeContext?.(workspaceId);
|
|
3820
|
+
const generation = guard.begin();
|
|
3821
|
+
try {
|
|
3822
|
+
const view = await viewReader.read(workspaceId);
|
|
3823
|
+
if (!guard.isCurrent(generation)) return;
|
|
3824
|
+
if (!workspaceIdsRef.current.includes(workspaceId)) return;
|
|
3825
|
+
setReadState((current) => {
|
|
3826
|
+
const remainingTargetError = current.targetError?.workspaceIds.filter((candidate) => candidate !== workspaceId) ?? [];
|
|
3827
|
+
const next = {
|
|
3828
|
+
...current,
|
|
3829
|
+
status: "ready",
|
|
3830
|
+
views: mergeWorktreeViews(current.views, workspaceIdsRef.current, [view]),
|
|
3831
|
+
error: void 0
|
|
3832
|
+
};
|
|
3833
|
+
if (remainingTargetError.length === 0) {
|
|
3834
|
+
const withoutTargetError = { ...next };
|
|
3835
|
+
delete withoutTargetError.targetError;
|
|
3836
|
+
return withoutTargetError;
|
|
3837
|
+
}
|
|
3838
|
+
return {
|
|
3839
|
+
...next,
|
|
3840
|
+
targetError: {
|
|
3841
|
+
workspaceIds: remainingTargetError,
|
|
3842
|
+
error: current.targetError.error
|
|
3843
|
+
}
|
|
3844
|
+
};
|
|
3845
|
+
});
|
|
3846
|
+
} catch (error) {
|
|
3847
|
+
if (!guard.isCurrent(generation)) return;
|
|
3848
|
+
if (!workspaceIdsRef.current.includes(workspaceId)) return;
|
|
3849
|
+
setReadState((current) => {
|
|
3850
|
+
const previousIds = current.targetError?.workspaceIds ?? [];
|
|
3851
|
+
return {
|
|
3852
|
+
...current,
|
|
3853
|
+
status: "ready",
|
|
3854
|
+
views: mergeWorktreeViews(current.views, workspaceIdsRef.current, []),
|
|
3855
|
+
error: void 0,
|
|
3856
|
+
targetError: {
|
|
3857
|
+
workspaceIds: [.../* @__PURE__ */ new Set([...previousIds, workspaceId])],
|
|
3858
|
+
error: {
|
|
3859
|
+
...toWorktreeViewError(error),
|
|
3860
|
+
retryable: true
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
};
|
|
3864
|
+
});
|
|
3865
|
+
} finally {
|
|
3866
|
+
await contextRefresh?.catch(() => void 0);
|
|
3867
|
+
}
|
|
3868
|
+
}));
|
|
2938
3869
|
}, [
|
|
2939
3870
|
invalidateWorktreeContext,
|
|
2940
3871
|
manager,
|
|
2941
|
-
|
|
3872
|
+
viewReader
|
|
2942
3873
|
]);
|
|
2943
3874
|
const loadModalWorktreeView = (0, react.useCallback)((workspaceId) => {
|
|
2944
3875
|
setModalReadError(void 0);
|
|
@@ -2952,7 +3883,7 @@ window.__ModuleLoader__.load({
|
|
|
2952
3883
|
});
|
|
2953
3884
|
return;
|
|
2954
3885
|
}
|
|
2955
|
-
modalReadLoader.current.load(
|
|
3886
|
+
modalReadLoader.current.load(workspaceId, (view) => {
|
|
2956
3887
|
modalReadViewRef.current = view;
|
|
2957
3888
|
setModalReadLoading(false);
|
|
2958
3889
|
setReadState((current) => ({
|
|
@@ -2963,11 +3894,12 @@ window.__ModuleLoader__.load({
|
|
|
2963
3894
|
setModalReadLoading(false);
|
|
2964
3895
|
setModalReadError(toWorktreeViewError(error));
|
|
2965
3896
|
});
|
|
2966
|
-
}, [manager]);
|
|
3897
|
+
}, [manager, viewReader]);
|
|
2967
3898
|
(0, react.useEffect)(() => {
|
|
2968
3899
|
if (mode === "worktree") refresh({ preserveCurrent: readStateRef.current.status === "ready" });
|
|
2969
3900
|
else {
|
|
2970
3901
|
refreshGuard.current.invalidate();
|
|
3902
|
+
for (const guard of targetRefreshGuards.current.values()) guard.invalidate();
|
|
2971
3903
|
modalReadLoader.current.invalidate();
|
|
2972
3904
|
modalReadViewRef.current = void 0;
|
|
2973
3905
|
setWorktreeModalWorkspaceId(void 0);
|
|
@@ -2976,15 +3908,60 @@ window.__ModuleLoader__.load({
|
|
|
2976
3908
|
setReadState(EMPTY_READ_STATE);
|
|
2977
3909
|
}
|
|
2978
3910
|
}, [mode, refresh]);
|
|
3911
|
+
const previousWorkspaceIdsRef = (0, react.useRef)(workspaceIds);
|
|
3912
|
+
(0, react.useEffect)(() => {
|
|
3913
|
+
const previousWorkspaceIds = previousWorkspaceIdsRef.current;
|
|
3914
|
+
previousWorkspaceIdsRef.current = workspaceIds;
|
|
3915
|
+
if (mode !== "worktree") return;
|
|
3916
|
+
const addedWorkspaceIds = workspaceIds.filter((workspaceId) => !previousWorkspaceIds.includes(workspaceId));
|
|
3917
|
+
const removedWorkspaceIds = previousWorkspaceIds.filter((workspaceId) => !workspaceIds.includes(workspaceId));
|
|
3918
|
+
for (const workspaceId of removedWorkspaceIds) targetRefreshGuards.current.delete(workspaceId);
|
|
3919
|
+
setReadState((current) => {
|
|
3920
|
+
const views = mergeWorktreeViews(current.views, workspaceIds, []);
|
|
3921
|
+
const remainingTargetError = current.targetError?.workspaceIds.filter((workspaceId) => workspaceIds.includes(workspaceId)) ?? [];
|
|
3922
|
+
const next = {
|
|
3923
|
+
...current,
|
|
3924
|
+
views
|
|
3925
|
+
};
|
|
3926
|
+
if (remainingTargetError.length === 0) {
|
|
3927
|
+
const withoutTargetError = { ...next };
|
|
3928
|
+
delete withoutTargetError.targetError;
|
|
3929
|
+
return withoutTargetError;
|
|
3930
|
+
}
|
|
3931
|
+
return {
|
|
3932
|
+
...next,
|
|
3933
|
+
targetError: {
|
|
3934
|
+
workspaceIds: remainingTargetError,
|
|
3935
|
+
error: current.targetError.error
|
|
3936
|
+
}
|
|
3937
|
+
};
|
|
3938
|
+
});
|
|
3939
|
+
for (const workspaceId of addedWorkspaceIds) refresh({
|
|
3940
|
+
scope: {
|
|
3941
|
+
kind: "workspace",
|
|
3942
|
+
workspaceId
|
|
3943
|
+
},
|
|
3944
|
+
preserveCurrent: true
|
|
3945
|
+
}).catch(() => {});
|
|
3946
|
+
}, [
|
|
3947
|
+
mode,
|
|
3948
|
+
refresh,
|
|
3949
|
+
workspaceIds
|
|
3950
|
+
]);
|
|
2979
3951
|
(0, react.useEffect)(() => {
|
|
2980
3952
|
if (forkRecoveryRevisionRef.current === forkRecoverySnapshot.revision) return;
|
|
2981
3953
|
forkRecoveryRevisionRef.current = forkRecoverySnapshot.revision;
|
|
2982
3954
|
if (mode !== "worktree" || manager === void 0) return;
|
|
3955
|
+
if (forkRecoverySnapshot.affectedWorkspaceIds.length === 0) return;
|
|
2983
3956
|
refresh({
|
|
2984
|
-
|
|
2985
|
-
|
|
3957
|
+
scope: {
|
|
3958
|
+
kind: "workspaces",
|
|
3959
|
+
workspaceIds: forkRecoverySnapshot.affectedWorkspaceIds
|
|
3960
|
+
},
|
|
3961
|
+
preserveCurrent: readStateRef.current.status === "ready"
|
|
2986
3962
|
}).catch(() => {});
|
|
2987
3963
|
}, [
|
|
3964
|
+
forkRecoverySnapshot.affectedWorkspaceIds,
|
|
2988
3965
|
forkRecoverySnapshot.revision,
|
|
2989
3966
|
manager,
|
|
2990
3967
|
mode,
|
|
@@ -3127,12 +4104,12 @@ window.__ModuleLoader__.load({
|
|
|
3127
4104
|
return createDefaultWorktreeName([...modalView.branches.map((branch) => branch.name), ...modalView.worktrees.map((worktree) => worktree.branch)]);
|
|
3128
4105
|
});
|
|
3129
4106
|
}, [modalView, worktreeModalWorkspaceId]);
|
|
3130
|
-
const runMutation = async (operation) => {
|
|
4107
|
+
const runMutation = async (operation, refreshOptions) => {
|
|
3131
4108
|
setActionPending(true);
|
|
3132
4109
|
setActionError(void 0);
|
|
3133
4110
|
try {
|
|
3134
4111
|
await operation();
|
|
3135
|
-
|
|
4112
|
+
if (refreshOptions !== void 0) await refresh(refreshOptions);
|
|
3136
4113
|
} catch (error) {
|
|
3137
4114
|
setActionError(toWorktreeViewError(error));
|
|
3138
4115
|
} finally {
|
|
@@ -3299,6 +4276,10 @@ window.__ModuleLoader__.load({
|
|
|
3299
4276
|
}
|
|
3300
4277
|
setActionError(void 0);
|
|
3301
4278
|
insertWorktreeBefore(workspaceId, activeDrag.worktreeId, move.beforeWorktreeId).then(() => refresh({
|
|
4279
|
+
scope: {
|
|
4280
|
+
kind: "workspace",
|
|
4281
|
+
workspaceId
|
|
4282
|
+
},
|
|
3302
4283
|
preserveCurrent: true,
|
|
3303
4284
|
invalidateContext: false
|
|
3304
4285
|
})).catch((error) => {
|
|
@@ -3455,7 +4436,13 @@ window.__ModuleLoader__.load({
|
|
|
3455
4436
|
const continueWorktreeRegistration = async (registeredWorktree) => {
|
|
3456
4437
|
closeWorktreeCreator(true);
|
|
3457
4438
|
if (createSessionCallback === void 0) {
|
|
3458
|
-
await refresh({
|
|
4439
|
+
await refresh({
|
|
4440
|
+
scope: {
|
|
4441
|
+
kind: "workspace",
|
|
4442
|
+
workspaceId: registeredWorktree.workspaceId
|
|
4443
|
+
},
|
|
4444
|
+
preserveCurrent: true
|
|
4445
|
+
});
|
|
3459
4446
|
setActionError({
|
|
3460
4447
|
code: "WORKTREE_REGISTRATION_SESSION_UNAVAILABLE",
|
|
3461
4448
|
message: "",
|
|
@@ -3470,7 +4457,6 @@ window.__ModuleLoader__.load({
|
|
|
3470
4457
|
};
|
|
3471
4458
|
try {
|
|
3472
4459
|
await createSessionCallback(sessionInput);
|
|
3473
|
-
await invalidateWorktreeContext?.(registeredWorktree.workspaceId);
|
|
3474
4460
|
} catch (error) {
|
|
3475
4461
|
if (error instanceof WorktreeSessionBindingError && error.retryable) setPendingSessionBinding({
|
|
3476
4462
|
...sessionInput,
|
|
@@ -3483,7 +4469,13 @@ window.__ModuleLoader__.load({
|
|
|
3483
4469
|
});
|
|
3484
4470
|
throw error;
|
|
3485
4471
|
}
|
|
3486
|
-
await refresh({
|
|
4472
|
+
await refresh({
|
|
4473
|
+
scope: {
|
|
4474
|
+
kind: "workspace",
|
|
4475
|
+
workspaceId: registeredWorktree.workspaceId
|
|
4476
|
+
},
|
|
4477
|
+
preserveCurrent: true
|
|
4478
|
+
});
|
|
3487
4479
|
};
|
|
3488
4480
|
const submitWorktree = async () => {
|
|
3489
4481
|
const worktreeName = newBranch.trim();
|
|
@@ -3535,8 +4527,13 @@ window.__ModuleLoader__.load({
|
|
|
3535
4527
|
setPendingSessionBinding(void 0);
|
|
3536
4528
|
try {
|
|
3537
4529
|
await createSessionCallback(input);
|
|
3538
|
-
await
|
|
3539
|
-
|
|
4530
|
+
await refresh({
|
|
4531
|
+
scope: {
|
|
4532
|
+
kind: "workspace",
|
|
4533
|
+
workspaceId: input.workspaceId
|
|
4534
|
+
},
|
|
4535
|
+
preserveCurrent: true
|
|
4536
|
+
});
|
|
3540
4537
|
} catch (error) {
|
|
3541
4538
|
if (error instanceof WorktreeSessionBindingError && error.retryable) setPendingSessionBinding({
|
|
3542
4539
|
...input,
|
|
@@ -3571,8 +4568,13 @@ window.__ModuleLoader__.load({
|
|
|
3571
4568
|
openSession
|
|
3572
4569
|
});
|
|
3573
4570
|
setPendingSessionBinding(void 0);
|
|
3574
|
-
await refresh({
|
|
3575
|
-
|
|
4571
|
+
await refresh({
|
|
4572
|
+
scope: {
|
|
4573
|
+
kind: "workspace",
|
|
4574
|
+
workspaceId: pending.workspaceId
|
|
4575
|
+
},
|
|
4576
|
+
preserveCurrent: true
|
|
4577
|
+
});
|
|
3576
4578
|
} catch (error) {
|
|
3577
4579
|
if (error instanceof WorktreeSessionBindingError && !error.retryable) setPendingSessionBinding(void 0);
|
|
3578
4580
|
setActionError(toWorktreeViewError(error));
|
|
@@ -3743,12 +4745,35 @@ window.__ModuleLoader__.load({
|
|
|
3743
4745
|
className: worktree_css_default.retryButton,
|
|
3744
4746
|
onClick: () => {
|
|
3745
4747
|
setActionError(void 0);
|
|
3746
|
-
refresh();
|
|
4748
|
+
refresh({ scope: { kind: "global" } });
|
|
3747
4749
|
},
|
|
3748
4750
|
children: t("action.retry")
|
|
3749
4751
|
})
|
|
3750
4752
|
]
|
|
3751
4753
|
}),
|
|
4754
|
+
readState.targetError !== void 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
4755
|
+
className: worktree_css_default.error,
|
|
4756
|
+
role: "alert",
|
|
4757
|
+
"data-worktree-target-error": true,
|
|
4758
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
4759
|
+
className: worktree_css_default.message,
|
|
4760
|
+
"data-error": "true",
|
|
4761
|
+
children: formatWorktreeViewError(readState.targetError.error, t)
|
|
4762
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
4763
|
+
type: "button",
|
|
4764
|
+
className: worktree_css_default.retryButton,
|
|
4765
|
+
onClick: () => {
|
|
4766
|
+
refresh({
|
|
4767
|
+
scope: {
|
|
4768
|
+
kind: "workspaces",
|
|
4769
|
+
workspaceIds: readState.targetError.workspaceIds
|
|
4770
|
+
},
|
|
4771
|
+
preserveCurrent: true
|
|
4772
|
+
});
|
|
4773
|
+
},
|
|
4774
|
+
children: t("action.retry")
|
|
4775
|
+
})]
|
|
4776
|
+
}),
|
|
3752
4777
|
readState.status === "loading" && (0, react_jsx_runtime.jsx)("p", {
|
|
3753
4778
|
className: worktree_css_default.message,
|
|
3754
4779
|
children: t("status.loading")
|
|
@@ -3765,7 +4790,7 @@ window.__ModuleLoader__.load({
|
|
|
3765
4790
|
type: "button",
|
|
3766
4791
|
className: worktree_css_default.retryButton,
|
|
3767
4792
|
onClick: () => {
|
|
3768
|
-
refresh();
|
|
4793
|
+
refresh({ scope: { kind: "global" } });
|
|
3769
4794
|
},
|
|
3770
4795
|
children: t("action.retry")
|
|
3771
4796
|
})]
|
|
@@ -3946,8 +4971,8 @@ window.__ModuleLoader__.load({
|
|
|
3946
4971
|
const sessionIds = orderedVisibleWorktreeSessionIds.length === visibleWorktreeSessionIds.length ? orderedVisibleWorktreeSessionIds : visibleWorktreeSessionIds;
|
|
3947
4972
|
const sessionGroupAutoExpanded = isSessionGroupAutoExpanded(sessionIds, currentSessionId, isCurrentSessionReveal("session-group:" + worktreeGroupKey));
|
|
3948
4973
|
const sessionGroupExpanded = expandedSessionGroups[worktreeGroupKey] === true || sessionGroupAutoExpanded;
|
|
3949
|
-
const state = record.status === "removed" ? "warning" : record.health === "repair" ? "error" : "done";
|
|
3950
|
-
const stateLabel = record.status === "removed" ? t("worktree.detached") : record.health === "repair" ? t("worktree.repair") : t("worktree.ready");
|
|
4974
|
+
const state = record.status === "removed" ? "warning" : record.health === "repair" || record.health === "recovery-needed" ? "error" : "done";
|
|
4975
|
+
const stateLabel = record.status === "removed" ? t("worktree.detached") : record.health === "recovery-needed" ? t("worktree.recovery") : record.health === "repair" ? t("worktree.repair") : t("worktree.ready");
|
|
3951
4976
|
const worktreeExpanded = isWorktreeExpanded(expandSnapshot, record.worktreeId) || isCurrentSessionReveal("worktree:" + record.worktreeId);
|
|
3952
4977
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
3953
4978
|
className: worktree_css_default.worktreeGroup,
|
|
@@ -3966,7 +4991,7 @@ window.__ModuleLoader__.load({
|
|
|
3966
4991
|
onToggle: () => {
|
|
3967
4992
|
toggleWorktree(record.worktreeId);
|
|
3968
4993
|
},
|
|
3969
|
-
onCreateSession: record.status === "active" && record.health !== "repair" ? () => {
|
|
4994
|
+
onCreateSession: record.status === "active" && record.health !== "repair" && record.health !== "recovery-needed" ? () => {
|
|
3970
4995
|
createSession({
|
|
3971
4996
|
workspaceId: record.workspaceId,
|
|
3972
4997
|
worktreeId: record.worktreeId,
|
|
@@ -3977,13 +5002,13 @@ window.__ModuleLoader__.load({
|
|
|
3977
5002
|
open: openWorktreeMenuId === record.worktreeId,
|
|
3978
5003
|
label: record.branch,
|
|
3979
5004
|
copyPath: record.absolutePath,
|
|
3980
|
-
showCreate: record.status === "active",
|
|
3981
|
-
showRemove: record.status === "active",
|
|
5005
|
+
showCreate: record.status === "active" && record.health !== "repair" && record.health !== "recovery-needed",
|
|
5006
|
+
showRemove: record.status === "active" && record.health !== "repair" && record.health !== "recovery-needed",
|
|
3982
5007
|
disabled: actionPending,
|
|
3983
5008
|
onOpenChange: (open) => {
|
|
3984
5009
|
setOpenWorktreeMenuId(open ? record.worktreeId : void 0);
|
|
3985
5010
|
},
|
|
3986
|
-
onCreateWorktree: record.status === "active" ? () => {
|
|
5011
|
+
onCreateWorktree: record.status === "active" && record.health !== "repair" && record.health !== "recovery-needed" ? () => {
|
|
3987
5012
|
openWorktreeCreator(workspace, {
|
|
3988
5013
|
baseBranch: record.branch,
|
|
3989
5014
|
newBranch: createNumberedWorktreeName(record.branch, workspaceWorktreeNames)
|
|
@@ -4157,16 +5182,35 @@ window.__ModuleLoader__.load({
|
|
|
4157
5182
|
onSubmit: () => {
|
|
4158
5183
|
if (manager === void 0 || worktreeRemoval === void 0) return;
|
|
4159
5184
|
const target = worktreeRemoval;
|
|
5185
|
+
const mutationToken = target.mutationToken;
|
|
5186
|
+
if (mutationToken === void 0) {
|
|
5187
|
+
setActionError({
|
|
5188
|
+
code: "WORKTREE_STATE_CONFLICT",
|
|
5189
|
+
message: "",
|
|
5190
|
+
retryable: true,
|
|
5191
|
+
details: {
|
|
5192
|
+
workspaceId: target.workspaceId,
|
|
5193
|
+
worktreeId: target.worktreeId
|
|
5194
|
+
}
|
|
5195
|
+
});
|
|
5196
|
+
return;
|
|
5197
|
+
}
|
|
4160
5198
|
runMutation(async () => {
|
|
4161
5199
|
await executeWorktreeAction(manager, {
|
|
4162
5200
|
type: "removeWorktree",
|
|
4163
5201
|
input: {
|
|
4164
5202
|
workspaceId: target.workspaceId,
|
|
4165
|
-
worktreeId: target.worktreeId
|
|
5203
|
+
worktreeId: target.worktreeId,
|
|
5204
|
+
mutationToken
|
|
4166
5205
|
}
|
|
4167
5206
|
}, permission, onPermissionNotice);
|
|
4168
|
-
await invalidateWorktreeContext?.(target.workspaceId);
|
|
4169
5207
|
setWorktreeRemoval(void 0);
|
|
5208
|
+
}, {
|
|
5209
|
+
scope: {
|
|
5210
|
+
kind: "workspace",
|
|
5211
|
+
workspaceId: target.workspaceId
|
|
5212
|
+
},
|
|
5213
|
+
preserveCurrent: true
|
|
4170
5214
|
});
|
|
4171
5215
|
}
|
|
4172
5216
|
}),
|
|
@@ -4176,6 +5220,7 @@ window.__ModuleLoader__.load({
|
|
|
4176
5220
|
description: fullAccessConfirmationSnapshot === void 0 ? "" : t("permission.fullAccessDescription", { cwd: fullAccessConfirmationSnapshot.cwd }),
|
|
4177
5221
|
acknowledgeLabel: t("permission.fullAccessAcknowledge"),
|
|
4178
5222
|
cancelLabel: t("dialog.cancel"),
|
|
5223
|
+
closeLabel: t("dialog.close"),
|
|
4179
5224
|
confirmLabel: t("permission.fullAccessEnable"),
|
|
4180
5225
|
acknowledged: fullAccessAcknowledged,
|
|
4181
5226
|
onAcknowledgedChange: setFullAccessAcknowledged,
|
|
@@ -4216,7 +5261,7 @@ window.__ModuleLoader__.load({
|
|
|
4216
5261
|
const record = input.worktrees.find((candidate) => candidate.worktreeId === binding.worktreeId);
|
|
4217
5262
|
if (record === void 0 || record.workspaceId !== workspace.workspaceId) return none(record === void 0 ? "stale" : "workspace-mismatch");
|
|
4218
5263
|
if (record.status !== "active") return none("stale");
|
|
4219
|
-
if (record.health === "repair") return none("repair");
|
|
5264
|
+
if (record.health === "repair" || record.health === "recovery-needed") return none("repair");
|
|
4220
5265
|
if (record.branch.length === 0) return none("not-ready");
|
|
4221
5266
|
return {
|
|
4222
5267
|
kind: "worktree",
|
|
@@ -4251,11 +5296,13 @@ window.__ModuleLoader__.load({
|
|
|
4251
5296
|
};
|
|
4252
5297
|
}
|
|
4253
5298
|
function identityFrom(sessions, workspaces) {
|
|
4254
|
-
const
|
|
4255
|
-
|
|
5299
|
+
const sessionSnapshot = sessions.getSnapshot();
|
|
5300
|
+
const workspaceSnapshot = workspaces.getSnapshot();
|
|
5301
|
+
const sessionId = sessionSnapshot.current;
|
|
5302
|
+
if (sessionId === void 0) return { workspaceId: deriveRecentWorkspaceId({ items: workspaceSnapshot.items }, sessionSnapshot.byId) };
|
|
4256
5303
|
return {
|
|
4257
5304
|
sessionId,
|
|
4258
|
-
workspaceId:
|
|
5305
|
+
workspaceId: workspaceSnapshot.items.find((candidate) => candidate.sessionIds.includes(sessionId))?.workspaceId
|
|
4259
5306
|
};
|
|
4260
5307
|
}
|
|
4261
5308
|
/**
|
|
@@ -4274,7 +5321,6 @@ window.__ModuleLoader__.load({
|
|
|
4274
5321
|
let scheduleTicket = 0;
|
|
4275
5322
|
let scheduledCompletion;
|
|
4276
5323
|
let latestRefresh = Promise.resolve();
|
|
4277
|
-
const viewCache = /* @__PURE__ */ new Map();
|
|
4278
5324
|
let lastObservedIdentity;
|
|
4279
5325
|
const sameIdentity = (left, right) => left?.sessionId === right.sessionId && left?.workspaceId === right.workspaceId;
|
|
4280
5326
|
const workspaceTitleFor = (workspaceId) => input.workspaces.getSnapshot().items.find((candidate) => candidate.workspaceId === workspaceId)?.title;
|
|
@@ -4311,14 +5357,15 @@ window.__ModuleLoader__.load({
|
|
|
4311
5357
|
});
|
|
4312
5358
|
return;
|
|
4313
5359
|
}
|
|
4314
|
-
const
|
|
4315
|
-
if (
|
|
5360
|
+
const current = store.getSnapshot();
|
|
5361
|
+
if (current.status === "ready" && current.workspaceId !== identity.workspaceId) return;
|
|
5362
|
+
if (current.status === "ready" && current.workspaceId === identity.workspaceId) {
|
|
4316
5363
|
store.set({
|
|
4317
5364
|
status: "ready",
|
|
4318
5365
|
...identity.sessionId === void 0 ? {} : { sessionId: identity.sessionId },
|
|
4319
5366
|
workspaceId: identity.workspaceId,
|
|
4320
5367
|
workspaceTitle: workspaceTitleFor(identity.workspaceId),
|
|
4321
|
-
value:
|
|
5368
|
+
value: current.value
|
|
4322
5369
|
});
|
|
4323
5370
|
return;
|
|
4324
5371
|
}
|
|
@@ -4347,7 +5394,7 @@ window.__ModuleLoader__.load({
|
|
|
4347
5394
|
workspaceId: identity.workspaceId
|
|
4348
5395
|
};
|
|
4349
5396
|
try {
|
|
4350
|
-
const data = await
|
|
5397
|
+
const data = await input.viewReader.read(request.workspaceId);
|
|
4351
5398
|
if (!matchesCurrent(request, requestGeneration)) return;
|
|
4352
5399
|
const workspace = input.workspaces.getSnapshot().items.find((candidate) => candidate.workspaceId === request.workspaceId);
|
|
4353
5400
|
if ("error" in data.readiness) {
|
|
@@ -4361,7 +5408,6 @@ window.__ModuleLoader__.load({
|
|
|
4361
5408
|
});
|
|
4362
5409
|
return;
|
|
4363
5410
|
}
|
|
4364
|
-
if (data.readiness.status === "ready") viewCache.set(request.workspaceId, data);
|
|
4365
5411
|
store.set({
|
|
4366
5412
|
status: "ready",
|
|
4367
5413
|
...request.sessionId === void 0 ? {} : { sessionId: request.sessionId },
|
|
@@ -4442,6 +5488,8 @@ window.__ModuleLoader__.load({
|
|
|
4442
5488
|
if (disposed) return;
|
|
4443
5489
|
const identity = identityFrom(input.sessions, input.workspaces);
|
|
4444
5490
|
if (workspaceId !== void 0 && workspaceId !== identity.workspaceId) return;
|
|
5491
|
+
if (identity.workspaceId === void 0) return;
|
|
5492
|
+
input.viewReader.invalidate(identity.workspaceId);
|
|
4445
5493
|
await refresh();
|
|
4446
5494
|
await waitForLatestRefresh(workspaceId ?? identity.workspaceId);
|
|
4447
5495
|
},
|
|
@@ -4452,7 +5500,6 @@ window.__ModuleLoader__.load({
|
|
|
4452
5500
|
cancelScheduledRefresh();
|
|
4453
5501
|
unsubscribeSessions();
|
|
4454
5502
|
unsubscribeWorkspaces();
|
|
4455
|
-
viewCache.clear();
|
|
4456
5503
|
store.set({
|
|
4457
5504
|
status: "idle",
|
|
4458
5505
|
value: notReady()
|
|
@@ -4464,7 +5511,7 @@ window.__ModuleLoader__.load({
|
|
|
4464
5511
|
//#region lib/client/view-mode-store.js
|
|
4465
5512
|
/** Create one root-scoped, browser-local view-mode store for the two slot entries. */
|
|
4466
5513
|
function createWorktreeViewStore() {
|
|
4467
|
-
return
|
|
5514
|
+
return defineStore({
|
|
4468
5515
|
init: () => ({ viewMode: "workspace-session" }),
|
|
4469
5516
|
persist: WORKTREE_VIEW_MODE_STORAGE_KEY,
|
|
4470
5517
|
actions: { setViewMode: (draft, mode) => {
|
|
@@ -4482,54 +5529,85 @@ window.__ModuleLoader__.load({
|
|
|
4482
5529
|
}
|
|
4483
5530
|
return [...bySession.values()];
|
|
4484
5531
|
}
|
|
4485
|
-
function
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
if (item === other) return true;
|
|
4490
|
-
if (typeof item !== "object" || item === null || typeof other !== "object" || other === null || !("workspaceId" in item) || !("workspaceId" in other) || !("sessionIds" in item) || !("sessionIds" in other)) return false;
|
|
4491
|
-
const leftSessionIds = item.sessionIds;
|
|
4492
|
-
const rightSessionIds = other.sessionIds;
|
|
4493
|
-
return item.workspaceId === other.workspaceId && Array.isArray(leftSessionIds) && Array.isArray(rightSessionIds) && leftSessionIds.length === rightSessionIds.length && leftSessionIds.every((sessionId, sessionIndex) => sessionId === rightSessionIds[sessionIndex]);
|
|
5532
|
+
function sameBindings(left, right) {
|
|
5533
|
+
return left.length === right.length && left.every((binding, index) => {
|
|
5534
|
+
const other = right[index];
|
|
5535
|
+
return binding.workspaceId === other.workspaceId && binding.sessionId === other.sessionId;
|
|
4494
5536
|
});
|
|
4495
5537
|
}
|
|
4496
5538
|
/**
|
|
4497
|
-
*
|
|
4498
|
-
*
|
|
4499
|
-
*
|
|
5539
|
+
* Decorate DSH's read-only WorkspaceSource with a browser-local membership
|
|
5540
|
+
* projection. The source's native model is never written: the wrapper keeps
|
|
5541
|
+
* the current sidecar bindings in memory, reprojects every native snapshot,
|
|
5542
|
+
* and publishes only the projected view to subscribers.
|
|
4500
5543
|
*/
|
|
4501
5544
|
function createVirtualWorkspaceMembership(list) {
|
|
5545
|
+
if (!Object.isExtensible(list)) throw new Error("Workspace membership projection requires an extensible WorkspaceSource");
|
|
5546
|
+
const nativeGetSnapshot = list.getSnapshot.bind(list);
|
|
5547
|
+
const nativeSubscribe = list.subscribe.bind(list);
|
|
5548
|
+
const originalGetSnapshot = Object.getOwnPropertyDescriptor(list, "getSnapshot");
|
|
5549
|
+
const originalSubscribe = Object.getOwnPropertyDescriptor(list, "subscribe");
|
|
5550
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
4502
5551
|
let bindings = [];
|
|
5552
|
+
let nativeSnapshot = nativeGetSnapshot();
|
|
5553
|
+
let projectedSnapshot = projectVirtualWorkspaceMembership(nativeSnapshot, [], bindings);
|
|
4503
5554
|
let disposed = false;
|
|
4504
|
-
|
|
4505
|
-
const
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
5555
|
+
const project = (nextNativeSnapshot, previousBindings, nextBindings) => projectVirtualWorkspaceMembership(nextNativeSnapshot, previousBindings, nextBindings);
|
|
5556
|
+
const refreshSnapshot = () => {
|
|
5557
|
+
const nextNativeSnapshot = nativeGetSnapshot();
|
|
5558
|
+
if (nextNativeSnapshot === nativeSnapshot) return false;
|
|
5559
|
+
nativeSnapshot = nextNativeSnapshot;
|
|
5560
|
+
const nextProjectedSnapshot = project(nextNativeSnapshot, bindings, bindings);
|
|
5561
|
+
const changed = nextProjectedSnapshot !== projectedSnapshot;
|
|
5562
|
+
projectedSnapshot = nextProjectedSnapshot;
|
|
5563
|
+
return changed;
|
|
4513
5564
|
};
|
|
4514
|
-
|
|
4515
|
-
|
|
5565
|
+
const notify = () => {
|
|
5566
|
+
if (disposed) return;
|
|
5567
|
+
if (!refreshSnapshot()) return;
|
|
5568
|
+
for (const listener of [...listeners]) listener();
|
|
4516
5569
|
};
|
|
4517
|
-
const
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
write(next);
|
|
5570
|
+
const getSnapshot = () => {
|
|
5571
|
+
if (disposed) return nativeGetSnapshot();
|
|
5572
|
+
refreshSnapshot();
|
|
5573
|
+
return projectedSnapshot;
|
|
4522
5574
|
};
|
|
4523
|
-
const
|
|
4524
|
-
if (disposed
|
|
4525
|
-
|
|
5575
|
+
const subscribe = (listener) => {
|
|
5576
|
+
if (disposed) return () => void 0;
|
|
5577
|
+
listeners.add(listener);
|
|
5578
|
+
return () => listeners.delete(listener);
|
|
4526
5579
|
};
|
|
4527
|
-
|
|
5580
|
+
Object.defineProperty(list, "getSnapshot", {
|
|
5581
|
+
configurable: true,
|
|
5582
|
+
enumerable: false,
|
|
5583
|
+
writable: true,
|
|
5584
|
+
value: getSnapshot
|
|
5585
|
+
});
|
|
5586
|
+
Object.defineProperty(list, "subscribe", {
|
|
5587
|
+
configurable: true,
|
|
5588
|
+
enumerable: false,
|
|
5589
|
+
writable: true,
|
|
5590
|
+
value: subscribe
|
|
5591
|
+
});
|
|
5592
|
+
const unsubscribeNative = nativeSubscribe(notify);
|
|
4528
5593
|
const sync = (nextBindings) => {
|
|
4529
5594
|
if (disposed) return;
|
|
4530
5595
|
const normalized = normalizeBindings(nextBindings);
|
|
4531
|
-
|
|
5596
|
+
if (sameBindings(bindings, normalized)) return;
|
|
5597
|
+
const previousBindings = bindings;
|
|
4532
5598
|
bindings = normalized;
|
|
5599
|
+
nativeSnapshot = nativeGetSnapshot();
|
|
5600
|
+
const previousProjectedSnapshot = projectedSnapshot;
|
|
5601
|
+
projectedSnapshot = project(nativeSnapshot, previousBindings, bindings);
|
|
5602
|
+
if (projectedSnapshot === previousProjectedSnapshot) return;
|
|
5603
|
+
for (const listener of [...listeners]) listener();
|
|
5604
|
+
};
|
|
5605
|
+
const restoreMethod = (name, descriptor) => {
|
|
5606
|
+
if (descriptor === void 0) {
|
|
5607
|
+
delete list[name];
|
|
5608
|
+
return;
|
|
5609
|
+
}
|
|
5610
|
+
Object.defineProperty(list, name, descriptor);
|
|
4533
5611
|
};
|
|
4534
5612
|
return {
|
|
4535
5613
|
ensure(binding) {
|
|
@@ -4543,11 +5621,14 @@ window.__ModuleLoader__.load({
|
|
|
4543
5621
|
},
|
|
4544
5622
|
dispose() {
|
|
4545
5623
|
if (disposed) return;
|
|
4546
|
-
apply([]);
|
|
4547
5624
|
bindings = [];
|
|
5625
|
+
projectedSnapshot = nativeGetSnapshot();
|
|
4548
5626
|
disposed = true;
|
|
4549
|
-
|
|
4550
|
-
|
|
5627
|
+
for (const listener of [...listeners]) listener();
|
|
5628
|
+
unsubscribeNative();
|
|
5629
|
+
listeners.clear();
|
|
5630
|
+
restoreMethod("getSnapshot", originalGetSnapshot);
|
|
5631
|
+
restoreMethod("subscribe", originalSubscribe);
|
|
4551
5632
|
}
|
|
4552
5633
|
};
|
|
4553
5634
|
}
|
|
@@ -4701,6 +5782,31 @@ window.__ModuleLoader__.load({
|
|
|
4701
5782
|
}
|
|
4702
5783
|
//#endregion
|
|
4703
5784
|
//#region lib/client/worktree-session-fork.js
|
|
5785
|
+
function reconciliationSnapshot(snapshot) {
|
|
5786
|
+
if (typeof snapshot !== "object" || snapshot === null || snapshot.phase === "pending" || snapshot.phase !== void 0 && snapshot.phase !== "ready" || !Array.isArray(snapshot.ids) || typeof snapshot.byId !== "object" || snapshot.byId === null || Array.isArray(snapshot.byId)) return;
|
|
5787
|
+
for (const sessionId of snapshot.ids) {
|
|
5788
|
+
if (typeof sessionId !== "string") return void 0;
|
|
5789
|
+
const summary = snapshot.byId[sessionId];
|
|
5790
|
+
if (summary !== void 0 && (typeof summary !== "object" || summary === null || Array.isArray(summary))) return;
|
|
5791
|
+
const parentId = summary?.parentId;
|
|
5792
|
+
const origin = summary?.origin;
|
|
5793
|
+
const blank = summary?.blank;
|
|
5794
|
+
if (parentId !== void 0 && typeof parentId !== "string" || origin !== void 0 && typeof origin !== "string" || blank !== void 0 && typeof blank !== "boolean") return;
|
|
5795
|
+
}
|
|
5796
|
+
const candidates = snapshot.ids.flatMap((childSessionId) => {
|
|
5797
|
+
const summary = snapshot.byId[childSessionId];
|
|
5798
|
+
if (summary?.parentId === void 0 || summary.parentId === childSessionId || summary.blank === true || summary.origin === "subagent" || snapshot.byId[summary.parentId] === void 0) return [];
|
|
5799
|
+
return [{
|
|
5800
|
+
childSessionId,
|
|
5801
|
+
sourceSessionId: summary.parentId
|
|
5802
|
+
}];
|
|
5803
|
+
});
|
|
5804
|
+
const candidateFacts = candidates.map(({ childSessionId, sourceSessionId }) => [childSessionId, sourceSessionId]).sort(([leftChild, leftSource], [rightChild, rightSource]) => leftChild < rightChild || leftChild === rightChild && leftSource < rightSource ? -1 : leftChild === rightChild && leftSource === rightSource ? 0 : 1);
|
|
5805
|
+
return {
|
|
5806
|
+
signature: JSON.stringify([snapshot.phase ?? "ready", candidateFacts]),
|
|
5807
|
+
candidates
|
|
5808
|
+
};
|
|
5809
|
+
}
|
|
4704
5810
|
function recoveryKey(sourceSessionId, childSessionId) {
|
|
4705
5811
|
return `${sourceSessionId}\u0000${childSessionId}`;
|
|
4706
5812
|
}
|
|
@@ -4715,18 +5821,22 @@ window.__ModuleLoader__.load({
|
|
|
4715
5821
|
let pending = /* @__PURE__ */ new Map();
|
|
4716
5822
|
let recoverySnapshot = {
|
|
4717
5823
|
revision: 0,
|
|
4718
|
-
pending: []
|
|
5824
|
+
pending: [],
|
|
5825
|
+
affectedWorkspaceIds: []
|
|
4719
5826
|
};
|
|
4720
5827
|
const subscribers = /* @__PURE__ */ new Set();
|
|
4721
5828
|
const boundChildren = /* @__PURE__ */ new Set();
|
|
4722
5829
|
const bindingInFlight = /* @__PURE__ */ new Map();
|
|
4723
5830
|
let reconciliationInFlight;
|
|
4724
5831
|
let reconciliationAgain = false;
|
|
4725
|
-
|
|
5832
|
+
let reconciliationForce = false;
|
|
5833
|
+
let lastSessionLineageSignature;
|
|
5834
|
+
const publish = (affectedWorkspaceIds = []) => {
|
|
4726
5835
|
revision += 1;
|
|
4727
5836
|
recoverySnapshot = {
|
|
4728
5837
|
revision,
|
|
4729
|
-
pending: [...pending.values()]
|
|
5838
|
+
pending: [...pending.values()],
|
|
5839
|
+
affectedWorkspaceIds: [...new Set(affectedWorkspaceIds)]
|
|
4730
5840
|
};
|
|
4731
5841
|
for (const subscriber of subscribers) subscriber();
|
|
4732
5842
|
};
|
|
@@ -4740,30 +5850,44 @@ window.__ModuleLoader__.load({
|
|
|
4740
5850
|
...target === void 0 ? {} : { binding: target },
|
|
4741
5851
|
error
|
|
4742
5852
|
});
|
|
4743
|
-
publish();
|
|
5853
|
+
publish(target === void 0 ? [] : [target.workspaceId]);
|
|
4744
5854
|
};
|
|
4745
5855
|
const clearRecovery = (sourceSessionId, childSessionId) => {
|
|
4746
5856
|
const key = recoveryKey(sourceSessionId, childSessionId);
|
|
4747
|
-
|
|
5857
|
+
const current = pending.get(key);
|
|
5858
|
+
if (current === void 0) return { cleared: false };
|
|
4748
5859
|
const next = new Map(pending);
|
|
4749
5860
|
next.delete(key);
|
|
4750
5861
|
pending = next;
|
|
4751
|
-
|
|
5862
|
+
return {
|
|
5863
|
+
cleared: true,
|
|
5864
|
+
workspaceId: current.binding?.workspaceId
|
|
5865
|
+
};
|
|
4752
5866
|
};
|
|
4753
|
-
const
|
|
4754
|
-
if (disposed || boundChildren.has(childSessionId)) return { bound: false };
|
|
4755
|
-
let target;
|
|
5867
|
+
const lookupBinding = async (sourceSessionId) => {
|
|
4756
5868
|
try {
|
|
4757
|
-
|
|
5869
|
+
return (await options.findBindings([sourceSessionId])).bySessionId.get(sourceSessionId) ?? { status: "missing" };
|
|
4758
5870
|
} catch (error) {
|
|
4759
|
-
|
|
5871
|
+
return {
|
|
5872
|
+
status: "error",
|
|
5873
|
+
error
|
|
5874
|
+
};
|
|
5875
|
+
}
|
|
5876
|
+
};
|
|
5877
|
+
const attemptBinding = async (sourceSessionId, childSessionId, providedLookup) => {
|
|
5878
|
+
if (disposed || boundChildren.has(childSessionId)) return { bound: false };
|
|
5879
|
+
const lookup = providedLookup ?? await lookupBinding(sourceSessionId);
|
|
5880
|
+
if (lookup.status === "error") {
|
|
5881
|
+
setRecovery(sourceSessionId, childSessionId, lookup.error);
|
|
4760
5882
|
return { bound: false };
|
|
4761
5883
|
}
|
|
4762
5884
|
if (disposed) return { bound: false };
|
|
4763
|
-
if (
|
|
4764
|
-
clearRecovery(sourceSessionId, childSessionId);
|
|
5885
|
+
if (lookup.status === "missing" || lookup.binding.status !== "active") {
|
|
5886
|
+
const cleared = clearRecovery(sourceSessionId, childSessionId);
|
|
5887
|
+
if (cleared.cleared) publish(cleared.workspaceId === void 0 ? [] : [cleared.workspaceId]);
|
|
4765
5888
|
return { bound: false };
|
|
4766
5889
|
}
|
|
5890
|
+
const target = lookup.binding;
|
|
4767
5891
|
try {
|
|
4768
5892
|
await options.bindSession({
|
|
4769
5893
|
workspaceId: target.workspaceId,
|
|
@@ -4778,50 +5902,64 @@ window.__ModuleLoader__.load({
|
|
|
4778
5902
|
status: "active"
|
|
4779
5903
|
};
|
|
4780
5904
|
boundChildren.add(childSessionId);
|
|
4781
|
-
clearRecovery(sourceSessionId, childSessionId);
|
|
5905
|
+
const { workspaceId: previousWorkspaceId } = clearRecovery(sourceSessionId, childSessionId);
|
|
4782
5906
|
options.onBound?.(childBinding);
|
|
4783
|
-
publish();
|
|
5907
|
+
publish([target.workspaceId, ...previousWorkspaceId === void 0 ? [] : [previousWorkspaceId]]);
|
|
4784
5908
|
return { bound: true };
|
|
4785
5909
|
} catch (error) {
|
|
4786
5910
|
setRecovery(sourceSessionId, childSessionId, error, target);
|
|
4787
5911
|
return { bound: false };
|
|
4788
5912
|
}
|
|
4789
5913
|
};
|
|
4790
|
-
const bindChild = (sourceSessionId, childSessionId) => {
|
|
5914
|
+
const bindChild = (sourceSessionId, childSessionId, providedLookup) => {
|
|
4791
5915
|
if (disposed || boundChildren.has(childSessionId)) return Promise.resolve({ bound: false });
|
|
4792
5916
|
const key = recoveryKey(sourceSessionId, childSessionId);
|
|
4793
5917
|
const current = bindingInFlight.get(key);
|
|
4794
5918
|
if (current !== void 0) return current;
|
|
4795
|
-
const promise = attemptBinding(sourceSessionId, childSessionId).finally(() => {
|
|
5919
|
+
const promise = attemptBinding(sourceSessionId, childSessionId, providedLookup).finally(() => {
|
|
4796
5920
|
if (bindingInFlight.get(key) === promise) bindingInFlight.delete(key);
|
|
4797
5921
|
});
|
|
4798
5922
|
bindingInFlight.set(key, promise);
|
|
4799
5923
|
return promise;
|
|
4800
5924
|
};
|
|
4801
|
-
const reconcileNow = async () => {
|
|
5925
|
+
const reconcileNow = async (force) => {
|
|
4802
5926
|
if (disposed || options.sessions === void 0) return;
|
|
4803
|
-
const
|
|
4804
|
-
if (
|
|
4805
|
-
if (!
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
})
|
|
4814
|
-
|
|
5927
|
+
const normalized = reconciliationSnapshot(options.sessions.getSnapshot());
|
|
5928
|
+
if (normalized === void 0) return;
|
|
5929
|
+
if (!force && normalized.signature === lastSessionLineageSignature) return;
|
|
5930
|
+
lastSessionLineageSignature = normalized.signature;
|
|
5931
|
+
const candidates = normalized.candidates.filter(({ childSessionId }) => !boundChildren.has(childSessionId));
|
|
5932
|
+
if (candidates.length === 0) return;
|
|
5933
|
+
const sourceSessionIds = [...new Set(candidates.map(({ sourceSessionId }) => sourceSessionId))];
|
|
5934
|
+
let index;
|
|
5935
|
+
try {
|
|
5936
|
+
index = await options.findBindings(sourceSessionIds);
|
|
5937
|
+
} catch (error) {
|
|
5938
|
+
const bySessionId = /* @__PURE__ */ new Map();
|
|
5939
|
+
for (const sourceSessionId of sourceSessionIds) bySessionId.set(sourceSessionId, {
|
|
5940
|
+
status: "error",
|
|
5941
|
+
error
|
|
5942
|
+
});
|
|
5943
|
+
index = { bySessionId };
|
|
5944
|
+
}
|
|
5945
|
+
if (disposed) return;
|
|
5946
|
+
await Promise.all(candidates.map(({ childSessionId, sourceSessionId }) => bindChild(sourceSessionId, childSessionId, index.bySessionId.get(sourceSessionId) ?? { status: "missing" }).then(() => void 0)));
|
|
4815
5947
|
};
|
|
4816
|
-
const reconcile = () => {
|
|
5948
|
+
const reconcile = (input = {}) => {
|
|
5949
|
+
const force = input.force === true;
|
|
4817
5950
|
if (reconciliationInFlight !== void 0) {
|
|
4818
5951
|
reconciliationAgain = true;
|
|
5952
|
+
reconciliationForce ||= force;
|
|
4819
5953
|
return reconciliationInFlight;
|
|
4820
5954
|
}
|
|
5955
|
+
let nextForce = force;
|
|
4821
5956
|
const run = async () => {
|
|
4822
5957
|
do {
|
|
5958
|
+
const forceThisPass = nextForce || reconciliationForce;
|
|
5959
|
+
nextForce = false;
|
|
5960
|
+
reconciliationForce = false;
|
|
4823
5961
|
reconciliationAgain = false;
|
|
4824
|
-
await reconcileNow();
|
|
5962
|
+
await reconcileNow(forceThisPass);
|
|
4825
5963
|
} while (!disposed && reconciliationAgain);
|
|
4826
5964
|
};
|
|
4827
5965
|
const promise = run().finally(() => {
|
|
@@ -4849,7 +5987,11 @@ window.__ModuleLoader__.load({
|
|
|
4849
5987
|
async retry(key) {
|
|
4850
5988
|
const item = pending.get(key);
|
|
4851
5989
|
if (item === void 0 || disposed) return false;
|
|
4852
|
-
|
|
5990
|
+
const providedLookup = item.binding === void 0 ? void 0 : {
|
|
5991
|
+
status: "found",
|
|
5992
|
+
binding: item.binding
|
|
5993
|
+
};
|
|
5994
|
+
return (await bindChild(item.sourceSessionId, item.childSessionId, providedLookup)).bound;
|
|
4853
5995
|
},
|
|
4854
5996
|
dispose() {
|
|
4855
5997
|
disposed = true;
|
|
@@ -4860,27 +6002,56 @@ window.__ModuleLoader__.load({
|
|
|
4860
6002
|
}
|
|
4861
6003
|
//#endregion
|
|
4862
6004
|
//#region lib/client/entry.js
|
|
6005
|
+
function forkRelatedSessionIds(snapshot) {
|
|
6006
|
+
if (typeof snapshot !== "object" || snapshot === null || !Array.isArray(snapshot.ids) || typeof snapshot.byId !== "object" || snapshot.byId === null || Array.isArray(snapshot.byId)) return;
|
|
6007
|
+
const related = /* @__PURE__ */ new Set();
|
|
6008
|
+
for (const childSessionId of snapshot.ids) {
|
|
6009
|
+
if (typeof childSessionId !== "string") return void 0;
|
|
6010
|
+
const summary = snapshot.byId[childSessionId];
|
|
6011
|
+
if (summary !== void 0 && (typeof summary !== "object" || summary === null || Array.isArray(summary))) return;
|
|
6012
|
+
const parentSessionId = summary?.parentId;
|
|
6013
|
+
if (parentSessionId === void 0 || parentSessionId === childSessionId || summary?.blank === true || summary?.origin === "subagent" || snapshot.byId[parentSessionId] === void 0) continue;
|
|
6014
|
+
related.add(childSessionId);
|
|
6015
|
+
related.add(parentSessionId);
|
|
6016
|
+
}
|
|
6017
|
+
return related;
|
|
6018
|
+
}
|
|
6019
|
+
function workspaceMembershipSignature(snapshot, sessions) {
|
|
6020
|
+
const relatedSessionIds = forkRelatedSessionIds(sessions);
|
|
6021
|
+
if (relatedSessionIds === void 0) return void 0;
|
|
6022
|
+
if (typeof snapshot !== "object" || snapshot === null || !Array.isArray(snapshot.items)) return;
|
|
6023
|
+
const facts = [];
|
|
6024
|
+
for (const workspace of snapshot.items) {
|
|
6025
|
+
if (typeof workspace !== "object" || workspace === null || typeof workspace.workspaceId !== "string" || !Array.isArray(workspace.sessionIds) || workspace.sessionIds.some((sessionId) => typeof sessionId !== "string")) return;
|
|
6026
|
+
facts.push([workspace.workspaceId, workspace.sessionIds.filter((sessionId) => relatedSessionIds.has(sessionId)).sort()]);
|
|
6027
|
+
}
|
|
6028
|
+
facts.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
6029
|
+
return JSON.stringify(facts);
|
|
6030
|
+
}
|
|
4863
6031
|
/** Required DSH Client services; Connection is the sole Worktree wire dependency. */
|
|
4864
6032
|
const inject = [
|
|
4865
6033
|
"connection",
|
|
4866
6034
|
"locale",
|
|
4867
6035
|
"slots",
|
|
4868
6036
|
"sessions",
|
|
4869
|
-
"workspaces"
|
|
6037
|
+
"workspaces",
|
|
6038
|
+
"uiWorkspace"
|
|
4870
6039
|
];
|
|
4871
6040
|
/**
|
|
4872
6041
|
* DSH Client entry. The adapter is composed once per plugin fiber and is disposed
|
|
4873
6042
|
* with that fiber, so slot consumers share one manager and one request lifetime.
|
|
4874
6043
|
*/
|
|
4875
6044
|
function apply(ctx) {
|
|
6045
|
+
const locale = ctx.locale;
|
|
4876
6046
|
if (typeof document !== "undefined") ctx.effect(() => installWorktreePermissionIcon(document), "clutch-dsh-worktree: permission icon cleanup");
|
|
4877
|
-
ctx.effect(() =>
|
|
6047
|
+
ctx.effect(() => locale.register(WORKTREE_NS, {
|
|
4878
6048
|
zh,
|
|
4879
6049
|
en
|
|
4880
6050
|
}), "clutch-dsh-worktree: locale dictionaries");
|
|
4881
6051
|
const manager = createWorktreeConnectionAdapter(ctx.connection.rpc);
|
|
6052
|
+
const viewReader = createWorktreeViewReader(manager);
|
|
4882
6053
|
const fullAccessConfirmation = createWorktreeFullAccessConfirmationController();
|
|
4883
|
-
const permissionNotice =
|
|
6054
|
+
const permissionNotice = createSnapshotStore(void 0);
|
|
4884
6055
|
const permissionManager = typeof document !== "undefined" ? manager : void 0;
|
|
4885
6056
|
const reportPermissionNotice = (input, result) => {
|
|
4886
6057
|
const visible = result.status === "fallback-workspace-write" || result.status === "user-restricted" || result.status === "unverified";
|
|
@@ -4890,21 +6061,23 @@ window.__ModuleLoader__.load({
|
|
|
4890
6061
|
} : void 0);
|
|
4891
6062
|
};
|
|
4892
6063
|
const sessions = ctx.sessions;
|
|
6064
|
+
const slots = ctx.slots;
|
|
4893
6065
|
const virtualWorkspaceMembership = createVirtualWorkspaceMembership(ctx.workspaces.list);
|
|
4894
6066
|
const contextProjection = createWorktreeContextProjection({
|
|
4895
6067
|
sessions: ctx.sessions.list,
|
|
4896
6068
|
workspaces: ctx.workspaces.list,
|
|
4897
|
-
|
|
4898
|
-
storeFactory:
|
|
6069
|
+
viewReader,
|
|
6070
|
+
storeFactory: createSnapshotStore
|
|
4899
6071
|
});
|
|
4900
6072
|
ctx.effect(() => () => manager.dispose(), "clutch-dsh-worktree: connection cleanup");
|
|
6073
|
+
ctx.effect(() => () => viewReader.dispose(), "clutch-dsh-worktree: view reader cleanup");
|
|
4901
6074
|
ctx.effect(() => () => fullAccessConfirmation.dispose(), "clutch-dsh-worktree: Full Access confirmation cleanup");
|
|
4902
6075
|
ctx.effect(() => () => virtualWorkspaceMembership.dispose(), "clutch-dsh-worktree: Workspace membership cleanup");
|
|
4903
6076
|
ctx.effect(() => () => contextProjection.dispose(), "clutch-dsh-worktree: Session context cleanup");
|
|
4904
6077
|
contextProjection.refresh();
|
|
4905
6078
|
const viewStore = createWorktreeViewStore();
|
|
4906
|
-
const expandState = createWorktreeExpandStateStore(
|
|
4907
|
-
const sessionOrder = createWorktreeSessionOrderStore(
|
|
6079
|
+
const expandState = createWorktreeExpandStateStore(createSnapshotStore);
|
|
6080
|
+
const sessionOrder = createWorktreeSessionOrderStore(createSnapshotStore);
|
|
4908
6081
|
ctx.effect(() => () => sessionOrder.dispose(), "clutch-dsh-worktree: Session order cleanup");
|
|
4909
6082
|
const ensureSessionWorkspace = (workspaceId, sessionId) => {
|
|
4910
6083
|
virtualWorkspaceMembership.ensure({
|
|
@@ -4917,19 +6090,64 @@ window.__ModuleLoader__.load({
|
|
|
4917
6090
|
};
|
|
4918
6091
|
const forkableSessions = ctx.sessions;
|
|
4919
6092
|
const nativeFork = forkableSessions.fork;
|
|
4920
|
-
const
|
|
4921
|
-
|
|
4922
|
-
const
|
|
4923
|
-
|
|
4924
|
-
}))
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
6093
|
+
const workspaceBindingReads = /* @__PURE__ */ new Map();
|
|
6094
|
+
const readWorkspaceBindings = (workspaceId) => {
|
|
6095
|
+
const current = workspaceBindingReads.get(workspaceId);
|
|
6096
|
+
if (current !== void 0) return current;
|
|
6097
|
+
const promise = manager.listBindings({ workspaceId }).finally(() => {
|
|
6098
|
+
if (workspaceBindingReads.get(workspaceId) === promise) workspaceBindingReads.delete(workspaceId);
|
|
6099
|
+
});
|
|
6100
|
+
workspaceBindingReads.set(workspaceId, promise);
|
|
6101
|
+
return promise;
|
|
6102
|
+
};
|
|
6103
|
+
const findWorktreeSessionBindings = async (sessionIds) => {
|
|
6104
|
+
const requested = [...new Set(sessionIds)];
|
|
6105
|
+
const requestedSet = new Set(requested);
|
|
6106
|
+
const workspaces = ctx.workspaces.list.getSnapshot().items;
|
|
6107
|
+
const workspaceIds = [...new Set(workspaces.map((workspace) => workspace.workspaceId))];
|
|
6108
|
+
const owners = /* @__PURE__ */ new Map();
|
|
6109
|
+
for (const workspace of workspaces) for (const sessionId of workspace.sessionIds) {
|
|
6110
|
+
if (!requestedSet.has(sessionId)) continue;
|
|
6111
|
+
const workspaceOwners = owners.get(sessionId) ?? [];
|
|
6112
|
+
if (!workspaceOwners.includes(workspace.workspaceId)) {
|
|
6113
|
+
workspaceOwners.push(workspace.workspaceId);
|
|
6114
|
+
owners.set(sessionId, workspaceOwners);
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
6117
|
+
const selectedWorkspaceIds = requested.some((sessionId) => (owners.get(sessionId)?.length ?? 0) === 0) ? workspaceIds : workspaceIds.filter((workspaceId) => requested.some((sessionId) => owners.get(sessionId)?.includes(workspaceId) === true));
|
|
6118
|
+
const results = await Promise.allSettled(selectedWorkspaceIds.map((workspaceId) => readWorkspaceBindings(workspaceId)));
|
|
6119
|
+
const resultByWorkspaceId = new Map(selectedWorkspaceIds.map((workspaceId, index) => [workspaceId, results[index]]));
|
|
6120
|
+
const bySessionId = /* @__PURE__ */ new Map();
|
|
6121
|
+
for (const sessionId of requested) {
|
|
6122
|
+
const relevantWorkspaceIds = owners.get(sessionId) ?? selectedWorkspaceIds;
|
|
6123
|
+
let found;
|
|
6124
|
+
let failed;
|
|
6125
|
+
for (const workspaceId of relevantWorkspaceIds) {
|
|
6126
|
+
const result = resultByWorkspaceId.get(workspaceId);
|
|
6127
|
+
if (result === void 0) continue;
|
|
6128
|
+
if (result.status === "rejected") {
|
|
6129
|
+
failed ??= result.reason;
|
|
6130
|
+
continue;
|
|
6131
|
+
}
|
|
6132
|
+
const binding = result.value.find((candidate) => candidate.sessionId === sessionId && candidate.status === "active");
|
|
6133
|
+
if (binding !== void 0) {
|
|
6134
|
+
found = binding;
|
|
6135
|
+
break;
|
|
6136
|
+
}
|
|
6137
|
+
}
|
|
6138
|
+
bySessionId.set(sessionId, found === void 0 ? failed === void 0 ? { status: "missing" } : {
|
|
6139
|
+
status: "error",
|
|
6140
|
+
error: failed
|
|
6141
|
+
} : {
|
|
6142
|
+
status: "found",
|
|
6143
|
+
binding: found
|
|
6144
|
+
});
|
|
6145
|
+
}
|
|
6146
|
+
return { bySessionId };
|
|
4929
6147
|
};
|
|
4930
6148
|
const forkCoordinator = typeof nativeFork !== "function" ? void 0 : createWorktreeSessionForkCoordinator({
|
|
4931
6149
|
fork: (input) => nativeFork.call(ctx.sessions, input),
|
|
4932
|
-
|
|
6150
|
+
findBindings: findWorktreeSessionBindings,
|
|
4933
6151
|
bindSession: (input) => manager.bindSession(input),
|
|
4934
6152
|
sessions: ctx.sessions.list
|
|
4935
6153
|
});
|
|
@@ -4938,8 +6156,15 @@ window.__ModuleLoader__.load({
|
|
|
4938
6156
|
const reconcileForkChildren = () => {
|
|
4939
6157
|
forkCoordinator.reconcile();
|
|
4940
6158
|
};
|
|
6159
|
+
let lastWorkspaceMembershipSignature = workspaceMembershipSignature(ctx.workspaces.list.getSnapshot(), ctx.sessions.list.getSnapshot());
|
|
6160
|
+
const reconcileForkChildrenForWorkspace = () => {
|
|
6161
|
+
const nextSignature = workspaceMembershipSignature(ctx.workspaces.list.getSnapshot(), ctx.sessions.list.getSnapshot());
|
|
6162
|
+
const changed = nextSignature !== void 0 && nextSignature !== lastWorkspaceMembershipSignature;
|
|
6163
|
+
if (nextSignature !== void 0) lastWorkspaceMembershipSignature = nextSignature;
|
|
6164
|
+
forkCoordinator.reconcile({ force: changed });
|
|
6165
|
+
};
|
|
4941
6166
|
const unsubscribeSessionList = ctx.sessions.list.subscribe(reconcileForkChildren);
|
|
4942
|
-
const unsubscribeWorkspaceList = ctx.workspaces.list.subscribe(
|
|
6167
|
+
const unsubscribeWorkspaceList = ctx.workspaces.list.subscribe(reconcileForkChildrenForWorkspace);
|
|
4943
6168
|
ctx.effect(() => () => {
|
|
4944
6169
|
unsubscribeSessionList();
|
|
4945
6170
|
unsubscribeWorkspaceList();
|
|
@@ -4951,7 +6176,7 @@ window.__ModuleLoader__.load({
|
|
|
4951
6176
|
const worktreeSessionConnector = createWorktreeSessionConnector({
|
|
4952
6177
|
manager,
|
|
4953
6178
|
sessions: ctx.sessions.list,
|
|
4954
|
-
archivedSessionIds: () => ctx.workspaces.list.getSnapshot().archivedSessionIds
|
|
6179
|
+
archivedSessionIds: () => ctx.workspaces.list.getSnapshot().archivedSessionIds,
|
|
4955
6180
|
createSession: async (input) => String(await sessions.create(input)),
|
|
4956
6181
|
ensureSessionWorkspace,
|
|
4957
6182
|
permission: permissionManager,
|
|
@@ -4962,21 +6187,21 @@ window.__ModuleLoader__.load({
|
|
|
4962
6187
|
}
|
|
4963
6188
|
});
|
|
4964
6189
|
ctx.effect(() => () => worktreeSessionConnector.dispose(), "clutch-dsh-worktree: Worktree Session connector cleanup");
|
|
4965
|
-
|
|
6190
|
+
slots.inject("conversation.session.header.actions", () => slots.register({
|
|
4966
6191
|
name: "conversation.session.header.actions",
|
|
4967
6192
|
id: "clutch-dsh-worktree-context-header",
|
|
4968
6193
|
order: -5,
|
|
4969
6194
|
locale: WORKTREE_NS,
|
|
4970
6195
|
inject: () => ({ hooks: { worktreeContext: contextProjection.store } })
|
|
4971
6196
|
}, WorktreeHeaderContext));
|
|
4972
|
-
|
|
6197
|
+
slots.inject("sidebar.footer.action", () => slots.register({
|
|
4973
6198
|
name: "sidebar.footer.action",
|
|
4974
6199
|
id: "clutch-dsh-worktree-mode-action",
|
|
4975
6200
|
store: viewStore,
|
|
4976
6201
|
locale: WORKTREE_NS,
|
|
4977
6202
|
inject: () => ({ available: true })
|
|
4978
6203
|
}, WorktreeModeAction));
|
|
4979
|
-
|
|
6204
|
+
slots.inject("shell.overlay", () => slots.register({
|
|
4980
6205
|
name: "shell.overlay",
|
|
4981
6206
|
id: "clutch-dsh-worktree-navigation",
|
|
4982
6207
|
store: viewStore,
|
|
@@ -4987,6 +6212,7 @@ window.__ModuleLoader__.load({
|
|
|
4987
6212
|
sessionOrder,
|
|
4988
6213
|
hooks: { worktreeContext: contextProjection.store },
|
|
4989
6214
|
manager,
|
|
6215
|
+
viewReader,
|
|
4990
6216
|
permission: permissionManager,
|
|
4991
6217
|
confirmFullAccess: permissionManager === void 0 ? void 0 : fullAccessConfirmation.request,
|
|
4992
6218
|
fullAccessConfirmation: typeof document === "undefined" ? void 0 : fullAccessConfirmation,
|
|
@@ -4994,13 +6220,13 @@ window.__ModuleLoader__.load({
|
|
|
4994
6220
|
onPermissionNotice: reportPermissionNotice,
|
|
4995
6221
|
permissionNotice,
|
|
4996
6222
|
createWorkspace: async () => {
|
|
4997
|
-
const workspacePath = await ctx.
|
|
6223
|
+
const workspacePath = await ctx.uiWorkspace.pickDirectory();
|
|
4998
6224
|
if (workspacePath !== null) await ctx.workspaces.create({ path: workspacePath });
|
|
4999
6225
|
},
|
|
5000
6226
|
createSessionForWorktree: (input) => worktreeSessionConnector.create(input),
|
|
5001
6227
|
invalidateWorktreeContext: (workspaceId) => contextProjection.invalidate(workspaceId),
|
|
5002
6228
|
createMainSession: (workspaceId) => {
|
|
5003
|
-
ctx.
|
|
6229
|
+
ctx.uiWorkspace.startSession(workspaceId);
|
|
5004
6230
|
},
|
|
5005
6231
|
renameWorkspace: async (workspaceId, title) => {
|
|
5006
6232
|
await ctx.workspaces.rename(workspaceId, title);
|