dsh-claude-desktop-theme 0.2.0

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 (76) hide show
  1. package/LICENSE +54 -0
  2. package/README.md +197 -0
  3. package/cordis.patch.yml +5 -0
  4. package/lib/client.js +5492 -0
  5. package/lib/index.js +666 -0
  6. package/lib/types/client/apply.d.ts +25 -0
  7. package/lib/types/client/compat/account-menu.d.ts +52 -0
  8. package/lib/types/client/compat/adapter.d.ts +17 -0
  9. package/lib/types/client/compat/blank-session-rows.d.ts +74 -0
  10. package/lib/types/client/compat/composer-menus.d.ts +11 -0
  11. package/lib/types/client/compat/composer-placeholder.d.ts +3 -0
  12. package/lib/types/client/compat/composer-status-order.d.ts +56 -0
  13. package/lib/types/client/compat/conversation-fade.d.ts +37 -0
  14. package/lib/types/client/compat/dom.d.ts +2 -0
  15. package/lib/types/client/compat/header-labels.d.ts +24 -0
  16. package/lib/types/client/compat/host-builds.d.ts +101 -0
  17. package/lib/types/client/compat/host-dom.d.ts +247 -0
  18. package/lib/types/client/compat/open-target.d.ts +9 -0
  19. package/lib/types/client/compat/pet-anchor.d.ts +30 -0
  20. package/lib/types/client/compat/plugin-manager.d.ts +32 -0
  21. package/lib/types/client/compat/session-menu.d.ts +47 -0
  22. package/lib/types/client/compat/slots.d.ts +21 -0
  23. package/lib/types/client/compat/stats-values.d.ts +20 -0
  24. package/lib/types/client/compat/view-switch.d.ts +14 -0
  25. package/lib/types/client/compat/workspace-menu.d.ts +15 -0
  26. package/lib/types/client/contracts/feature.d.ts +22 -0
  27. package/lib/types/client/contracts/ports.d.ts +169 -0
  28. package/lib/types/client/core/cleanup.d.ts +10 -0
  29. package/lib/types/client/core/mount-features.d.ts +3 -0
  30. package/lib/types/client/features/composer-pet/BlankPet.d.ts +17 -0
  31. package/lib/types/client/features/composer-pet/ComposerPet.d.ts +7 -0
  32. package/lib/types/client/features/composer-pet/Whale.d.ts +18 -0
  33. package/lib/types/client/features/composer-pet/mount.d.ts +9 -0
  34. package/lib/types/client/features/composer-pet/reactions.d.ts +15 -0
  35. package/lib/types/client/features/conversation/header-actions/HeaderActionButton.d.ts +25 -0
  36. package/lib/types/client/features/conversation/header-actions/entries.d.ts +101 -0
  37. package/lib/types/client/features/conversation/header-actions/mount.d.ts +28 -0
  38. package/lib/types/client/features/conversation/header-actions/sidebar-view.d.ts +21 -0
  39. package/lib/types/client/features/conversation/index.d.ts +16 -0
  40. package/lib/types/client/features/model-controls/EffortPanel.d.ts +14 -0
  41. package/lib/types/client/features/model-controls/ModelControls.d.ts +11 -0
  42. package/lib/types/client/features/model-controls/mount.d.ts +5 -0
  43. package/lib/types/client/features/model-controls/pixels.d.ts +4 -0
  44. package/lib/types/client/features/model-controls/popup.d.ts +10 -0
  45. package/lib/types/client/features/model-controls/selection.d.ts +66 -0
  46. package/lib/types/client/features/new-session/index.d.ts +8 -0
  47. package/lib/types/client/features/settings/ConfigPage.d.ts +28 -0
  48. package/lib/types/client/features/settings/FontMenu.d.ts +37 -0
  49. package/lib/types/client/features/settings/edits.d.ts +39 -0
  50. package/lib/types/client/features/settings/fields.d.ts +83 -0
  51. package/lib/types/client/features/settings/fonts.d.ts +40 -0
  52. package/lib/types/client/features/settings/locales.d.ts +61 -0
  53. package/lib/types/client/features/settings/mount.d.ts +17 -0
  54. package/lib/types/client/features/settings/register.d.ts +17 -0
  55. package/lib/types/client/features/shell/index.d.ts +8 -0
  56. package/lib/types/client/features/sidebar/index.d.ts +14 -0
  57. package/lib/types/client/features/statistics/books.d.ts +34 -0
  58. package/lib/types/client/features/statistics/card.d.ts +143 -0
  59. package/lib/types/client/features/statistics/format.d.ts +36 -0
  60. package/lib/types/client/features/statistics/index.d.ts +8 -0
  61. package/lib/types/client/features/statistics/mount.d.ts +12 -0
  62. package/lib/types/client/features/statistics/source.d.ts +35 -0
  63. package/lib/types/client/features/statistics/view.d.ts +95 -0
  64. package/lib/types/client/features/tool-calls/index.d.ts +2 -0
  65. package/lib/types/client/index.d.ts +1 -0
  66. package/lib/types/client/theme/mount.d.ts +15 -0
  67. package/lib/types/client/theme/palette.d.ts +80 -0
  68. package/lib/types/client/theme/tokens.d.ts +27 -0
  69. package/lib/types/host/index.d.ts +106 -0
  70. package/lib/types/host/stats/aggregate.d.ts +38 -0
  71. package/lib/types/host/stats/service.d.ts +18 -0
  72. package/lib/types/host/stats/unit.d.ts +115 -0
  73. package/lib/types/shared/config.d.ts +61 -0
  74. package/lib/types/shared/identity.d.ts +5 -0
  75. package/lib/types/shared/stats.d.ts +133 -0
  76. package/package.json +116 -0
