dsh-claude-desktop-theme 0.5.0 → 0.5.2

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 (44) hide show
  1. package/README.md +8 -6
  2. package/lib/client.js +7916 -5407
  3. package/lib/index.js +6 -6
  4. package/lib/types/client/compat/account-balance-hint.d.ts +11 -0
  5. package/lib/types/client/compat/account-balance.d.ts +2 -0
  6. package/lib/types/client/compat/conversation-stream.d.ts +5 -0
  7. package/lib/types/client/compat/files-pane.d.ts +2 -2
  8. package/lib/types/client/compat/header-menu-actions.d.ts +0 -2
  9. package/lib/types/client/compat/host-builds.d.ts +5 -4
  10. package/lib/types/client/compat/host-dom.d.ts +19 -2
  11. package/lib/types/client/compat/search-palette-port.d.ts +4 -0
  12. package/lib/types/client/compat/sidebar-editor.d.ts +32 -0
  13. package/lib/types/client/compat/sidebar-more-menu.d.ts +13 -0
  14. package/lib/types/client/compat/sidebar-navigation.d.ts +8 -0
  15. package/lib/types/client/compat/sidebar-panels.d.ts +62 -0
  16. package/lib/types/client/compat/sidebar-search.d.ts +13 -14
  17. package/lib/types/client/compat/workspace-menu.d.ts +2 -1
  18. package/lib/types/client/compat/workspace-worktree.d.ts +12 -2
  19. package/lib/types/client/contracts/ports.d.ts +102 -0
  20. package/lib/types/client/features/composer-pet/claude-frames.d.ts +2 -2
  21. package/lib/types/client/features/right-panel/mount.d.ts +5 -0
  22. package/lib/types/client/features/search-palette/SearchPalette.d.ts +23 -0
  23. package/lib/types/client/features/search-palette/model.d.ts +109 -0
  24. package/lib/types/client/features/search-palette/mount.d.ts +17 -0
  25. package/lib/types/client/features/settings/ColourMenu.d.ts +16 -0
  26. package/lib/types/client/features/settings/FontMenu.d.ts +1 -1
  27. package/lib/types/client/features/settings/ThemePanel.d.ts +21 -0
  28. package/lib/types/client/features/settings/colours.d.ts +8 -0
  29. package/lib/types/client/features/settings/fields.d.ts +6 -3
  30. package/lib/types/client/features/settings/locales.d.ts +16 -5
  31. package/lib/types/client/features/settings/mount.d.ts +7 -5
  32. package/lib/types/client/features/settings/register.d.ts +13 -2
  33. package/lib/types/client/features/sidebar/navigation-icons.d.ts +3 -0
  34. package/lib/types/client/features/sidebar-editor/MoreCardRows.d.ts +24 -0
  35. package/lib/types/client/features/sidebar-editor/SidebarEditor.d.ts +20 -0
  36. package/lib/types/client/features/sidebar-editor/copy.d.ts +9 -0
  37. package/lib/types/client/features/sidebar-editor/mount.d.ts +18 -0
  38. package/lib/types/client/features/sidebar-editor/state.d.ts +31 -0
  39. package/lib/types/client/theme/palette.d.ts +1 -1
  40. package/lib/types/host/index.d.ts +16 -16
  41. package/lib/types/shared/config.d.ts +4 -4
  42. package/lib/types/shared/identity.d.ts +1 -1
  43. package/package.json +16 -16
  44. package/lib/types/client/features/shell/index.d.ts +0 -14
package/lib/index.js CHANGED
@@ -10,8 +10,8 @@ var DEFAULT_PERSONALIZATION = Object.freeze({
10
10
  greetingName: ""
11
11
  });
