@dsh-electron/dsh-plugin-multi-root-workspace 0.1.9 → 0.1.11

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 (62) hide show
  1. package/CHANGELOG.en.md +26 -1
  2. package/CHANGELOG.md +26 -1
  3. package/README.en.md +24 -8
  4. package/README.md +24 -8
  5. package/cordis.patch.yml +4 -0
  6. package/lib/auto-review.js +1 -1
  7. package/lib/client.js +727 -209
  8. package/lib/client.js.map +1 -1
  9. package/lib/{command-Bwd6EGNV.js → command-BGdz7VBH.js} +65 -5
  10. package/lib/command.js +1 -1
  11. package/lib/{compat-CagUNzRB.js → compat-BiW-m_q0.js} +14 -1
  12. package/lib/compat.js +1 -1
  13. package/lib/{contract-DZ4zczDT.js → contract-CStSEOcK.js} +44 -1
  14. package/lib/index.js +6 -5
  15. package/lib/{instructions-CGNS3H0H.js → instructions-MePoRigy.js} +2 -206
  16. package/lib/instructions.js +1 -1
  17. package/lib/llm-message-fYORacvh.js +206 -0
  18. package/lib/metadata-DgqSdVX_.js +35 -0
  19. package/lib/{registry-BClm6wfh.js → registry-KSsmU393.js} +60 -7
  20. package/lib/registry.js +1 -1
  21. package/lib/{review-context-D-ohSVmA.js → review-context-AH-SXHmj.js} +1 -1
  22. package/lib/review-context.js +1 -1
  23. package/lib/scope.js +6 -2
  24. package/lib/skills.js +790 -0
  25. package/lib/types/client/WorkspaceFoldersAction.d.ts +1 -1
  26. package/lib/types/client/locales.d.ts +26 -16
  27. package/lib/types/client/open-in-app/OpenInAppAction.d.ts +5 -3
  28. package/lib/types/client/open-in-app/OpenTargetButton.d.ts +16 -10
  29. package/lib/types/client/open-in-app/adapter.d.ts +5 -1
  30. package/lib/types/client/open-in-app/controller.d.ts +5 -2
  31. package/lib/types/client/open-in-app/device.d.ts +11 -0
  32. package/lib/types/client/open-in-app/locales.d.ts +1 -1
  33. package/lib/types/client/open-in-app/open-path.d.ts +6 -5
  34. package/lib/types/client/open-in-app/open-toast.d.ts +39 -0
  35. package/lib/types/client/open-in-app/outcome.d.ts +23 -0
  36. package/lib/types/client/skills-client.d.ts +23 -0
  37. package/lib/types/client/skills-settings.d.ts +10 -0
  38. package/lib/types/client/skills.d.ts +36 -0
  39. package/lib/types/client/styles.d.ts +1 -1
  40. package/lib/types/command.d.ts +1 -1
  41. package/lib/types/compat/client-session.d.ts +2 -0
  42. package/lib/types/compat/dsh-version.d.ts +5 -5
  43. package/lib/types/compat/open-in-app-device.d.ts +23 -0
  44. package/lib/types/compat/skill-content.d.ts +3 -0
  45. package/lib/types/compat/skills-client.d.ts +33 -0
  46. package/lib/types/compat/skills-settings.d.ts +8 -0
  47. package/lib/types/compat/skills.d.ts +13 -0
  48. package/lib/types/compat/storage-zod.d.ts +1 -0
  49. package/lib/types/contract.d.ts +12 -1
  50. package/lib/types/registry.d.ts +7 -0
  51. package/lib/types/roots.d.ts +4 -1
  52. package/lib/types/scope.d.ts +2 -0
  53. package/lib/types/skills/catalog.d.ts +34 -0
  54. package/lib/types/skills/invocation.d.ts +7 -0
  55. package/lib/types/skills/metadata.d.ts +13 -0
  56. package/lib/types/skills/pool.d.ts +19 -0
  57. package/lib/types/skills/reader.d.ts +3 -0
  58. package/lib/types/skills/routing.d.ts +2 -0
  59. package/lib/types/skills-contract.d.ts +39 -0
  60. package/lib/types/skills.d.ts +43 -0
  61. package/package.json +98 -85
  62. package/lib/types/client/open-in-app/open-failure-toast.d.ts +0 -11
@@ -2,7 +2,7 @@
2
2
  * The Workspace Folders panel: one sidebar footer action that opens a dialog
3
3
  * listing this session's workspace root and the additional roots registered for
4
4
  * it, with the actions the milestone requires — Add (through the composed
5
- * directory picker), Remove, Alias, Copy Path, Reveal, and reordering.
5
+ * directory picker), Remove, Alias, Copy Path, and Reveal.
6
6
  *
7
7
  * Why a footer action and its own dialog, instead of a first-class panel: the
