@dsh-electron/dsh-plugin-multi-root-workspace 0.1.6 → 0.1.8

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.
Files changed (95) hide show
  1. package/CHANGELOG.en.md +51 -1
  2. package/CHANGELOG.md +51 -1
  3. package/README.en.md +155 -92
  4. package/README.md +156 -91
  5. package/cordis.patch.yml +69 -4
  6. package/lib/client.js +2678 -276
  7. package/lib/client.js.map +1 -1
  8. package/lib/command-CESuwlVt.js +1306 -0
  9. package/lib/command.js +2 -2
  10. package/lib/compat-DR2LvmYZ.js +600 -0
  11. package/lib/compat.js +1 -1
  12. package/lib/contract-DZ4zczDT.js +238 -0
  13. package/lib/{fs-D_Sw3o5z.js → fs-Nlp2AiRX.js} +56 -24
  14. package/lib/fs.js +2 -1
  15. package/lib/index.js +10 -8
  16. package/lib/{instructions-DfzN-wVf.js → instructions-D19jRxN0.js} +3 -2
  17. package/lib/instructions.js +1 -1
  18. package/lib/lsp.js +7 -2
  19. package/lib/method-wrapper-CFY3p3j2.js +26 -0
  20. package/lib/process-fence.js +280 -0
  21. package/lib/providers.js +87 -0
  22. package/lib/{registry-DySMbjgW.js → registry-DCxC5Lay.js} +256 -35
  23. package/lib/registry.js +2 -2
  24. package/lib/{roots-DasWEBJk.js → roots-B-0l8mNG.js} +117 -20
  25. package/lib/sandbox-CVAcT_gy.js +743 -0
  26. package/lib/sandbox.js +2 -1
  27. package/lib/scope.js +229 -20
  28. package/lib/types/client/RootSwitcher.d.ts +63 -0
  29. package/lib/types/client/WorkspaceFoldersAction.d.ts +39 -1
  30. package/lib/types/client/files-tree/FilesBody.module.css.d.ts +24 -0
  31. package/lib/types/client/files-tree/FilesTree.d.ts +47 -0
  32. package/lib/types/client/files-tree/adapter.d.ts +94 -0
  33. package/lib/types/client/files-tree/directory-node.d.ts +62 -0
  34. package/lib/types/client/files-tree/face.d.ts +107 -0
  35. package/lib/types/client/files-tree/locales.d.ts +10 -0
  36. package/lib/types/client/files-tree/primitives.d.ts +33 -0
  37. package/lib/types/client/files-tree/store.d.ts +81 -0
  38. package/lib/types/client/locales.d.ts +56 -10
  39. package/lib/types/client/open-in-app/OpenInAppAction.d.ts +27 -0
  40. package/lib/types/client/open-in-app/OpenTargetButton.d.ts +52 -0
  41. package/lib/types/client/open-in-app/OpenTargetButton.module.css.d.ts +29 -0
  42. package/lib/types/client/open-in-app/adapter.d.ts +46 -0
  43. package/lib/types/client/open-in-app/applications.d.ts +4 -0
  44. package/lib/types/client/open-in-app/controller.d.ts +53 -0
  45. package/lib/types/client/open-in-app/locales.d.ts +10 -0
  46. package/lib/types/client/open-in-app/open-failure-toast.d.ts +11 -0
  47. package/lib/types/client/open-in-app/open-path.d.ts +13 -0
  48. package/lib/types/client/panel-client.d.ts +2 -1
  49. package/lib/types/client/styles.d.ts +1 -1
  50. package/lib/types/command.d.ts +16 -3
  51. package/lib/types/compat/client-icons.d.ts +18 -0
  52. package/lib/types/compat/dsh-version.d.ts +57 -9
  53. package/lib/types/compat/open-in-app-routes.d.ts +38 -0
  54. package/lib/types/compat/process-entrypoints.d.ts +31 -0
  55. package/lib/types/compat/process-result.d.ts +14 -0
  56. package/lib/types/compat/pty-marker.d.ts +83 -0
  57. package/lib/types/compat/sandbox-confine.d.ts +2 -0
  58. package/lib/types/compat/storage-zod.d.ts +65 -0
  59. package/lib/types/compat/windows-deny-preflight.d.ts +21 -0
  60. package/lib/types/compat/windows-native.d.ts +68 -0
  61. package/lib/types/compat/windows-path-guard.d.ts +56 -0
  62. package/lib/types/compat/workspace-files.d.ts +2 -0
  63. package/lib/types/compat.d.ts +15 -5
  64. package/lib/types/contract.d.ts +31 -19
  65. package/lib/types/dialects.d.ts +99 -3
  66. package/lib/types/fs.d.ts +32 -10
  67. package/lib/types/index.d.ts +8 -3
  68. package/lib/types/process-fence.d.ts +54 -0
  69. package/lib/types/profile-isolation.d.ts +178 -0
  70. package/lib/types/providers.d.ts +70 -0
  71. package/lib/types/registry.d.ts +132 -31
  72. package/lib/types/roots.d.ts +100 -13
  73. package/lib/types/sandbox.d.ts +59 -3
  74. package/lib/types/scope.d.ts +200 -14
  75. package/lib/types/windows-broker.d.ts +17 -0
  76. package/lib/types/windows-deny.d.ts +1 -0
  77. package/lib/types/windows-profile.d.ts +10 -13
  78. package/lib/types/windows-runner-contract.d.ts +97 -0
  79. package/lib/windows-runner-contract-CEfmZzu0.js +141 -0
  80. package/lib/windows-runner.js +1256 -13
  81. package/lib/workspace-files.js +138 -3
  82. package/lib/workspace-routing-YPxaQ4V3.js +26 -0
  83. package/package.json +84 -62
  84. package/scripts/windows-broker/install.ps1 +89 -0
  85. package/scripts/windows-broker/rights.cs +58 -0
  86. package/scripts/windows-broker/service.cs +323 -0
  87. package/scripts/windows-broker/verify-install.ps1 +80 -0
  88. package/lib/command-CTdNvwt1.js +0 -791
  89. package/lib/compat-B6ZqbQBy.js +0 -295
  90. package/lib/method-wrapper-Zc-JlDP3.js +0 -51
  91. package/lib/sandbox-BRXl_FpF.js +0 -132
  92. package/lib/types/client/RootFileTree.d.ts +0 -12
  93. package/lib/types/panel-files.d.ts +0 -3
  94. package/lib/windows-profile-CiFsFYIV.js +0 -216
  95. package/lib/{registry-lease-win32-dy8TQMD9.js → registry-lease-win32-DTO6QpT9.js} +2 -2
