@morlay/dsh-client-ui-primitives 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +29 -5
  2. package/dist/client.d.mts +186 -11201
  3. package/dist/client.mjs +981 -1827
  4. package/package.json +5 -7
  5. package/src/client/controls/Bubble.module.css +17 -0
  6. package/src/client/controls/Bubble.tsx +17 -0
  7. package/src/client/controls/Button.module.css +10 -0
  8. package/src/client/controls/Button.tsx +29 -0
  9. package/src/client/controls/DisclosureRow.module.css +22 -0
  10. package/src/client/controls/DisclosureRow.tsx +20 -0
  11. package/src/client/controls/IconButton.module.css +50 -0
  12. package/src/client/controls/IconButton.tsx +45 -0
  13. package/src/client/controls/ModelRouteList.module.css +100 -0
  14. package/src/client/controls/ModelRouteList.tsx +118 -0
  15. package/src/client/controls/MultilineField.module.css +26 -0
  16. package/src/client/controls/MultilineField.tsx +19 -0
  17. package/src/client/controls/SearchInput.module.css +11 -0
  18. package/src/client/controls/SearchInput.tsx +22 -0
  19. package/src/client/controls/SearchSelect.module.css +34 -0
  20. package/src/client/controls/SearchSelect.tsx +138 -0
  21. package/src/client/controls/SeatButton.module.css +53 -0
  22. package/src/client/controls/SeatButton.tsx +40 -0
  23. package/src/client/controls/Spinner.module.css +18 -0
  24. package/src/client/controls/Spinner.tsx +14 -0
  25. package/src/client/controls/Tabs.module.css +48 -0
  26. package/src/client/controls/Tabs.tsx +57 -0
  27. package/src/client/controls/TagInput.module.css +105 -0
  28. package/src/client/controls/TagInput.tsx +152 -0
  29. package/src/client/controls/TimeLabel.module.css +16 -0
  30. package/src/client/controls/TimeLabel.tsx +15 -0
  31. package/src/client/controls/VisuallyHidden.module.css +9 -0
  32. package/src/client/controls/VisuallyHidden.tsx +16 -0
  33. package/src/client/controls/classes.ts +7 -0
  34. package/src/client/controls/index.ts +30 -0
  35. package/src/client/controls/tags.ts +24 -0
  36. package/src/client/index.ts +10 -2
  37. package/src/client/layout/Row.tsx +86 -0
  38. package/src/client/layout/Stack.tsx +79 -0
  39. package/src/client/layout/Text.tsx +54 -0
  40. package/src/client/layout/index.ts +16 -0
  41. package/src/client/layout/layout.module.css +217 -0
  42. package/src/client/layout/scales.ts +91 -0
  43. package/src/client/schema-form/Editor.tsx +48 -65
  44. package/src/client/schema-form/SchemaForm.tsx +4 -4
  45. package/src/client/schema-form/controller.ts +5 -2
  46. package/src/client/schema-form/geometry.ts +3 -0
  47. package/src/client/schema-form/schema-form.module.css +335 -0
  48. package/src/client/schema-form/value.tsx +10 -9
  49. package/src/client/settings-form/SettingsFieldRow.module.css +93 -0
  50. package/src/client/settings-form/SettingsFieldRow.tsx +103 -0
  51. package/src/client/settings-form/index.ts +5 -18
  52. package/src/css-modules.d.ts +6 -0
  53. package/src/client/schema-form/styles.ts +0 -321
  54. package/src/client/settings-form/SettingsForm.styles.ts +0 -58
  55. package/src/client/settings-form/SettingsForm.tsx +0 -68
  56. package/src/client/settings-form/fields.styles.ts +0 -158
  57. package/src/client/settings-form/fields.tsx +0 -184
  58. package/src/client/settings-form/form-model.ts +0 -339
  59. package/src/client/styling/css.ts +0 -27
  60. package/src/client/styling/focus.ts +0 -9
  61. package/src/client/styling/index.ts +0 -6
  62. package/src/client/styling/styled.tsx +0 -54
  63. package/src/client/styling/styling.ts +0 -142
  64. package/src/client/styling/token.ts +0 -275
  65. package/src/client/styling/toolkit.ts +0 -37
  66. package/src/client/theme.generated.ts +0 -846
  67. package/src/client/theme.ts +0 -5