8
8
  * slots that can host a full panel (`sidebar.panellist` + the keyed `main`
@@ -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
- 'panel.mode': string;
16
- 'panel.effectiveAccess': string;
15
+ 'skills.globalEnable': string;
16
+ 'skills.globalHint': string;
17
+ 'skills.settingsFailed': string;
18
+ 'skills.userOnly': string;
19
+ 'panel.skills': string;
20
+ 'panel.skillsEnabled': string;
21
+ 'panel.skillsDisabled': string;
22
+ 'panel.aliasIdEdit': string;
23
+ 'panel.aliasIdPlaceholder': string;
24
+ 'panel.aliasIdInvalid': string;
25
+ 'error.invalid-alias-id': string;
26
+ 'panel.accessEffective': string;
17
27
  'panel.toolCaps': string;
18
28
  'panel.invalidCap': string;
19
- 'mode.read-only': string;
20
- 'mode.workspace-write': string;
21
- 'mode.danger-full-access': string;
22
29
  'effective.read-only': string;
23
30
  'effective.read-write': string;
24
31
  'effective.full-access': string;
@@ -27,11 +34,9 @@ export declare const zh: {
27
34
  'action.title': string;
28
35
  'switcher.manage': string;
29
36
  'panel.title': string;
30
- 'panel.subtitle': string;
31
37
  'panel.primary': string;
32
38
  'panel.primaryNote': string;
33
- 'panel.cwd': string;
34
- 'panel.cwdNote': string;
39
+ 'panel.currentAgentCwd': string;
35
40
  'panel.additional': string;
36
41
  'panel.limitReached': string;
37
42
  'panel.empty': string;
@@ -96,13 +101,20 @@ export declare const zh: {
96
101
  export type Key = keyof typeof zh;
97
102
  /** English copy, pinned to the Chinese key set. */
98
103
  export declare const en: {
99
- 'panel.mode': string;
100
- 'panel.effectiveAccess': string;
104
+ 'skills.globalEnable': string;
105
+ 'skills.globalHint': string;
106
+ 'skills.settingsFailed': string;
107
+ 'skills.userOnly': string;
108
+ 'panel.skills': string;
109
+ 'panel.skillsEnabled': string;
110
+ 'panel.skillsDisabled': string;
111
+ 'panel.aliasIdEdit': string;
112
+ 'panel.aliasIdPlaceholder': string;
113
+ 'panel.aliasIdInvalid': string;
114
+ 'error.invalid-alias-id': string;
115
+ 'panel.accessEffective': string;
101
116
  'panel.toolCaps': string;
102
117
  'panel.invalidCap': string;
103
- 'mode.read-only': string;
104
- 'mode.workspace-write': string;
105
- 'mode.danger-full-access': string;
106
118
  'effective.read-only': string;
107
119
  'effective.read-write': string;
108
120
  'effective.full-access': string;
@@ -111,11 +123,9 @@ export declare const en: {
111
123
  'action.title': string;
112
124
  'switcher.manage': string;
113
125
  'panel.title': string;
114
- 'panel.subtitle': string;
115
126
  'panel.primary': string;
116
127
  'panel.primaryNote': string;
117
- 'panel.cwd': string;
118
- 'panel.cwdNote': string;
128
+ 'panel.currentAgentCwd': string;
119
129
  'panel.additional': string;
120
130
  'panel.limitReached': string;
121
131
  'panel.empty': string;
@@ -1,17 +1,19 @@
1
1
  import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
2
2
  import type { InjectFace, PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
3
3
  import type { OpenInAppLaunchState } from './controller.ts';
4
+ import type { OpenInAppOutcome } from './outcome.ts';
4
5
  import type { ShortcutCatalogEntry } from '@deepseek-ai/dsh-client-shortcuts/client';
5
6
  import { NS } from './locales.ts';
7
+ import { type OpenTargetReporter } from './OpenTargetButton.tsx';
6
8
  /** Browser operations and state shared by the Session header and file tree contributions. */
7
- export interface OpenInAppActionInjected {
9
+ export interface OpenInAppActionInjected extends OpenTargetReporter {
8
10
  hooks: {
9
11
  openInAppApps: ObservableSnapshot<readonly string[] | null>;
10
12
  openInAppChoice: ObservableSnapshot<string>;
11
13
  openInAppLaunch: ObservableSnapshot<OpenInAppLaunchState>;
12
14
  shortcuts: ObservableSnapshot<readonly ShortcutCatalogEntry[]>;
13
15
  };
14
- launch: (appId: string, path: string) => Promise<void>;
16
+ launch: (appId: string, path: string) => Promise<OpenInAppOutcome>;
15
17
  choose: (appId: string) => void;
16
18
  iconUrl: (appId: string) => string;
17
19
  }
@@ -21,7 +23,7 @@ export type OpenInAppActionProps = PropsLocale<typeof NS> & InjectFace<OpenInApp
21
23
  };
22
24
  /**
23
25
  * Adapt the installed directory catalog to the shared opening control.
24
- * @param props - displayed directory, installed catalog, and launch operations.
26
+ * @param props - displayed directory, installed catalog, launch operations, and the announcement seat.
25
27
  * @returns the shared control, or null without an eligible application.
26
28
  */
27
29
  export declare function OpenInAppAction(props: OpenInAppActionProps): React.JSX.Element | null;
@@ -1,7 +1,7 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { ShortcutCatalogEntry } from '@deepseek-ai/dsh-client-shortcuts/client';
3
3
  import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
4
- import type { OpenInAppPathFailure } from './open-path.ts';
4
+ import type { OpenInAppOutcome } from './outcome.ts';
5
5
  import type { NS } from './locales.ts';
6
6
  /** Application metadata supplied by either the directory catalog or a file association query. */
7
7
  export interface OpenTargetApplication {
@@ -18,8 +18,13 @@ export type OpenTargetOperation = {
18
18
  } | {
19
19
  readonly kind: 'reveal';
20
20
  };
21
+ /** Seat a contribution sends one settled gesture to for app-wide announcement. */
22
+ export interface OpenTargetReporter {
23
+ /** Report the settled outcome; `{ kind: 'skipped' }` announces nothing. */
24
+ readonly report: (outcome: OpenInAppOutcome) => void;
25
+ }
21
26
  /** Inputs shared by both target adapters and the file empty-state action. */
22
- export interface OpenTargetButtonProps {
27
+ export interface OpenTargetButtonProps extends OpenTargetReporter {
23
28
  readonly shortcut?: Pick<ShortcutCatalogEntry, 'keys' | 'aria'> | undefined;
24
29
  readonly kind: 'file' | 'directory';
25
30
  readonly applications: readonly OpenTargetApplication[];
@@ -29,24 +34,25 @@ export interface OpenTargetButtonProps {
29
34
  readonly loading?: boolean;
30
35
  readonly prominent?: boolean;
31
36
  readonly t: TranslateNS<typeof NS>;
32
- readonly execute: (operation: OpenTargetOperation) => Promise<OpenInAppPathFailure | null>;
37
+ /** Resolve every gesture to an outcome, including Host and transport failures; never reject. */
38
+ readonly execute: (operation: OpenTargetOperation) => Promise<OpenInAppOutcome>;
33
39
  readonly refresh?: () => void;
34
40
  }
35
41
  /**
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.
42
+ * Serialize gestures and report the outcome each one settles on to the app-wide
43
+ * announcer, which outlives this control and the panel holding it.
44
+ * @param execute - target adapter that settles on the gesture's outcome.
45
+ * @param report - announcement seat the settled outcome is sent to.
46
+ * @returns the pending state and the guarded action callback.
40
47
  */
41
- export declare function useOpenTargetGesture(execute: OpenTargetButtonProps['execute'], t: TranslateNS<typeof NS>): {
48
+ export declare function useOpenTargetGesture(execute: OpenTargetButtonProps['execute'], report: OpenTargetButtonProps['report']): {
42
49
  pending: boolean;
43
- toast: ReactNode;
44
50
  act: (operation: OpenTargetOperation) => void;
45
51
  };
46
52
  /**
47
53
  * Render identical split buttons for files and directories. File reveal always
48
54
  * stays last; it is the default only when no application is registered.
49
55
  * @param props - target applications, default selection, and operations.
50
- * @returns the control and its transient failure feedback.
56
+ * @returns the control alone: its settled outcomes are announced by the seat it reports to.
51
57
  */
52
58
  export declare function OpenTargetButton(props: OpenTargetButtonProps): ReactNode;
@@ -25,6 +25,7 @@
25
25
  import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
26
26
  import type { ShortcutCatalogEntry } from '@deepseek-ai/dsh-client-shortcuts/client';
27
27
  import { OpenInAppAction, type OpenInAppActionProps } from './OpenInAppAction.tsx';
28
+ import { type OpenInAppToastSource } from './open-toast.tsx';
28
29
  import { NS } from './locales.ts';
29
30
  /**
30
31
  * The props `OpenInAppAction` consumes, minus the per-render `absolutePath`
@@ -42,5 +43,8 @@ export type OpenInAppAdapter = Omit<OpenInAppActionProps, 'absolutePath'>;
42
43
  export declare function createOpenInAppAdapter(deps: {
43
44
  readonly t: (key: string, params?: Record<string, unknown>) => string;
44
45
  readonly shortcuts?: ObservableSnapshot<readonly ShortcutCatalogEntry[]> | undefined;
45
- }): OpenInAppAdapter | undefined;
46
+ }): {
47
+ readonly adapter: OpenInAppAdapter;
48
+ readonly toast: OpenInAppToastSource;
49
+ } | undefined;
46
50
  export { OpenInAppAction, NS as OPEN_IN_APP_NS };
@@ -1,5 +1,6 @@
1
1
  /** Browser availability/choice state and the launch carrier for the split button. */
2
2
  import { type SnapshotStore } from '@deepseek-ai/dsh-client-store';
3
+ import type { OpenInAppOutcome } from './outcome.ts';
3
4
  /** Shared launch status for controls targeting the captured workspace path. */
4
5
  export interface OpenInAppLaunchState {
5
6
  readonly phase: 'idle' | 'busy' | 'error';
@@ -45,9 +46,11 @@ export declare class OpenInAppController {
45
46
  * @param appId - catalog id from the availability list.
46
47
  * @param path - the session's absolute workspace directory.
47
48
  * Concurrent gestures are ignored until the current Host request settles.
48
- * @returns after the host acknowledged the launch; rejects on any failure.
49
+ * @returns the acknowledged handoff, or `{ kind: 'skipped' }` while another
50
+ * launch holds the operation, or a named failure when the Host refuses or
51
+ * cannot be reached. Host and transport failures resolve without rejecting.
49
52
  */
50
- launch(appId: string, path: string): Promise<void>;
53
+ launch(appId: string, path: string): Promise<OpenInAppOutcome>;
51
54
  private run;
52
55
  }
53
56
  export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Fork-facing shim for `isMobileDevice` from
3
+ * `@deepseek-ai/dsh-client-ui-primitives`.
4
+ *
5
+ * The verbatim `open-toast.tsx` spells this specifier where upstream writes
6
+ * the primitives import (joined only in `0.2.2-alpha.2`); the
7
+ * release-dependent probe and fallback live in
8
+ * `src/compat/open-in-app-device.ts` — version-dependent code belongs only
9
+ * under `src/compat/`. See FORK.md.
10
+ */
11
+ export { isMobileDevice } from '../../compat/open-in-app-device.ts';
@@ -1,7 +1,7 @@
1
1
  /** Dictionary namespace owned by upstream's plugin (verbatim). */
2
2
  export declare const NS = "open-in-app";
3
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';
4
+ export type OpenInAppKey = 'open.title' | 'path.appDefault' | 'path.appsError' | 'shortcut.busy' | 'shortcut.unavailable' | 'open.tooltip' | 'path.open' | 'path.more' | 'path.reveal' | 'path.opened' | 'path.revealed' | '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
5
  declare module '@deepseek-ai/dsh-client-ui-slots' {
6
6
  interface LocaleNamespaceMap {
7
7
  /** Session-header "open workspace in application" copy (upstream declaration). */
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Fork-facing shim for upstream `ui-open-in-app/src/client/open-path.ts`.
3
3
  *
4
- * The fork only needs the failure union `OpenInAppPathButton`'s `execute`
4
+ * The fork only needs the failure union the path gestures' `execute`
5
5
  * returns; upstream's file also carries `OpenInAppPathController`, the
6
6
  * document-preview path controller that speaks to a Session Remote this
7
7
  * plugin does not hold. Keeping the verbatim file would pull
8
8
  * `@deepseek-ai/dsh-api-remotes` and `dsh-api-session-controller` into the
9
- * bundle for a class nothing here calls — so this shim owns just the type,
10
- * spelled verbatim. See FORK.md.
9
+ * bundle for a class nothing here calls. From `0.2.2-alpha.2` the failure
10
+ * union lives in upstream's `outcome.ts` (an alias of the deliverables
11
+ * `PresentedOpenFailure`), so this shim re-exports the fork's own outcome
12
+ * shim under the same name. See FORK.md.
11
13
  */
12
- /** Failure kind of one settled path gesture, for the initiating control to announce. */
13
- export type OpenInAppPathFailure = 'openError' | 'revealError';
14
+ export type { OpenInAppPathFailure } from './outcome.ts';
@@ -0,0 +1,39 @@
1
+ /** Native-open feedback that survives the initiating panel: mobile success and all failures. */
2
+ import type { ReactNode } from 'react';
3
+ import type { HostObservable, InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
4
+ import { NS } from './locales.ts';
5
+ import type { OpenInAppOutcome } from './outcome.ts';
6
+ /** One announced gesture; `seq` keys the banner so a repeat restarts it. */
7
+ export type OpenInAppToastState = Exclude<OpenInAppOutcome, {
8
+ kind: 'skipped';
9
+ }> & {
10
+ readonly seq: number;
11
+ };
12
+ /** Store every contribution reports into and the overlay entry reads. */
13
+ export interface OpenInAppToastSource {
14
+ /** Current notice for the overlay entry, or null while none shows. */
15
+ readonly hooks: {
16
+ readonly toast: HostObservable<OpenInAppToastState | null>;
17
+ };
18
+ /**
19
+ * Announce failures on every device and acknowledged opens or reveals on mobile.
20
+ * Desktop successes and skipped gestures leave the current notice unchanged.
21
+ * @param outcome - settled native-open result.
22
+ */
23
+ readonly report: (outcome: OpenInAppOutcome) => void;
24
+ /** Clear the shown notice. */
25
+ readonly dismiss: () => void;
26
+ }
27
+ /**
28
+ * Create the one outcome store the overlay entry shows.
29
+ * @returns the observable notice with its report and dismiss actions.
30
+ */
31
+ export declare function createOpenInAppToastSource(): OpenInAppToastSource;
32
+ /** Props of the `shell.overlay` entry: the notice store, its dismissal, and localized copy. */
33
+ export type OpenInAppToastProps = PropsRuntime<'shell.overlay'> & PropsLocale<typeof NS> & InjectFace<Omit<OpenInAppToastSource, 'report'>>;
34
+ /**
35
+ * Render a mobile handoff confirmation or a native-open failure.
36
+ * @param props - the notice hook, its dismissal, and the locale seat.
37
+ * @returns the banner on display, or null.
38
+ */
39
+ export declare function OpenInAppToast({ useToast, dismiss, t }: OpenInAppToastProps): ReactNode;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Fork-facing shim for upstream `ui-open-in-app/src/client/outcome.ts`.
3
+ *
4
+ * Upstream's file aliases `PresentedOpenFailure`/`PresentedOpenOutcome` out
5
+ * of `@deepseek-ai/dsh-client-ui-deliverables` — a package this plugin does
6
+ * not depend on. Both aliases are types only, so this shim spells the same
7
+ * structural unions verbatim (upstream
8
+ * `ui-deliverables/src/client/present-open.ts`) instead of importing them.
9
+ * See FORK.md.
10
+ */
11
+ /** Failure kind of one settled gesture, keyed to the `path.*` copy it announces. */
12
+ export type OpenInAppPathFailure = 'openError' | 'revealError';
13
+ /** Acknowledged open or reveal, named failure, or deliberately skipped gesture. */
14
+ export type OpenInAppOutcome = {
15
+ readonly kind: 'opened';
16
+ } | {
17
+ readonly kind: 'revealed';
18
+ } | {
19
+ readonly kind: 'failed';
20
+ readonly failure: OpenInAppPathFailure;
21
+ } | {
22
+ readonly kind: 'skipped';
23
+ };
@@ -0,0 +1,23 @@
1
+ import { type SkillsCatalogView, type SkillsPreviewView } from '../skills-contract.ts';
2
+ interface Connection {
3
+ rpc: {
4
+ call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<unknown>;
5
+ };
6
+ }
7
+ export declare class SkillsClient {
8
+ private readonly connection;
9
+ private readonly states;
10
+ private closed;
11
+ constructor(connection: Connection);
12
+ private state;
13
+ private call;
14
+ list(sessionId: string, signal?: AbortSignal): Promise<SkillsCatalogView>;
15
+ snapshot(sessionId: string): SkillsCatalogView | undefined;
16
+ preview(sessionId: string, name: string, signal?: AbortSignal): Promise<SkillsPreviewView>;
17
+ subscribe(sessionId: string, listener: () => void): () => void;
18
+ private invalidate;
19
+ reset(): void;
20
+ dispose(): void;
21
+ private watch;
22
+ }
23
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import { type SkillsForm } from '../compat/skills-client.ts';
3
+ import type { Translate } from './WorkspaceFoldersAction.tsx';
4
+ interface CardProps {
5
+ form: SkillsForm;
6
+ t: Translate;
7
+ }
8
+ export declare function SkillsSettingsCard({ form, t }: CardProps): import("react").JSX.Element;
9
+ export declare function installSkillsSettingsCard(ctx: Context, t: Translate): void;
10
+ export {};
@@ -0,0 +1,36 @@
1
+ /** Namespaced Slash completion and plain-text sent-message references. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import { type NamespaceRule } from '../compat/skills-client.ts';
4
+ import { SkillsClient } from './skills-client.ts';
5
+ import type { Translate } from './WorkspaceFoldersAction.tsx';
6
+ interface Source {
7
+ trigger: '/';
8
+ name: string;
9
+ order: number;
10
+ candidates(session: {
11
+ sessionId: string;
12
+ }, request: {
13
+ query: string;
14
+ signal: AbortSignal;
15
+ }): Promise<readonly {
16
+ name: string;
17
+ description: string;
18
+ }[]>;
19
+ onPick(pick: {
20
+ candidate: {
21
+ name: string;
22
+ };
23
+ }): {
24
+ text: string;
25
+ };
26
+ lexicon(session: {
27
+ sessionId: string;
28
+ }): readonly string[] | undefined;
29
+ subscribeLexicon(session: {
30
+ sessionId: string;
31
+ }, listener: () => void): () => void;
32
+ }
33
+ export declare const namespaceSkillRule: NamespaceRule;
34
+ export declare function namespaceSource(client: SkillsClient, t: Translate): Source;
35
+ export declare function installSkillsClient(ctx: Context, t: Translate, subscribeSession: (listener: () => void) => () => void): void;
36
+ export {};
@@ -11,4 +11,4 @@
11
11
  * @module @dsh-electron/dsh-plugin-multi-root-workspace/client/styles
12
12
  */
13
13
  /** The stylesheet text; every class is prefixed `mrfw-` to stay collision-free. */
14
- export declare const STYLES = "\n/* Sidebar footer row, mirroring the host ui-settings-general trigger: the row\n * wrapper overhangs each side by 2px (width calc(100% + 4px) against the\n * shell's 12px inline padding) so the icon's ink line lands at 18px from the\n * column edge \u2014 exactly where the Settings gear below sits. */\n.mrfw-triggerRow {\n flex: none;\n display: flex;\n align-items: center;\n gap: 8px;\n width: calc(100% + 4px);\n margin: 4px -2px;\n}\n\n.mrfw-triggerRow.mrfw-railRow {\n width: 36px;\n margin: 8px 0 10px;\n}\n\n.mrfw-trigger {\n box-sizing: border-box;\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n width: auto;\n height: 42px;\n margin: 0;\n padding: 0 10px 0 8px;\n border: none;\n border-radius: 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n cursor: pointer;\n overflow: hidden;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-trigger:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-triggerIcon {\n flex: none;\n display: inline-flex;\n}\n\n.mrfw-triggerLabel {\n overflow: hidden;\n white-space: nowrap;\n}\n\n.mrfw-triggerRail {\n flex: none;\n width: 36px;\n height: 36px;\n margin: 0;\n padding: 0;\n gap: 0;\n border-radius: 50%;\n justify-content: center;\n}\n\n/* Icon-only launcher inside the upstream Files tab header. Upstream's\n FilesBody renders .header as one flat flex row \u2014 PathLabel, then the\n reload button, then the files.actions slot occupants \u2014 and each of those\n children carries a stable data-files-* attribute on the flex item itself\n (data-files-root on the pane, data-files-path, data-files-reload).\n A registration order sorts only INSIDE the actions slot and can never\n overtake the reload button, so the launcher is moved with the flex order\n property instead: this wrapper is the slot's root element and therefore a\n direct flex item of .header, and order: -1 puts it ahead of every\n order-0 sibling while the :has() rule below keeps the path first. */\n.mrfw-fileLauncher {\n order: -1;\n flex: none;\n display: flex;\n align-items: center;\n}\n\n/* Pin the upstream path label ahead of the launcher \u2014 but only while this\n plugin is in the row: the :has() guard keeps a rule that names an\n upstream element inert whenever the launcher is absent. The\n :not([data-files-entry]) qualifier is load-bearing, not decorative:\n upstream puts data-files-path on EVERY tree row too (the row's own\n absolute path), so an unqualified selector would match the whole tree,\n not just the header's PathLabel. Both selectors depend on upstream's DOM\n shape, which is not part of the slot contract; if it changes, the rules\n simply stop matching and the launcher falls back to its in-slot position \u2014\n a silent degradation, never a broken header. */\n[data-files-root]:has(.mrfw-fileLauncher) [data-files-path]:not([data-files-entry]) {\n order: -2;\n}\n\n/* The root switcher is the launcher's grown-up form on runtimes whose\n files.actions owner carries absolutePath. It takes the path label's flex\n position and stretch: order: -1 ahead of every order-0 sibling, and a\n growing min-width:0 item so both the trigger label and the PathLabel\n inside it can ellipsize instead of pushing the header controls out. */\n.mrfw-rootSwitcher {\n order: -1;\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n align-items: center;\n /* Same 4px the upstream .header puts between its own flex items, so the\n takeover controls (reload, open-in-app fork) sit at the same offsets the\n hidden native ones did \u2014 no drift between primary and additional state. */\n gap: 4px;\n}\n\n/* While the switcher exists it hides the upstream path label \u2014 the\n :has() guard again keeps the upstream-naming rule inert unless this\n plugin is actually in the row. The switcher draws its own PathLabel of\n the selected root next to the trigger (className mrfw-switcherPath, no\n data-files-* attributes), so the header keeps a path in the same spot.\n :not([data-files-entry]) is what confines this rule to the header:\n every tree row carries data-files-path with its own path as the value,\n and only the PathLabel carries it bare. Without the qualifier this\n display:none would blank the entire native tree. */\n[data-files-root]:has(.mrfw-rootSwitcher) [data-files-path]:not([data-files-entry]) {\n display: none;\n}\n\n/* While the switcher names an additional root, the native tree body and its\n reload hide: the reload refreshes the very tree that is no longer\n visible, and the plugin's forked tree is portaled in as the body's sibling.\n The header still offers a reload \u2014 the switcher's own .mrfw-switcherReload\n calls the forked face's refresh. The native tree keeps watching in the\n background, so switching back to the primary root restores it instantly. */\n[data-files-root]:has(.mrfw-rootSwitcher[data-root-kind=\"additional\"]) [data-files-body],\n[data-files-root]:has(.mrfw-rootSwitcher[data-root-kind=\"additional\"]) [data-files-reload] {\n display: none;\n}\n\n/* While the switcher names an additional root, the native open-in-app split\n button hides too: its slot props carry the pane's primary root, so it\n would always open the primary root no matter what the switcher selected.\n The header gets the plugin's own fork of that same control instead (the\n open-in-app directory), wired to the selected root.\n\n The slot's render site is wrapped in the renderer's own anchor \u2014\n <div data-slot=\"sidebar.right.tab.files.actions\" style=\"display:contents\">\n \u2014 so the header's flex items inside it are the occupants themselves, and\n the native item is the ui-primitives Menu anchor span wrapping the marked\n .split. The first selector hides that occupant item through the data-slot\n anchor: hiding only the inner control would leave the empty anchor span\n as a zero-width flex item whose header gap pushes the fork 4px left of\n the native position. The wrapper itself can never be the target \u2014 its\n inline display:contents beats any stylesheet rule, and hiding it would\n take the switcher with it. The second selector is the fallback for an\n upstream that moves the attribute off the item. The fork survives both:\n the only data-open-target inside .mrfw-rootSwitcher is its own\n .mrfw-openSplit, which :not() excludes. */\n[data-files-root]:has(.mrfw-rootSwitcher[data-root-kind=\"additional\"]) [data-slot=\"sidebar.right.tab.files.actions\"] > *:has([data-open-target=\"directory\"]:not(.mrfw-openSplit)),\n[data-files-root]:has(.mrfw-rootSwitcher[data-root-kind=\"additional\"]) [data-open-target=\"directory\"]:not(.mrfw-openSplit) {\n display: none;\n}\n\n/* The trigger borrows the path label's look: quiet text with a folder\n glyph, the hover of the neighboring header buttons, and the overflow\n behavior PathLabel itself uses in a narrow header. It may shrink but\n never grows \u2014 the PathLabel after it owns the free header space. */\n.mrfw-rootSwitchBtn {\n flex: 0 1 auto;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 28px;\n margin: 0;\n padding: 0 4px;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: inherit;\n line-height: 20px;\n text-align: left;\n cursor: pointer;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-rootSwitchBtn:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-rootSwitchBtn .mrfw-triggerIcon {\n color: var(--dsw-alias-label-secondary);\n}\n\n.mrfw-rootSwitchLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mrfw-rootSwitchChevron {\n margin-left: auto;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* The selected root's full path sits between the trigger and the reload \u2014\n the very PathLabel upstream draws there, so it brings its own grow,\n left-clip and hover-title behavior; only the gutters are ours. The right\n margin plus the container gap reproduces upstream's .path margin-right\n 12px + header gap 4px; the left gutter comes from the same container gap. */\n.mrfw-switcherPath {\n margin: 0 12px 0 0;\n}\n\n/* The takeover's own reload, standing exactly where the hidden native one\n did: the upstream .tool recipe verbatim (28px hit area, 15px glyph,\n secondary voice, hover fills). It calls the forked face's refresh \u2014 an\n in-place re-list, not a remount. */\n.mrfw-switcherReload {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n border: none;\n border-radius: var(--dsw-radius-sm);\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n line-height: 1;\n cursor: pointer;\n}\n\n.mrfw-switcherReload:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n/* \u2500\u2500 files-tree fork (verbatim rules, renamed classes) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Copied from upstream's FilesBody.module.css (ui-sidebar-files @\n 0.2.1-alpha.2) with local names mapped to mrfw-ft* so the rules cannot\n collide with host DOM \u2014 and so the fork's own body (which must NOT carry\n data-files-body) stays visible while the rules above hide the native one.\n The header-only rules (root/header/path/tool/status/statusLine/titleIcon)\n belong to the upstream shell this fork drops and are not mapped. Fork sync\n re-diffs the upstream file and re-applies the rename \u2014 see\n src/client/files-tree/FORK.md. */\n\n/* The pane's own .root already supplies the flex column, color, and font;\n the fork's body takes the same stretch item the hidden native body held,\n including the workspace list's 2px scrollbar offset and stable gutter. */\n.mrfw-ftBody {\n flex: 1 1 auto;\n min-height: 0;\n margin-right: 2px;\n padding: 8px 0 8px 8px;\n overflow: auto;\n scrollbar-gutter: stable;\n}\n\n.mrfw-ftBody::-webkit-scrollbar-track {\n margin: 2px;\n}\n\n.mrfw-ftLevel {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.mrfw-ftLevel .mrfw-ftLevel {\n padding-left: 18px;\n}\n\n.mrfw-ftItem {\n margin: 0;\n padding: 0;\n}\n\n/* Rows abut, so the hover fill is the whole row; the tree's air is this\n inset, not a gap between rows. */\n.mrfw-ftRow {\n display: flex;\n gap: 6px;\n align-items: center;\n width: 100%;\n min-width: 0;\n padding: 5px 10px;\n color: inherit;\n font: inherit;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: var(--dsw-radius-md);\n cursor: pointer;\n}\n\n.mrfw-ftRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n/* The folder glyph rides the tertiary ink, as the workspace tree's does. */\n.mrfw-ftIcon {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* The type's coloured sheet at the folder glyph's 16px; its colour is its own. */\n.mrfw-ftFileIcon {\n flex: 0 0 auto;\n}\n\n.mrfw-ftName {\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n/* Neither a file nor a directory: shown so the directory is reported whole,\n greyed so nobody tries to open it. */\n.mrfw-ftOther {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n\n.mrfw-ftOther:hover {\n background: transparent;\n}\n\n.mrfw-ftNote {\n margin: 0;\n padding: 3px 10px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n/* \u2500\u2500 open-in-app fork (verbatim rules, renamed classes) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Copied from upstream's OpenTargetButton.module.css\n (dsh-client-ui-open-in-app @ 0.2.1-alpha.2) with local names mapped to\n mrfw-open* so the rules cannot collide with host DOM. Fork sync re-diffs\n the upstream file and re-applies the rename \u2014 see\n src/client/open-in-app/FORK.md. */\n\n/* Sized to sit in the document header's 24px control row beside its round\n icon buttons: a hairline pill with a 12px label and a narrow chevron half. */\n.mrfw-openMenuAnchor {\n display: inline-flex;\n flex: none;\n align-self: center;\n}\n\n.mrfw-openSplit {\n display: inline-flex;\n align-items: stretch;\n box-sizing: border-box;\n height: 24px;\n overflow: hidden;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: var(--dsw-radius-sm);\n font-family: var(--dsw-font-family);\n}\n\n.mrfw-openMain,\n.mrfw-openChevron {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 0;\n background: none;\n color: var(--dsw-alias-label-primary);\n font-size: 11px;\n line-height: 16px;\n white-space: nowrap;\n cursor: pointer;\n}\n\n.mrfw-openMain {\n gap: 4px;\n padding: 3px 5px;\n}\n\n.mrfw-openChevron {\n padding: 3px 4px 3px 3px;\n border-left: 0.5px solid var(--dsw-alias-border-l4);\n color: var(--dsw-alias-label-secondary);\n}\n\n.mrfw-openMain:hover:not(:disabled),\n.mrfw-openMain:focus-visible,\n.mrfw-openChevron:hover:not(:disabled),\n.mrfw-openChevron:focus-visible {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n/* Pending disables clicks without a restyle; only hover feedback pauses. */\n.mrfw-openMain:disabled,\n.mrfw-openChevron:disabled {\n cursor: default;\n}\n\n.mrfw-openAppIcon {\n object-fit: contain;\n}\n\n/* The unavailable preview emphasizes the same actions with a labeled button. */\n.mrfw-openSplit[data-size='large'] {\n height: 36px;\n border-radius: var(--dsw-radius-md);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.mrfw-openSplit[data-size='large'] .mrfw-openMain {\n gap: 6px;\n padding: 6px 14px;\n font-size: 14px;\n}\n\n.mrfw-openSplit[data-size='large'] .mrfw-openChevron {\n padding: 6px 10px;\n}\n\n.mrfw-openSkeleton { display: inline-block; flex: none; border-radius: var(--dsw-radius-xs); background: var(--dsw-alias-interactive-bg-hover); }\n\n.mrfw-overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n}\n\n.mrfw-mask {\n position: absolute;\n inset: 0;\n background: var(--dsw-alias-bg-mask-1);\n backdrop-filter: var(--dsw-mask-blur);\n}\n\n.mrfw-dialog {\n position: relative;\n z-index: 1;\n display: flex;\n flex-direction: column;\n width: min(640px, 100%);\n max-height: 100%;\n overflow: hidden;\n border: none;\n border-radius: 24px;\n background: var(--dsw-alias-bg-layer-2);\n box-shadow: var(--dsw-elevation-prominent);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n outline: none;\n}\n\n.mrfw-header {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 22px 14px 12px 24px;\n}\n\n.mrfw-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-close {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 8px;\n background: transparent;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-close:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-body {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n padding: 0 24px 24px;\n}\n\n.mrfw-description {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-alert {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.mrfw-section {\n display: flex;\n flex-direction: column;\n}\n\n.mrfw-section + .mrfw-section {\n margin-top: 8px;\n}\n\n.mrfw-sectionTitleRow {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 0 4px;\n}\n\n.mrfw-sectionTitle {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-sectionNote {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.mrfw-rowBare {\n border-bottom: none;\n}\n\n/* A root entry's two lines: the display name on top, the absolute path under\n it in the tertiary voice (the workspace rows recipe of the host sidebar). */\n.mrfw-rootText {\n display: flex;\n flex: 1 1 240px;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n/* The name line is a flex row: the truncating name text first, the access\n badge beside it never shrinking \u2014 baseline-aligned so the badge's smaller\n voice sits on the name's baseline rather than its middle. */\n.mrfw-rootName {\n display: flex;\n align-items: baseline;\n gap: 6px;\n min-width: 0;\n overflow: hidden;\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-rootNameText {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mrfw-rootPath {\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n line-height: 18px;\n word-break: break-all;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* The path doubles as the copy control: an unstyled button that keeps the\n text recipe and makes the whole string clickable \u2014 the whole region, not\n just the icon, copies on click. */\n.mrfw-pathCopy {\n display: block;\n width: 100%;\n padding: 0;\n border: 0;\n background: transparent;\n text-align: left;\n cursor: pointer;\n}\n\n/* The trailing icon stays in flow but invisible until the path is hovered\n or focused: appearing never re-wraps the text, and the icon inherits the\n path's own tertiary ink. */\n.mrfw-pathCopyIcon {\n display: inline-flex;\n margin-left: 4px;\n vertical-align: -2px;\n opacity: 0;\n transition: opacity var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-pathCopy:hover .mrfw-pathCopyIcon,\n.mrfw-pathCopy:focus-visible .mrfw-pathCopyIcon {\n opacity: 1;\n}\n\n.mrfw-note {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-stateWarn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.mrfw-btn {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: 14px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-btn:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.mrfw-btnGhost:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnGhost:active:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n/* The solid destructive action of the capsule family: the upgrade confirm's\n red button. state-error-primary is the error ink upstream also uses as a\n fill (StateDot); the hover mixes in the host's primary label token to\n follow its theme and contrast choices. Neither color is a guessed token:\n an undefined var() would fall back\n to transparent and white-out the whole button. */\n.mrfw-btnDanger {\n background: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.mrfw-btnDanger:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 85%, var(--dsw-alias-label-primary));\n}\n\n.mrfw-btnOutline {\n border: 0.5px solid var(--dsw-alias-border-l3);\n background: transparent;\n}\n\n.mrfw-btnOutline:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnPrimary {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.mrfw-btnPrimary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n/* One icon-only action: a square of the capsule family, meaning in title.\n Declared after the .mrfw-btn family so its padding wins the cascade tie:\n the capsule's 0 10px padding would otherwise squeeze the icon glyph. */\n.mrfw-iconBtn {\n width: 28px;\n padding: 0;\n border-radius: 8px;\n}\n\n.mrfw-input {\n flex: 1 1 220px;\n min-width: 0;\n height: 32px;\n padding: 0 8px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n outline: none;\n transition: border-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-input:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.mrfw-input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n.mrfw-inputInline {\n flex: 1 1 auto;\n}\n\n.mrfw-accessSelect {\n flex: 0 0 auto;\n}\n\n/* The access dropdown's capsule trigger: the Menu anchor wrapper is the flex\n item, the button inside keeps the control row's 32px capsule recipe. */\n.mrfw-accessTrigger {\n padding: 0 12px;\n}\n\n.mrfw-accessBadge,\n.mrfw-effectiveAccess {\n flex: none;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-aliasEditor {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex-basis: 100%;\n}\n\n.mrfw-actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 16px;\n padding-top: 16px;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n/* One control row, one height: the capsule buttons match the 32px input. */\n.mrfw-actions .mrfw-btn {\n height: 32px;\n border-radius: 16px;\n}\n\n.mrfw-actions .mrfw-iconBtn {\n width: 32px;\n}\n";
14
+ export declare const STYLES = "\n/* Sidebar footer row, mirroring the host ui-settings-general trigger: the row\n * wrapper overhangs each side by 2px (width calc(100% + 4px) against the\n * shell's 12px inline padding) so the icon's ink line lands at 18px from the\n * column edge \u2014 exactly where the Settings gear below sits. */\n.mrfw-triggerRow {\n flex: none;\n display: flex;\n align-items: center;\n gap: 8px;\n width: calc(100% + 4px);\n margin: 4px -2px;\n}\n\n.mrfw-triggerRow.mrfw-railRow {\n width: 36px;\n margin: 8px 0 10px;\n}\n\n.mrfw-trigger {\n box-sizing: border-box;\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n width: auto;\n height: 42px;\n margin: 0;\n padding: 0 10px 0 8px;\n border: none;\n border-radius: 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n cursor: pointer;\n overflow: hidden;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-trigger:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-triggerIcon {\n flex: none;\n display: inline-flex;\n}\n\n.mrfw-triggerLabel {\n overflow: hidden;\n white-space: nowrap;\n}\n\n.mrfw-triggerRail {\n flex: none;\n width: 36px;\n height: 36px;\n margin: 0;\n padding: 0;\n gap: 0;\n border-radius: 50%;\n justify-content: center;\n}\n\n/* Icon-only launcher inside the upstream Files tab header. Upstream's\n FilesBody renders .header as one flat flex row \u2014 PathLabel, then the\n reload button, then the files.actions slot occupants \u2014 and each of those\n children carries a stable data-files-* attribute on the flex item itself\n (data-files-root on the pane, data-files-path, data-files-reload).\n A registration order sorts only INSIDE the actions slot and can never\n overtake the reload button, so the launcher is moved with the flex order\n property instead: this wrapper is the slot's root element and therefore a\n direct flex item of .header, and order: -1 puts it ahead of every\n order-0 sibling while the :has() rule below keeps the path first. */\n.mrfw-fileLauncher {\n order: -1;\n flex: none;\n display: flex;\n align-items: center;\n}\n\n/* Pin the upstream path label ahead of the launcher \u2014 but only while this\n plugin is in the row: the :has() guard keeps a rule that names an\n upstream element inert whenever the launcher is absent. The\n :not([data-files-entry]) qualifier is load-bearing, not decorative:\n upstream puts data-files-path on EVERY tree row too (the row's own\n absolute path), so an unqualified selector would match the whole tree,\n not just the header's PathLabel. Both selectors depend on upstream's DOM\n shape, which is not part of the slot contract; if it changes, the rules\n simply stop matching and the launcher falls back to its in-slot position \u2014\n a silent degradation, never a broken header. */\n[data-files-root]:has(.mrfw-fileLauncher) [data-files-path]:not([data-files-entry]) {\n order: -2;\n}\n\n/* The root switcher is the launcher's grown-up form on runtimes whose\n files.actions owner carries absolutePath. It takes the path label's flex\n position and stretch: order: -1 ahead of every order-0 sibling, and a\n growing min-width:0 item so both the trigger label and the PathLabel\n inside it can ellipsize instead of pushing the header controls out. */\n.mrfw-rootSwitcher {\n order: -1;\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n align-items: center;\n /* Same 4px the upstream .header puts between its own flex items, so the\n takeover controls (reload, open-in-app fork) sit at the same offsets the\n hidden native ones did \u2014 no drift between primary and additional state. */\n gap: 4px;\n}\n\n/* While the switcher exists it hides the upstream path label \u2014 the\n :has() guard again keeps the upstream-naming rule inert unless this\n plugin is actually in the row. The switcher draws its own PathLabel of\n the selected root next to the trigger (className mrfw-switcherPath, no\n data-files-* attributes), so the header keeps a path in the same spot.\n :not([data-files-entry]) is what confines this rule to the header:\n every tree row carries data-files-path with its own path as the value,\n and only the PathLabel carries it bare. Without the qualifier this\n display:none would blank the entire native tree. */\n[data-files-root]:has(.mrfw-rootSwitcher) [data-files-path]:not([data-files-entry]) {\n display: none;\n}\n\n/* While the switcher names an additional root, the native tree body and its\n reload hide: the reload refreshes the very tree that is no longer\n visible, and the plugin's forked tree is portaled in as the body's sibling.\n The header still offers a reload \u2014 the switcher's own .mrfw-switcherReload\n calls the forked face's refresh. The native tree keeps watching in the\n background, so switching back to the primary root restores it instantly. */\n[data-files-root]:has(.mrfw-rootSwitcher[data-root-kind=\"additional\"]) [data-files-body],\n[data-files-root]:has(.mrfw-rootSwitcher[data-root-kind=\"additional\"]) [data-files-reload] {\n display: none;\n}\n\n/* While the switcher names an additional root, the native open-in-app split\n button hides too: its slot props carry the pane's primary root, so it\n would always open the primary root no matter what the switcher selected.\n The header gets the plugin's own fork of that same control instead (the\n open-in-app directory), wired to the selected root.\n\n The slot's render site is wrapped in the renderer's own anchor \u2014\n <div data-slot=\"sidebar.right.tab.files.actions\" style=\"display:contents\">\n \u2014 so the header's flex items inside it are the occupants themselves, and\n the native item is the ui-primitives Menu anchor span wrapping the marked\n .split. The first selector hides that occupant item through the data-slot\n anchor: hiding only the inner control would leave the empty anchor span\n as a zero-width flex item whose header gap pushes the fork 4px left of\n the native position. The wrapper itself can never be the target \u2014 its\n inline display:contents beats any stylesheet rule, and hiding it would\n take the switcher with it. The second selector is the fallback for an\n upstream that moves the attribute off the item. The fork survives both:\n the only data-open-target inside .mrfw-rootSwitcher is its own\n .mrfw-openSplit, which :not() excludes. */\n[data-files-root]:has(.mrfw-rootSwitcher[data-root-kind=\"additional\"]) [data-slot=\"sidebar.right.tab.files.actions\"] > *:has([data-open-target=\"directory\"]:not(.mrfw-openSplit)),\n[data-files-root]:has(.mrfw-rootSwitcher[data-root-kind=\"additional\"]) [data-open-target=\"directory\"]:not(.mrfw-openSplit) {\n display: none;\n}\n\n/* The trigger borrows the path label's look: quiet text with a folder\n glyph, the hover of the neighboring header buttons, and the overflow\n behavior PathLabel itself uses in a narrow header. It may shrink but\n never grows \u2014 the PathLabel after it owns the free header space. */\n.mrfw-rootSwitchBtn {\n flex: 0 1 auto;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 28px;\n margin: 0;\n padding: 0 4px;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: inherit;\n line-height: 20px;\n text-align: left;\n cursor: pointer;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-rootSwitchBtn:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-rootSwitchBtn .mrfw-triggerIcon {\n color: var(--dsw-alias-label-secondary);\n}\n\n.mrfw-rootSwitchLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mrfw-rootSwitchChevron {\n margin-left: auto;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* The selected root's full path sits between the trigger and the reload \u2014\n the very PathLabel upstream draws there, so it brings its own grow,\n left-clip and hover-title behavior; only the gutters are ours. The right\n margin plus the container gap reproduces upstream's .path margin-right\n 12px + header gap 4px; the left gutter comes from the same container gap. */\n.mrfw-switcherPath {\n margin: 0 12px 0 0;\n}\n\n/* The takeover's own reload, standing exactly where the hidden native one\n did: the upstream .tool recipe verbatim (28px hit area, 15px glyph,\n secondary voice, hover fills). It calls the forked face's refresh \u2014 an\n in-place re-list, not a remount. */\n.mrfw-switcherReload {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n border: none;\n border-radius: var(--dsw-radius-sm);\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n line-height: 1;\n cursor: pointer;\n}\n\n.mrfw-switcherReload:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n/* \u2500\u2500 files-tree fork (verbatim rules, renamed classes) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Copied from upstream's FilesBody.module.css (ui-sidebar-files @\n 0.2.2-alpha.2) with local names mapped to mrfw-ft* so the rules cannot\n collide with host DOM \u2014 and so the fork's own body (which must NOT carry\n data-files-body) stays visible while the rules above hide the native one.\n The header-only rules (root/header/path/tool/status/statusLine/titleIcon)\n belong to the upstream shell this fork drops and are not mapped. Fork sync\n re-diffs the upstream file and re-applies the rename \u2014 see\n src/client/files-tree/FORK.md. */\n\n/* The pane's own .root already supplies the flex column, color, and font;\n the fork's body takes the same stretch item the hidden native body held,\n including the workspace list's 2px scrollbar offset and stable gutter. */\n.mrfw-ftBody {\n flex: 1 1 auto;\n min-height: 0;\n margin-right: 2px;\n padding: 8px 0 8px 8px;\n overflow: auto;\n scrollbar-gutter: stable;\n}\n\n.mrfw-ftBody::-webkit-scrollbar-track {\n margin: 2px;\n}\n\n.mrfw-ftLevel {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.mrfw-ftLevel .mrfw-ftLevel {\n padding-left: 18px;\n}\n\n.mrfw-ftItem {\n margin: 0;\n padding: 0;\n}\n\n/* Rows abut, so the hover fill is the whole row; the tree's air is this\n inset, not a gap between rows. */\n.mrfw-ftRow {\n display: flex;\n gap: 6px;\n align-items: center;\n width: 100%;\n min-width: 0;\n padding: 5px 10px;\n color: inherit;\n font: inherit;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: var(--dsw-radius-md);\n cursor: pointer;\n}\n\n@media (hover: hover) {\n .mrfw-ftRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n }\n}\n\n/* The folder glyph rides the tertiary ink, as the workspace tree's does. */\n.mrfw-ftIcon {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* The type's coloured sheet at the folder glyph's 16px; its colour is its own. */\n.mrfw-ftFileIcon {\n flex: 0 0 auto;\n}\n\n.mrfw-ftName {\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n/* Neither a file nor a directory: shown so the directory is reported whole,\n greyed so nobody tries to open it. */\n.mrfw-ftOther {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n\n@media (hover: hover) {\n .mrfw-ftOther:hover {\n background: transparent;\n }\n}\n\n.mrfw-ftNote {\n margin: 0;\n padding: 3px 10px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n/* \u2500\u2500 open-in-app fork (verbatim rules, renamed classes) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Copied from upstream's OpenTargetButton.module.css\n (dsh-client-ui-open-in-app @ 0.2.1-alpha.2) with local names mapped to\n mrfw-open* so the rules cannot collide with host DOM. Fork sync re-diffs\n the upstream file and re-applies the rename \u2014 see\n src/client/open-in-app/FORK.md. */\n\n/* Sized to sit in the document header's 24px control row beside its round\n icon buttons: a hairline pill with a 12px label and a narrow chevron half. */\n.mrfw-openMenuAnchor {\n display: inline-flex;\n flex: none;\n align-self: center;\n}\n\n.mrfw-openSplit {\n display: inline-flex;\n align-items: stretch;\n box-sizing: border-box;\n height: 24px;\n overflow: hidden;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: var(--dsw-radius-sm);\n font-family: var(--dsw-font-family);\n}\n\n.mrfw-openMain,\n.mrfw-openChevron {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 0;\n background: none;\n color: var(--dsw-alias-label-primary);\n font-size: 11px;\n line-height: 16px;\n white-space: nowrap;\n cursor: pointer;\n}\n\n.mrfw-openMain {\n gap: 4px;\n padding: 3px 5px;\n}\n\n.mrfw-openChevron {\n padding: 3px 4px 3px 3px;\n border-left: 0.5px solid var(--dsw-alias-border-l4);\n color: var(--dsw-alias-label-secondary);\n}\n\n.mrfw-openMain:hover:not(:disabled),\n.mrfw-openMain:focus-visible,\n.mrfw-openChevron:hover:not(:disabled),\n.mrfw-openChevron:focus-visible {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n/* Pending disables clicks without a restyle; only hover feedback pauses. */\n.mrfw-openMain:disabled,\n.mrfw-openChevron:disabled {\n cursor: default;\n}\n\n.mrfw-openAppIcon {\n object-fit: contain;\n}\n\n/* The unavailable preview emphasizes the same actions with a labeled button. */\n.mrfw-openSplit[data-size='large'] {\n height: 36px;\n border-radius: var(--dsw-radius-md);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.mrfw-openSplit[data-size='large'] .mrfw-openMain {\n gap: 6px;\n padding: 6px 14px;\n font-size: 14px;\n}\n\n.mrfw-openSplit[data-size='large'] .mrfw-openChevron {\n padding: 6px 10px;\n}\n\n.mrfw-openSkeleton { display: inline-block; flex: none; border-radius: var(--dsw-radius-xs); background: var(--dsw-alias-interactive-bg-hover); }\n\n.mrfw-overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n}\n\n.mrfw-mask {\n position: absolute;\n inset: 0;\n background: var(--dsw-alias-bg-mask-1);\n backdrop-filter: var(--dsw-mask-blur);\n}\n\n.mrfw-dialog {\n position: relative;\n z-index: 1;\n display: flex;\n flex-direction: column;\n width: min(640px, 100%);\n max-height: 100%;\n overflow: hidden;\n border: none;\n border-radius: 24px;\n background: var(--dsw-alias-bg-layer-2);\n box-shadow: var(--dsw-elevation-prominent);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n outline: none;\n}\n\n.mrfw-header {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 22px 14px 12px 24px;\n}\n\n.mrfw-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-close {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 8px;\n background: transparent;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-close:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-body {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n padding: 0 24px 24px;\n}\n\n.mrfw-alert {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.mrfw-section {\n display: flex;\n flex-direction: column;\n}\n\n.mrfw-section + .mrfw-section {\n margin-top: 8px;\n}\n\n.mrfw-sectionTitleRow {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 0 4px;\n}\n\n.mrfw-sectionTitle {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-sectionNote {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.mrfw-rowBare {\n border-bottom: none;\n}\n\n/* A root entry's two lines: the display name on top, the absolute path under\n it in the tertiary voice (the workspace rows recipe of the host sidebar). */\n/* Match upstream General Settings' DeveloperToolsRow layout. */\n.mrfw-skillsSettings {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 24px;\n padding: 16px 0;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n.mrfw-skillsSettingsText { min-width: 0; }\n.mrfw-skillsSettingsTitle { font-size: 14px; line-height: 20px; }\n.mrfw-skillsSettingsDescription {\n margin-top: 4px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 18px;\n}\n.mrfw-rootText {\n display: flex;\n flex: 1 1 240px;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n/* Keep status tags and permission glyphs visible beside the alias, wrapping\n onto another line when the dialog is narrow. */\n.mrfw-rootName {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n min-width: 0;\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-rootNameText {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mrfw-rootPath {\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n line-height: 18px;\n word-break: break-all;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* The path doubles as the copy control: an unstyled button that keeps the\n text recipe and makes the whole string clickable \u2014 the whole region, not\n just the icon, copies on click. */\n.mrfw-pathCopy {\n display: block;\n width: 100%;\n padding: 0;\n border: 0;\n background: transparent;\n text-align: left;\n cursor: pointer;\n}\n\n/* The trailing icon stays in flow but invisible until the path is hovered\n or focused: appearing never re-wraps the text, and the icon inherits the\n path's own tertiary ink. */\n.mrfw-pathCopyIcon {\n display: inline-flex;\n margin-left: 4px;\n vertical-align: -2px;\n opacity: 0;\n transition: opacity var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-pathCopy:hover .mrfw-pathCopyIcon,\n.mrfw-pathCopy:focus-visible .mrfw-pathCopyIcon {\n opacity: 1;\n}\n\n.mrfw-note {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-stateWarn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.mrfw-btn {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: 14px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-btn[aria-busy='true'] {\n cursor: wait;\n}\n\n.mrfw-btn:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.mrfw-btnGhost:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnGhost:active:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n/* The solid destructive action of the capsule family: the upgrade confirm's\n red button. state-error-primary is the error ink upstream also uses as a\n fill (StateDot); the hover mixes in the host's primary label token to\n follow its theme and contrast choices. Neither color is a guessed token:\n an undefined var() would fall back\n to transparent and white-out the whole button. */\n.mrfw-btnDanger {\n background: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.mrfw-btnDanger:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 85%, var(--dsw-alias-label-primary));\n}\n\n.mrfw-btnOutline {\n border: 0.5px solid var(--dsw-alias-border-l3);\n background: transparent;\n}\n\n.mrfw-btnOutline:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnPrimary {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.mrfw-btnPrimary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n/* One icon-only action: a square of the capsule family, meaning in title.\n Declared after the .mrfw-btn family so its padding wins the cascade tie:\n the capsule's 0 10px padding would otherwise squeeze the icon glyph. */\n.mrfw-iconBtn {\n width: 28px;\n padding: 0;\n border-radius: 8px;\n}\n\n.mrfw-input {\n flex: 1 1 220px;\n min-width: 0;\n height: 32px;\n padding: 0 8px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n outline: none;\n transition: border-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-input:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.mrfw-input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n.mrfw-inputInline {\n flex: 1 1 auto;\n}\n\n.mrfw-accessSelect {\n flex: 0 0 auto;\n}\n\n/* The access dropdown's capsule trigger: the Menu anchor wrapper is the flex\n item, the button inside keeps the control row's 32px capsule recipe. */\n.mrfw-accessTrigger {\n padding: 0 12px;\n}\n\n.mrfw-cwdTag {\n flex: none;\n padding: 0 6px;\n border-radius: 4px;\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n line-height: 18px;\n}\n\n.mrfw-accessBadge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.mrfw-accessBadge {\n flex: none;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-aliasEditor {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex-basis: 100%;\n}\n\n.mrfw-actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 16px;\n padding-top: 16px;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n/* One control row, one height: the capsule buttons match the 32px input. */\n.mrfw-actions .mrfw-btn {\n height: 32px;\n border-radius: 16px;\n}\n\n.mrfw-actions .mrfw-iconBtn {\n width: 32px;\n}\n";
@@ -37,7 +37,7 @@ export declare const COMMAND_NAME = "workspace-folders";
37
37
  /** One parsed command line. */
38
38
  export interface FoldersCommand {
39
39
  /** The subcommand; `list` when the line named none. */
40
- readonly verb: 'list' | 'add' | 'remove' | 'alias' | 'access' | 'reveal' | 'isolate-profiles' | 'help';
40
+ readonly verb: 'list' | 'add' | 'remove' | 'alias' | 'alias-id' | 'skills' | 'access' | 'reveal' | 'isolate-profiles' | 'help';
41
41
  /** Everything after the verb, verbatim (paths may contain spaces). */
42
42
  readonly rest: string;
43
43
  }
@@ -32,3 +32,5 @@
32
32
  export declare function currentSessionIdOf(sessions: unknown): string | undefined;
33
33
  /** Observe native catalog changes when this surface provides a subscribable store. */
34
34
  export declare function subscribeSessions(sessions: unknown, listener: () => void): () => void;
35
+ /** Selection changes only: activity/title updates must not close a Skill preview. */
36
+ export declare function subscribeCurrentSession(sessions: unknown, listener: () => void): () => void;
@@ -28,7 +28,7 @@
28
28
  * the upgrade smoke passed on that exact version — a green CI run on an
29
29
  * unlisted release is evidence, not authorization (ADR-0009).
30
30
  */
31
- export declare const SUPPORTED_DSH_RELEASES: readonly ["0.1.5-rc.2", "0.1.6-alpha.1", "0.1.6-alpha.2", "0.1.7-alpha.1", "0.1.7-alpha.2", "0.1.7-rc.1", "0.1.7-rc.2", "0.2.0-rc.1", "0.2.0-rc.2", "0.2.1-alpha.1", "0.2.1-alpha.2", "0.2.1-alpha.3", "0.2.1-alpha.4", "0.2.1-alpha.5", "0.2.1-rc.1", "0.2.1-rc.2"];
31
+ export declare const SUPPORTED_DSH_RELEASES: readonly ["0.1.5-rc.2", "0.1.6-alpha.1", "0.1.6-alpha.2", "0.1.7-alpha.1", "0.1.7-alpha.2", "0.1.7-rc.1", "0.1.7-rc.2", "0.2.0-rc.1", "0.2.0-rc.2", "0.2.1-alpha.1", "0.2.1-alpha.2", "0.2.1-alpha.3", "0.2.1-alpha.4", "0.2.1-alpha.5", "0.2.1-rc.1", "0.2.1-rc.2", "0.2.1-rc.3", "0.2.1-rc.4", "0.2.2-alpha.2"];
32
32
  /** One release this plugin claims to support. */
33
33
  export type SupportedDshRelease = typeof SUPPORTED_DSH_RELEASES[number];
34
34
  /**
@@ -42,7 +42,7 @@ export declare const REQUIRED_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@
42
42
  * Absence is fine; a version that DISAGREES with the required packages is not —
43
43
  * that is the mixed-installation failure this contract exists to catch.
44
44
  */
45
- export declare const OPTIONAL_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
45
+ export declare const OPTIONAL_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-api-session-controller", "@deepseek-ai/dsh-session-query", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
46
46
  /**
47
47
  * The optional packages this plugin loads itself at run time, through dynamic
48
48
  * `import()`s inside this package's own modules. The copy this module resolves
@@ -50,7 +50,7 @@ export declare const OPTIONAL_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-tools",
50
50
  * private copy inside this package's own tree genuinely loads, and a version
51
51
  * that disagrees with the host is a real mixed installation, not a phantom.
52
52
  */
53
- export declare const RUNTIME_OPTIONAL_PACKAGES: readonly ["@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-typert-protocol"];
53
+ export declare const RUNTIME_OPTIONAL_PACKAGES: readonly ["@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-typert-protocol"];
54
54
  /**
55
55
  * The optional packages this plugin consumes only as mounted host services or
56
56
  * ambient types — it never imports them, so a copy inside this package's own
@@ -64,7 +64,7 @@ export declare const RUNTIME_OPTIONAL_PACKAGES: readonly ["@deepseek-ai/dsh-agen
64
64
  * devDependencies on a `0.2.0-rc.2` host produced a `mixed` verdict on a
65
65
  * uniformly `0.2.0-rc.2` installation).
66
66
  */
67
- export declare const HOST_SERVICE_PACKAGES: readonly ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory"];
67
+ export declare const HOST_SERVICE_PACKAGES: readonly ["@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-api-session-controller", "@deepseek-ai/dsh-session-query", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory"];
68
68
  /**
69
69
  * Optional packages this plugin executes that are resolved through an anchor
70
70
  * OTHER than this module. The standalone Windows ACL runner
@@ -79,7 +79,7 @@ export declare const HOST_SERVICE_PACKAGES: readonly ["@deepseek-ai/dsh-agent",
79
79
  */
80
80
  export declare const RUNNER_ANCHORED_PACKAGES: readonly ["@deepseek-ai/dsh-sandbox-windows-acl"];
81
81
  /** Every package the contract inspects, required first. */
82
- export declare const CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-fs-local", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-local", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-storage-domain", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
82
+ export declare const CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-fs-local", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-local", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-storage-domain", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-api-session-controller", "@deepseek-ai/dsh-session-query", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
83
83
  /** How this plugin reacts to an installation it does not support. */
84
84
  export type CompatEnforcement = 'enforce' | 'warn';
85
85
  /** The environment variable that relaxes the gate. */
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Compatibility adapter for `isMobileDevice` from
3
+ * `@deepseek-ai/dsh-client-ui-primitives`.
4
+ *
5
+ * ```text
6
+ * 0.1.5-rc.2 through 0.2.1-rc.4 no `isMobileDevice` export
7
+ * 0.2.2-alpha.2 + `isMobileDevice` exported
8
+ * ```
9
+ *
10
+ * The open-in-app fork's verbatim `open-toast.tsx` spells the detector as a
11
+ * primitives import — a name absent before `0.2.2-alpha.2` — so the lookup
12
+ * is by export name, not by release string (the `client-icons.ts` pattern).
13
+ * An older runtime falls back to upstream's identical detection: mobile
14
+ * userAgent, or a touch-capable MacIntel platform, memoized like upstream's.
15
+ *
16
+ * @module @dsh-electron/dsh-plugin-multi-root-workspace/compat/open-in-app-device
17
+ */
18
+ /**
19
+ * Resolve the installed release's `isMobileDevice`, or the identical local
20
+ * fallback on releases that predate the export.
21
+ * @returns the mobile-device verdict.
22
+ */
23
+ export declare function isMobileDevice(): boolean;
@@ -0,0 +1,3 @@
1
+ /** Native rendering is loaded only when a namespaced invocation delivers a body. */
2
+ import type { SkillDefinition } from '@deepseek-ai/dsh-skill';
3
+ export declare function renderAdditionalSkill(skill: SkillDefinition): Promise<string>;