12
12
  var DEFAULT_FEATURES = Object.freeze({
13
- shell: true,
14
13
  sidebar: true,
14
+ "right-panel": true,
15
15
  "new-session": true,
16
16
  conversation: true,
17
17
  "tool-calls": true,
@@ -1258,11 +1258,11 @@ function mountWorktrees(ctx, enabled) {
1258
1258
  var HEX_COLOUR = /^(#[0-9a-fA-F]{3}|#[0-9a-fA-F]{6})?$/;
1259
1259
  var FONT_NAME = new RegExp(`^[\\p{L}\\p{N} ._-]{0,${FONT_NAME_MAX_LENGTH}}$`, "u");
1260
1260
  var Config = z.object({
1261
- enabled: z.boolean().default(false).description("\u542F\u7528 CCD \u98CE\u683C\u754C\u9762").volatile(),
1261
+ enabled: z.boolean().default(false).description("\u5F00\u542F / \u5173\u95ED Claude \u4E3B\u9898").volatile(),
1262
1262
  debug: z.boolean().default(false).description("\u5728\u63A7\u5236\u53F0\u8F93\u51FA\u8C03\u8BD5\u65E5\u5FD7").volatile(),
1263
1263
  features: z.object({
1264
- shell: z.boolean().default(DEFAULT_FEATURES.shell),
1265
1264
  sidebar: z.boolean().default(DEFAULT_FEATURES.sidebar),
1265
+ "right-panel": z.boolean().default(DEFAULT_FEATURES["right-panel"]),
1266
1266
  "new-session": z.boolean().default(DEFAULT_FEATURES["new-session"]),
1267
1267
  conversation: z.boolean().default(DEFAULT_FEATURES.conversation),
1268
1268
  "tool-calls": z.boolean().default(DEFAULT_FEATURES["tool-calls"]),
@@ -1286,10 +1286,10 @@ var Config = z.object({
1286
1286
  code: z.string().default(DEFAULT_FONTS.code).description("\u4EE3\u7801\u4E0E\u7B49\u5BBD\u5B57\u4F53\u540D\uFF08\u7559\u7A7A\u7528\u7CFB\u7EDF\u6808\uFF09").pattern(FONT_NAME)
1287
1287
  }).default({ ...DEFAULT_FONTS }).description("\u5B57\u4F53").volatile(),
1288
1288
  personalization: z.object({
1289
- petStyle: z.union(["whale", "claude"]).default(DEFAULT_PERSONALIZATION.petStyle).description("\u8F93\u5165\u6846\u5BA0\u7269\u6837\u5F0F"),
1289
+ petStyle: z.union(["whale", "claude", "none"]).default(DEFAULT_PERSONALIZATION.petStyle).description("\u8F93\u5165\u6846\u5BA0\u7269\u6837\u5F0F"),
1290
1290
  runningStyle: z.union(["whale", "claude"]).default(DEFAULT_PERSONALIZATION.runningStyle).description("\u804A\u5929\u8F93\u51FA\u4E2D\u7684\u7B49\u5F85\u56FE\u6807\u4E0E\u72B6\u6001\u6837\u5F0F"),
1291
- replySignatureStyle: z.union(["whale", "claude"]).default(DEFAULT_PERSONALIZATION.replySignatureStyle).description("\u56DE\u590D\u7ED3\u675F\u540E\u7684\u7B7E\u540D\u56FE\u6807\u6837\u5F0F"),
1292
- greetingStyle: z.union(["explore", "claude"]).default(DEFAULT_PERSONALIZATION.greetingStyle).description("\u9996\u9875\u95EE\u5019\u8BED\u4E0E\u56FE\u6807\u6837\u5F0F"),
1291
+ replySignatureStyle: z.union(["whale", "claude", "none"]).default(DEFAULT_PERSONALIZATION.replySignatureStyle).description("\u56DE\u590D\u7ED3\u675F\u540E\u7684\u7B7E\u540D\u56FE\u6807\u6837\u5F0F"),
1292
+ greetingStyle: z.union(["explore", "claude", "none"]).default(DEFAULT_PERSONALIZATION.greetingStyle).description("\u9996\u9875\u95EE\u5019\u8BED\u4E0E\u56FE\u6807\u6837\u5F0F"),
1293
1293
  greetingName: z.string().default("").pattern(/^.{0,64}$/u).description("\u95EE\u5019\u8BED\u4E2D\u7684\u79F0\u547C\uFF08\u53EF\u7559\u7A7A\uFF09")
1294
1294
  }).default({ ...DEFAULT_PERSONALIZATION }).description("\u4E2A\u6027\u5316").volatile(),
1295
1295
  view: z.object({
@@ -0,0 +1,11 @@
1
+ import type { Disposer } from '../contracts/ports.ts';
2
+ export interface AccountBalanceBreakdown {
3
+ total: string;
4
+ toppedUp: string;
5
+ granted: string;
6
+ }
7
+ /** The account menu owns this top-layer hint, including its delayed teardown. */
8
+ export declare function mountAccountBalanceHint(menu: HTMLElement, row: HTMLElement, zh: boolean): {
9
+ update: (balance: AccountBalanceBreakdown | undefined, message: string) => void;
10
+ dispose: Disposer;
11
+ };
@@ -1,8 +1,10 @@
1
1
  import type { Disposer } from '../contracts/ports.ts';
2
+ import { type AccountBalanceBreakdown } from './account-balance-hint.ts';
2
3
  /** Last completed display, retained across menu openings for the same account. */
3
4
  export interface AccountBalanceCache {
4
5
  text?: string;
5
6
  description?: string;
7
+ balance?: AccountBalanceBreakdown;
6
8
  }
7
9
  /** One usage row in the lower-left account menu; polling lives only while open. */
8
10
  export declare function mountAccountBalance(menu: HTMLElement, cache?: AccountBalanceCache): Disposer;
@@ -0,0 +1,5 @@
1
+ import type { Disposer } from '../contracts/ports.ts';
2
+ /** Only append-only visible text gets an entrance; reparsing is already visible. */
3
+ export declare function streamingAppendStart(previous: string, text: string): number | null;
4
+ /** Fade new reply text through native Ranges: never wrap, clone or delay Markdown. */
5
+ export declare function mountConversationStream(document: Document, report: (error: unknown) => void): Disposer;
@@ -40,8 +40,8 @@ import type { Disposer } from '../contracts/ports.ts';
40
40
  *
41
41
  * Everything written here lands on host nodes as `data-ccd-files-*` markers or
42
42
  * as plugin-owned nodes beside them, and the disposer takes all of it back.
43
- * The module is unconditional: the right panel exists in both DSH builds and
44
- * behind every feature switch.
43
+ * The module shares the right-panel feature lifetime with its stylesheet,
44
+ * so disabling that feature removes every enhancement before its styles.
45
45
  */
46
46
  /** The state of the tree, written on the Files root: `hidden` or absent. */
47
47
  export declare const TREE_ATTRIBUTE = "data-ccd-files-tree";
@@ -3,8 +3,6 @@ import type { Context } from '@deepseek-ai/cordis';
3
3
  export interface HeaderMenuActions {
4
4
  read(id: string): {
5
5
  title: string;
6
- folder: string;
7
- path: string;
8
6
  } | null;
9
7
  rename(id: string, title: string): Promise<void>;
10
8
  fork(id: string): Promise<void>;
@@ -13,10 +13,9 @@
13
13
  * and the static web frontend's markdown classes (`_markdown_1ypvv_5`) are
14
14
  * identical in both builds, so neither needs an entry here.
15
15
  *
16
- * The tables are pinned to the DSH version the plugin declares
17
- * (`dsh.engines.dsh = "=0.2.0-rc.2"`): a later release may rehash every prefix
18
- * again, and both tables then have to be refreshed from that build's
19
- * `app.asar` — the same single-place update `host-dom.ts` documents.
16
+ * Keep the authored build stable and register each verified release below.
17
+ * A new release may rehash every prefix; read its tables from `app.asar`.
18
+ * The latest macOS build is also the preload fallback before the frame mounts.
20
19
  */
21
20
  /** One shipped DSH build and the prefixes it uses for the pinned classes. */
22
21
  export interface HostBuild {
@@ -38,6 +37,8 @@ export declare const PINNED_BUILD: HostBuild;
38
37
  * prefix this plugin authored.
39
38
  */
40
39
  export declare const WINDOWS_BUILD: HostBuild;
40
+ /** DSH `0.2.1-rc.3`, macOS commit `be72761e`, read from its shipped app.asar. */
41
+ export declare const MACOS_BUILD: HostBuild;
41
42
  /** Every build whose class names this plugin knows how to speak. */
42
43
  export declare const HOST_BUILDS: readonly HostBuild[];
43
44
  /** How a document's build was decided. */
@@ -65,7 +65,7 @@ export declare const HOST: Readonly<{
65
65
  * inline (the resolved width, or `100vw` in full screen). Its
66
66
  * `data-sidebar-right-panel` (`push` / `fullscreen`) and
67
67
  * `data-sidebar-right-open` are the package's own stable hooks, so the card
68
- * `shell.css` paints on it survives a build whose classes were rehashed; this
68
+ * `features/right-panel/frame.css` paints on it survives a build whose classes were rehashed; this
69
69
  * plugin authors no selector for the panel's hashed class.
70
70
  */
71
71
  readonly rightbarPanel: '[data-sidebar-right-panel]';
@@ -77,7 +77,7 @@ export declare const HOST: Readonly<{
77
77
  * on, `pane` a dock column that would otherwise paint the canvas over it,
78
78
  * `strip` the tab strip (the card's header row), `divider` the hairline
79
79
  * between two columns, `empty` a column no tab has claimed. Only
80
- * `features/shell/right-panel.css` reads them.
80
+ * `features/right-panel/right-panel.css` reads them.
81
81
  */
82
82
  readonly dockSurface: '[data-dockkit-surface]';
83
83
  readonly dockPane: '[data-dockkit-pane]';
@@ -144,6 +144,8 @@ export declare const HOST: Readonly<{
144
144
  readonly sidebarPanelRow: '._3WPZCG_panelRow';
145
145
  readonly sidebarPanelTitle: '._3WPZCG_panelTitle';
146
146
  readonly sidebarPanelGlyph: '._3WPZCG_panelGlyph';
147
+ /** SidebarRoot.module.css: the panel list's More card (`PanelMoreMenu`). */
148
+ readonly sidebarMoreMenu: '._3WPZCG_moreMenu';
147
149
  readonly sidebarRegionArea: '._3WPZCG_regionArea';
148
150
  readonly sidebarFootArea: '._3WPZCG_footArea';
149
151
  readonly sidebarSettingsArea: '._3WPZCG_settingsArea';
@@ -346,6 +348,13 @@ export declare const HOST: Readonly<{
346
348
  no entry in `host-builds.ts` — the same fact `tokens.css` records for the
347
349
  primitive's check mark. */
348
350
  readonly menuItemLabel: '._itemLabel_4ub78_190';
351
+ /** Menu.module.css: `MenuItemButton`'s wrap, its button and the separator a
352
+ wrap may lead with — the markup the plugin's own rows in a host menu
353
+ repeat (`features/sidebar-editor/MoreCardRows.tsx`), so the card's
354
+ keyboard walk and every popup rule reach them as host rows. */
355
+ readonly menuItemWrap: '._itemWrap_4ub78_90';
356
+ readonly menuItemButton: '._item_4ub78_90';
357
+ readonly menuSeparator: '._separator_4ub78_80';
349
358
  /** Menu.module.css: the trailing mark a *selected* row draws — `Menu` renders
350
359
  it only for the row whose id is in `selectedIds`, so its presence is how
351
360
  `compat/view-options.ts` reads which option is current. */
@@ -438,6 +447,14 @@ export type HostAnchors = Readonly<Record<keyof typeof ANCHOR, string>>;
438
447
  * @param document - the renderer document to speak to.
439
448
  */
440
449
  export declare function hostSelectors(document: Document): HostSelectors;
450
+ export interface MenuRowClasses {
451
+ readonly itemWrap: string;
452
+ readonly item: string;
453
+ readonly itemLabel: string;
454
+ readonly separator: string;
455
+ }
456
+ /** The `Menu` primitive's row classes, for rows this plugin renders into a host menu. */
457
+ export declare function menuRowClasses(document: Document): MenuRowClasses;
441
458
  /** The anchors of this document's build; see {@link hostSelectors}. */
442
459
  export declare function hostAnchors(document: Document): HostAnchors;
443
460
  export interface HostProbe {
@@ -0,0 +1,4 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { SearchPalettePort } from '../contracts/ports.ts';
3
+ /** Read the palette's lists from the host catalogs and drive the host's own navigation. */
4
+ export declare function searchPalettePort(ctx: Context, document: Document, report: (error: unknown) => void): SearchPalettePort;
@@ -0,0 +1,32 @@
1
+ import type { Disposer } from '../contracts/ports.ts';
2
+ import type { SidebarPanelsPort } from './sidebar-panels.ts';
3
+ /** The panel id this module read off a row's position in the list. */
4
+ export declare const SIDEBAR_PANEL_ATTRIBUTE = "data-ccd-panel";
5
+ /** Marks the list child the Edit sidebar dialog took out of the column. */
6
+ export declare const SIDEBAR_PANEL_HIDDEN_ATTRIBUTE = "data-ccd-panel-hidden";
7
+ /** The ids the Edit sidebar dialog keeps out of the column. */
8
+ export interface HiddenPanels {
9
+ has(id: string): boolean;
10
+ subscribe(listener: () => void): Disposer;
11
+ }
12
+ export interface SidebarPanelRows {
13
+ /** A copy of the glyph a panel's row draws, for the dialog's list. */
14
+ glyph(id: string): Element | null;
15
+ dispose: Disposer;
16
+ }
17
+ /**
18
+ * Name the panel rows by id and keep the hidden ones out of the column.
19
+ *
20
+ * `PanelRow` exposes no id, but `SidebarRoot` renders exactly one row per
21
+ * `sidebar.panellist` entry, in the order the port reads — the host's own pin
22
+ * filter is never written (its only setter reaches the More card's rows, which
23
+ * leave it alone) — so a row's position is its identity. When the counts
24
+ * disagree for a frame (an entry registering while the column has not caught
25
+ * up), the marks are withdrawn rather than guessed: nothing is hidden until the
26
+ * two agree again.
27
+ *
28
+ * A hidden row stays where React rendered it: the mark goes on the list child
29
+ * that holds it, and `sidebar-editor.css` takes that child out of the flow, so
30
+ * the list's gap closes and DSH keeps every node and handler.
31
+ */
32
+ export declare function mountSidebarPanelRows(document: Document, port: SidebarPanelsPort, hidden: HiddenPanels, report: (error: unknown) => void): SidebarPanelRows;
@@ -0,0 +1,13 @@
1
+ import type { Disposer } from '../contracts/ports.ts';
2
+ /** Marks the sidebar More card this module places beside its row. */
3
+ export declare const SIDEBAR_MORE_MENU_ATTRIBUTE = "data-ccd-sidebar-more-menu";
4
+ /**
5
+ * Open the sidebar panel list's More card to the right of its row, like the
6
+ * reference's sidebar menu, and shorten its two entry-customization rows.
7
+ *
8
+ * `SidebarRoot` renders the card with the primitive's default bottom side and
9
+ * rewrites `left`/`top` on every frame, so the position is published as two
10
+ * custom properties the stylesheet applies with `!important`; React keeps
11
+ * owning the inline coordinates and the rows keep their host handlers.
12
+ */
13
+ export declare function mountSidebarMoreMenu(document: Document, report: (error: unknown) => void): Disposer;
@@ -3,8 +3,16 @@ export interface SidebarNavigationIcons {
3
3
  readonly newSession: string;
4
4
  readonly plugins: string;
5
5
  readonly routines: string;
6
+ readonly more: string;
7
+ readonly theme: string;
6
8
  }
7
9
  export declare const SIDEBAR_NAV_ATTRIBUTE = "data-ccd-nav";
10
+ /** Marks the glyph this plugin's own Theme panel draws, so its row is known by
11
+ what it renders rather than by a label another panel could share. */
12
+ export declare const THEME_PANEL_GLYPH_ATTRIBUTE = "data-ccd-theme-glyph";
13
+ /** The name a panel row shows once this module has renamed it: 插件 reads 自定义
14
+ and 自动化任务 reads 定时任务, every other panel keeps its own label. */
15
+ export declare function panelDisplayName(document: Document, name: string): string;
8
16
  /** Rename and decorate the native buttons without replacing React-owned nodes
9
17
  * or handlers. Every write is conditional and reversible, including delayed
10
18
  * mounts, locale changes, wide/rail rerenders and the native tooltip portals. */
@@ -0,0 +1,62 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { ReactElement } from 'react';
3
+ import type { Disposer } from '../contracts/ports.ts';
4
+ /**
5
+ * The sidebar's global panels as `ui-sidebar` lists them.
6
+ *
7
+ * Verified against the installed DSH 0.2.1-rc.3 client bundle: `SidebarRoot`
8
+ * reads `sidebar.panellist` entries' `id`, `order` and `label` (a string or a
9
+ * thunk), sorts them by `order`, and renders one `PanelRow` per entry — the
10
+ * entry's component is only the row's glyph, rendered with `{ size, active }`.
11
+ * The centre column renders the `main` keyed entry whose `key` is the active
12
+ * panel id. `ui-sidebar` is outside the SDK baseline, so the boundary stays
13
+ * structural here, like `settings-section.ts`.
14
+ */
15
+ export declare const SIDEBAR_PANEL_SLOT = "sidebar.panellist";
16
+ /** Rows of the panel list's More card; `ui-sidebar` shows More while this list has entries. */
17
+ export declare const SIDEBAR_PANEL_ACTIONS_SLOT = "sidebar.panellist.actions";
18
+ export interface SidebarPanel {
19
+ readonly id: string;
20
+ /** The name the row shows, after `sidebar-navigation.ts` renamed it. */
21
+ readonly label: string;
22
+ }
23
+ /** The panel list `ui-sidebar` renders and the one call that opens an entry. */
24
+ export interface SidebarPanelsPort {
25
+ /** Every registered panel in the order the column lists them. */
26
+ panels(): readonly SidebarPanel[];
27
+ subscribe(listener: () => void): Disposer;
28
+ /** The panel the centre column shows, or null on a Session. */
29
+ active(): string | null;
30
+ select(id: string): void;
31
+ }
32
+ /**
33
+ * @param ctx - client context carrying the slot and layout services.
34
+ * @param document - the renderer document, whose language names renamed rows.
35
+ */
36
+ export declare function sidebarPanelsPort(ctx: Context, document: Document): SidebarPanelsPort;
37
+ export interface MainPanelRegistration<P> {
38
+ readonly id: string;
39
+ readonly order: number;
40
+ readonly locale: string;
41
+ readonly label: () => string;
42
+ /** The centre column's page while the panel is active. */
43
+ readonly page: (props: P) => ReactElement | null;
44
+ /** The row's glyph, rendered with the column's `{ size, active }`. */
45
+ readonly glyph: (props: {
46
+ readonly size?: number;
47
+ readonly active?: boolean;
48
+ }) => ReactElement | null;
49
+ }
50
+ /** Register a global panel: its sidebar row and the page it opens. */
51
+ export declare function mountMainPanel<P>(ctx: Context, panel: MainPanelRegistration<P>): Disposer;
52
+ /**
53
+ * Add rows to the panel list's More card. `PanelMoreMenu` renders every
54
+ * `sidebar.panellist.actions` entry, by `order`, inside its `Menu` list with
55
+ * `{ entries, onOpen, onPinnedChange, onDismiss }`; `inject`'s face is merged
56
+ * over those owner props.
57
+ */
58
+ export declare function mountMoreCardRows<P>(ctx: Context, entry: {
59
+ readonly id: string;
60
+ readonly order: number;
61
+ readonly inject: () => Record<string, unknown>;
62
+ }, rows: (props: P) => ReactElement | null): Disposer;
@@ -1,18 +1,17 @@
1
- import type { Disposer } from '../contracts/ports.ts';
2
- /** Marks the sidebar column whose field is currently parked above New Session. */
1
+ import type { Disposer, SearchPaletteOpener } from '../contracts/ports.ts';
2
+ /** Marks the sidebar column whose Search row this module placed below New Session. */
3
3
  export declare const SIDEBAR_SEARCH_PLACED_ATTRIBUTE = "data-ccd-sidebar-search-placed";
4
4
  /**
5
- * Park the browsing region's search field above New Session.
5
+ * Give Search its own navigation row below New Session, like the reference's
6
+ * sidebar, and open the search palette from it.
6
7
  *
7
- * The field belongs to `ui-workspace`'s `WorkspaceBrowser`, and React removes
8
- * it from the section header whenever the sidebar collapses to its rail.
9
- * Moving the React-owned node would make that removal target the wrong parent,
10
- * so the node stays where React put it and is positioned over the gap that New
11
- * Session opens above itself. That gap sits after the brand row when the brand
12
- * is visible and directly below the window strip when it is not.
13
- *
14
- * The section header keeps its own row. With the search slot out of flow, its
15
- * label takes the leading edge and the native View options trigger stays on the
16
- * trailing edge.
8
+ * The host's field belongs to `ui-workspace`'s `WorkspaceBrowser`, and React
9
+ * removes it from the section header whenever the sidebar collapses to its
10
+ * rail, so the node stays where React put it. While the row is placed,
11
+ * `sidebar.css` takes the field out of sight without making it unfocusable: the
12
+ * host's own `session.search` shortcut (⌘K) still focuses it, and that focus is
13
+ * what this module turns into opening the palette. The row itself is the
14
+ * plugin's own button, inserted after New Session's node; React never addresses
15
+ * it, so a re-render only needs it put back.
17
16
  */
18
- export declare function mountSidebarSearch(document: Document, report: (error: unknown) => void): Disposer;
17
+ export declare function mountSidebarSearch(document: Document, icon: string, palette: SearchPaletteOpener, report: (error: unknown) => void): Disposer;
@@ -17,7 +17,8 @@ export declare function openFolderLabel(text: string, zh: boolean): string | nul
17
17
  * commit that mounts it and never shows the host's glyph first.
18
18
  *
19
19
  * @param document - renderer document carrying the hero chip and the portal.
20
- * @param port - Workspace order and new-worktree choices.
20
+ * @param port - Workspace order and new-worktree choices (the toggle itself is
21
+ * `mountWorktreeChip`, beside the folder chip).
21
22
  * @param report - sink for observer failures; the native label stays untouched.
22
23
  * @returns disposer that disconnects every observer and restores each label.
23
24
  */
@@ -26,7 +26,17 @@ export declare class WorktreeChoices implements WorktreePickerPort {
26
26
  subscribe(listener: () => void): Disposer;
27
27
  prepare(session: string, create: (workspace: string, from?: string) => Promise<WorktreeResult>): Promise<void>;
28
28
  }
29
- /** Claude's compact bottom row: a menu checkbox with a purely visual switch. */
30
- export declare function mountWorktreeToggle(card: HTMLElement, workspace: string, port: WorktreePickerPort): Disposer;
29
+ /**
30
+ * Claude's worktree toggle beside the folder chip: a pressed chip, not a menu
31
+ * row. It is shown only for a new session whose folder is a Git repository,
32
+ * and the optional runtime is enabled. Recheck while visible and on returning
33
+ * to the composer so plugin enable/disable does not leave a stale button.
34
+ *
35
+ * @param document - renderer document carrying the hero workspace row.
36
+ * @param port - per-session worktree choices.
37
+ * @param workspaceOf - the Workspace a session belongs to.
38
+ * @param report - sink for observer failures.
39
+ */
40
+ export declare function mountWorktreeChip(document: Document, port: WorktreePickerPort, workspaceOf: (session: string) => string | undefined, report: (error: unknown) => void): Disposer;
31
41
  /** Request retries reuse the Host's per-session result, including after a lost response. */
32
42
  export declare function createSessionWorktree(session: string, workspace: string, from?: string): Promise<WorktreeResult>;
@@ -296,6 +296,108 @@ export interface ContextPanelPort {
296
296
  /** Flip the panel between its collapsed and expanded states. */
297
297
  toggleExpanded(): void;
298
298
  }
299
+ /** One Session as the search palette lists it. */
300
+ export interface PaletteSession {
301
+ readonly id: string;
302
+ readonly title: string;
303
+ /** Title of the Workspace the Session is accounted to; null when ungrouped. */
304
+ readonly workspace: string | null;
305
+ /** Durable activity time, milliseconds since the epoch. */
306
+ readonly updatedAt: number;
307
+ readonly running: boolean;
308
+ readonly archived: boolean;
309
+ readonly pinned: boolean;
310
+ }
311
+ /** One Workspace as the search palette lists it. */
312
+ export interface PaletteProject {
313
+ readonly id: string;
314
+ readonly title: string;
315
+ readonly path: string;
316
+ /** Sessions accounted to it, archived ones included. */
317
+ readonly sessions: number;
318
+ }
319
+ /** A schedule's stored rule, reduced to what its label needs. */
320
+ export type PaletteScheduleRule = {
321
+ readonly kind: 'once';
322
+ readonly at: string;
323
+ } | {
324
+ readonly kind: 'every';
325
+ readonly seconds: number;
326
+ } | {
327
+ readonly kind: 'daily';
328
+ readonly time: string;
329
+ } | {
330
+ readonly kind: 'weekly';
331
+ readonly time: string;
332
+ readonly weekdays: readonly number[];
333
+ } | {
334
+ readonly kind: 'cron';
335
+ readonly expression: string;
336
+ };
337
+ /** One scheduled task from the host catalog. */
338
+ export interface PaletteSchedule {
339
+ readonly id: string;
340
+ readonly sessionId: string;
341
+ readonly title: string;
342
+ readonly prompt: string;
343
+ readonly rule: PaletteScheduleRule;
344
+ readonly active: boolean;
345
+ /** Last durably delivered run, milliseconds since the epoch; null before the first. */
346
+ readonly lastRunAt: number | null;
347
+ }
348
+ /** Everything the palette lists, as one immutable value. */
349
+ export interface SearchPaletteSnapshot {
350
+ /** Listed Sessions: no provisional blanks and no subagent children. */
351
+ readonly sessions: readonly PaletteSession[];
352
+ /** Workspaces in the host's own order. */
353
+ readonly projects: readonly PaletteProject[];
354
+ /** Null on a build without the schedule interface, or while it cannot be read. */
355
+ readonly schedules: readonly PaletteSchedule[] | null;
356
+ }
357
+ /** Run count read from a task's delivery history; `more` when the page was full. */
358
+ export interface PaletteScheduleRuns {
359
+ readonly count: number;
360
+ readonly more: boolean;
361
+ }
362
+ /**
363
+ * The host data and navigation the search palette is drawn from.
364
+ *
365
+ * Every list is read from the host's own catalogs (`sessions.list`,
366
+ * `workspaces.list`, `schedule/catalog()`) and every action goes through the
367
+ * host's own navigation or mutation, so the palette keeps no copy of state.
368
+ */
369
+ export interface SearchPalettePort {
370
+ /** The current lists; the object identity changes only when a list does. */
371
+ getSnapshot(): SearchPaletteSnapshot;
372
+ subscribe(listener: () => void): Disposer;
373
+ /** Re-read the schedule catalog; the palette asks on every open. */
374
+ refreshSchedules(): void;
375
+ scheduleRuns(id: string): Promise<PaletteScheduleRuns>;
376
+ /** The host's message-content search; at most its own result bound. */
377
+ searchContent(query: string, signal: AbortSignal): Promise<readonly {
378
+ readonly sessionId: string;
379
+ readonly snippet: string;
380
+ }[]>;
381
+ openSession(id: string): void;
382
+ /** Open the New Session page, in a Workspace and with a draft when given. */
383
+ startSession(options?: {
384
+ readonly workspaceId?: string;
385
+ readonly prompt?: string;
386
+ }): void;
387
+ /** Show the Scheduled tasks page and the task's own detail. */
388
+ openSchedule(id: string): void;
389
+ setPinned(id: string, pinned: boolean): Promise<void>;
390
+ setArchived(id: string, archived: boolean): Promise<void>;
391
+ /** Fork a Session and open the child. */
392
+ fork(id: string): Promise<void>;
393
+ copy(text: string): Promise<void>;
394
+ }
395
+ /** Opens the search palette; the sidebar row mirrors its open state. */
396
+ export interface SearchPaletteOpener {
397
+ open(): void;
398
+ isOpen(): boolean;
399
+ subscribe(listener: () => void): Disposer;
400
+ }
299
401
  /** Actual SDK types, never a second definition of DSH component props. */
300
402
  export interface HostServices {
301
403
  readonly slots: Context['slots'];
@@ -6,7 +6,7 @@
6
6
  export type ClaudeFrame = readonly [body: string, shade: string, eyes: string, laptop: string];
7
7
  export declare const CLAUDE_GROUND_Y = 23.5;
8
8
  export declare const CLAUDE_IDLE: ClaudeFrame;
9
- /** Pose order and hold times follow the recording; identical clean silhouettes
10
- * are reused so compression noise cannot animate otherwise stationary edges. */
9
+ /** Reuse clean silhouettes for the recorded sequence; typing uses a steady
10
+ * far / passing / near cadence instead of changing the hands' contours. */
11
11
  export declare const CLAUDE_FRAMES: readonly ClaudeFrame[];
12
12
  export declare const CLAUDE_TRACK: readonly number[];
@@ -0,0 +1,5 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { FeatureEnvironment } from '../../contracts/feature.ts';
3
+ import type { CleanupScope } from '../../core/cleanup.ts';
4
+ /** Styles, injected controls and pane layout share the right-sidebar switch. */
5
+ export declare function mountRightPanel(ctx: Context, environment: FeatureEnvironment, scope: CleanupScope): void;
@@ -0,0 +1,23 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { SearchPaletteOpener, SearchPalettePort } from '../../contracts/ports.ts';
3
+ /** The palette's open state, shared with the sidebar row that opens it. */
4
+ export interface PaletteState extends SearchPaletteOpener {
5
+ close(): void;
6
+ }
7
+ interface Props {
8
+ readonly state: PaletteState;
9
+ readonly port: SearchPalettePort;
10
+ readonly report: (error: unknown) => void;
11
+ }
12
+ /**
13
+ * The search palette: a modal card opened from the sidebar's Search row or ⌘K.
14
+ *
15
+ * It is drawn after the reference — a borderless field with a close button, a
16
+ * row of type tabs, sectioned results with the current row's `…` and ↵, and a
17
+ * footer of key hints — over lists read from the host's own catalogs. The
18
+ * card is a native modal `<dialog>`, so focus, the top layer and Esc are the
19
+ * browser's; the body is mounted per opening, so each opening starts from 全部
20
+ * with an empty field.
21
+ */
22
+ export declare function SearchPalette({ state, port, report }: Props): ReactElement;
23
+ export {};
@@ -0,0 +1,109 @@
1
+ import type { PaletteProject, PaletteSchedule, PaletteScheduleRule, PaletteScheduleRuns, PaletteSession, SearchPaletteSnapshot } from '../../contracts/ports.ts';
2
+ /** The palette's type filter; `all` mixes every list. */
3
+ export type PaletteTab = 'all' | 'session' | 'project' | 'schedule';
4
+ /** The first matched run of a label, as UTF-16 offsets. */
5
+ export type MatchRange = readonly [start: number, end: number] | null;
6
+ export type PaletteEntry = {
7
+ readonly kind: 'session';
8
+ readonly key: string;
9
+ readonly session: PaletteSession;
10
+ readonly match: MatchRange;
11
+ readonly snippet: string | null;
12
+ } | {
13
+ readonly kind: 'project';
14
+ readonly key: string;
15
+ readonly project: PaletteProject;
16
+ readonly match: MatchRange;
17
+ } | {
18
+ readonly kind: 'schedule';
19
+ readonly key: string;
20
+ readonly schedule: PaletteSchedule;
21
+ readonly match: MatchRange;
22
+ } | {
23
+ readonly kind: 'action';
24
+ readonly key: string;
25
+ readonly prompt: string;
26
+ } | {
27
+ readonly kind: 'filter';
28
+ readonly key: string;
29
+ readonly tab: PaletteTab;
30
+ };
31
+ export interface PaletteSection {
32
+ readonly id: string;
33
+ readonly title: string;
34
+ readonly entries: readonly PaletteEntry[];
35
+ }
36
+ /** One row of an entry's `…` menu; `primary` is what Enter does on the row. */
37
+ export interface PaletteAction {
38
+ readonly id: 'open' | 'pin' | 'unpin' | 'fork' | 'archive' | 'unarchive' | 'new-session' | 'copy-path' | 'open-session';
39
+ readonly label: string;
40
+ readonly primary?: boolean;
41
+ }
42
+ export interface PaletteCopy {
43
+ readonly placeholder: string;
44
+ readonly close: string;
45
+ readonly tabs: Readonly<Record<PaletteTab, string>>;
46
+ readonly recent: string;
47
+ readonly actions: string;
48
+ readonly filters: string;
49
+ readonly newSession: string;
50
+ readonly empty: string;
51
+ readonly archived: string;
52
+ readonly running: string;
53
+ readonly ended: string;
54
+ readonly more: string;
55
+ readonly hints: {
56
+ readonly close: string;
57
+ readonly tabs: string;
58
+ readonly filter: string;
59
+ readonly actions: string;
60
+ readonly compose: string;
61
+ };
62
+ readonly menu: Readonly<Record<PaletteAction['id'], string>>;
63
+ readonly runs: (count: number, more: boolean) => string;
64
+ readonly sessionCount: (count: number) => string;
65
+ readonly daily: string;
66
+ readonly weekdays: string;
67
+ readonly weekday: readonly string[];
68
+ readonly weeklyPrefix: string;
69
+ readonly weekdaySeparator: string;
70
+ readonly every: (amount: number, unit: 'hour' | 'minute' | 'second') => string;
71
+ readonly date: (month: number, day: number, year: number | null) => string;
72
+ readonly failed: (message: string) => string;
73
+ }
74
+ /** The palette's own words, in the document's language. */
75
+ export declare function paletteCopy(language: string): PaletteCopy;
76
+ /** The repeat rule as the reference's title suffix: 每天 9:51 / Daily 9:51. */
77
+ export declare function scheduleRuleLabel(rule: PaletteScheduleRule, copy: PaletteCopy, now?: Date): string;
78
+ /** What a schedule row reads: the task name, then its repeat rule. */
79
+ export declare function scheduleLabel(schedule: PaletteSchedule, copy: PaletteCopy, now?: Date): string;
80
+ /** The right-hand reading of a schedule row. */
81
+ export declare function scheduleMeta(schedule: PaletteSchedule, runs: PaletteScheduleRuns | undefined, copy: PaletteCopy): string;
82
+ /** Case-insensitive first occurrence, or null; an empty query matches nothing. */
83
+ export declare function matchRange(text: string, query: string): MatchRange;
84
+ export interface PaletteQuery {
85
+ /** The field's raw text; a leading `/` asks for the filter list. */
86
+ readonly query: string;
87
+ readonly tab: PaletteTab;
88
+ /** Content hits from the host search for this query, by Session id. */
89
+ readonly content?: ReadonlyMap<string, string> | undefined;
90
+ readonly now?: Date;
91
+ }
92
+ /** Which tabs this build can fill. */
93
+ export declare function paletteTabs(snapshot: SearchPaletteSnapshot): readonly PaletteTab[];
94
+ /** Whether the field's text asks for the filter list rather than a search. */
95
+ export declare function isFilterQuery(query: string): boolean;
96
+ /**
97
+ * The palette's sections for one query and tab.
98
+ *
99
+ * Without a query, 全部 shows the reference's 最近使用 (Sessions and tasks by
100
+ * their last activity) above its 操作, and each type tab lists its own rows.
101
+ * With a query, rows are found by title (a project also by path, a task also
102
+ * by its prompt), ranked prefix → word start → anywhere, then by recency;
103
+ * Sessions the host's content search found are appended with their snippet.
104
+ */
105
+ export declare function paletteSections(snapshot: SearchPaletteSnapshot, input: PaletteQuery, copy: PaletteCopy): PaletteSection[];
106
+ /** An entry's `…` menu, primary action first; empty for rows without one. */
107
+ export declare function entryActions(entry: PaletteEntry, copy: PaletteCopy): readonly PaletteAction[];
108
+ /** Relative activity time for a Session row: 3 小时前 / 3 hours ago. */
109
+ export declare function relativeTime(at: number, language: string, now?: number): string;