@@ -1,68 +0,0 @@
1
- // 搬自 vendor/deepseek-harness/packages/client/ui-primitives/src/settings-form/SettingsForm.tsx
2
- // (上游 `@deepseek-ai/dsh-client-ui-primitives`):行为同形,样式从 CSS Modules 换成包内 css-in-js。
3
-
4
- import { useEffect, useRef, type ReactNode } from "react";
5
- import { styled } from "../styling/styled.tsx";
6
- import type { SettingsFormShell } from "./form-model.ts";
7
- import { styles } from "./SettingsForm.styles.ts";
8
-
9
- const Form = styled("div")(styles.form);
10
- const Notice = styled("p")(styles.notice);
11
- const Footer = styled("div")(styles.footer);
12
- const Failed = styled("p")(styles.failed);
13
- const Save = styled("button")(styles.save);
14
-
15
- // The copy the form frame renders, from the owning plugin's dictionary.
16
- export interface SettingsFormLabels {
17
- // Shown in place of the controls while the namespace is not served.
18
- unavailable: string;
19
- // Shown over the controls while the document is read-only.
20
- readOnly: string;
21
- // Shown beside the save after a save the Host did not accept.
22
- saveFailed: string;
23
- // The save control.
24
- save: string;
25
- // The save control while a save is crossing the wire.
26
- saving: string;
27
- }
28
-
29
- // Form chrome shared by every settings page.
30
- export interface SettingsFormProps {
31
- // The frame's copy.
32
- labels: SettingsFormLabels;
33
- // The form state: availability, writability, and what a save would do.
34
- state: SettingsFormShell;
35
- // Write every staged edit.
36
- onSave: () => void;
37
- // Drop every staged edit; the form calls it when it leaves the page.
38
- onDiscard: () => void;
39
- // The plugin's controls.
40
- children: ReactNode;
41
- }
42
-
43
- // Render one plugin's settings form.
44
- export function SettingsForm(props: SettingsFormProps) {
45
- const { state, labels } = props;
46
- const discard = useRef(props.onDiscard);
47
- discard.current = props.onDiscard;
48
- useEffect(
49
- () => () => {
50
- discard.current();
51
- },
52
- [],
53
- );
54
- if (!state.available) return <Notice role="status">{labels.unavailable}</Notice>;
55
- const blocked = !state.dirty || state.invalid || state.saving;
56
- return (
57
- <Form>
58
- {!state.writable ? <Notice role="status">{labels.readOnly}</Notice> : null}
59
- {props.children}
60
- <Footer>
61
- {state.failed ? <Failed role="status">{labels.saveFailed}</Failed> : null}
62
- <Save type="button" disabled={blocked} onClick={props.onSave}>
63
- {state.saving ? labels.saving : labels.save}
64
- </Save>
65
- </Footer>
66
- </Form>
67
- );
68
- }
@@ -1,158 +0,0 @@
1
- // 样式搬自 vendor/deepseek-harness/packages/client/ui-primitives/src/settings-form/fields.module.css,
2
- // 换成包内 css-in-js(CSSProps + dsw);上游那份是 CSS Modules,本包不做预编译。
3
-
4
- import type { CSSProps } from "../styling/css.ts";
5
- import { focusRing } from "../styling/focus.ts";
6
- import { dsw } from "../theme.ts";
7
-
8
- // 上游引用的 --dsw-alias-bg-layer-4 不在官方主题定义集内(token 树里没有这个变量),
9
- // 照抄引用而不是换一个近义 token——官方主题哪天定义它,这里自动跟着生效。
10
- const helpButtonHoverBackground = "var(--dsw-alias-bg-layer-4)";
11
-
12
- export const styles = {
13
- field: {
14
- display: "flex",
15
- flexDirection: "column",
16
- gap: "6px",
17
- padding: "12px 0",
18
- "& + &": {
19
- borderTop: `0.5px solid ${String(dsw.alias.border.l2)}`,
20
- },
21
- },
22
- head: {
23
- display: "flex",
24
- alignItems: "center",
25
- gap: "8px",
26
- },
27
- label: {
28
- flex: 1,
29
- minWidth: 0,
30
- fontSize: "13px",
31
- fontWeight: 500,
32
- lineHeight: 1.5,
33
- color: dsw.alias.label.primary,
34
- },
35
- // 标签进 labelGroup 后由外层承担伸缩,自己不再抢空间(上游的 `.labelGroup > .label`)。
36
- groupLabel: {
37
- flex: "0 1 auto",
38
- },
39
- labelGroup: {
40
- display: "flex",
41
- alignItems: "center",
42
- gap: "4px",
43
- flex: 1,
44
- minWidth: 0,
45
- },
46
- helpButton: {
47
- display: "inline-flex",
48
- alignItems: "center",
49
- justifyContent: "center",
50
- flex: "none",
51
- width: "24px",
52
- height: "24px",
53
- padding: 0,
54
- border: 0,
55
- borderRadius: "6px",
56
- background: "none",
57
- color: dsw.alias.label.tertiary,
58
- cursor: "pointer",
59
- "&:hover": {
60
- background: helpButtonHoverBackground,
61
- color: dsw.alias.label.secondary,
62
- },
63
- "&[aria-expanded='true']": {
64
- background: helpButtonHoverBackground,
65
- color: dsw.alias.label.secondary,
66
- },
67
- "&:focus-visible": {
68
- outline: focusRing,
69
- outlineOffset: "1px",
70
- },
71
- },
72
- help: {
73
- padding: "10px 0 0",
74
- fontSize: "12px",
75
- lineHeight: 1.6,
76
- color: dsw.alias.label.secondary,
77
- "& > p": {
78
- margin: 0,
79
- },
80
- "& > p + p": {
81
- marginTop: "8px",
82
- },
83
- },
84
- badges: {
85
- display: "inline-flex",
86
- alignItems: "center",
87
- gap: "8px",
88
- },
89
- // 上游用 Tag(tone="neutral" | "quiet"):这里按变体拆两个 styled,形态与颜色照抄 Tag.module.css。
90
- badge: {
91
- display: "inline-flex",
92
- alignItems: "center",
93
- borderRadius: "999px",
94
- cornerShape: "round",
95
- padding: "1px 8px",
96
- fontSize: "11px",
97
- lineHeight: "17px",
98
- fontWeight: 500,
99
- whiteSpace: "nowrap",
100
- },
101
- badgeNeutral: {
102
- background: dsw.alias.bg.module.platform,
103
- color: dsw.alias.label.secondary,
104
- },
105
- badgeQuiet: {
106
- color: dsw.alias.label.tertiary,
107
- },
108
- reset: {
109
- border: "none",
110
- background: "none",
111
- padding: 0,
112
- font: "inherit",
113
- fontSize: "12px",
114
- lineHeight: 1.5,
115
- color: dsw.alias.label.secondary,
116
- cursor: "pointer",
117
- "&:hover:not(:disabled)": {
118
- color: dsw.alias.label.primary,
119
- },
120
- "&:disabled": {
121
- cursor: "default",
122
- },
123
- },
124
- input: {
125
- height: "34px",
126
- padding: "0 12px",
127
- border: `0.5px solid ${String(dsw.alias.border.l4)}`,
128
- borderRadius: "8px",
129
- background: dsw.alias.bg.layer["3"],
130
- font: "inherit",
131
- fontSize: "13px",
132
- lineHeight: 1.5,
133
- color: dsw.alias.label.primary,
134
- "&:focus-visible": {
135
- outline: "none",
136
- borderColor: dsw.alias.state.business.primary,
137
- },
138
- "&:disabled": {
139
- color: dsw.alias.label.tertiary,
140
- cursor: "default",
141
- },
142
- "&[aria-invalid='true']": {
143
- borderColor: dsw.alias.state.error.primary,
144
- },
145
- },
146
- invalid: {
147
- margin: 0,
148
- fontSize: "12px",
149
- lineHeight: 1.5,
150
- color: dsw.alias.state.error.primary,
151
- },
152
- hint: {
153
- margin: 0,
154
- fontSize: "12px",
155
- lineHeight: 1.5,
156
- color: dsw.alias.label.tertiary,
157
- },
158
- } satisfies Record<string, CSSProps>;
@@ -1,184 +0,0 @@
1
- // 搬自 vendor/deepseek-harness/packages/client/ui-primitives/src/settings-form/fields.tsx
2
- // (上游 `@deepseek-ai/dsh-client-ui-primitives`):结构与行为同形,样式从 CSS Modules 换成包内 css-in-js。
3
- // 上游用的 Tag 与 IconInfoOutlineRegular 没有一起搬——本包不依赖上游 primitives,用等价的本地徽标与图标。
4
-
5
- import { useState, type ReactNode } from "react";
6
- import { styled } from "../styling/styled.tsx";
7
- import { styles } from "./fields.styles.ts";
8
-
9
- const Field = styled("div")(styles.field);
10
- const Head = styled("div")(styles.head);
11
- const LabelGroup = styled("div")(styles.labelGroup);
12
- const GroupLabel = styled("label")(styles.label, styles.groupLabel);
13
- const HeadLabel = styled("label")(styles.label);
14
- const HelpButton = styled("button")(styles.helpButton);
15
- const Help = styled("div")(styles.help);
16
- const Badges = styled("span")(styles.badges);
17
- const NeutralBadge = styled("span")(styles.badge, styles.badgeNeutral);
18
- const QuietBadge = styled("span")(styles.badge, styles.badgeQuiet);
19
- const Reset = styled("button")(styles.reset);
20
- const Input = styled("input")(styles.input);
21
- const Invalid = styled("p")(styles.invalid);
22
- const Hint = styled("p")(styles.hint);
23
-
24
- // 上游 icons 里的 IconInfoOutlineRegular(fill-only 几何,整份图标表不搬)。
25
- function InfoGlyph({ size }: { size: number }) {
26
- return (
27
- <svg
28
- width={size}
29
- height={size}
30
- viewBox="0 0 14 14"
31
- fill="none"
32
- xmlns="http://www.w3.org/2000/svg"
33
- aria-hidden="true"
34
- >
35
- <path
36
- d="M12.5757 7.00012C12.5757 3.92085 10.0794 1.42463 7.00012 1.42456C3.9208 1.42456 1.42456 3.9208 1.42456 7.00012C1.42463 10.0794 3.92085 12.5757 7.00012 12.5757C10.0793 12.5756 12.5756 10.0793 12.5757 7.00012ZM13.8002 7.00012C13.8001 10.7559 10.7559 13.8001 7.00012 13.8002C3.2443 13.8002 0.199291 10.7559 0.199219 7.00012C0.199219 3.24426 3.24426 0.199219 7.00012 0.199219C10.7559 0.199291 13.8002 3.2443 13.8002 7.00012Z"
37
- fill="currentColor"
38
- />
39
- <path d="M7.6127 3.18921V4.55986H6.38735V3.18921H7.6127Z" fill="currentColor" />
40
- <path d="M7.6127 5.68921V10.8109H6.38735V5.68921H7.6127Z" fill="currentColor" />
41
- </svg>
42
- );
43
- }
44
-
45
- // What every settings field control needs regardless of its value type.
46
- export interface SettingsFieldProps {
47
- // Stable id associating the label with its control.
48
- id: string;
49
- // Visible label.
50
- label: string;
51
- // One-line explanation rendered under the control.
52
- hint: string;
53
- // Draft text this control renders.
54
- text: string;
55
- // True when saving would leave a user-layer entry for this field.
56
- overridden: boolean;
57
- // True when the draft is not a value this field accepts.
58
- invalid: boolean;
59
- // Copy for the overridden badge.
60
- overriddenLabel: string;
61
- // Copy for the reset control.
62
- resetLabel: string;
63
- // Copy shown in place of the hint while the draft is invalid.
64
- invalidLabel: string;
65
- // Disables every control (read-only document, or an unavailable namespace).
66
- disabled: boolean;
67
- // Stage draft text.
68
- onEdit: (text: string) => void;
69
- // Stage a clear so the field re-inherits the composition layer.
70
- onReset: () => void;
71
- }
72
-
73
- // A staged value field. `numeric` only hints the keypad: which drafts a field accepts is decided by its spec, so the
74
- // control never silently rewrites what the user typed.
75
- export function SettingsValueField(
76
- props: Omit<SettingsFieldProps, "hint"> & {
77
- // Optional explanation shown below the input.
78
- hint?: string;
79
- // Rules disclosed by the information button beside the label.
80
- help?: { label: string; content: ReactNode };
81
- // Hints a numeric keypad without narrowing what the control accepts.
82
- numeric?: boolean;
83
- // Placeholder shown while the draft is empty.
84
- placeholder?: string;
85
- },
86
- ) {
87
- const [helpOpen, setHelpOpen] = useState(false);
88
- const helpId = `${props.id}-help`;
89
- const messageId = `${props.id}-message`;
90
- const hasMessage = props.invalid || Boolean(props.hint);
91
- const description = [hasMessage ? messageId : "", helpOpen ? helpId : ""]
92
- .filter(Boolean)
93
- .join(" ");
94
- return (
95
- <Field>
96
- <Head>
97
- <LabelGroup>
98
- <GroupLabel htmlFor={props.id}>{props.label}</GroupLabel>
99
- {props.help !== undefined ? (
100
- <HelpButton
101
- type="button"
102
- aria-label={props.help.label}
103
- aria-expanded={helpOpen}
104
- aria-controls={helpId}
105
- onClick={() => {
106
- setHelpOpen(!helpOpen);
107
- }}
108
- >
109
- <InfoGlyph size={12} />
110
- </HelpButton>
111
- ) : null}
112
- </LabelGroup>
113
- {props.overridden ? (
114
- <Badges>
115
- <NeutralBadge>{props.overriddenLabel}</NeutralBadge>
116
- <Reset type="button" disabled={props.disabled} onClick={props.onReset}>
117
- {props.resetLabel}
118
- </Reset>
119
- </Badges>
120
- ) : null}
121
- </Head>
122
- <Input
123
- id={props.id}
124
- type="text"
125
- {...(props.numeric === true ? { inputMode: "numeric" as const } : {})}
126
- {...(props.invalid ? { "aria-invalid": true } : {})}
127
- aria-describedby={description || undefined}
128
- value={props.text}
129
- placeholder={props.placeholder ?? ""}
130
- disabled={props.disabled}
131
- onChange={(event) => {
132
- props.onEdit(event.target.value);
133
- }}
134
- />
135
- {hasMessage ? (
136
- props.invalid ? (
137
- <Invalid id={messageId}>{props.invalidLabel}</Invalid>
138
- ) : (
139
- <Hint id={messageId}>{props.hint}</Hint>
140
- )
141
- ) : null}
142
- {props.help !== undefined && helpOpen ? (
143
- <Help id={helpId} role="region" aria-label={props.help.label}>
144
- {props.help.content}
145
- </Help>
146
- ) : null}
147
- </Field>
148
- );
149
- }
150
-
151
- // A write-only credential control. The value never rides a response, so the control reports only whether one is
152
- // configured and starts blank; a blank draft writes nothing, which keeps the stored key rather than clearing it.
153
- // 控件要求浏览器不要把已存的登录密码自动填进来(上游 11c0511271)。
154
- export function SettingsSecretField(
155
- props: Pick<SettingsFieldProps, "id" | "label" | "hint" | "text" | "disabled" | "onEdit"> & {
156
- // Whether the Host reports a configured credential for this reference.
157
- configured: boolean;
158
- // Copy describing the configured state.
159
- stateLabel: string;
160
- },
161
- ) {
162
- const Badge = props.configured ? NeutralBadge : QuietBadge;
163
- return (
164
- <Field>
165
- <Head>
166
- <HeadLabel htmlFor={props.id}>{props.label}</HeadLabel>
167
- <Badges>
168
- <Badge>{props.stateLabel}</Badge>
169
- </Badges>
170
- </Head>
171
- <Input
172
- id={props.id}
173
- type="password"
174
- autoComplete="new-password"
175
- value={props.text}
176
- disabled={props.disabled}
177
- onChange={(event) => {
178
- props.onEdit(event.target.value);
179
- }}
180
- />
181
- <Hint>{props.hint}</Hint>
182
- </Field>
183
- );
184
- }