@@ -0,0 +1,52 @@
1
+ import type { Disposer } from '../contracts/ports.ts';
2
+ /** Marks the portalled account menu for the stylesheet. */
3
+ export declare const ACCOUNT_MENU_ATTRIBUTE = "data-ccd-account-menu";
4
+ /**
5
+ * Custom property carrying the signed-in name shown as the menu header.
6
+ *
7
+ * The value is one *quoted* CSS string token (`accountNameToken`); the
8
+ * stylesheet paints it with `content: var(--ccd-account-name, "")`.
9
+ */
10
+ export declare const ACCOUNT_NAME_PROPERTY = "--ccd-account-name";
11
+ /**
12
+ * One account name as a complete CSS string token.
13
+ *
14
+ * The quotes are part of the value, not decoration: `content` has no production
15
+ * that takes a bare word, so after `var()` substitution the value is matched
16
+ * against the property's grammar, and a name that is not exactly one `<string>`
17
+ * leaves the declaration invalid at computed-value time. `content` is not
18
+ * inherited, so that behaves as `unset` — the initial `normal`, which computes
19
+ * to `none` on `::before`. The pseudo-element is then never generated, and the
20
+ * header disappears whole, its padding with it. CSS's own custom-property
21
+ * examples keep the quotes for the same reason (`--external-link: "external
22
+ * link"` feeding `content: " (" var(--external-link) ")"`).
23
+ *
24
+ * Inside the quotes a name travels as written, except for the three things CSS
25
+ * string syntax cannot carry raw: the quote and the backslash take a preceding
26
+ * backslash, and a control character takes a hex escape padded to six digits
27
+ * followed by one space (`\00000a `) — the form CSS serializes strings in. That
28
+ * space terminates the escape while the value is tokenized, so it is consumed
29
+ * rather than painted, and the six digits leave no room for a hex digit of the
30
+ * following character to be swallowed by the escape.
31
+ *
32
+ * @param name - the signed-in name as read from the trigger.
33
+ * @returns the name as one quoted CSS string token for the custom property.
34
+ */
35
+ export declare function accountNameToken(name: string): string;
36
+ /**
37
+ * The reference draws the signed-in account menu as a card whose first block is
38
+ * the account itself (the account name over a workspace line). DSH's account
39
+ * menu is a portalled `Menu` of plain rows and the signed-in name only exists on
40
+ * the sidebar trigger, so this mirrors that real value onto the open menu and
41
+ * tags it: no row is added, renamed or reordered — the stylesheet only reshapes
42
+ * the card and prints the name it was given.
43
+ *
44
+ * The menu is recognised structurally (a fixed-position `[role="menu"]` that is
45
+ * open while the trigger reports `aria-expanded`), so no menu class hash is
46
+ * needed here.
47
+ *
48
+ * @param document - renderer document carrying the sidebar and the portal.
49
+ * @param report - sink for observer failures; the native menu stays untouched.
50
+ * @returns disposer that disconnects the observers and clears every mark.
51
+ */
52
+ export declare function mountAccountMenu(document: Document, report: (error: unknown) => void): Disposer;
@@ -0,0 +1,17 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { HostServices, ThemePreferencePort } from '../contracts/ports.ts';
3
+ /**
4
+ * Build the theme service's preference face.
5
+ *
6
+ * `getTheme`/`setTheme` are the service's only read and write entries and
7
+ * `theme/change` is its only change signal (see `ThemeRuntime`), so the plugin's
8
+ * Appearance control is another surface for the native preference rather than a
9
+ * second switch of its own. A build that does not publish them yields null and
10
+ * the page keeps its other rows; activation is not failed over an optional
11
+ * control.
12
+ * @param ctx - client context carrying the theme service and its event bus.
13
+ * @returns the port, or null when this build cannot read or write the preference.
14
+ */
15
+ export declare function createThemePreferencePort(ctx: Context): ThemePreferencePort | null;
16
+ /** This compatibility boundary targets the pinned SDK, not arbitrary DSH versions. */
17
+ export declare function createHostServices(ctx: Context): HostServices;
@@ -0,0 +1,74 @@
1
+ import type { BlankSessionsPort, Disposer } from '../contracts/ports.ts';
2
+ /**
3
+ * Hide provisional root sessions using SessionSummary.blank from
4
+ * dsh-api-session-controller and ui-workspace's data-row-key=session:<id>.
5
+ * Remove the display marker after the first message or scope release.
6
+ * Fork children retain their rows while their summaries are settling.
7
+ *
8
+ * The hide has to be in force before the host measures the list, not merely
9
+ * before it paints. `ui-workspace`'s AnimatedRows reads every keyed row's
10
+ * rectangle inside the commit that mounts it — `getSnapshotBeforeUpdate` then
11
+ * `componentDidUpdate`, both before any microtask — and glides the rows below
12
+ * from their previous position. A marker written by a MutationObserver lands
13
+ * one microtask too late: the row is measured as a real box, and the height it
14
+ * then drops out of the flow becomes a jump the host's 200ms glide plays back
15
+ * (issue 04). The ids are therefore written twice: as a rule in the plugin's
16
+ * own adopted sheet, keyed by `data-row-key`, which is the one value the
17
+ * element carries from the moment React creates it and which is known one
18
+ * store notification earlier, before the row is committed at all; and as the
19
+ * marker attribute below, which is the same fact recorded on an element that
20
+ * already exists and the fallback wherever a document cannot carry the sheet.
21
+ */
22
+ /** Marks one host row the stylesheet keeps out of the column. */
23
+ export declare const BLANK_ROW_ATTRIBUTE = "data-ccd-blank-session";
24
+ /** The slice of one host list row this filter reads. */
25
+ export interface SessionRowFacts {
26
+ /** Host presentation fact: created and not yet given durable history. */
27
+ readonly blank?: boolean;
28
+ /** Direct parent, present on fork and subagent children. */
29
+ readonly parentId?: string;
30
+ }
31
+ /** The slice of the host session list this filter reads (`SessionListState`). */
32
+ export interface SessionListFacts {
33
+ readonly ids: readonly string[];
34
+ readonly byId: Readonly<Record<string, SessionRowFacts | undefined>>;
35
+ }
36
+ /**
37
+ * Ids whose provisional row must stay out of the column.
38
+ *
39
+ * A row qualifies when the host still calls it blank and it is a root Session:
40
+ * a blank child is a fork that has not received its authoritative summary yet,
41
+ * not a New Session placeholder.
42
+ *
43
+ * @param state - host session-list snapshot.
44
+ * @returns the ids to hide, in no particular order.
45
+ */
46
+ export declare function blankSessionIds(state: SessionListFacts): ReadonlySet<string>;
47
+ /**
48
+ * Session id carried by one `data-row-key`, or null for any other row.
49
+ *
50
+ * @param key - the attribute value, as read from the DOM.
51
+ * @returns the id, or null when the row is not a Session row.
52
+ */
53
+ export declare function rowSessionId(key: string | null | undefined): string | null;
54
+ /**
55
+ * Keep the host's provisional New Session rows out of the sidebar column.
56
+ *
57
+ * Reconciliation runs synchronously (never on an animation frame, which a
58
+ * hidden or occluded window throttles) and is driven by two signals: the host
59
+ * session list, which is what makes a row stop being provisional, and the
60
+ * document's own childList changes, which is how a row arrives at all. A
61
+ * mutation outside the Workspace browser is ignored before any query runs, so
62
+ * a streaming Conversation never pays for this observer.
63
+ *
64
+ * Both signals write the same fact to two places: the sheet, which reaches the
65
+ * list before the row lays out, and the marker attribute on the row, which is
66
+ * what a stylesheet can hide and what the release path clears.
67
+ *
68
+ * @param document - renderer document carrying the sidebar.
69
+ * @param source - provisional-id source built over the host session list.
70
+ * @param report - sink for source failures; the native rows stay untouched.
71
+ * @returns disposer that stops both signals, removes the sheet, and clears
72
+ * every tag it wrote.
73
+ */
74
+ export declare function mountBlankSessionRows(document: Document, source: BlankSessionsPort, report: (error: unknown) => void): Disposer;
@@ -0,0 +1,11 @@
1
+ import type { Disposer } from '../contracts/ports.ts';
2
+ /** Marks a menu this module has already placed above its anchor. */
3
+ export declare const FLIPPED_MENU_ATTRIBUTE = "data-ccd-menu-flipped";
4
+ /**
5
+ * Place bottom-edge Composer and hero-chip menus above their anchors, using
6
+ * the Menu primitive's side=top geometry. Cap only cards taller than the free
7
+ * space above the anchor so later content growth keeps its natural height.
8
+ * MutationObserver and resize reapply placement when the host updates it.
9
+ * @returns disposer that restores each menu and releases the observers.
10
+ */
11
+ export declare function mountComposerMenuPlacement(document: Document, report: (error: unknown) => void): Disposer;
@@ -0,0 +1,3 @@
1
+ import type { Disposer } from '../contracts/ports.ts';
2
+ /** Unify the default hero hint with chat, preserving the official editor. */
3
+ export declare function mountComposerPlaceholder(document: Document, report: (error: unknown) => void): Disposer;
@@ -0,0 +1,56 @@
1
+ import type { Disposer } from '../contracts/ports.ts';
2
+ /**
3
+ * The Composer status bar's seat order, and the context ring's accessible name.
4
+ *
5
+ * Host facts (DSH `0.2.0-rc.2`, read from the installed `app.asar`):
6
+ *
7
+ * - The status bar is `ui-conversation`'s `InputBar` control row
8
+ * (`.yhfFVG_row`). Its flex children are the leading `.yhfFVG_tools` group —
9
+ * command button, then `.yhfFVG_modes` holding the `conversation.input.permission`
10
+ * and `conversation.input.plan` seats — and the trailing `.yhfFVG_trailing`
11
+ * group — the model seat, then the `conversation.input.activity` seat, then the
12
+ * absolutely positioned send/stop buttons.
13
+ * - `@deepseek-ai/dsh-experimental-client-ui-voice-input` registers its control
14
+ * into `conversation.input.activity`. The renderer seats every slot entry in a
15
+ * `display: contents` outlet (`div[data-slot="…"]`, see
16
+ * `dsh-client-ui-renderer`'s `SlotOutlet`), so the seat the slot occupies is
17
+ * that outlet's parent element.
18
+ * - `ContextMeter` (`ui-conversation`'s skeleton) renders the ring's trigger
19
+ * with an accessible name carrying the reading, and returns nothing at all
20
+ * until the token meter knows both a numerator and the context window.
21
+ *
22
+ * The voice control therefore sits one flex container away from the permission
23
+ * control it is asked to follow, and `order` cannot cross containers: the seat
24
+ * itself has to move. It is moved into `.yhfFVG_modes`, immediately after the
25
+ * permission seat and before the plan seat, and marked with
26
+ * {@link VOICE_SEAT_ATTRIBUTE} so the stylesheet and the release path can find
27
+ * it.
28
+ *
29
+ * Moving a node React owns is only safe while React never removes that node from
30
+ * the parent it was moved out of. It does not: `InputBar` renders the seat under
31
+ * two guards, `input === void 0` and `sessionId === void 0` — `input` is the
32
+ * shell's own store snapshot, which exists as soon as the shell does — and the
33
+ * `conversation.composer.bar` entry that owns the whole Composer is a
34
+ * `session-maybe` slot, whose documented contract is that such an entry adopts
35
+ * its first session in place and remounts, as one deleted subtree, on every later
36
+ * identity change. Within one instance the seat is therefore only ever inserted,
37
+ * never removed. Moving the *ring* instead is not available at all: `InputBar`
38
+ * unmounts the ring whenever the voice control expands, and React would then call
39
+ * `removeChild` on a parent the node no longer belongs to.
40
+ *
41
+ * The seat goes back to its recorded host position — and the mark comes off —
42
+ * whenever the host switches the row to the voice control's capture layout
43
+ * (`InputBar` hides `.yhfFVG_tools` and lets `.yhfFVG_trailing` span the row), so
44
+ * that layout keeps the host's own geometry, and again on release.
45
+ */
46
+ /** Mark on a relocated voice seat; the composer sheet reads it. */
47
+ export declare const VOICE_SEAT_ATTRIBUTE = "data-ccd-voice-seat";
48
+ /**
49
+ * Keep the voice control beside the permission control, and the context ring's
50
+ * accessible name free of the percentage it no longer shows.
51
+ *
52
+ * @param document - renderer document carrying the Composer.
53
+ * @param report - sink for observer failures; the host's own order stays.
54
+ * @returns disposer that restores every seat and label it touched.
55
+ */
56
+ export declare function mountComposerStatusOrder(document: Document, report: (error: unknown) => void): Disposer;
@@ -0,0 +1,37 @@
1
+ import type { Disposer } from '../contracts/ports.ts';
2
+ /** Marks that the transcript still has content above the scroll viewport. */
3
+ export declare const FADE_TOP_ATTRIBUTE = "data-ccd-fade-top";
4
+ /** Marks that the transcript still has content below the scroll viewport. */
5
+ export declare const FADE_BOTTOM_ATTRIBUTE = "data-ccd-fade-bottom";
6
+ /**
7
+ * Custom property carrying how much of the scroll viewport the resident
8
+ * Composer covers. The bottom fade has to end at the Composer's own top edge:
9
+ * the scroller runs to the bottom of the window, so a stencil pinned to its
10
+ * bottom edge would sit behind the Composer and fade nothing.
11
+ */
12
+ export declare const FADE_BOTTOM_INSET_PROPERTY = "--ccd-fade-bottom-inset";
13
+ export interface EdgeFadeState {
14
+ readonly top: boolean;
15
+ readonly bottom: boolean;
16
+ }
17
+ /**
18
+ * Determine whether content extends past each edge of the scroll viewport.
19
+ * The mount publishes those flags and the sticky Composer's top inset for
20
+ * the page's gradient overlays; the Composer is a child of the scroll viewport.
21
+ */
22
+ export declare function edgeFadeState(scrollTop: number, scrollHeight: number, clientHeight: number): EdgeFadeState;
23
+ /**
24
+ * Keeps the transcript's edge-fade attributes in step with its scroll state.
25
+ *
26
+ * The scroller is a session-scoped element, so the module re-resolves it from
27
+ * the document and moves its own listener across element swaps. Content growth
28
+ * (streaming), column drags and window resizes all change the answer, so the
29
+ * observer watches the same three signals the host's own view does — DOM
30
+ * mutations, inline framework geometry and the window — rather than animation
31
+ * frames, which an occluded window throttles.
32
+ *
33
+ * @param document - renderer document carrying the Conversation page.
34
+ * @param report - sink for observer failures; the transcript stays usable.
35
+ * @returns disposer that disconnects every observer and clears both attributes.
36
+ */
37
+ export declare function mountConversationFade(document: Document, report: (error: unknown) => void): Disposer;
@@ -0,0 +1,2 @@
1
+ import type { DomPort } from '../contracts/ports.ts';
2
+ export declare function createDomPort(document: Document): DomPort;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Copy for the controls this plugin adds to DSH's own header cluster.
3
+ *
4
+ * DSH localises its own strings through `ctx.locale`; a third-party plugin may
5
+ * not borrow another package's namespace, so these two labels follow the
6
+ * document language instead. The glyphs and the actions stay native either way.
7
+ */
8
+ export interface HeaderLabels {
9
+ /** Names the stretched hit area that opens the application menu. */
10
+ readonly open: string;
11
+ /** Names the right panel's terminal view. */
12
+ readonly terminal: string;
13
+ /** Names the right panel's browser view. */
14
+ readonly browser: string;
15
+ /** Names the corner control that shows the session's project directory. */
16
+ readonly files: string;
17
+ /** Names the Session menu's row that reveals a collapsed right panel. */
18
+ readonly expand: string;
19
+ }
20
+ /**
21
+ * @param document - renderer document whose language DSH sets from the locale.
22
+ * @returns Chinese copy for a Chinese document, English otherwise.
23
+ */
24
+ export declare function headerLabels(document: Document): HeaderLabels;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * CSS-module prefix tables for the shipped DSH builds.
3
+ *
4
+ * DSH hashes CSS-module class names per build, and the installers are separate
5
+ * builds even at one version: `0.2.0-rc.2` ships commit `5e9e301d` for macOS and
6
+ * `04f392c9` for Windows, and every class of the pinned UI packages hashes
7
+ * differently between them. This plugin's stylesheets and `HOST` selectors are
8
+ * authored against the macOS build; a build listed below is rewritten through
9
+ * {@link aliasHostClasses} before it reaches the document, so one source styles
10
+ * both installers instead of duplicating every selector.
11
+ *
12
+ * Only the prefix differs. The local name after it (`_frame`, `_newSession`)
13
+ * and the static web frontend's markdown classes (`_markdown_1ypvv_5`) are
14
+ * identical in both builds, so neither needs an entry here.
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.
20
+ */
21
+ /** One shipped DSH build and the prefixes it uses for the pinned classes. */
22
+ export interface HostBuild {
23
+ /** Stable identity of the build, used for diagnostics and memoized tables. */
24
+ readonly id: string;
25
+ /** The layout frame's own class name, which identifies the build in a document. */
26
+ readonly frameClass: string;
27
+ /** Authored prefix → this build's prefix; empty for the authored build. */
28
+ readonly prefixes: Readonly<Record<string, string>>;
29
+ }
30
+ /**
31
+ * The build the stylesheets are authored against: DSH `0.2.0-rc.2`, macOS
32
+ * commit `5e9e301d`. An empty table is the identity rewrite.
33
+ */
34
+ export declare const PINNED_BUILD: HostBuild;
35
+ /**
36
+ * DSH `0.2.0-rc.2`, Windows commit `04f392c9`. Read from that build's
37
+ * `app.asar`: each entry is the same CSS module's class-name map, keyed by the
38
+ * prefix this plugin authored.
39
+ */
40
+ export declare const WINDOWS_BUILD: HostBuild;
41
+ /** Every build whose class names this plugin knows how to speak. */
42
+ export declare const HOST_BUILDS: readonly HostBuild[];
43
+ /** How a document's build was decided. */
44
+ export type HostBuildState =
45
+ /** The frame named a registered build. */
46
+ 'identified'
47
+ /** The shell has not rendered its frame yet; the preload's platform decided. */
48
+ | 'pending'
49
+ /** A frame exists and belongs to no registered build: the classes cannot match. */
50
+ | 'unknown';
51
+ /** One document's build and how confidently it was recognised. */
52
+ export interface HostBuildStatus {
53
+ readonly build: HostBuild;
54
+ readonly state: HostBuildState;
55
+ }
56
+ /**
57
+ * Identify the build behind one document, and say how it was identified.
58
+ *
59
+ * The frame's own class name is the evidence: it is present from the first
60
+ * paint of the shell and names the build exactly. Before the shell renders,
61
+ * the Electron preload's `data-platform` attribute decides — it is written
62
+ * before any page script runs — so a stylesheet mounted during client boot is
63
+ * already rewritten for the right build.
64
+ *
65
+ * `unknown` is the state worth reporting: the frame is on screen, its classes
66
+ * match no table here, and every host selector in this plugin will therefore
67
+ * miss until the tables are refreshed from that build.
68
+ *
69
+ * @param document - the renderer document to identify.
70
+ * @returns the matching build and whether the document itself confirmed it.
71
+ */
72
+ export declare function hostBuildStatus(document: Document): HostBuildStatus;
73
+ /**
74
+ * Identify the build behind one document.
75
+ * @param document - the renderer document to identify.
76
+ * @returns the matching build, or the authored one when nothing is recognised.
77
+ */
78
+ export declare function detectHostBuild(document: Document): HostBuild;
79
+ /**
80
+ * Report the build as soon as the shell's frame settles the question.
81
+ *
82
+ * The frame appears after this plugin's client boots, so a status read at mount
83
+ * time is still `pending`; this watches for the first frame and then stops,
84
+ * which is also the moment an unregistered build becomes visible.
85
+ *
86
+ * @param document - the renderer document to watch.
87
+ * @param settle - receives the first non-pending status, exactly once.
88
+ * @returns disposer that stops the watch.
89
+ */
90
+ export declare function watchHostBuild(document: Document, settle: (status: HostBuildStatus) => void): () => void;
91
+ /**
92
+ * Rewrite authored host prefixes in one stylesheet or selector.
93
+ *
94
+ * The authored build is returned untouched, and a build without a table for a
95
+ * prefix keeps it, so an unknown class degrades to "the selector does not
96
+ * match" instead of corrupting the rule.
97
+ * @param text - stylesheet text or a selector list using authored prefixes.
98
+ * @param build - build whose prefixes the text should carry.
99
+ * @returns the text as that build names its classes.
100
+ */
101
+ export declare function aliasHostClasses(text: string, build: HostBuild): string;
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Version-pinned host DOM facts.
3
+ *
4
+ * Every selector here was read from the running DSH `0.2.0-rc.2` renderer and
5
+ * cross-checked against the shipped CSS-module class names of the pinned UI
6
+ * packages. DSH hashes CSS-module class names per build, so these are the one
7
+ * place that may need updating for another DSH release; nothing outside this
8
+ * module may hard-code a host class name.
9
+ *
10
+ * The names below are the macOS build's. `host-builds.ts` holds the same
11
+ * classes as the Windows build spells them, and `hostSelectors` /
12
+ * `hostAnchors` resolve this table for the document actually on screen, so
13
+ * callers never branch on the platform themselves.
14
+ *
15
+ * Sources (app.asar, `0.2.0-rc.2`):
16
+ * - `packages/client/ui-layout/src/client/AppFrame.module.css` (`_6Qf49G_`)
17
+ * - `packages/client/ui-sidebar/src/client/SidebarRoot.module.css` (`_3WPZCG_`)
18
+ * - `packages/client/ui-conversation/src/client/ConversationRoot.module.css` (`ST7X_W_`)
19
+ * - `packages/client/ui-conversation/src/client/InputBar.module.css` (`yhfFVG_`)
20
+ * - `packages/client/ui-model-selection/src/client/ModelSelect.module.css` (`cl2Rlq_`)
21
+ * - `packages/client/ui-permission-presets/src/client/PermissionSelect.module.css` (`wXeviG_`)
22
+ * - `packages/experimental/client-ui-agent-team/src/client/TeamAction.module.css` (`_2tNPVa_`)
23
+ * - `packages/client/ui-agent-preset/src/client/AgentPresetLabel.module.css` (`_3li69W_`)
24
+ * - `packages/client/ui-open-in-app/src/client/OpenTargetButton.module.css` (`iq4beG_`)
25
+ * - `packages/session-query/session-log-export/src/client/HeaderAction.module.css` (`Da3aKq_`)
26
+ * - `packages/client/ui-conversation/src/client/ConversationHero.module.css` (`bocITq_`)
27
+ * - `packages/client/ui-conversation/src/client/skeleton/ContextMeter.module.css` (`y0jqnG_`)
28
+ * - `packages/client/ui-attachment/src/client/ComposerAttachments.module.css` (`dVdiKa_`)
29
+ * - `packages/client/ui-chat/src/client/chat/StatsPills.module.css` (`OpZ85W_`)
30
+ * - `packages/client/ui-workspace/src/client/WorkspaceBrowser.module.css` (`_7514NG_`)
31
+ * - `packages/client/ui-workspace/src/client/rows/Rows.tsx` (`data-row-key` on the
32
+ * Workspace and Session rows: `workspace:<key>` / `session:<id>`)
33
+ * - `packages/client/ui-settings/src/client/SettingsRoot.module.css` (`Dws9Sa_`)
34
+ * - `packages/client/ui-settings-account/src/client/AccountMenu.module.css` (`ZogL4G_`)
35
+ * - `packages/client/ui-primitives/lib/Menu.module.css` (`_4ub78_`, `_ri079_`)
36
+ * - `packages/client/ui-primitives/lib/Menu.js` (`MenuItemButton`: the icon seat
37
+ * `account-menu.css` scales; its `[role="menuitem"] > span` structure)
38
+ * - `packages/client/ui-chat/src/client/chat/AssistantMarkdown.module.css` (`gKv1-q_`)
39
+ * - `packages/client/ui-chat/src/client/chat/ChatView.module.css` (`icaHSq_`: the
40
+ * transcript scroll area and the floating jump-to-bottom control it renders
41
+ * below the transcript while the reader is off the tail)
42
+ * - `packages/client/ui-primitives/lib/markdown/MarkdownText.module.css`
43
+ * (static web-frontend bundle: `_1ypvv_`; shared by Web and Desktop)
44
+ */
45
+ export declare const HOST: Readonly<{
46
+ readonly frame: '._6Qf49G_frame';
47
+ readonly sidebarColumn: '._6Qf49G_sidebarCol';
48
+ readonly centerColumn: '._6Qf49G_centerCol';
49
+ readonly rightbarColumn: '._6Qf49G_rightbarCol';
50
+ readonly resizeHandle: '._6Qf49G_handle';
51
+ readonly sidebarRoot: '._3WPZCG_root';
52
+ readonly sidebarLogoRow: '._3WPZCG_logoRow';
53
+ readonly sidebarBrand: '._3WPZCG_brand';
54
+ readonly sidebarBrandName: '._3WPZCG_brandName';
55
+ readonly sidebarTopStrip: '._3WPZCG_topStrip';
56
+ /** SidebarRoot.module.css: expanded macOS window-chrome toggle. */
57
+ readonly sidebarToggle: '._3WPZCG_toggle';
58
+ readonly sidebarNewSession: '._3WPZCG_newSession';
59
+ readonly sidebarNewSessionLabel: '._3WPZCG_newSessionLabel';
60
+ readonly sidebarNewSessionShortcut: '._3WPZCG_newSessionShortcut';
61
+ readonly sidebarPanelList: '._3WPZCG_panelList';
62
+ readonly sidebarPanelRow: '._3WPZCG_panelRow';
63
+ readonly sidebarPanelTitle: '._3WPZCG_panelTitle';
64
+ readonly sidebarPanelGlyph: '._3WPZCG_panelGlyph';
65
+ readonly sidebarRegionArea: '._3WPZCG_regionArea';
66
+ readonly sidebarFootArea: '._3WPZCG_footArea';
67
+ readonly sidebarSettingsArea: '._3WPZCG_settingsArea';
68
+ readonly sidebarIconButton: '._3WPZCG_iconButton';
69
+ readonly browserRoot: '._7514NG_root';
70
+ readonly browserSectionHeader: '._7514NG_sectionHeader';
71
+ readonly browserSectionLabel: '._7514NG_sectionLabel';
72
+ readonly browserListArea: '._7514NG_listArea';
73
+ readonly settingsTrigger: '.Dws9Sa_trigger';
74
+ readonly settingsTriggerRow: '.Dws9Sa_triggerRow';
75
+ readonly accountTrigger: '.ZogL4G_trigger';
76
+ readonly accountLabel: '.ZogL4G_label';
77
+ readonly conversationRoot: '.ST7X_W_root';
78
+ readonly conversationHeader: '.ST7X_W_header';
79
+ readonly conversationCrumb: '.ST7X_W_crumb';
80
+ /** ConversationRoot.tsx: the header's trailing corner seat. */
81
+ readonly headerCorner: '[data-conversation-header-corner]';
82
+ /** OpenTargetButton.module.css: the anchor the native application menu hangs from. */
83
+ readonly openTargetAnchor: '.iq4beG_menuAnchor';
84
+ /** OpenTargetButton.module.css: the bordered split box that becomes one hit box here. */
85
+ readonly openTargetSplit: '.iq4beG_split';
86
+ /** OpenTargetButton.module.css: leading icon half of the native split button. */
87
+ readonly openTargetMain: '.iq4beG_main';
88
+ /** OpenTargetButton.module.css: the only control that opens the application menu. */
89
+ readonly openTargetChevron: '.iq4beG_chevron';
90
+ /** OpenTargetButton.module.css: the chosen application's own image. */
91
+ readonly openTargetAppIcon: '.iq4beG_appIcon';
92
+ /** session-log-export/HeaderAction.module.css: native session action menu. */
93
+ readonly sessionMoreButton: '.Da3aKq_moreButton';
94
+ /**
95
+ * sidebar-right/ExpandButton.tsx: the collapsed rightbar's only re-entry
96
+ * control. This plugin no longer styles it: the corner entry shadows it in
97
+ * that single cell (`features/conversation/header-actions/entries.ts`,
98
+ * `OVERRIDE_PRIORITY`) and the expand action moves into the Session menu, so
99
+ * the marker is the pinned record of the control that was displaced — the
100
+ * thing to re-check against a later build.
101
+ */
102
+ readonly sidebarRightExpand: '[data-sidebar-right-expand]';
103
+ readonly conversationBody: '.ST7X_W_body';
104
+ readonly assistantMarkdown: '.gKv1-q_root';
105
+ readonly markdownBody: '._markdown_1ypvv_5:not([data-markdown-variant="compact"])';
106
+ readonly markdownFileMention: '._fileMention_1ypvv_85';
107
+ readonly conversationScroll: '.ST7X_W_scrollBody';
108
+ /** ChatView.module.css: the transcript's own scroll column, inside the page's
109
+ scroll viewport, and the two shells of the floating jump-to-bottom control
110
+ that ChatView renders off the tail. `conversation.css` places and sizes
111
+ that control; no TypeScript reads it. */
112
+ readonly chatViewScroll: '.icaHSq_scroll';
113
+ readonly chatViewColumn: '.icaHSq_column';
114
+ readonly chatViewToBottomSlot: '.icaHSq_toBottomSlot';
115
+ readonly chatViewToBottom: '.icaHSq_toBottom';
116
+ /**
117
+ * ConversationRoot.module.css + skeleton/ConversationWidthControls.tsx: the
118
+ * two transcript-width drag handles, one per side, rendered as the
119
+ * Conversation body's last child (`data-width-handle="left" | "right"` is the
120
+ * host's own stable hook on them; the class is hashed). `conversation.css`
121
+ * hides them: dragging one publishes `--dsh-chat-user-width` on the
122
+ * Conversation root, and `theme/composer.css` replaces the body rule that is
123
+ * that property's only consumer, so in this interface the handles carry a
124
+ * cursor and a hover rule and move nothing. This entry is the pinned record of
125
+ * what is hidden, and the selector a later build can be re-checked against.
126
+ */
127
+ readonly conversationWidthHandle: '.ST7X_W_root [data-width-handle]';
128
+ readonly conversationTabs: '.ST7X_W_tabs';
129
+ readonly conversationTab: '.ST7X_W_tab';
130
+ readonly conversationTabActive: '.ST7X_W_tabActive';
131
+ readonly composerSeat: '.ST7X_W_composerSeat';
132
+ readonly composerStack: '.ST7X_W_composerStack';
133
+ readonly composerRoot: '.yhfFVG_root';
134
+ readonly composerCard: '[data-composer-card]';
135
+ readonly composerCardClass: '.yhfFVG_card';
136
+ /** InputBar.module.css: workspace-less card's dashed ::after picker outline. */
137
+ readonly composerCardWorkspaceTrigger: '.yhfFVG_cardWorkspaceTrigger';
138
+ readonly composerScroll: '.yhfFVG_scroll';
139
+ readonly composerInput: '.yhfFVG_input';
140
+ /** InputBar.module.css: overlay hint, distinct from the editable text. */
141
+ readonly composerPlaceholder: '.yhfFVG_placeholder';
142
+ readonly composerAdd: '.yhfFVG_add';
143
+ readonly composerPrimary: '.yhfFVG_primary';
144
+ readonly composerRow: '.yhfFVG_row';
145
+ readonly composerTools: '.yhfFVG_tools';
146
+ readonly composerDock: '.yhfFVG_dock';
147
+ readonly composerTrailing: '.yhfFVG_trailing';
148
+ /** The draft-image rail the `ui-attachment` plugin renders into the card,
149
+ above the input panel (composer.css merges it into the input surface). */
150
+ readonly composerAttachmentRail: '.dVdiKa_rail';
151
+ /** ComposerAttachments.module.css: the draft image button clips its image. */
152
+ readonly composerAttachmentThumbnail: '.dVdiKa_thumbnail';
153
+ /** The Composer's model selector: one trigger carrying both texts. */
154
+ readonly modelSelectTrigger: '.cl2Rlq_trigger, [data-ccd-model-controls]';
155
+ readonly modelSelectLabel: '.cl2Rlq_triggerLabel';
156
+ readonly modelSelectEffort: '.cl2Rlq_triggerEffort';
157
+ /** PermissionSelect.module.css: compact permission text, keeping its button. */
158
+ readonly permissionSelectLabel: '.wXeviG_triggerLabel';
159
+ /** TeamAction.module.css: native team dialog entry, retaining its label at every width. */
160
+ readonly teamActionTrigger: '._2tNPVa_trigger';
161
+ readonly teamActionLabel: '._2tNPVa_triggerLabel';
162
+ /** AgentPresetLabel.module.css: passive mode description, omitted from the conversation header. */
163
+ readonly presetModeLabel: '._3li69W_label';
164
+ /** AgentPresetLabel.module.css: redundant icon may yield space to its text. */
165
+ readonly presetModeIcon: '._3li69W_icon';
166
+ readonly statsAnchor: '.OpZ85W_anchor';
167
+ readonly statsRoot: '.OpZ85W_root';
168
+ readonly statsLabel: '.OpZ85W_label';
169
+ readonly statsSeparator: '.OpZ85W_sep';
170
+ readonly statsPill: '.OpZ85W_pill';
171
+ readonly contextMeterTrigger: '.y0jqnG_trigger';
172
+ readonly heroRoot: '.bocITq_root';
173
+ readonly heroHeadline: '.bocITq_headline';
174
+ readonly heroWorkspaceChip: '.bocITq_workspace';
175
+ readonly heroWorkspaceRow: '.ST7X_W_heroWorkspaceRow';
176
+ /** Hero-phase Composer column: the greeting block and the input stack are its
177
+ children, so the statistics card is appended here and positioned against
178
+ the greeting. */
179
+ readonly heroComposerStack: '.ST7X_W_composerStack';
180
+ /** Every menu surface the `Menu` primitive renders, portal or in place. */
181
+ readonly menu: '[role="menu"]';
182
+ readonly menuItem: '[role="menuitem"]';
183
+ }>;
184
+ /**
185
+ * Anchors that must exist before a feature may touch the host DOM. They are
186
+ * stable data attributes rather than hashed class names, so a failed probe
187
+ * means the frame is not the pinned one and the feature must stay off.
188
+ */
189
+ export declare const ANCHOR: Readonly<{
190
+ readonly frame: '[data-slot="root"] > div';
191
+ readonly sidebar: '[data-slot="sidebar"]';
192
+ readonly workspaces: '[data-slot="sidebar.workspaces"]';
193
+ /**
194
+ * Every keyed row of the Workspace browser's tree, including the Session rows
195
+ * `blank-session-rows.ts` filters. `data-row-key` is the host's own identity
196
+ * for a row and survives its class hashes; the `empty` placeholder and the
197
+ * Workspace headers share the attribute with other prefixes.
198
+ */
199
+ readonly sessionRows: '[data-row-key^="session:"]';
200
+ readonly settings: '[data-slot="sidebar.settings"]';
201
+ readonly conversation: '[data-conversation-content]';
202
+ /**
203
+ * AppFrame.tsx: the frame element itself while the right panel's column has no
204
+ * width. The host writes `data-rightbar-collapsed` from `cols.rightbar === 0`,
205
+ * so it is the collapsed state's own statement — the header's expand control
206
+ * reads it rather than a measurement of our own.
207
+ */
208
+ readonly rightbarCollapsed: '[data-slot="root"] > div[data-rightbar-collapsed]';
209
+ /** The element that actually scrolls the Conversation page. In the pinned
210
+ layout the transcript column inside it is `overflow: visible`, so this is
211
+ the scroll viewport the edge fade has to describe. */
212
+ readonly conversationScroll: '[data-conversation-scroll]';
213
+ readonly composerSeat: '[data-composer-seat]';
214
+ readonly composerCard: '[data-composer-card]';
215
+ /**
216
+ * The blank-session Composer card. `ConversationRoot` (the pinned `.ST7X_W_root`
217
+ * class, `ConversationMainPanel`) writes `data-phase` on its root and `InputBar`
218
+ * renders the card inside it; the root class is what keeps this match off the
219
+ * editor's own `data-phase` input. `conversation.input.overlay` renders only
220
+ * with a Session, so this is the seat the no-session pet is measured from.
221
+ */
222
+ readonly composerCardHero: ".ST7X_W_root[data-phase=\"hero\"] [data-composer-card]";
223
+ }>;
224
+ /** One document's selector table, resolved for the build that document runs. */
225
+ export type HostSelectors = Readonly<Record<keyof typeof HOST, string>>;
226
+ /** One document's anchor table, resolved for the build that document runs. */
227
+ export type HostAnchors = Readonly<Record<keyof typeof ANCHOR, string>>;
228
+ /**
229
+ * The selectors this document's build actually answers to.
230
+ *
231
+ * `HOST` above is written against the authored build; a document running
232
+ * another build needs the same selectors in that build's class names. Read this
233
+ * once per mount and use the table, rather than mixing the two.
234
+ * @param document - the renderer document to speak to.
235
+ */
236
+ export declare function hostSelectors(document: Document): HostSelectors;
237
+ /** The anchors of this document's build; see {@link hostSelectors}. */
238
+ export declare function hostAnchors(document: Document): HostAnchors;
239
+ export interface HostProbe {
240
+ readonly frame: boolean;
241
+ readonly sidebar: boolean;
242
+ readonly workspaces: boolean;
243
+ readonly conversation: boolean;
244
+ readonly composer: boolean;
245
+ }
246
+ /** @param document - the renderer document to probe. */
247
+ export declare function probeHost(document: Document): HostProbe;
@@ -0,0 +1,9 @@
1
+ import type { Disposer } from '../contracts/ports.ts';
2
+ /**
3
+ * Annotate ui-open-in-app/OpenTargetButton for a full-box application-menu
4
+ * trigger. Its main half launches directly; the chevron opens the menu and
5
+ * exists only when applications are available. Publish menu availability and
6
+ * update accessible names to match the enlarged trigger.
7
+ * @returns disposer that restores attributes and markers.
8
+ */
9
+ export declare function mountOpenTargetMode(document: Document, report: (error: unknown) => void): Disposer;