@deepseek-ai/dsh-client-ui-primitives 0.1.6-alpha.2 → 0.1.7-alpha.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 (62) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +31 -13
  3. package/README.zh.md +31 -13
  4. package/lib/CodeCard.module.css +83 -0
  5. package/lib/ConnectionIndicator.module.css +1 -10
  6. package/lib/DiffBlock.module.css +24 -53
  7. package/lib/HoverCard.module.css +27 -0
  8. package/lib/Menu.module.css +68 -48
  9. package/lib/Modal.module.css +6 -2
  10. package/lib/OnboardingSurface.module.css +1 -2
  11. package/lib/PathLabel.module.css +28 -0
  12. package/lib/ReadBlock.module.css +13 -81
  13. package/lib/RiskConfirmation.module.css +1 -1
  14. package/lib/SegmentedControl.module.css +76 -0
  15. package/lib/SegmentedTabs.module.css +55 -0
  16. package/lib/StateDot.module.css +64 -37
  17. package/lib/TerminalBlock.module.css +13 -6
  18. package/lib/TextShimmer.module.css +27 -0
  19. package/lib/Toast.module.css +31 -2
  20. package/lib/Tooltip.module.css +12 -0
  21. package/lib/index.js +5892 -3142
  22. package/lib/markdown/CodeBlock.module.css +15 -0
  23. package/lib/markdown/MarkdownText.module.css +1 -19
  24. package/lib/settings-form/SettingsForm.module.css +53 -0
  25. package/lib/settings-form/fields.module.css +145 -0
  26. package/lib/types/CodeToolbar.d.ts +30 -0
  27. package/lib/types/ConfigField.d.ts +33 -0
  28. package/lib/types/DiffBlock.d.ts +3 -3
  29. package/lib/types/DisclosureRow.d.ts +4 -1
  30. package/lib/types/HoverCard.d.ts +14 -4
  31. package/lib/types/LinkIcon.d.ts +15 -30
  32. package/lib/types/Menu.d.ts +53 -8
  33. package/lib/types/PathLabel.d.ts +13 -0
  34. package/lib/types/PermissionIcon.d.ts +39 -0
  35. package/lib/types/ReadBlock.d.ts +2 -1
  36. package/lib/types/ReferenceIcon.d.ts +12 -7
  37. package/lib/types/SegmentedControl.d.ts +35 -0
  38. package/lib/types/SegmentedTabs.d.ts +26 -0
  39. package/lib/types/SiteGlyph.d.ts +1 -1
  40. package/lib/types/StateDot.d.ts +6 -4
  41. package/lib/types/TerminalBlock.d.ts +15 -3
  42. package/lib/types/TextShimmer.d.ts +13 -0
  43. package/lib/types/Toast.d.ts +19 -2
  44. package/lib/types/Tooltip.d.ts +13 -2
  45. package/lib/types/code-file-types.d.ts +1 -1
  46. package/lib/types/code-highlighting.d.ts +19 -0
  47. package/lib/types/icons/index.d.ts +376 -195
  48. package/lib/types/icons/props.d.ts +1 -1
  49. package/lib/types/icons/shared-artwork.d.ts +36 -0
  50. package/lib/types/index.d.ts +22 -4
  51. package/lib/types/markdown/CodeBlock.d.ts +6 -1
  52. package/lib/types/markdown/render.d.ts +3 -0
  53. package/lib/types/overlay-top-margin.d.ts +14 -0
  54. package/lib/types/plugin-artwork.d.ts +17 -0
  55. package/lib/types/settings-form/SettingsForm.d.ts +46 -0
  56. package/lib/types/settings-form/fields.d.ts +69 -0
  57. package/lib/types/settings-form/form-model.d.ts +214 -0
  58. package/lib/types/useAnchoredMaxHeight.d.ts +4 -1
  59. package/lib/types/useAnchoredPosition.d.ts +3 -1
  60. package/lib/types/user-text.d.ts +2 -2
  61. package/lib/user-text.module.css +4 -1
  62. package/package.json +5 -3