@@ -0,0 +1,62 @@
1
+ /** Watch and read ownership for the Files panel's expanded directory tree. */
2
+ import type { DirLevel } from './store.ts';
3
+ /** One open directory and its active child nodes; cached view preferences live in the store. */
4
+ export declare class DirectoryNode {
5
+ readonly path: string;
6
+ private readonly load;
7
+ private readonly watch;
8
+ private readonly failed;
9
+ private restore;
10
+ /** Open direct-child directories, keyed by absolute path. */
11
+ readonly children: Map<string, DirectoryNode>;
12
+ private readonly controller;
13
+ private readonly signal;
14
+ private task;
15
+ private reading;
16
+ private dirty;
17
+ private initialized;
18
+ private automatic;
19
+ constructor(path: string, load: (path: string, signal: AbortSignal) => Promise<DirLevel | undefined>, watch: (path: string, signal: AbortSignal) => AsyncIterable<'ready' | 'change'>, failed: (path: string, error: unknown) => void, lifetime: AbortSignal, restore?: readonly string[]);
20
+ /**
21
+ * Start observation once; readiness triggers the initial listing.
22
+ * @returns this node.
23
+ */
24
+ open(): this;
25
+ /**
26
+ * Find an active node in this subtree.
27
+ * @param path - absolute directory path.
28
+ * @returns the matching node, or undefined when that directory is closed.
29
+ */
30
+ find(path: string): DirectoryNode | undefined;
31
+ /**
32
+ * Update the expansion preferences used by pending directory listings.
33
+ * @param expanded - latest expansion preferences from the store.
34
+ */
35
+ setExpanded(expanded: readonly string[]): void;
36
+ /**
37
+ * Open a direct child using this node's lifetime and automatic-refresh setting.
38
+ * @param path - absolute direct-child directory path.
39
+ * @param restore - descendant expansion preferences to restore after listing.
40
+ * @returns the active child, or undefined after cancellation.
41
+ */
42
+ expand(path: string, restore?: readonly string[]): DirectoryNode | undefined;
43
+ /**
44
+ * Remove a child and its pending restoration preferences.
45
+ * @param path - absolute direct-child directory path.
46
+ * @returns once the child subtree's reads and watches have ended.
47
+ */
48
+ collapse(path: string): Promise<void>;
49
+ /**
50
+ * Control subtree rereads without ending subscriptions.
51
+ * @param enabled - refresh dirty nodes automatically.
52
+ */
53
+ setAutomatic(enabled: boolean): void;
54
+ /** Queue a reread, coalescing with active work. @returns completion of the active read and its coalesced rereads. */
55
+ refresh(): Promise<void>;
56
+ /** Refresh this node and its open descendants. @returns once their listings settle. */
57
+ refreshTree(): Promise<void>;
58
+ /** Cancel the active subtree. @returns once all owned reads and watches have ended. */
59
+ close(): Promise<void>;
60
+ private follow;
61
+ private read;
62
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The tree's asynchronous half: listing directories into the store.
3
+ *
4
+ * The component never awaits anything. It calls `start` / `refresh` / `toggle`, and
5
+ * this face performs the listing and writes the outcome through the store's own
6
+ * actions — the Slot-standard `inject` shape, so the session id is resolved by
7
+ * the framework and the write set stays the store's.
8
+ *
9
+ * The listing itself is bound here to the Client Remote face: the tree keys
10
+ * every level by absolute path and hands the endpoint that same absolute path;
11
+ * the endpoint answers with the directory's workspace-relative path as well,
12
+ * which the tree has no use for and drops.
13
+ *
14
+ * One level has one listing in force: asking for a level again — the reload
15
+ * gesture, a directory reopened after a reset — retires the listing still in
16
+ * flight for it, whose settlement then writes nothing. Cleanup rides the owner's
17
+ * `signal`: a request is not made for a record that already ended, and when the
18
+ * record goes away the bucket and the tab's listing bookkeeping are forgotten,
19
+ * so no later settlement writes to it.
20
+ */
21
+ import type { ClientRemote, RemoteResult } from '@deepseek-ai/dsh-api-remotes/client';
22
+ import type { BoundActions } from '@deepseek-ai/dsh-client-store';
23
+ import type { TabId } from '@deepseek-ai/dsh-client-ui-dockkit';
24
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
25
+ import type { DirLevel, createFilesStore } from './store.ts';
26
+ /**
27
+ * Observe one directory without recursively watching its descendants.
28
+ * @param sessionId - Session owning the directory tree.
29
+ * @param path - absolute directory path.
30
+ * @param signal - node lifetime.
31
+ * @returns readiness and invalidation notifications.
32
+ */
33
+ export type WatchWorkspaceDirectory = (sessionId: SessionId, path: string, signal: AbortSignal) => AsyncIterable<'ready' | 'change'>;
34
+ /**
35
+ * Bind directory observation to the Remote stream supervisor.
36
+ * @param remote - Client Remote with workspace file streams.
37
+ * @returns a watcher that awaits stream disposal when its node ends.
38
+ */
39
+ export declare function createWatch(remote: ClientRemote): WatchWorkspaceDirectory;
40
+ /**
41
+ * One directory listing, bound to a Remote face.
42
+ *
43
+ * The session travels with the call because the endpoint resolves the workspace
44
+ * root from it: the same path means different directories in different sessions.
45
+ * A Remote call does not reject — the result carries the failure.
46
+ */
47
+ export type ListWorkspaceDirectory = (sessionId: SessionId, path: string, signal: AbortSignal) => Promise<RemoteResult<DirLevel>>;
48
+ /**
49
+ * The slice of the Client Remote face this package calls: the `workspaceFiles`
50
+ * namespace's `list`, exactly as the Host's generated client declares it.
51
+ */
52
+ export type WorkspaceFilesListRemote = {
53
+ readonly workspaceFiles: Pick<ClientRemote['workspaceFiles'], 'list'>;
54
+ };
55
+ /**
56
+ * Bind the listing to one Remote face, keeping only what the tree stores.
57
+ * @param remote - the Client Remote face carrying the `workspaceFiles` namespace.
58
+ * @returns the listing the tree's face performs.
59
+ */
60
+ export declare function createList(remote: WorkspaceFilesListRemote): ListWorkspaceDirectory;
61
+ /**
62
+ * The absolute path of one child entry.
63
+ *
64
+ * Joined with `/` whatever the parent's separators: the Host resolves mixed
65
+ * separators, and the tree only needs a stable key.
66
+ * @param parent - absolute path of the listed directory.
67
+ * @param name - the entry's basename.
68
+ * @returns the child's absolute path.
69
+ */
70
+ export declare function childPath(parent: string, name: string): string;
71
+ /** The tree's injected business face, as the body receives it. */
72
+ export interface FilesInjected {
73
+ /** Refresh the open directory tree. @param tabId - owning tab. */
74
+ readonly refresh: (tabId: TabId) => void;
75
+ /** Control automatic rereads without closing watches. @param tabId - owning tab. @param enabled - automatic-refresh setting. */
76
+ readonly setAutoRefresh: (tabId: TabId, enabled: boolean) => void;
77
+ /**
78
+ * Seed this tab's tree and list its root.
79
+ * @param tabId - the tab being drawn.
80
+ * @param root - absolute path of the workspace root.
81
+ * @param signal - the tab record's lifetime.
82
+ */
83
+ readonly start: (tabId: TabId, root: string, signal: AbortSignal) => void;
84
+ /**
85
+ * List one directory into the store.
86
+ * @param tabId - the tab being drawn.
87
+ * @param path - absolute directory path.
88
+ * @param signal - the tab record's lifetime.
89
+ */
90
+ readonly load: (tabId: TabId, path: string, signal: AbortSignal) => void;
91
+ /**
92
+ * Open or collapse one directory, retaining intent during ancestor restoration.
93
+ * @param tabId - the tab being drawn.
94
+ * @param parentPath - the listed parent directory's exact tree key.
95
+ * @param path - absolute directory path.
96
+ * @param expanded - current expansion preferences, including descendants to restore.
97
+ * @param signal - the tab record's lifetime.
98
+ */
99
+ readonly toggle: (tabId: TabId, parentPath: string, path: string, expanded: readonly string[], signal: AbortSignal) => void;
100
+ }
101
+ /**
102
+ * Bind the tree's face to one directory listing.
103
+ * @param list - the bound `workspaceFiles.list` call.
104
+ * @param watch - target-scoped directory observation.
105
+ * @returns the Slot `inject` factory: session and bound actions in, face out.
106
+ */
107
+ export declare function filesFace(list: ListWorkspaceDirectory, watch: WatchWorkspaceDirectory): (sessionId: SessionId, actions: BoundActions<ReturnType<typeof createFilesStore>>) => FilesInjected;
@@ -0,0 +1,10 @@
1
+ /** Dictionary namespace owned by upstream's plugin (verbatim). */
2
+ export declare const NS = "sidebarFiles";
3
+ /** Key domain of the `sidebarFiles` namespace (verbatim union of upstream's zh keys). */
4
+ export type SidebarFilesKey = 'shortcut.noSession' | 'type.label' | 'guide.title' | 'guide.description' | 'loading' | 'empty' | 'truncated' | 'noWorkspace' | 'reload' | 'autoRefresh' | 'autoRefresh.enable' | 'autoRefresh.disable' | 'entry.other' | 'error.notFound' | 'error.outsideWorkspace' | 'error.notDirectory' | 'error.unavailable';
5
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
6
+ interface LocaleNamespaceMap {
7
+ /** File-tree type name, guide entry, row states, and failure lines. */
8
+ sidebarFiles: SidebarFilesKey;
9
+ }
10
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Runtime probes for the `@deepseek-ai/dsh-client-ui-primitives` exports the
3
+ * verbatim Files-tree files spell.
4
+ *
5
+ * The upstream imports name the `*Regular` glyph family (0.1.7+; the pixel
6
+ * names are kept as fallbacks for the same reason `compat/client-icons`
7
+ * dual-names them) plus `FileTypeIcon` and `classifyFileType`, which every
8
+ * supported release carries. A named import cannot spell two release eras at
9
+ * once, so the resolution goes through `maybeIcon`/`maybeComponent` — the
10
+ * same probe the open-in-app fork uses.
11
+ *
12
+ * The exported bindings are typed definite because the adapter mounts the
13
+ * tree only after {@link filesTreeSurfaceSupported} passes; reaching for a
14
+ * binding on a composition that failed the gate is a caller bug, and the
15
+ * gate — not this module — is what a missing export degrades into.
16
+ *
17
+ * @module @dsh-electron/dsh-plugin-multi-root-workspace/client/files-tree/primitives
18
+ */
19
+ import { type PanelIcon } from '../../compat/client-icons.ts';
20
+ /** The upstream types the probed exports spell, kept honest by the type-only import. */
21
+ type Primitives = typeof import('@deepseek-ai/dsh-client-ui-primitives');
22
+ /**
23
+ * Whether this composition's primitives carry everything the forked rows
24
+ * spell. The adapter consults this once: `false` means the additional-root
25
+ * menu stays menu-only and no fork mount is attempted — the same absence
26
+ * upstream's own no-such-export state would produce.
27
+ */
28
+ export declare function filesTreeSurfaceSupported(): boolean;
29
+ export declare const IconFolderCloseRegular: PanelIcon;
30
+ export declare const IconFolderOpenRegular: PanelIcon;
31
+ export declare const FileTypeIcon: Primitives["FileTypeIcon"];
32
+ export declare const classifyFileType: Primitives["classifyFileType"];
33
+ export {};
@@ -0,0 +1,81 @@
1
+ /**
2
+ * The file tree's view state: which directories are expanded, what each
3
+ * loaded level contains, and where the body is scrolled to.
4
+ *
5
+ * The tree is not one resource. A directory listing per level, expanded lazily,
6
+ * is state the type owns — so it lives in a Slot-standard exclusive store
7
+ * (one instance per session), bucketed by tab id because two tabs of this kind
8
+ * in one session expand independently.
9
+ *
10
+ * Writers run between `start` and `forget`: the owner's `signal` is what ends a
11
+ * bucket's life, and the face stops dispatching once it aborts.
12
+ */
13
+ import { type EngineStoreHandle } from '@deepseek-ai/dsh-client-store';
14
+ import type { RemoteFailure } from '@deepseek-ai/dsh-api-remotes/client';
15
+ import type { TabId } from '@deepseek-ai/dsh-client-ui-dockkit';
16
+ import type { WorkspaceDirectoryEntry } from '@deepseek-ai/dsh-api-workspace-files/types';
17
+ /**
18
+ * One directory's contents, as one expanded level of the tree.
19
+ *
20
+ * The endpoint's listing also names the directory as a workspace-relative path;
21
+ * the tree keys every level by absolute path instead, so the adapter drops it.
22
+ */
23
+ export interface DirLevel {
24
+ /** The directory's entries, in the endpoint's order. */
25
+ readonly entries: readonly WorkspaceDirectoryEntry[];
26
+ /** The listing hit the endpoint's entry cap, so entries are missing. */
27
+ readonly truncated: boolean;
28
+ }
29
+ /** What one directory level is doing right now. */
30
+ export type LevelState = {
31
+ readonly kind: 'loading';
32
+ } | {
33
+ readonly kind: 'ready';
34
+ readonly level: DirLevel;
35
+ readonly failure?: RemoteFailure;
36
+ } | {
37
+ readonly kind: 'failed';
38
+ readonly failure: RemoteFailure;
39
+ };
40
+ /**
41
+ * One tab's tree: its root, the levels it has asked for, and what is open.
42
+ *
43
+ * Every path here is absolute: the root is the session's working directory as
44
+ * the Host reports it, and a child is the parent joined with the entry name.
45
+ */
46
+ export interface FilesTabState {
47
+ autoRefresh: boolean;
48
+ /** Absolute path of the workspace root this tree is rooted at. */
49
+ root: string;
50
+ /** Level state by absolute directory path; a path absent here was never asked for. */
51
+ levels: Record<string, LevelState>;
52
+ /** Expanded absolute directory paths, root included. */
53
+ expanded: string[];
54
+ /** The body's scroll offset in px, so a remounted tree comes back where the reader was. */
55
+ scrollTop: number;
56
+ }
57
+ /** Every tab's tree, keyed by tab id. */
58
+ export interface FilesState {
59
+ byTab: Record<TabId, FilesTabState>;
60
+ }
61
+ /** The tree store's write set; every action names the tab it writes. */
62
+ type FilesActions = {
63
+ autoRefresh: (draft: FilesState, tabId: TabId, enabled: boolean) => void;
64
+ start: (draft: FilesState, tabId: TabId, root: string) => void;
65
+ loading: (draft: FilesState, tabId: TabId, path: string) => void;
66
+ loaded: (draft: FilesState, tabId: TabId, path: string, level: DirLevel) => void;
67
+ failed: (draft: FilesState, tabId: TabId, path: string, failure: RemoteFailure) => void;
68
+ toggled: (draft: FilesState, tabId: TabId, path: string) => void;
69
+ scrolled: (draft: FilesState, tabId: TabId, scrollTop: number) => void;
70
+ reset: (draft: FilesState, tabId: TabId) => void;
71
+ forget: (draft: FilesState, tabId: TabId) => void;
72
+ };
73
+ /**
74
+ * Declare the file tree's store.
75
+ *
76
+ * A factory rather than a shared handle: the registration declares it as an
77
+ * exclusive store, so the framework mints one instance per session.
78
+ * @returns the store handle to declare on the registration.
79
+ */
80
+ export declare function createFilesStore(): EngineStoreHandle<FilesState, FilesActions>;
81
+ export {};
@@ -12,13 +12,20 @@
12
12
  export declare const NS = "multiRootWorkspace";
13
13
  /** Chinese copy (the canonical shape). */
14
14
  export declare const zh: {
15
- 'files.browse': string;
16
- 'files.reload': string;
17
- 'files.empty': string;
18
- 'files.truncated': string;
19
- 'files.previewTruncated': string;
15
+ 'panel.mode': string;
16
+ 'panel.effectiveAccess': string;
17
+ 'panel.toolCaps': string;
18
+ 'panel.invalidCap': string;
19
+ 'mode.read-only': string;
20
+ 'mode.workspace-write': string;
21
+ 'mode.danger-full-access': string;
22
+ 'effective.read-only': string;
23
+ 'effective.read-write': string;
24
+ 'effective.full-access': string;
25
+ 'effective.unavailable': string;
20
26
  'action.label': string;
21
27
  'action.title': string;
28
+ 'switcher.manage': string;
22
29
  'panel.title': string;
23
30
  'panel.subtitle': string;
24
31
  'panel.primary': string;
@@ -26,6 +33,7 @@ export declare const zh: {
26
33
  'panel.cwd': string;
27
34
  'panel.cwdNote': string;
28
35
  'panel.additional': string;
36
+ 'panel.limitReached': string;
29
37
  'panel.empty': string;
30
38
  'panel.emptyHint': string;
31
39
  'panel.noSession': string;
@@ -45,8 +53,19 @@ export declare const zh: {
45
53
  'panel.copyPath': string;
46
54
  'panel.copied': string;
47
55
  'panel.reveal': string;
56
+ 'panel.reRegister': string;
48
57
  'panel.moveUp': string;
49
58
  'panel.moveDown': string;
59
+ 'access.inherit': string;
60
+ 'access.readWrite': string;
61
+ 'access.readOnly': string;
62
+ 'panel.accessLabel': string;
63
+ 'panel.accessInherit': string;
64
+ 'panel.accessReadOnly': string;
65
+ 'panel.accessReadWrite': string;
66
+ 'panel.accessUpgradeConfirm': string;
67
+ 'panel.accessUpgradeTitle': string;
68
+ 'panel.accessUpgradeDo': string;
50
69
  'state.available': string;
51
70
  'state.missing': string;
52
71
  'state.redirected': string;
@@ -63,23 +82,34 @@ export declare const zh: {
63
82
  'error.invalid-ref': string;
64
83
  'error.storage-unavailable': string;
65
84
  'error.registry-contended': string;
85
+ 'error.root-limit-reached': string;
86
+ 'error.config-invalid': string;
87
+ 'error.root-unavailable': string;
66
88
  'error.reveal-unavailable': string;
67
89
  'error.unavailable': string;
68
90
  'error.copy-failed': string;
69
91
  'error.session-not-found': string;
92
+ 'error.unsupported-path': string;
70
93
  'error.fallback': string;
71
94
  };
72
95
  /** The canonical key set. */
73
96
  export type Key = keyof typeof zh;
74
97
  /** English copy, pinned to the Chinese key set. */
75
98
  export declare const en: {
76
- 'files.browse': string;
77
- 'files.reload': string;
78
- 'files.empty': string;
79
- 'files.truncated': string;
80
- 'files.previewTruncated': string;
99
+ 'panel.mode': string;
100
+ 'panel.effectiveAccess': string;
101
+ 'panel.toolCaps': string;
102
+ 'panel.invalidCap': string;
103
+ 'mode.read-only': string;
104
+ 'mode.workspace-write': string;
105
+ 'mode.danger-full-access': string;
106
+ 'effective.read-only': string;
107
+ 'effective.read-write': string;
108
+ 'effective.full-access': string;
109
+ 'effective.unavailable': string;
81
110
  'action.label': string;
82
111
  'action.title': string;
112
+ 'switcher.manage': string;
83
113
  'panel.title': string;
84
114
  'panel.subtitle': string;
85
115
  'panel.primary': string;
@@ -87,6 +117,7 @@ export declare const en: {
87
117
  'panel.cwd': string;
88
118
  'panel.cwdNote': string;
89
119
  'panel.additional': string;
120
+ 'panel.limitReached': string;
90
121
  'panel.empty': string;
91
122
  'panel.emptyHint': string;
92
123
  'panel.noSession': string;
@@ -106,8 +137,19 @@ export declare const en: {
106
137
  'panel.copyPath': string;
107
138
  'panel.copied': string;
108
139
  'panel.reveal': string;
140
+ 'panel.reRegister': string;
109
141
  'panel.moveUp': string;
110
142
  'panel.moveDown': string;
143
+ 'access.inherit': string;
144
+ 'access.readWrite': string;
145
+ 'access.readOnly': string;
146
+ 'panel.accessLabel': string;
147
+ 'panel.accessInherit': string;
148
+ 'panel.accessReadOnly': string;
149
+ 'panel.accessReadWrite': string;
150
+ 'panel.accessUpgradeConfirm': string;
151
+ 'panel.accessUpgradeTitle': string;
152
+ 'panel.accessUpgradeDo': string;
111
153
  'state.available': string;
112
154
  'state.missing': string;
113
155
  'state.redirected': string;
@@ -124,10 +166,14 @@ export declare const en: {
124
166
  'error.invalid-ref': string;
125
167
  'error.storage-unavailable': string;
126
168
  'error.registry-contended': string;
169
+ 'error.root-limit-reached': string;
170
+ 'error.config-invalid': string;
171
+ 'error.root-unavailable': string;
127
172
  'error.reveal-unavailable': string;
128
173
  'error.unavailable': string;
129
174
  'error.copy-failed': string;
130
175
  'error.session-not-found': string;
176
+ 'error.unsupported-path': string;
131
177
  'error.fallback': string;
132
178
  };
133
179
  declare module '@deepseek-ai/dsh-client-ui-slots' {
@@ -0,0 +1,27 @@
1
+ import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
2
+ import type { InjectFace, PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
3
+ import type { OpenInAppLaunchState } from './controller.ts';
4
+ import type { ShortcutCatalogEntry } from '@deepseek-ai/dsh-client-shortcuts/client';
5
+ import { NS } from './locales.ts';
6
+ /** Browser operations and state shared by the Session header and file tree contributions. */
7
+ export interface OpenInAppActionInjected {
8
+ hooks: {
9
+ openInAppApps: ObservableSnapshot<readonly string[] | null>;
10
+ openInAppChoice: ObservableSnapshot<string>;
11
+ openInAppLaunch: ObservableSnapshot<OpenInAppLaunchState>;
12
+ shortcuts: ObservableSnapshot<readonly ShortcutCatalogEntry[]>;
13
+ };
14
+ launch: (appId: string, path: string) => Promise<void>;
15
+ choose: (appId: string) => void;
16
+ iconUrl: (appId: string) => string;
17
+ }
18
+ /** Directory path, installed applications, and launch operations for the split button. */
19
+ export type OpenInAppActionProps = PropsLocale<typeof NS> & InjectFace<OpenInAppActionInjected> & {
20
+ absolutePath: string;
21
+ };
22
+ /**
23
+ * Adapt the installed directory catalog to the shared opening control.
24
+ * @param props - displayed directory, installed catalog, and launch operations.
25
+ * @returns the shared control, or null without an eligible application.
26
+ */
27
+ export declare function OpenInAppAction(props: OpenInAppActionProps): React.JSX.Element | null;
@@ -0,0 +1,52 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { ShortcutCatalogEntry } from '@deepseek-ai/dsh-client-shortcuts/client';
3
+ import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
4
+ import type { OpenInAppPathFailure } from './open-path.ts';
5
+ import type { NS } from './locales.ts';
6
+ /** Application metadata supplied by either the directory catalog or a file association query. */
7
+ export interface OpenTargetApplication {
8
+ readonly id: string;
9
+ readonly name: string;
10
+ readonly icon: string | null;
11
+ }
12
+ /** Opening intent; default selection remains owned by the target's adapter. */
13
+ export type OpenTargetOperation = {
14
+ readonly kind: 'default';
15
+ } | {
16
+ readonly kind: 'application';
17
+ readonly id: string;
18
+ } | {
19
+ readonly kind: 'reveal';
20
+ };
21
+ /** Inputs shared by both target adapters and the file empty-state action. */
22
+ export interface OpenTargetButtonProps {
23
+ readonly shortcut?: Pick<ShortcutCatalogEntry, 'keys' | 'aria'> | undefined;
24
+ readonly kind: 'file' | 'directory';
25
+ readonly applications: readonly OpenTargetApplication[];
26
+ readonly defaultId: string | undefined;
27
+ readonly failed: boolean;
28
+ readonly busy?: boolean;
29
+ readonly loading?: boolean;
30
+ readonly prominent?: boolean;
31
+ readonly t: TranslateNS<typeof NS>;
32
+ readonly execute: (operation: OpenTargetOperation) => Promise<OpenInAppPathFailure | null>;
33
+ readonly refresh?: () => void;
34
+ }
35
+ /**
36
+ * Serialize gestures and announce their failures through the initiating control's toast.
37
+ * @param execute - target adapter that returns the failure to announce, or null.
38
+ * @param t - localized control copy.
39
+ * @returns the pending state, feedback, and guarded action callback.
40
+ */
41
+ export declare function useOpenTargetGesture(execute: OpenTargetButtonProps['execute'], t: TranslateNS<typeof NS>): {
42
+ pending: boolean;
43
+ toast: ReactNode;
44
+ act: (operation: OpenTargetOperation) => void;
45
+ };
46
+ /**
47
+ * Render identical split buttons for files and directories. File reveal always
48
+ * stays last; it is the default only when no application is registered.
49
+ * @param props - target applications, default selection, and operations.
50
+ * @returns the control and its transient failure feedback.
51
+ */
52
+ export declare function OpenTargetButton(props: OpenTargetButtonProps): ReactNode;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Fork-facing shim for upstream `OpenTargetButton.module.css`.
3
+ *
4
+ * This plugin's bundle has no CSS-module pipeline: the `<style>` element it
5
+ * installs carries one flat sheet (`src/client/styles.ts`). The upstream
6
+ * component reads `css.<name>` so the forked file needs one importable
7
+ * module exporting that same key map — this file, reached through the
8
+ * verbatim `./OpenTargetButton.module.css.ts` specifier (the one sanctioned
9
+ * fork edit: the specifier gains the `.ts` this toolchain resolves
10
+ * everywhere — tsc, tsdown, vitest). The specifier text is spelled without
11
+ * its `import` clause on purpose: the packed-artifact check scans every
12
+ * bundle comment too, and quoting the full statement would read as a
13
+ * relative import the tarball does not carry.
14
+ *
15
+ * Each upstream local class maps to an `mrfw-open*` name so the copied
16
+ * rules cannot collide with host DOM; the rules themselves live in
17
+ * styles.ts verbatim apart from that rename. `data-open-target` and the
18
+ * other data attributes are literals in the component and stay byte-exact.
19
+ * See FORK.md.
20
+ */
21
+ declare const css: {
22
+ readonly menuAnchor: "mrfw-openMenuAnchor";
23
+ readonly split: "mrfw-openSplit";
24
+ readonly main: "mrfw-openMain";
25
+ readonly chevron: "mrfw-openChevron";
26
+ readonly appIcon: "mrfw-openAppIcon";
27
+ readonly skeleton: "mrfw-openSkeleton";
28
+ };
29
+ export default css;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The plugin-owned wiring around the verbatim open-in-app fork.
3
+ *
4
+ * Upstream builds the same props in its `apply` (`directoryInjected`): one
5
+ * controller per page, its SnapshotStores bound by the slot renderer into
6
+ * `use*` props, and a `t` bound to the `open-in-app` namespace. This module
7
+ * reproduces exactly that — minus slot machinery the fork never sees —
8
+ * plus the two degradations only a fork needs:
9
+ *
10
+ * - the glyph set the fork spells is the `*Regular` family (0.1.7+); a
11
+ * pixel-name runtime (0.1.5/0.1.6) leaves the adapter undefined, so the
12
+ * header simply keeps the native button, mirroring upstream's own
13
+ * "no apps → no button" absence;
14
+ * - `ctx.locale.bind('open-in-app')` consumes upstream's registered
15
+ * dictionary verbatim — same strings, upstream's updates included;
16
+ * - the icon route comes through `compat/open-in-app-routes`: the `*_ROUTE`
17
+ * spelling is another 0.1.7+ name, so the constant is probed by export
18
+ * name rather than imported (a pixel-era manifest lacks it entirely —
19
+ * a named import would fail the bundle's own build).
20
+ *
21
+ * Everything here is ours; nothing in this file needs upstream sync.
22
+ *
23
+ * @module @dsh-electron/dsh-plugin-multi-root-workspace/client/open-in-app/adapter
24
+ */
25
+ import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
26
+ import type { ShortcutCatalogEntry } from '@deepseek-ai/dsh-client-shortcuts/client';
27
+ import { OpenInAppAction, type OpenInAppActionProps } from './OpenInAppAction.tsx';
28
+ import { NS } from './locales.ts';
29
+ /**
30
+ * The props `OpenInAppAction` consumes, minus the per-render `absolutePath`
31
+ * the switcher supplies — the shape upstream's `directoryInjected` returns.
32
+ */
33
+ export type OpenInAppAdapter = Omit<OpenInAppActionProps, 'absolutePath'>;
34
+ /**
35
+ * Build the open-in-app props upstream hands its own registration.
36
+ * @param deps - the namespace-bound translate for `open-in-app`, and the
37
+ * composition's shortcuts catalog when it exists.
38
+ * @returns the adapter, or `undefined` when this runtime cannot render the
39
+ * fork (pixel-era icon names — the forked files would crash on missing
40
+ * exports, so absence mirrors upstream's own no-button path).
41
+ */
42
+ export declare function createOpenInAppAdapter(deps: {
43
+ readonly t: (key: string, params?: Record<string, unknown>) => string;
44
+ readonly shortcuts?: ObservableSnapshot<readonly ShortcutCatalogEntry[]> | undefined;
45
+ }): OpenInAppAdapter | undefined;
46
+ export { OpenInAppAction, NS as OPEN_IN_APP_NS };
@@ -0,0 +1,4 @@
1
+ /** Nameable application ids shared by the button and its keyboard command. */
2
+ import type { OpenInAppKey } from './locales.ts';
3
+ /** Label keys for applications supported by this client. */
4
+ export declare const APP_LABEL_KEY: Record<string, OpenInAppKey | undefined>;
@@ -0,0 +1,53 @@
1
+ /** Browser availability/choice state and the launch carrier for the split button. */
2
+ import { type SnapshotStore } from '@deepseek-ai/dsh-client-store';
3
+ /** Shared launch status for controls targeting the captured workspace path. */
4
+ export interface OpenInAppLaunchState {
5
+ readonly phase: 'idle' | 'busy' | 'error';
6
+ readonly path: string | null;
7
+ }
8
+ type Fetch = (input: string | URL, init?: RequestInit) => Promise<Response>;
9
+ /**
10
+ * Owns the once-per-page availability read, the persisted last choice, and
11
+ * the launch POST. Availability and choice publish through uSES-safe sources
12
+ * so every Session header shares one truth.
13
+ */
14
+ export declare class OpenInAppController {
15
+ private readonly fetcher;
16
+ /** Installed app ids in host menu order; null until the host answered. */
17
+ readonly apps: SnapshotStore<readonly string[] | null>;
18
+ /** Last chosen app id, or empty before the first choice, shared across sessions and browser restarts. */
19
+ readonly choice: SnapshotStore<string>;
20
+ /** Current launch, shared by pointer and keyboard gestures. */
21
+ readonly operation: SnapshotStore<OpenInAppLaunchState>;
22
+ /**
23
+ * Resolve the remembered nameable installed application, with the button's first-app fallback.
24
+ * @returns the installed app id, or undefined while unavailable.
25
+ */
26
+ currentApp(): string | undefined;
27
+ private loading;
28
+ /**
29
+ * @param fetcher - HTTP carrier for the apps read and the launch POST.
30
+ */
31
+ constructor(fetcher?: Fetch);
32
+ /**
33
+ * Read availability once per controller life; concurrent calls share the read.
34
+ * A failed read publishes an empty list, which renders no button at all.
35
+ * @returns after availability is published.
36
+ */
37
+ load(): Promise<void>;
38
+ /**
39
+ * Remember one picked app id.
40
+ * @param appId - catalog id from the availability list.
41
+ */
42
+ choose(appId: string): void;
43
+ /**
44
+ * Launch one installed app on a workspace directory.
45
+ * @param appId - catalog id from the availability list.
46
+ * @param path - the session's absolute workspace directory.
47
+ * Concurrent gestures are ignored until the current Host request settles.
48
+ * @returns after the host acknowledged the launch; rejects on any failure.
49
+ */
50
+ launch(appId: string, path: string): Promise<void>;
51
+ private run;
52
+ }
53
+ export {};
@@ -0,0 +1,10 @@
1
+ /** Dictionary namespace owned by upstream's plugin (verbatim). */
2
+ export declare const NS = "open-in-app";
3
+ /** Key domain of the `open-in-app` namespace (verbatim union of upstream's zh keys). */
4
+ export type OpenInAppKey = 'open.title' | 'path.appDefault' | 'path.appsError' | 'shortcut.busy' | 'shortcut.unavailable' | 'open.tooltip' | 'path.open' | 'path.more' | 'path.reveal' | 'path.openError' | 'path.revealError' | 'app.cursor' | 'app.vscode' | 'app.vscodeinsiders' | 'app.windsurf' | 'app.zed' | 'app.sublimetext' | 'app.xcode' | 'app.androidstudio' | 'app.intellij' | 'app.pycharm' | 'app.webstorm' | 'app.phpstorm' | 'app.goland' | 'app.rider' | 'app.rustrover' | 'app.fork' | 'app.sourcetree' | 'app.github' | 'app.tower' | 'app.gitkraken' | 'app.smartgit' | 'app.sublimemerge' | 'app.ghostty' | 'app.warp' | 'app.iterm' | 'app.kitty' | 'app.terminal' | 'app.windowsterminal' | 'app.gitbash' | 'app.gnometerminal' | 'app.konsole' | 'app.finder' | 'app.explorer' | 'app.filemanager';
5
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
6
+ interface LocaleNamespaceMap {
7
+ /** Session-header "open workspace in application" copy (upstream declaration). */
8
+ 'open-in-app': OpenInAppKey;
9
+ }
10
+ }