@dsh-electron/dsh-plugin-multi-root-workspace 0.1.14 → 0.1.16
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/CHANGELOG.en.md +32 -1
- package/CHANGELOG.md +32 -1
- package/README.en.md +25 -8
- package/README.md +25 -8
- package/cordis.patch.yml +5 -1
- package/lib/{llm-message-fYORacvh.js → agent-instructions-BXuKhOzr.js} +1 -84
- package/lib/auto-review.js +2 -2
- package/lib/client.js +969 -100
- package/lib/client.js.map +1 -1
- package/lib/{command-Dcz6nULo.js → command-DaVJ-mK3.js} +76 -11
- package/lib/command.js +1 -1
- package/lib/compat-CTJbpCxu.js +267 -0
- package/lib/compat.js +1 -1
- package/lib/{dialects-Dr4rDR-H.js → dialects-CdlG3RF9.js} +1 -1
- package/lib/{compat-veDqCOoa.js → dsh-version-DCwF7REk.js} +2 -264
- package/lib/index.js +10 -8
- package/lib/{instructions-KsUwUFE0.js → instructions-Ca80SkZq.js} +696 -27
- package/lib/instructions.js +1 -1
- package/lib/llm-message-CepyzEBQ.js +84 -0
- package/lib/lsp.js +4 -4
- package/lib/{metadata-DgqSdVX_.js → metadata-99jgViHQ.js} +1 -1
- package/lib/{method-wrapper-CFY3p3j2.js → method-wrapper-D4oUCsEF.js} +22 -2
- package/lib/{native-win32-CRNXr11U.js → native-win32-C5oMtt7f.js} +1 -1
- package/lib/ops-compatibility.js +58 -0
- package/lib/ops-settings-UN2oOKEp.js +40 -0
- package/lib/ops-tools-k4JYaOrM.js +152 -0
- package/lib/process-fence.js +193 -34
- package/lib/{protocol-CnR1KTXy.js → protocol-CwDnFEyK.js} +8 -4
- package/lib/{registry-DR_sMuvS.js → registry-BibiukWk.js} +236 -80
- package/lib/{registry-lease-win32-DDO-c7ig.js → registry-lease-win32-r4puac9m.js} +1 -1
- package/lib/{registry-operation-C2ZV3Jhw.js → registry-operation-CIlmt6_c.js} +5 -1
- package/lib/registry.js +2 -2
- package/lib/{review-context-NDpKeMCc.js → review-context-OxO-fMWR.js} +24 -7
- package/lib/review-context.js +1 -1
- package/lib/{roots-Ca_W1S9n.js → roots-CmaWxRVy.js} +112 -22
- package/lib/{sandbox-C-vCJCp3.js → sandbox-aobdyQGi.js} +7 -4
- package/lib/sandbox.js +1 -1
- package/lib/scope.js +162 -12
- package/lib/skills-utyuvmwr.js +104 -0
- package/lib/skills.js +5 -106
- package/lib/{tool-context-CpgIFcCB.js → tool-context-CCeY3mrK.js} +1 -1
- package/lib/types/authority/protocol.d.ts +5 -1
- package/lib/types/client/files-tree/FilesTree.d.ts +49 -6
- package/lib/types/client/files-tree/adapter.d.ts +69 -16
- package/lib/types/client/files-tree/face.d.ts +31 -2
- package/lib/types/client/files-tree/locate.d.ts +74 -0
- package/lib/types/client/files-tree/navigator.d.ts +98 -0
- package/lib/types/client/index.d.ts +7 -0
- package/lib/types/client/locales.d.ts +30 -0
- package/lib/types/client/ops-settings.d.ts +6 -0
- package/lib/types/client/skills-settings.d.ts +3 -2
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/command.d.ts +3 -3
- package/lib/types/compat/files-tree.d.ts +8 -1
- package/lib/types/compat/forge-entrypoints.d.ts +44 -0
- package/lib/types/compat/instruction-activity.d.ts +32 -1
- package/lib/types/compat/ops-confirmation.d.ts +17 -0
- package/lib/types/compat/ops-settings.d.ts +11 -0
- package/lib/types/compat/ops-tools.d.ts +25 -0
- package/lib/types/compat/session-existence.d.ts +41 -0
- package/lib/types/compat/skills-client.d.ts +1 -0
- package/lib/types/compat/tool-context.d.ts +4 -0
- package/lib/types/compat.d.ts +2 -2
- package/lib/types/instructions.d.ts +6 -1
- package/lib/types/ops-compatibility.d.ts +34 -0
- package/lib/types/registry-operation.d.ts +7 -0
- package/lib/types/registry.d.ts +67 -8
- package/lib/types/review-context.d.ts +2 -0
- package/lib/types/roots.d.ts +116 -20
- package/lib/types/scope.d.ts +95 -3
- package/lib/workspace-files.js +4 -4
- package/lib/{workspace-routing-YPxaQ4V3.js → workspace-routing-D_qwMUh-.js} +4 -4
- package/package.json +5 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { n as isPackageInstalled } from "./agent-instructions-BXuKhOzr.js";
|
|
2
|
+
//#region \0@oxc-project+runtime@0.149.0/helpers/esm/usingCtx.js
|
|
3
|
+
function _usingCtx() {
|
|
4
|
+
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
5
|
+
var n = Error();
|
|
6
|
+
return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
|
|
7
|
+
}, e = {}, n = [];
|
|
8
|
+
function using(r, e) {
|
|
9
|
+
if (null != e) {
|
|
10
|
+
if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
|
|
11
|
+
if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
|
|
12
|
+
if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
|
|
13
|
+
if ("function" != typeof o) throw new TypeError("Object is not disposable.");
|
|
14
|
+
t && (o = function o() {
|
|
15
|
+
try {
|
|
16
|
+
t.call(e);
|
|
17
|
+
} catch (r) {
|
|
18
|
+
return Promise.reject(r);
|
|
19
|
+
}
|
|
20
|
+
}), n.push({
|
|
21
|
+
v: e,
|
|
22
|
+
d: o,
|
|
23
|
+
a: r
|
|
24
|
+
});
|
|
25
|
+
} else r && n.push({
|
|
26
|
+
d: e,
|
|
27
|
+
a: r
|
|
28
|
+
});
|
|
29
|
+
return e;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
e,
|
|
33
|
+
u: using.bind(null, !1),
|
|
34
|
+
a: using.bind(null, !0),
|
|
35
|
+
d: function d() {
|
|
36
|
+
var o, t = this.e, s = 0;
|
|
37
|
+
function next() {
|
|
38
|
+
for (; o = n.pop();) try {
|
|
39
|
+
if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
|
|
40
|
+
if (o.d) {
|
|
41
|
+
var r = o.d.call(o.v);
|
|
42
|
+
if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
|
|
43
|
+
} else s |= 1;
|
|
44
|
+
} catch (r) {
|
|
45
|
+
return err(r);
|
|
46
|
+
}
|
|
47
|
+
if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
|
|
48
|
+
if (t !== e) throw t;
|
|
49
|
+
}
|
|
50
|
+
function err(n) {
|
|
51
|
+
return t = t !== e ? new r(n, t) : n, next();
|
|
52
|
+
}
|
|
53
|
+
return next();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/compat/skills.ts
|
|
59
|
+
/** Never infer authority from cwd, a preset key, or a lookalike Agent. */
|
|
60
|
+
function skillAgent(ctx, options) {
|
|
61
|
+
const scope = options.scope;
|
|
62
|
+
if (typeof scope !== "object" || scope === null || !("id" in scope) || typeof scope.id !== "string") return void 0;
|
|
63
|
+
const agent = ctx.get("agents")?.get(scope.id);
|
|
64
|
+
return agent === scope ? agent : void 0;
|
|
65
|
+
}
|
|
66
|
+
function skillOrigin(ctx, options) {
|
|
67
|
+
const cwd = skillAgent(ctx, options)?.session.header.cwd;
|
|
68
|
+
return typeof cwd === "string" && cwd !== "" ? cwd : void 0;
|
|
69
|
+
}
|
|
70
|
+
/** Absence is optional; a present but broken module must fail visibly. */
|
|
71
|
+
async function filesystemSkillsApi() {
|
|
72
|
+
if (!isPackageInstalled("@deepseek-ai/dsh-skill-filesystem")) return void 0;
|
|
73
|
+
const api = await import("@deepseek-ai/dsh-skill-filesystem");
|
|
74
|
+
if (typeof api.FileSystemSkillProvider !== "function") throw new Error("multi-root skills: FileSystemSkillProvider is unavailable");
|
|
75
|
+
return api;
|
|
76
|
+
}
|
|
77
|
+
function volatileField(schema) {
|
|
78
|
+
const method = schema.volatile;
|
|
79
|
+
return typeof method === "function" ? method.call(schema) : schema;
|
|
80
|
+
}
|
|
81
|
+
function configValue(value) {
|
|
82
|
+
if (typeof value === "object" && value !== null && Symbol.for("cosmokit.volatile.write") in value) {
|
|
83
|
+
const get = value.get;
|
|
84
|
+
if (typeof get === "function") return get.call(value);
|
|
85
|
+
}
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
/** Cold Browser Catalog supplies a Session header, never a fabricated Agent. */
|
|
89
|
+
async function observedSkillOrigin(ctx, sessionId) {
|
|
90
|
+
try {
|
|
91
|
+
var _usingCtx$1 = _usingCtx();
|
|
92
|
+
const query = ctx.get("sessionQuery");
|
|
93
|
+
if (query === void 0) throw new Error("multi-root skills: SessionQuery is unavailable");
|
|
94
|
+
const cwd = _usingCtx$1.u(await query.observeSession(sessionId)).header.cwd;
|
|
95
|
+
if (typeof cwd !== "string" || cwd === "") throw new Error("multi-root skills: Session has no origin");
|
|
96
|
+
return cwd;
|
|
97
|
+
} catch (_) {
|
|
98
|
+
_usingCtx$1.e = _;
|
|
99
|
+
} finally {
|
|
100
|
+
_usingCtx$1.d();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { skillOrigin as a, skillAgent as i, filesystemSkillsApi as n, volatileField as o, observedSkillOrigin as r, configValue as t };
|
package/lib/skills.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { t as wrapMethod } from "./method-wrapper-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { n as skillsMetadataValid } from "./metadata-
|
|
1
|
+
import { t as wrapMethod } from "./method-wrapper-D4oUCsEF.js";
|
|
2
|
+
import { l as withRegistryRead } from "./registry-operation-CIlmt6_c.js";
|
|
3
|
+
import { t as createInstructionMessage } from "./llm-message-CepyzEBQ.js";
|
|
4
|
+
import { n as skillsMetadataValid } from "./metadata-99jgViHQ.js";
|
|
5
|
+
import { a as skillOrigin, i as skillAgent, n as filesystemSkillsApi, o as volatileField, r as observedSkillOrigin, t as configValue } from "./skills-utyuvmwr.js";
|
|
5
6
|
import { constants, statSync } from "node:fs";
|
|
6
7
|
import { createHash } from "node:crypto";
|
|
7
8
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
@@ -10,108 +11,6 @@ import { canonicalPath } from "@deepseek-ai/dsh-sandbox";
|
|
|
10
11
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
11
12
|
import { lstat, open, readdir, stat } from "node:fs/promises";
|
|
12
13
|
import z from "@deepseek-ai/schemastery";
|
|
13
|
-
//#region \0@oxc-project+runtime@0.149.0/helpers/esm/usingCtx.js
|
|
14
|
-
function _usingCtx() {
|
|
15
|
-
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
16
|
-
var n = Error();
|
|
17
|
-
return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
|
|
18
|
-
}, e = {}, n = [];
|
|
19
|
-
function using(r, e) {
|
|
20
|
-
if (null != e) {
|
|
21
|
-
if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
|
|
22
|
-
if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
|
|
23
|
-
if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
|
|
24
|
-
if ("function" != typeof o) throw new TypeError("Object is not disposable.");
|
|
25
|
-
t && (o = function o() {
|
|
26
|
-
try {
|
|
27
|
-
t.call(e);
|
|
28
|
-
} catch (r) {
|
|
29
|
-
return Promise.reject(r);
|
|
30
|
-
}
|
|
31
|
-
}), n.push({
|
|
32
|
-
v: e,
|
|
33
|
-
d: o,
|
|
34
|
-
a: r
|
|
35
|
-
});
|
|
36
|
-
} else r && n.push({
|
|
37
|
-
d: e,
|
|
38
|
-
a: r
|
|
39
|
-
});
|
|
40
|
-
return e;
|
|
41
|
-
}
|
|
42
|
-
return {
|
|
43
|
-
e,
|
|
44
|
-
u: using.bind(null, !1),
|
|
45
|
-
a: using.bind(null, !0),
|
|
46
|
-
d: function d() {
|
|
47
|
-
var o, t = this.e, s = 0;
|
|
48
|
-
function next() {
|
|
49
|
-
for (; o = n.pop();) try {
|
|
50
|
-
if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
|
|
51
|
-
if (o.d) {
|
|
52
|
-
var r = o.d.call(o.v);
|
|
53
|
-
if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
|
|
54
|
-
} else s |= 1;
|
|
55
|
-
} catch (r) {
|
|
56
|
-
return err(r);
|
|
57
|
-
}
|
|
58
|
-
if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
|
|
59
|
-
if (t !== e) throw t;
|
|
60
|
-
}
|
|
61
|
-
function err(n) {
|
|
62
|
-
return t = t !== e ? new r(n, t) : n, next();
|
|
63
|
-
}
|
|
64
|
-
return next();
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
//#endregion
|
|
69
|
-
//#region src/compat/skills.ts
|
|
70
|
-
/** Never infer authority from cwd, a preset key, or a lookalike Agent. */
|
|
71
|
-
function skillAgent(ctx, options) {
|
|
72
|
-
const scope = options.scope;
|
|
73
|
-
if (typeof scope !== "object" || scope === null || !("id" in scope) || typeof scope.id !== "string") return void 0;
|
|
74
|
-
const agent = ctx.get("agents")?.get(scope.id);
|
|
75
|
-
return agent === scope ? agent : void 0;
|
|
76
|
-
}
|
|
77
|
-
function skillOrigin(ctx, options) {
|
|
78
|
-
const cwd = skillAgent(ctx, options)?.session.header.cwd;
|
|
79
|
-
return typeof cwd === "string" && cwd !== "" ? canonicalPath(cwd) : void 0;
|
|
80
|
-
}
|
|
81
|
-
/** Absence is optional; a present but broken module must fail visibly. */
|
|
82
|
-
async function filesystemSkillsApi() {
|
|
83
|
-
if (!isPackageInstalled("@deepseek-ai/dsh-skill-filesystem")) return void 0;
|
|
84
|
-
const api = await import("@deepseek-ai/dsh-skill-filesystem");
|
|
85
|
-
if (typeof api.FileSystemSkillProvider !== "function") throw new Error("multi-root skills: FileSystemSkillProvider is unavailable");
|
|
86
|
-
return api;
|
|
87
|
-
}
|
|
88
|
-
function volatileField(schema) {
|
|
89
|
-
const method = schema.volatile;
|
|
90
|
-
return typeof method === "function" ? method.call(schema) : schema;
|
|
91
|
-
}
|
|
92
|
-
function configValue(value) {
|
|
93
|
-
if (typeof value === "object" && value !== null && Symbol.for("cosmokit.volatile.write") in value) {
|
|
94
|
-
const get = value.get;
|
|
95
|
-
if (typeof get === "function") return get.call(value);
|
|
96
|
-
}
|
|
97
|
-
return value;
|
|
98
|
-
}
|
|
99
|
-
/** Cold Browser Catalog supplies a Session header, never a fabricated Agent. */
|
|
100
|
-
async function observedSkillOrigin(ctx, sessionId) {
|
|
101
|
-
try {
|
|
102
|
-
var _usingCtx$1 = _usingCtx();
|
|
103
|
-
const query = ctx.get("sessionQuery");
|
|
104
|
-
if (query === void 0) throw new Error("multi-root skills: SessionQuery is unavailable");
|
|
105
|
-
const cwd = _usingCtx$1.u(await query.observeSession(sessionId)).header.cwd;
|
|
106
|
-
if (typeof cwd !== "string" || cwd === "") throw new Error("multi-root skills: Session has no origin");
|
|
107
|
-
return canonicalPath(cwd);
|
|
108
|
-
} catch (_) {
|
|
109
|
-
_usingCtx$1.e = _;
|
|
110
|
-
} finally {
|
|
111
|
-
_usingCtx$1.d();
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
//#endregion
|
|
115
14
|
//#region src/skills/reader.ts
|
|
116
15
|
/** Constrain the native parser before discovery can read a candidate's body. */
|
|
117
16
|
function containsPath(root, target) {
|
|
@@ -29,7 +29,11 @@ export declare const snapshotSchema: z.ZodObject<{
|
|
|
29
29
|
invalid: "invalid";
|
|
30
30
|
}>;
|
|
31
31
|
detail: z.ZodOptional<z.ZodString>;
|
|
32
|
-
invalidReason: z.ZodOptional<z.
|
|
32
|
+
invalidReason: z.ZodOptional<z.ZodEnum<{
|
|
33
|
+
"membership-conflict": "membership-conflict";
|
|
34
|
+
"protected-path": "protected-path";
|
|
35
|
+
"primary-overlap": "primary-overlap";
|
|
36
|
+
}>>;
|
|
33
37
|
}, z.core.$strict>>;
|
|
34
38
|
available: z.ZodBoolean;
|
|
35
39
|
}, z.core.$strict>;
|
|
@@ -9,23 +9,66 @@ import type { FilesInjected } from './face.ts';
|
|
|
9
9
|
import type { createFilesStore } from './store.ts';
|
|
10
10
|
/**
|
|
11
11
|
* The fork's props: the store/locale shares verbatim from upstream's
|
|
12
|
-
* composed props, `
|
|
13
|
-
* seams upstream binds inside `FilesBody` — tab id, lifetime, root, and
|
|
14
|
-
* resource-opening callback — supplied by the adapter instead.
|
|
12
|
+
* composed props, `hold`/`release`/`toggle` from the injected face, and the
|
|
13
|
+
* four seams upstream binds inside `FilesBody` — tab id, lifetime, root, and
|
|
14
|
+
* the resource-opening callback — supplied by the adapter instead.
|
|
15
15
|
*/
|
|
16
|
-
export type FilesTreeProps = PropsStore<ReturnType<typeof createFilesStore>> & Pick<FilesInjected, '
|
|
16
|
+
export type FilesTreeProps = PropsStore<ReturnType<typeof createFilesStore>> & Pick<FilesInjected, 'hold' | 'release' | 'toggle'> & PropsLocale<'sidebarFiles'> & {
|
|
17
17
|
/** The store bucket this tree reads and writes — the pane's own dockkit tab id. */
|
|
18
18
|
readonly tabId: TabId;
|
|
19
19
|
/** Session the listing and the resource addresses belong to. */
|
|
20
20
|
readonly sessionId: SessionId;
|
|
21
21
|
/** Absolute path of the root this tree lists (the selected additional root). */
|
|
22
22
|
readonly root: string;
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The owner's record lifetime: aborting it forgets the bucket and closes
|
|
25
|
+
* the tree. A body unmount alone only ends the hold — the bucket, its
|
|
26
|
+
* listed levels, and its expansion stay for the next mount to resume.
|
|
27
|
+
*/
|
|
24
28
|
readonly signal: AbortSignal;
|
|
25
29
|
/** Open one `dsh-resource://file` address (the adapter's sidebarRight route). */
|
|
26
30
|
readonly onOpen: (address: string) => void;
|
|
27
31
|
/** Optional host-controlled tree Factory, available on the newer Files page. */
|
|
28
32
|
readonly nativeTree?: NativeTreeRenderer | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* The owner's live scroll mirror — upstream's `scrolled` writing
|
|
35
|
+
* `pane.scrollTop`: fired on every scroll and once more at unmount with
|
|
36
|
+
* the body's final offset (the adapter refuses the write when the owner
|
|
37
|
+
* is already dead, so a normal close keeps its last position while a
|
|
38
|
+
* killed one cannot resurrect). The fork's own store still hears the
|
|
39
|
+
* offset only at unmount through `actions.scrolled`, as upstream.
|
|
40
|
+
*/
|
|
41
|
+
readonly onScrollOffset?: ((offset: number) => void) | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* The preview navigator's locate-current-file request: expand the file's
|
|
44
|
+
* ancestors, mark its row current, and scroll it into view — what the
|
|
45
|
+
* native navigator's `revealing`/`step()`/`revealed` does with
|
|
46
|
+
* `pane.current` and `scrollToCurrent`. `revealed` is the owner's memory
|
|
47
|
+
* of the last completed walk: a path equal to it suppresses stepping —
|
|
48
|
+
* exactly upstream's `revealing = revealed === address ? undefined :
|
|
49
|
+
* address` on a body mount — while `onSettled` reports each completed
|
|
50
|
+
* walk (found, unreachable, or canceled) so the owner can record it.
|
|
51
|
+
* Absent on the Files page, which has no current file.
|
|
52
|
+
*/
|
|
53
|
+
readonly reveal?: {
|
|
54
|
+
readonly path: string;
|
|
55
|
+
readonly revealed?: string | undefined;
|
|
56
|
+
readonly onSettled?: ((path: string) => void) | undefined;
|
|
57
|
+
} | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Expansion, scroll offset, and reveal memory saved while this tree's
|
|
60
|
+
* owner was closed — the pane record's surviving `expanded`/`scrollTop`/
|
|
61
|
+
* `revealed` upstream. `expanded` is replayed through `toggle` (the
|
|
62
|
+
* verbatim face's own restore-preferences channel) right after `start`,
|
|
63
|
+
* so every remembered directory re-expands as its level lands; the
|
|
64
|
+
* scroll offset is deferred until the body overflows, and `revealed`
|
|
65
|
+
* seeds the reveal's suppression.
|
|
66
|
+
*/
|
|
67
|
+
readonly saved?: {
|
|
68
|
+
readonly expanded?: readonly string[];
|
|
69
|
+
readonly scrollTop?: number;
|
|
70
|
+
readonly revealed?: string;
|
|
71
|
+
} | undefined;
|
|
29
72
|
};
|
|
30
73
|
/**
|
|
31
74
|
* Order one level's entries for display: directories first, then everything
|
|
@@ -47,4 +90,4 @@ export declare function failureLine(t: TranslateNS<'sidebarFiles'>, failure: Rem
|
|
|
47
90
|
* has opened under it — the upstream body's scroll, seed, and render logic
|
|
48
91
|
* verbatim, with the root arriving as a prop instead of `useSessions(cwd)`.
|
|
49
92
|
*/
|
|
50
|
-
export declare function FilesTree({ tabId, sessionId, root, signal, useStore, actions,
|
|
93
|
+
export declare function FilesTree({ tabId, sessionId, root, signal, useStore, actions, hold, release, toggle, onOpen, t, nativeTree, reveal, saved, onScrollOffset, }: FilesTreeProps): ReactNode;
|
|
@@ -47,26 +47,67 @@ export type TreeSelection = Readonly<Record<string, RootView>>;
|
|
|
47
47
|
* native tree across a body remount (opening a file deactivates this tab,
|
|
48
48
|
* unmounting its body without ending the record).
|
|
49
49
|
*/
|
|
50
|
+
/**
|
|
51
|
+
* The tree-view memory one closed owner keeps for its next open — the
|
|
52
|
+
* fork's spelling of upstream's pane record: `expanded`/`scrollTop` survive
|
|
53
|
+
* the hold's end, and `revealed` is the last previewed file the reveal
|
|
54
|
+
* walk completed (upstream's `pane.revealed`). Entries live per
|
|
55
|
+
* (key, root), so one pane previewing files of different roots keeps each
|
|
56
|
+
* root's memory apart.
|
|
57
|
+
*/
|
|
58
|
+
export interface FilesTreeSaved {
|
|
59
|
+
/** The root this snapshot belongs to; a stale snapshot never crosses roots. */
|
|
60
|
+
readonly root: string;
|
|
61
|
+
/** Expansion preferences at the moment the owner closed. */
|
|
62
|
+
readonly expanded?: readonly string[];
|
|
63
|
+
/** The body's scroll offset at the moment it closed. */
|
|
64
|
+
readonly scrollTop?: number;
|
|
65
|
+
/** The last previewed file whose reveal completed — suppresses re-stepping. */
|
|
66
|
+
readonly revealed?: string;
|
|
67
|
+
}
|
|
50
68
|
export interface FilesTreeTakeover {
|
|
51
69
|
/**
|
|
52
|
-
* The
|
|
53
|
-
* body finds its bucket and
|
|
54
|
-
*
|
|
55
|
-
*
|
|
70
|
+
* The record signal for (key, root); reuses the live entry so a remounted
|
|
71
|
+
* body finds its tree bucket and resumes it. Only the record's end — the
|
|
72
|
+
* owner dying, or a different root superseding it — aborts the controller;
|
|
73
|
+
* a navigator closing is a hold ending and leaves this signal live.
|
|
74
|
+
* @param key - the owner's dockkit identity: the Files pane's tab id, or
|
|
75
|
+
* the preview navigator's pane id.
|
|
56
76
|
* @param root - absolute path of the root being browsed.
|
|
57
|
-
* @param section - the
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
77
|
+
* @param section - how the owner's survival is checked: an Element (kept
|
|
78
|
+
* while `isConnected`) or a predicate (kept while it returns true —
|
|
79
|
+
* the preview navigator's "some connected element still carries this
|
|
80
|
+
* pane id" query, since a dockkit pane has no stable element across a
|
|
81
|
+
* `replaceTab`). The watch lives at this scope and outlives `end()`:
|
|
82
|
+
* ending the hold never disarms it, so the owner's real death drops
|
|
83
|
+
* the saved memory even after the body that mounted it is gone.
|
|
61
84
|
* @returns the signal the forked tree lives under.
|
|
62
85
|
*/
|
|
63
|
-
readonly signal: (
|
|
86
|
+
readonly signal: (key: TabId, root: string, section?: Element | (() => boolean)) => AbortSignal;
|
|
64
87
|
/**
|
|
65
|
-
* Abort and drop one
|
|
66
|
-
*
|
|
67
|
-
*
|
|
88
|
+
* Abort and drop one owner's record: the owner died, the selection moved to
|
|
89
|
+
* another root, or a body with no pane carrier closed. A normal navigator
|
|
90
|
+
* close is not a record end — the body release keeps the bucket. The saved
|
|
91
|
+
* memory survives here because the death watch owns the only real drop;
|
|
92
|
+
* this method only guarantees a superseded record stops writing.
|
|
93
|
+
* @param key - the owner's dockkit identity.
|
|
68
94
|
*/
|
|
69
|
-
readonly end: (
|
|
95
|
+
readonly end: (key: TabId) => void;
|
|
96
|
+
/**
|
|
97
|
+
* The tree-view memory kept for (key, root) — `undefined` before the
|
|
98
|
+
* first close or after the owner's section died.
|
|
99
|
+
* @param key - the owner's dockkit identity.
|
|
100
|
+
* @param root - absolute path of the root the memory belongs to.
|
|
101
|
+
*/
|
|
102
|
+
readonly saved: (key: TabId, root: string) => FilesTreeSaved | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* Merge memory for (key, snapshot.root): fields present in the snapshot
|
|
105
|
+
* overwrite, fields absent keep what was saved before — so a settle-time
|
|
106
|
+
* `revealed` write survives the close-time `expanded`/`scrollTop` write.
|
|
107
|
+
* @param key - the owner's dockkit identity.
|
|
108
|
+
* @param snapshot - the memory fields to record.
|
|
109
|
+
*/
|
|
110
|
+
readonly save: (key: TabId, snapshot: FilesTreeSaved) => void;
|
|
70
111
|
}
|
|
71
112
|
/** The inject face the registration composes onto the occupant's props. */
|
|
72
113
|
export interface FilesTreeInjected {
|
|
@@ -74,6 +115,14 @@ export interface FilesTreeInjected {
|
|
|
74
115
|
readonly face: FilesInjected;
|
|
75
116
|
/** Open one `dsh-resource://file` address in the host viewer. */
|
|
76
117
|
readonly openFile: (address: string) => void;
|
|
118
|
+
/**
|
|
119
|
+
* Open one `dsh-resource://file` address replacing the given tab — the
|
|
120
|
+
* document-preview navigator's open route, where the native navigator's
|
|
121
|
+
* `onOpen` is `tab.actions.openResource(address, { replaceTab: true })`.
|
|
122
|
+
* @param tabId - the preview tab the opened file replaces.
|
|
123
|
+
* @param address - the file's `dsh-resource://file` address.
|
|
124
|
+
*/
|
|
125
|
+
readonly openFileIn: (tabId: TabId, address: string) => void;
|
|
77
126
|
/**
|
|
78
127
|
* The reserved inject compartment: the slot renderer binds each source to a
|
|
79
128
|
* `use<Name>` selector hook, so `treeSelection` reaches the component as
|
|
@@ -99,6 +148,8 @@ export interface FilesTreeSurface {
|
|
|
99
148
|
readonly actions: FilesTreeProps['actions'];
|
|
100
149
|
readonly face: FilesInjected;
|
|
101
150
|
readonly openFile: (address: string) => void;
|
|
151
|
+
/** The preview navigator's replace-tab open route; see {@link FilesTreeInjected.openFileIn}. */
|
|
152
|
+
readonly openFileIn: FilesTreeInjected['openFileIn'];
|
|
102
153
|
readonly t: FilesTreeProps['t'];
|
|
103
154
|
/** Selector hook over this session's per-tab root selections. */
|
|
104
155
|
readonly useTreeSelection: SnapshotSelectorHook<TreeSelection>;
|
|
@@ -123,9 +174,10 @@ export declare function lazyFilesRemote(ctx: ClientContext): ClientRemote;
|
|
|
123
174
|
/**
|
|
124
175
|
* The registration's `store`/`inject` declarations, built once in `apply`.
|
|
125
176
|
*
|
|
126
|
-
* The inject face nests the
|
|
127
|
-
* session-bound `openFile
|
|
128
|
-
*
|
|
177
|
+
* The inject face nests the files face under `face` and adds the
|
|
178
|
+
* session-bound `openFile`; the fork's call sites stay upstream-shaped, the
|
|
179
|
+
* one lifecycle delta being the hold/release pair the preview navigator needs
|
|
180
|
+
* (see FORK.md).
|
|
129
181
|
* @param ctx - the client root context (the remote/sidebarRight lookups).
|
|
130
182
|
* @returns the store handle and the inject factory for `register`.
|
|
131
183
|
*/
|
|
@@ -148,6 +200,7 @@ export declare function createFilesTreeSurface(props: {
|
|
|
148
200
|
readonly actions: FilesTreeSurface['actions'];
|
|
149
201
|
readonly face: FilesInjected | undefined;
|
|
150
202
|
readonly openFile: ((address: string) => void) | undefined;
|
|
203
|
+
readonly openFileIn: FilesTreeInjected['openFileIn'] | undefined;
|
|
151
204
|
readonly useTreeSelection: FilesTreeSurface['useTreeSelection'] | undefined;
|
|
152
205
|
readonly selectRoot: FilesTreeSurface['selectRoot'] | undefined;
|
|
153
206
|
readonly takeover: FilesTreeSurface['takeover'] | undefined;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The tree's asynchronous half: listing directories into the store.
|
|
3
3
|
*
|
|
4
|
-
* The component never awaits anything. It calls `
|
|
4
|
+
* The component never awaits anything. It calls `hold` / `release` / `refresh` / `toggle`, and
|
|
5
5
|
* this face performs the listing and writes the outcome through the store's own
|
|
6
6
|
* actions — the Slot-standard `inject` shape, so the session id is resolved by
|
|
7
7
|
* the framework and the write set stays the store's.
|
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
* `signal`: a request is not made for a record that already ended, and when the
|
|
18
18
|
* record goes away the bucket and the tab's listing bookkeeping are forgotten,
|
|
19
19
|
* so no later settlement writes to it.
|
|
20
|
+
*
|
|
21
|
+
* A hold can end without its record ending: `release` closes the hold's
|
|
22
|
+
* directory node and watches but keeps the listed levels and expansion, and
|
|
23
|
+
* the next `hold` reopens them. That is upstream's `hold(paneId, cwd)` —
|
|
24
|
+
* closing a navigator is not closing its pane, so reopening draws the kept
|
|
25
|
+
* rows instead of falling back to a loading row.
|
|
20
26
|
*/
|
|
21
27
|
import type { ClientRemote, RemoteResult } from '@deepseek-ai/dsh-api-remotes/client';
|
|
22
28
|
import type { BoundActions } from '@deepseek-ai/dsh-client-store';
|
|
@@ -75,12 +81,35 @@ export interface FilesInjected {
|
|
|
75
81
|
/** Control automatic rereads without closing watches. @param tabId - owning tab. @param enabled - automatic-refresh setting. */
|
|
76
82
|
readonly setAutoRefresh: (tabId: TabId, enabled: boolean) => void;
|
|
77
83
|
/**
|
|
78
|
-
* Seed this tab's tree and list its root.
|
|
84
|
+
* Seed this tab's tree and list its root. This is the record's first open:
|
|
85
|
+
* it always reseeds the store at `root`, so use {@link hold} for later holds.
|
|
79
86
|
* @param tabId - the tab being drawn.
|
|
80
87
|
* @param root - absolute path of the workspace root.
|
|
81
88
|
* @param signal - the tab record's lifetime.
|
|
82
89
|
*/
|
|
83
90
|
readonly start: (tabId: TabId, root: string, signal: AbortSignal) => void;
|
|
91
|
+
/**
|
|
92
|
+
* Open one hold on this tab's tree and list its root.
|
|
93
|
+
*
|
|
94
|
+
* The store bucket is reused when it already belongs to `root`; its listed
|
|
95
|
+
* levels stay drawn while the reopened node's listings settle, so a navigator
|
|
96
|
+
* closed and reopened by the reader never flashes a loading row over rows it
|
|
97
|
+
* already had. A different root reseeds the bucket, exactly like `start`.
|
|
98
|
+
* @param tabId - the tab being drawn.
|
|
99
|
+
* @param root - absolute path of the workspace root.
|
|
100
|
+
* @param signal - the tab record's lifetime; aborting it still forgets the
|
|
101
|
+
* bucket and closes the node.
|
|
102
|
+
* @param restore - expanded directories to reopen as their parent listing
|
|
103
|
+
* returns (the kept tree's expansion preferences).
|
|
104
|
+
*/
|
|
105
|
+
readonly hold: (tabId: TabId, root: string, signal: AbortSignal, restore?: readonly string[]) => void;
|
|
106
|
+
/**
|
|
107
|
+
* End one hold: close its directory node and watches while keeping the store
|
|
108
|
+
* bucket and expansion for the next `hold`. The record's `signal` still owns
|
|
109
|
+
* the bucket; aborting it forgets the tree.
|
|
110
|
+
* @param tabId - the tab whose hold ends.
|
|
111
|
+
*/
|
|
112
|
+
readonly release: (tabId: TabId) => void;
|
|
84
113
|
/**
|
|
85
114
|
* List one directory into the store.
|
|
86
115
|
* @param tabId - the tab being drawn.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path-location mechanics for the preview navigator's takeover and reveal,
|
|
3
|
+
* ported from upstream's `ui-sidebar-files` navigator internals
|
|
4
|
+
* (`0.2.2-alpha.3`; the package build is byte-identical at `0.2.2-rc.1`):
|
|
5
|
+
* the same `windowsRoot`, `underRoot`, and `locateFile` the native
|
|
6
|
+
* FileNavigator runs against its pane tree, spelled against this fork's
|
|
7
|
+
* `FilesTabState` — the store shape is the same (`root`, `levels` keyed by
|
|
8
|
+
* absolute path, `expanded`), only the `refreshing` flag is absent, so a
|
|
9
|
+
* stale level reports `pending: false` exactly like a settled native one.
|
|
10
|
+
*
|
|
11
|
+
* Everything here is a port or plugin-owned logic on top of it; nothing in
|
|
12
|
+
* this file needs upstream sync beyond the documented provenance.
|
|
13
|
+
*
|
|
14
|
+
* @module @dsh-electron/dsh-plugin-multi-root-workspace/client/files-tree/locate
|
|
15
|
+
*/
|
|
16
|
+
import type { RootView, RootsView } from '../../contract.ts';
|
|
17
|
+
import type { FilesTabState } from './store.ts';
|
|
18
|
+
/**
|
|
19
|
+
* Whether a root is a Windows drive or UNC path, in either separator
|
|
20
|
+
* spelling, where names compare regardless of case.
|
|
21
|
+
* @param root - the tree's root, or a prefix built from it.
|
|
22
|
+
* @returns `true` for a drive letter followed by a separator, or a leading
|
|
23
|
+
* double separator.
|
|
24
|
+
*/
|
|
25
|
+
export declare function windowsRoot(root: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* An absolute `/`-separated path relative to a tree's root.
|
|
28
|
+
* @param path - the absolute path, in either separator spelling.
|
|
29
|
+
* @param root - the tree's root, in either separator spelling.
|
|
30
|
+
* @returns the path under the root, or `undefined` outside it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function underRoot(path: string, root: string): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* The workspace-relative path of a file under a tree's root, validated the
|
|
35
|
+
* way upstream's `relativeFileOf` tail validates it: nothing absolute, and
|
|
36
|
+
* no empty or dot segments.
|
|
37
|
+
* @param path - the file's absolute path, in either separator spelling.
|
|
38
|
+
* @param root - the root it must sit under.
|
|
39
|
+
* @returns the `/`-separated relative path, or `undefined` outside the root.
|
|
40
|
+
*/
|
|
41
|
+
export declare function relativeUnderRoot(path: string, root: string): string | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* The additional root that owns one absolute file path: available roots
|
|
44
|
+
* only, the longest matching root path winning when registrations nest.
|
|
45
|
+
* @param view - the session's registry view.
|
|
46
|
+
* @param path - the file's absolute path, in either separator spelling.
|
|
47
|
+
* @returns the owning root, or `undefined` when the file is outside every
|
|
48
|
+
* available additional root (the primary root included).
|
|
49
|
+
*/
|
|
50
|
+
export declare function rootForPath(view: RootsView, path: string): RootView | undefined;
|
|
51
|
+
/** What walking the listed levels toward a workspace-relative file found. */
|
|
52
|
+
export interface FileLocation {
|
|
53
|
+
/** The directories verified so far, each named by its listing's spelling. */
|
|
54
|
+
readonly ancestors: readonly {
|
|
55
|
+
readonly parent: string;
|
|
56
|
+
readonly path: string;
|
|
57
|
+
}[];
|
|
58
|
+
/** The file's tree key once its row exists, `undefined` until or unless it does. */
|
|
59
|
+
readonly current: string | undefined;
|
|
60
|
+
/** Whether a level on the way is still pending its listing. */
|
|
61
|
+
readonly pending: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Walk the listed levels to a workspace-relative file, keeping the listing's
|
|
65
|
+
* spelling of each name; a Windows root also matches names regardless of
|
|
66
|
+
* case. A verbatim port of upstream's navigator helper — the fork's
|
|
67
|
+
* `LevelState` carries no `refreshing` flag, so a stale level reports
|
|
68
|
+
* `pending: false` like a settled native one.
|
|
69
|
+
* @param tree - the tab's tree.
|
|
70
|
+
* @param relativePath - the file's path under the tree's root, `/`-separated.
|
|
71
|
+
* @returns the ancestors found so far, the file's tree key, and whether a
|
|
72
|
+
* level is pending.
|
|
73
|
+
*/
|
|
74
|
+
export declare function locateFile(tree: Pick<FilesTabState, 'root' | 'levels'>, relativePath: string): FileLocation;
|