@@ -1,4 +1,4 @@
1
- /** Shared props for every ic_ds_* icon component. */
1
+ /** Shared props for every product icon component. */
2
2
  export interface IconProps {
3
3
  /** Square edge in px; defaults to the glyph's own drawn size. */
4
4
  size?: number | undefined;
@@ -0,0 +1,36 @@
1
+ import type { IconProps } from './props.ts';
2
+ interface WeightedArtworkProps extends IconProps {
3
+ strokeWidth: number;
4
+ }
5
+ /**
6
+ * Render shared new-conversation geometry for product and reference icons.
7
+ * @param props - Size, optional CSS class, and inherited stroke width.
8
+ * @returns The decorative SVG artwork.
9
+ */
10
+ export declare const NewChatOutlineArtwork: ({ size, className, strokeWidth }: WeightedArtworkProps) => import("react").JSX.Element;
11
+ /**
12
+ * Render shared globe geometry for product and link icons.
13
+ * @param props - Size, optional CSS class, and inherited stroke width.
14
+ * @returns The decorative SVG artwork.
15
+ */
16
+ export declare const GlobeOutlineArtwork: ({ size, className, strokeWidth }: WeightedArtworkProps) => import("react").JSX.Element;
17
+ /**
18
+ * Render shared code-bracket geometry for product and link icons.
19
+ * @param props - Size, optional CSS class, and inherited stroke width.
20
+ * @returns The decorative SVG artwork.
21
+ */
22
+ export declare const CodeBracketsArtwork: ({ size, className, strokeWidth }: WeightedArtworkProps) => import("react").JSX.Element;
23
+ /**
24
+ * Render shared document-browse geometry for product and reference icons.
25
+ * @param props - Size, optional CSS class, and inherited stroke width.
26
+ * @returns The decorative SVG artwork.
27
+ */
28
+ export declare const BrowseOutlineArtwork: ({ size, className, strokeWidth }: WeightedArtworkProps) => import("react").JSX.Element;
29
+ /**
30
+ * Render shared closed-folder geometry for product, reference, and link icons.
31
+ * @param props - Size, optional CSS class, and inherited stroke width.
32
+ * @returns The decorative SVG artwork.
33
+ */
34
+ export declare const FolderCloseArtwork: ({ size, className, strokeWidth }: WeightedArtworkProps) => import("react").JSX.Element;
35
+ export {};
36
+ //# sourceMappingURL=shared-artwork.d.ts.map
@@ -1,20 +1,28 @@
1
1
  /**
2
2
  * Cordis-free React primitives styled only through `--dsw-*` tokens.
3
3
  */
4
+ export type { CodeToolbarLabels } from './CodeToolbar.tsx';
4
5
  export { StateDot } from './StateDot.tsx';
5
6
  export type { StateDotState } from './StateDot.tsx';
6
7
  export { DisclosureRow } from './DisclosureRow.tsx';
7
8
  export type { DisclosureRowProps } from './DisclosureRow.tsx';
9
+ export { TextShimmer } from './TextShimmer.tsx';
10
+ export type { TextShimmerProps } from './TextShimmer.tsx';
8
11
  export { Button } from './Button.tsx';
9
12
  export type { ButtonVariant } from './Button.tsx';
10
13
  export { Pill } from './Pill.tsx';
14
+ export { SegmentedTabs } from './SegmentedTabs.tsx';
15
+ export type { SegmentedTab } from './SegmentedTabs.tsx';
11
16
  export { Tag } from './Tag.tsx';
12
17
  export type { TagTone } from './Tag.tsx';
18
+ export { PathLabel } from './PathLabel.tsx';
13
19
  export { Switch } from './Switch.tsx';
20
+ export { SegmentedControl } from './SegmentedControl.tsx';
21
+ export type { SegmentedControlOption } from './SegmentedControl.tsx';
14
22
  export { Checkbox } from './Checkbox.tsx';
15
23
  export { Input } from './Input.tsx';
16
- export { Menu } from './Menu.tsx';
17
- export type { MenuEntry, MenuItem, MenuSeparator, MenuLabel } from './Menu.tsx';
24
+ export { Menu, MenuItemButton } from './Menu.tsx';
25
+ export type { MenuItemButtonProps, MenuEntry, MenuItem, MenuSeparator, MenuLabel } from './Menu.tsx';
18
26
  export { useAnchoredMaxHeight } from './useAnchoredMaxHeight.ts';
19
27
  export { useAnchoredPosition } from './useAnchoredPosition.ts';
20
28
  export type { AnchoredPositionOptions } from './useAnchoredPosition.ts';
@@ -29,9 +37,10 @@ export type { ConnectionIndicatorState } from './ConnectionIndicator.tsx';
29
37
  export { FishLogo, FISH_LOGO_PATH, FISH_LOGO_VIEWBOX } from './FishLogo.tsx';
30
38
  export { BrandWordmark } from './BrandWordmark.tsx';
31
39
  export type { BrandWordmarkProps } from './BrandWordmark.tsx';
32
- export { ReferenceIcon } from './ReferenceIcon.tsx';
40
+ export { PermissionIconFullAccessMedium, PermissionIconFullAccessRegular, PermissionIconReadOnlyMedium, PermissionIconReadOnlyRegular, PermissionIconWorkspaceWriteMedium, PermissionIconWorkspaceWriteRegular, } from './PermissionIcon.tsx';
41
+ export { ReferenceIconMedium, ReferenceIconRegular } from './ReferenceIcon.tsx';
33
42
  export type { ReferenceIconKind, ReferenceIconProps } from './ReferenceIcon.tsx';
34
- export { LinkIcon, classifyLinkPath } from './LinkIcon.tsx';
43
+ export { LinkIconMedium, LinkIconRegular, classifyLinkPath } from './LinkIcon.tsx';
35
44
  export type { LinkIconKind, LinkIconProps } from './LinkIcon.tsx';
36
45
  export { FileTypeIcon, classifyFileType, fileExtension } from './FileTypeIcon.tsx';
37
46
  export type { CodeFileType, FileType, FileTypeIconProps, FileTypeKind, FileTypeProjectContext, } from './FileTypeIcon.tsx';
@@ -41,6 +50,14 @@ export type { TooltipSide } from './Tooltip.tsx';
41
50
  export { Toast } from './Toast.tsx';
42
51
  export { fileSizeText } from './file-size.ts';
43
52
  export { writeClipboard } from './clipboard.ts';
53
+ export { SettingsForm } from './settings-form/SettingsForm.tsx';
54
+ export type { SettingsFormLabels, SettingsFormProps } from './settings-form/SettingsForm.tsx';
55
+ export { SettingsSecretField, SettingsValueField } from './settings-form/fields.tsx';
56
+ export type { SettingsFieldProps } from './settings-form/fields.tsx';
57
+ export { SettingsFormModel, settingsNumberField, settingsTextField } from './settings-form/form-model.ts';
58
+ export type { SettingsFieldSpec, SettingsFieldState, SettingsFieldWrite, SettingsFormActions, SettingsFormPathOp, SettingsFormScope, SettingsFormScopeSnapshot, SettingsFormShell, SettingsSecretSpec, } from './settings-form/form-model.ts';
59
+ export { CODE_HIGHLIGHT_EXTENSIONS, languageForPath, useCodeHighlighter } from './code-highlighting.ts';
60
+ export type { CodeHighlighter, HighlightSpan } from './code-highlighting.ts';
44
61
  export { relativeTime } from './relative-time.ts';
45
62
  export { rankByName } from './rank-by-name.ts';
46
63
  export { isDarwinDesktop } from './darwin-desktop.ts';
@@ -67,4 +84,5 @@ export type { MarkdownCodeLabels, MarkdownFileMentions, MarkdownLabels, Markdown
67
84
  export { extractMarkdownPlainText } from './markdown/plain-text.ts';
68
85
  export type { MarkdownPlainTextMode, MarkdownPlainTextOptions } from './markdown/plain-text.ts';
69
86
  export * from './icons/index.tsx';
87
+ export { PluginArtworkTerminal, PluginArtworkLoop, PluginArtworkSubagent, PluginArtworkSearch, PluginArtworkDefault, } from './plugin-artwork.tsx';
70
88
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import type { Ref } from 'react';
2
+ import { type CodeToolbarLabels } from '../CodeToolbar.tsx';
2
3
  export interface CodeBlockProps {
3
4
  /** The source text, rendered verbatim (trailing newline trimmed for display). */
4
5
  code: string;
@@ -25,6 +26,10 @@ export interface CodeBlockProps {
25
26
  copyLabel: string;
26
27
  /** Copy-button label during the post-copy confirmation window. */
27
28
  copiedLabel: string;
29
+ /** Enable the shared card toolbar and spacing; omit for custom toolbar layouts. */
30
+ toolbarLabels?: CodeToolbarLabels | undefined;
31
+ /** With toolbarLabels, use the owner's wrapping preference and omit the toolbar's local wrap action. */
32
+ wrap?: boolean | undefined;
28
33
  }
29
- export declare function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers, showHeader, copyLabel, copiedLabel, }: CodeBlockProps): import("react").JSX.Element;
34
+ export declare function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers, showHeader, copyLabel, copiedLabel, toolbarLabels, wrap, }: CodeBlockProps): import("react").JSX.Element;
30
35
  //# sourceMappingURL=CodeBlock.d.ts.map
@@ -17,6 +17,7 @@
17
17
  */
18
18
  import type { ReactNode } from 'react';
19
19
  import type * as Md from 'mdast';
20
+ import type { CodeToolbarLabels } from '../CodeToolbar.tsx';
20
21
  import type { PositionedBlock } from './incremental.ts';
21
22
  /** Copy-button labels forwarded to fence CodeBlocks (this package is cordis-free, so copy arrives via props). */
22
23
  export interface MarkdownCodeLabels {
@@ -24,6 +25,8 @@ export interface MarkdownCodeLabels {
24
25
  copyLabel: string;
25
26
  /** Copy-button label during the post-copy confirmation window. */
26
27
  copiedLabel: string;
28
+ /** Shared card controls; omitted for custom toolbar layouts. */
29
+ toolbarLabels?: CodeToolbarLabels | undefined;
27
30
  }
28
31
  /** Localized chrome for a Markdown document. */
29
32
  export interface MarkdownLabels {
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Overlay clearance from the window's top strip. On macOS desktop the frame
3
+ * publishes `--dsh-frame-top-clearance` on the root element — the constant
4
+ * step below the traffic-light strip, where clicks drag the window instead of
5
+ * the overlay. JS-clamped overlays keep at least that much air above them.
6
+ * Elsewhere the property is absent and the caller's own margin applies.
7
+ */
8
+ /**
9
+ * Resolve the top margin an overlay keeps from the viewport edge.
10
+ * @param min - the overlay's own viewport margin in px, used as the floor.
11
+ * @returns the larger of `min` and the frame's published top clearance.
12
+ */
13
+ export declare function overlayTopMargin(min: number): number;
14
+ //# sourceMappingURL=overlay-top-margin.d.ts.map
@@ -0,0 +1,17 @@
1
+ import type { IconProps } from './icons/props.ts';
2
+ /** Terminal plugin artwork (prompt chevron and cursor bar). */
3
+ export declare const PluginArtworkTerminal: ({ size, className }: IconProps) => import("react").JSX.Element;
4
+ /** Agent-loop plugin artwork (four leaves circling a center). */
5
+ export declare const PluginArtworkLoop: ({ size, className }: IconProps) => import("react").JSX.Element;
6
+ /** Subagent plugin artwork (two stacked rounded squares); also marks every row inside a bundle. */
7
+ export declare const PluginArtworkSubagent: ({ size, className }: IconProps) => import("react").JSX.Element;
8
+ /**
9
+ * Web-search plugin artwork (conic-gradient ring and handle). SVG has no
10
+ * native conic gradient, so the ring clips an HTML div painted with CSS
11
+ * `conic-gradient` — the same emulation Figma exports; it renders inline in
12
+ * the browser UI but would stay empty in an `<img>` or mask context.
13
+ */
14
+ export declare const PluginArtworkSearch: ({ size, className }: IconProps) => import("react").JSX.Element;
15
+ /** Default plugin artwork for plugins without one of their own (connector blocks and a node). */
16
+ export declare const PluginArtworkDefault: ({ size, className }: IconProps) => import("react").JSX.Element;
17
+ //# sourceMappingURL=plugin-artwork.d.ts.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * One plugin's settings form as its page on the Plugins page shows it: the
3
+ * read-only notice when the deployment stores settings read-only, the
4
+ * plugin's controls, and the save that writes every staged edit. The page
5
+ * draws the plugin's title and one-liner itself.
6
+ *
7
+ * Only a save writes. Leaving the page drops every staged edit, so the form
8
+ * discards on unmount and offers no discard control. A form whose namespace
9
+ * the Host stopped serving says so in place of its controls rather than
10
+ * showing fields nothing would accept.
11
+ */
12
+ import { type ReactNode } from 'react';
13
+ import type { SettingsFormShell } from './form-model.ts';
14
+ /** The copy the form frame renders, from the owning plugin's dictionary. */
15
+ export interface SettingsFormLabels {
16
+ /** Shown in place of the controls while the namespace is not served. */
17
+ unavailable: string;
18
+ /** Shown over the controls while the document is read-only. */
19
+ readOnly: string;
20
+ /** Shown beside the save after a save the Host did not accept. */
21
+ saveFailed: string;
22
+ /** The save control. */
23
+ save: string;
24
+ /** The save control while a save is crossing the wire. */
25
+ saving: string;
26
+ }
27
+ /** Form chrome shared by every settings page. */
28
+ export interface SettingsFormProps {
29
+ /** The frame's copy. */
30
+ labels: SettingsFormLabels;
31
+ /** The form state: availability, writability, and what a save would do. */
32
+ state: SettingsFormShell;
33
+ /** Write every staged edit. */
34
+ onSave: () => void;
35
+ /** Drop every staged edit; the form calls it when it leaves the page. */
36
+ onDiscard: () => void;
37
+ /** The plugin's controls. */
38
+ children: ReactNode;
39
+ }
40
+ /**
41
+ * Render one plugin's settings form.
42
+ * @param props - the form's copy and state, its controls, and the save and discard actions.
43
+ * @returns the form, or the unavailable line while the namespace is not served.
44
+ */
45
+ export declare function SettingsForm(props: SettingsFormProps): import("react").JSX.Element;
46
+ //# sourceMappingURL=SettingsForm.d.ts.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The controls of a settings form. Each renders one field's label, its staged
3
+ * text, whether saving would leave an override, and — when one stands — the
4
+ * reset that stages a clear back to the composition layer. Nothing here
5
+ * writes: a control reports what the user typed, and the form's save is the
6
+ * single point where a draft becomes a document mutation.
7
+ */
8
+ import { type ReactNode } from 'react';
9
+ /** What every settings field control needs regardless of its value type. */
10
+ export interface SettingsFieldProps {
11
+ /** Stable id associating the label with its control. */
12
+ id: string;
13
+ /** Visible label. */
14
+ label: string;
15
+ /** One-line explanation rendered under the control. */
16
+ hint: string;
17
+ /** Draft text this control renders. */
18
+ text: string;
19
+ /** True when saving would leave a user-layer entry for this field. */
20
+ overridden: boolean;
21
+ /** True when the draft is not a value this field accepts. */
22
+ invalid: boolean;
23
+ /** Copy for the overridden badge. */
24
+ overriddenLabel: string;
25
+ /** Copy for the reset control. */
26
+ resetLabel: string;
27
+ /** Copy shown in place of the hint while the draft is invalid. */
28
+ invalidLabel: string;
29
+ /** Disables every control (read-only document, or an unavailable namespace). */
30
+ disabled: boolean;
31
+ /** Stage draft text. */
32
+ onEdit: (text: string) => void;
33
+ /** Stage a clear so the field re-inherits the composition layer. */
34
+ onReset: () => void;
35
+ }
36
+ /**
37
+ * A staged value field. `numeric` only hints the keypad: which drafts a field
38
+ * accepts is decided by its spec, so the control never silently rewrites what
39
+ * the user typed.
40
+ * @param props - the field's copy, its staged text, and the edit actions.
41
+ * @returns the labelled control.
42
+ */
43
+ export declare function SettingsValueField(props: Omit<SettingsFieldProps, 'hint'> & {
44
+ /** Optional explanation shown below the input. */
45
+ hint?: string;
46
+ /** Rules disclosed by the information button beside the label. */
47
+ help?: {
48
+ label: string;
49
+ content: ReactNode;
50
+ };
51
+ /** Hints a numeric keypad without narrowing what the control accepts. */
52
+ numeric?: boolean;
53
+ /** Placeholder shown while the draft is empty. */
54
+ placeholder?: string;
55
+ }): import("react").JSX.Element;
56
+ /**
57
+ * A write-only credential control. The value never rides a response, so the
58
+ * control reports only whether one is configured and starts blank; a blank
59
+ * draft writes nothing, which keeps the stored key rather than clearing it.
60
+ * @param props - the field's copy, its staged text, and the configured state.
61
+ * @returns the labelled control.
62
+ */
63
+ export declare function SettingsSecretField(props: Pick<SettingsFieldProps, 'id' | 'label' | 'hint' | 'text' | 'disabled' | 'onEdit'> & {
64
+ /** Whether the Host reports a configured credential for this reference. */
65
+ configured: boolean;
66
+ /** Copy describing the configured state. */
67
+ stateLabel: string;
68
+ }): import("react").JSX.Element;
69
+ //# sourceMappingURL=fields.d.ts.map
@@ -0,0 +1,214 @@
1
+ /**
2
+ * The staged form model behind a plugin's settings page.
3
+ *
4
+ * A card stages what the user types and writes it only when they save. Each
5
+ * settings write is a durable, revision-fenced document mutation, so a control
6
+ * that committed as it settled turned one edit into a write the user never
7
+ * asked for and could not preview; staged text makes what is on screen exactly
8
+ * what a save would store.
9
+ *
10
+ * A field shows its effective value — the user layer over the composition
11
+ * layer over the schema default — and whether the user layer carries it. That
12
+ * presence, not a value comparison, is what marks a field overridden: an
13
+ * override equal to the composition default is still an override.
14
+ */
15
+ import { type SnapshotStore } from '@deepseek-ai/dsh-client-store';
16
+ /** What the model reads of one Host entry's form. */
17
+ export interface SettingsFormScopeSnapshot<T> {
18
+ /** `ready` while the Host serves the entry to this client; the form renders nothing otherwise. */
19
+ status: 'loading' | 'ready' | 'unavailable';
20
+ /** Last accepted schema-resolved section; undefined before the first acceptance. */
21
+ value: T | undefined;
22
+ /** Composition layer the value resolves over: what a field reverts to once cleared. */
23
+ base: unknown;
24
+ /** Raw user layer as stored; a field's PRESENCE here is what marks it overridden. */
25
+ user: unknown;
26
+ /** Whether the Host document accepts writes. */
27
+ writable: boolean;
28
+ /** Revision the snapshot was read at; a save fences its mutation with the revision its drafts started from. */
29
+ revision: number | undefined;
30
+ }
31
+ /** One path edit a save sends, as the shared configuration form's `mutate` accepts it. */
32
+ export type SettingsFormPathOp = {
33
+ op: 'set';
34
+ path: readonly string[];
35
+ value: unknown;
36
+ } | {
37
+ op: 'unset';
38
+ path: readonly string[];
39
+ };
40
+ /** The entry form the model stages over: the reads and the atomic write of the form `ui-settings` shares per Host entry. */
41
+ export interface SettingsFormScope<T> {
42
+ /** @returns the current sync snapshot. */
43
+ getSnapshot(): SettingsFormScopeSnapshot<T>;
44
+ /**
45
+ * Observe snapshot replacements.
46
+ * @param listener - invoked after each snapshot change.
47
+ * @returns the disposer removing this listener.
48
+ */
49
+ subscribe(listener: () => void): () => void;
50
+ /**
51
+ * Apply ordered field edits in one revision-fenced write.
52
+ * @param ops - the edits, in staging order.
53
+ * @param expectedRevision - the revision the drafts were staged against, when known.
54
+ * @returns true for Host acceptance, false for refusal, after any recovery read.
55
+ */
56
+ mutate(ops: readonly SettingsFormPathOp[], expectedRevision?: number): Promise<boolean>;
57
+ }
58
+ /** The write one field's staged text performs when the card is saved. */
59
+ export type SettingsFieldWrite = {
60
+ kind: 'set';
61
+ value: unknown;
62
+ } | {
63
+ kind: 'clear';
64
+ };
65
+ /** How one section field converts between its stored value and its draft text. */
66
+ export interface SettingsFieldSpec {
67
+ /** Field name inside the namespace section. */
68
+ field: string;
69
+ /** Render a stored value as draft text; the empty string when the section carries none. */
70
+ format: (value: unknown) => string;
71
+ /**
72
+ * The write this draft text stages, or undefined when the text is not a
73
+ * value this field accepts — which blocks the save rather than discarding it.
74
+ */
75
+ parse: (text: string) => SettingsFieldWrite | undefined;
76
+ }
77
+ /**
78
+ * A control whose value is written outside the settings section. A credential
79
+ * literal never rides a response, so its draft has nothing to seed from: it is
80
+ * blank until typed, and a blank draft writes nothing.
81
+ */
82
+ export interface SettingsSecretSpec {
83
+ /** Field name addressing this control inside the card's form. */
84
+ field: string;
85
+ /** Write the staged text; resolves to whether the Host accepted it. */
86
+ write: (text: string) => Promise<boolean>;
87
+ }
88
+ /** One field as a card's control renders it. */
89
+ export interface SettingsFieldState {
90
+ /** Draft text the control renders. */
91
+ text: string;
92
+ /**
93
+ * Whether saving would leave a user-layer entry for this field. A staged
94
+ * edit answers for itself, so the badge previews the save rather than
95
+ * reporting a state the pending edit already contradicts.
96
+ */
97
+ overridden: boolean;
98
+ /** Whether the draft is not a value this field accepts, which blocks saving. */
99
+ invalid: boolean;
100
+ }
101
+ /** Form state every plugin card shares. */
102
+ export interface SettingsFormShell {
103
+ /** False while the namespace is not served to this client; the card renders nothing. */
104
+ available: boolean;
105
+ /** Whether the Host document accepts writes. */
106
+ writable: boolean;
107
+ /** Whether the form holds edits that a save would write. */
108
+ dirty: boolean;
109
+ /** Whether any staged draft is invalid, which blocks the save. */
110
+ invalid: boolean;
111
+ /** Whether a save is crossing the wire. */
112
+ saving: boolean;
113
+ /** Whether the last save did not land as staged; cleared by the next edit or save. */
114
+ failed: boolean;
115
+ }
116
+ /** The write actions every plugin card's slot entry injects. */
117
+ export interface SettingsFormActions {
118
+ /** Stage draft text for one field. */
119
+ edit: (field: string, text: string) => void;
120
+ /** Stage a clear, so saving lets the field re-inherit the composition layer. */
121
+ resetField: (field: string) => void;
122
+ /** Write every staged edit, then re-seed from what the Host accepted. */
123
+ save: () => void;
124
+ /** Drop every staged edit. */
125
+ discard: () => void;
126
+ }
127
+ /**
128
+ * A whole-number field. An empty draft clears the field; any other draft that
129
+ * is not a finite number blocks the save.
130
+ * @param field - field name inside the namespace section.
131
+ * @returns the field's conversion spec.
132
+ */
133
+ export declare function settingsNumberField(field: string): SettingsFieldSpec;
134
+ /**
135
+ * A free-text field. An empty draft clears the field, so emptying the control
136
+ * and saving is the same gesture as resetting it.
137
+ * @param field - field name inside the namespace section.
138
+ * @returns the field's conversion spec.
139
+ */
140
+ export declare function settingsTextField(field: string): SettingsFieldSpec;
141
+ /**
142
+ * Stages one card's edits over one settings namespace and writes them on save.
143
+ *
144
+ * The form publishes through a snapshot store because slot components read
145
+ * through a snapshot selector, while both the scope and the local drafts
146
+ * change underneath; every projection is rebuilt from the two together.
147
+ */
148
+ export declare class SettingsFormModel<T> {
149
+ private readonly scope;
150
+ private readonly specs;
151
+ private readonly secretSpecs;
152
+ private readonly staged;
153
+ private readonly listeners;
154
+ private baseline;
155
+ private readonly unsubscribe;
156
+ private saving;
157
+ private failed;
158
+ /**
159
+ * @param scope - the shared configuration form for this card's namespace.
160
+ * @param specs - the section fields this card edits.
161
+ * @param secrets - the card's write-only controls, written outside the section.
162
+ */
163
+ constructor(scope: SettingsFormScope<T>, specs: SettingsFieldSpec[], secrets?: SettingsSecretSpec[]);
164
+ /**
165
+ * Publish a projection of this form, rebuilt whenever the scope or a draft changes.
166
+ * @param project - build the card's state from the form's current reads.
167
+ * @returns the store the card's component reads through its bound selector.
168
+ */
169
+ bind<S>(project: () => S): SnapshotStore<S>;
170
+ /**
171
+ * Read the card-level state: what the Host serves, and what a save would do.
172
+ * @returns the form state every card shares.
173
+ */
174
+ shell(): SettingsFormShell;
175
+ /**
176
+ * Read one control's state.
177
+ * @param field - field name of a section field or of a write-only control.
178
+ * @returns the draft text, whether a save would leave an override, and whether it is invalid.
179
+ */
180
+ field(field: string): SettingsFieldState;
181
+ /**
182
+ * Build the edit, reset, save, and discard actions bound to this form.
183
+ * @returns the actions a card's slot entry injects.
184
+ */
185
+ actions(): SettingsFormActions;
186
+ /**
187
+ * Write every staged edit, then re-seed from what the Host accepted.
188
+ *
189
+ * The Host is the only authority on whether a value was accepted — its
190
+ * validators own the constraints no schema can express — so the outcome is
191
+ * read back from the section rather than predicted here. A save that did not
192
+ * land keeps its drafts, so the user can correct them instead of retyping.
193
+ * @returns settlement after every write and the read-back.
194
+ */
195
+ save(): Promise<void>;
196
+ /** Release the form's accepted-value subscription. */
197
+ dispose(): void;
198
+ /**
199
+ * Every staged edit a save would write. An entry whose draft is not a value
200
+ * its field accepts carries no write: the form is still dirty, and the save
201
+ * refuses rather than dropping the edit.
202
+ * @returns the planned writes, in the order the fields were staged.
203
+ */
204
+ private plan;
205
+ private stage;
206
+ private spec;
207
+ private snapshotOf;
208
+ private sectionValue;
209
+ private baseValue;
210
+ private userLayer;
211
+ private stored;
212
+ private publish;
213
+ }
214
+ //# sourceMappingURL=form-model.d.ts.map
@@ -5,7 +5,10 @@ import type { RefObject } from 'react';
5
5
  * @param cap - design max-height in px (the clamp never exceeds it).
6
6
  * @param signal - re-measure trigger: pass the overlay's render state so anchor
7
7
  * moves (composer growth) re-fit; resize/scroll re-fit while mounted.
8
+ * @param margin - viewport top margin floor in px; the frame's published top
9
+ * clearance widens it. Callers under fixed chrome (the conversation header)
10
+ * raise it past their chrome's height.
8
11
  * @returns the max-height to apply inline, in px.
9
12
  */
10
- export declare function useAnchoredMaxHeight(ref: RefObject<HTMLElement>, cap: number, signal: unknown): number;
13
+ export declare function useAnchoredMaxHeight(ref: RefObject<HTMLElement>, cap: number, signal: unknown, margin?: number): number;
11
14
  //# sourceMappingURL=useAnchoredMaxHeight.d.ts.map
@@ -20,6 +20,8 @@ export interface AnchoredPositionOptions {
20
20
  panelRef: RefObject<HTMLElement | null>;
21
21
  /** Which anchor edge the panel hangs from: below it (`bottom`, the default) or above it (`top`). */
22
22
  side?: 'top' | 'bottom';
23
+ /** Which anchor edge the panel lines up with: its left edge (`start`, the default) or its right edge (`end`). */
24
+ align?: 'start' | 'end';
23
25
  /** Distance kept between the anchor edge named by `side` and the panel. */
24
26
  gap: number;
25
27
  /** Distance kept between the panel and each viewport edge. */
@@ -27,7 +29,7 @@ export interface AnchoredPositionOptions {
27
29
  }
28
30
  /**
29
31
  * Track an anchor and return the panel's fixed coordinates.
30
- * @param options - the open state, the two refs, the placement side, and the gap/margin distances.
32
+ * @param options - the open state, the two refs, the placement side and alignment, and the gap/margin distances.
31
33
  * @returns `left`/`top` for the panel, or `null` before the first measurement.
32
34
  */
33
35
  export declare function useAnchoredPosition(options: AnchoredPositionOptions): CSSProperties | null;
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Display projection of reference forms in sent user text (bubble and queue
3
3
  * rows). The logged model text remains the single truth; this is presentation
4
- * only, and every part renders inline so a single-line message never breaks
5
- * across lines. Four decoration sources, by precedence: the wire session form
4
+ * only. Inline references follow the consumer's wrapping policy and keep long
5
+ * labels within its width. Four decoration sources, by precedence: the wire session form
6
6
  * `@[label](dsh-session:...)` folds to its label; exact session labels
7
7
  * supplied by an adjacent recall decorate their bare `@label` mention; plain
8
8
  * `@name` word-boundary tokens decorate by shape alone; and a plain `/name`
@@ -14,10 +14,13 @@
14
14
  chip's label rode ~3px above the surrounding text in both surfaces. An
15
15
  inline chip shares the consumer's baseline by construction. */
16
16
  display: inline;
17
+ max-width: 100%;
17
18
  margin: 0 2px;
18
19
  color: var(--dsw-alias-state-business-primary);
19
20
  font-weight: 500;
20
- white-space: nowrap;
21
+ white-space: inherit;
22
+ overflow-wrap: anywhere;
23
+ text-align: inherit;
21
24
  }
22
25
 
23
26
  /* Skill and command tokens read as code: the theme's code family at the
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-primitives",
3
3
  "description": "Pure React atoms for the dsh web UI: controls, icons, markdown, and JSON inspectors (zero cordis)",
4
- "version": "0.1.6-alpha.2",
4
+ "version": "0.1.7-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -47,7 +47,9 @@
47
47
  "micromark-extension-gfm": "^3.0.0",
48
48
  "micromark-util-sanitize-uri": "^2.0.1",
49
49
  "simple-icons": "16.31.0",
50
- "@deepseek-ai/cordis": "^4.0.2"
50
+ "@deepseek-ai/cordis": "~4.0.4",
51
+ "@deepseek-ai/dsh-util-workspace-path": "0.1.7-alpha.2",
52
+ "@deepseek-ai/dsh-client-store": "0.1.7-alpha.2"
51
53
  },
52
54
  "files": [
53
55
  "lib/index.js",
@@ -55,6 +57,6 @@
55
57
  "lib/types/**/*.d.ts"
56
58
  ],
57
59
  "peerDependencies": {
58
- "@deepseek-ai/cordis": "^4.0.2"
60
+ "@deepseek-ai/cordis": "~4.0.4"
59
61
  }
60
62
  }