@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.
- package/README.md +29 -5
- package/dist/client.d.mts +186 -11201
- package/dist/client.mjs +981 -1827
- package/package.json +5 -7
- package/src/client/controls/Bubble.module.css +17 -0
- package/src/client/controls/Bubble.tsx +17 -0
- package/src/client/controls/Button.module.css +10 -0
- package/src/client/controls/Button.tsx +29 -0
- package/src/client/controls/DisclosureRow.module.css +22 -0
- package/src/client/controls/DisclosureRow.tsx +20 -0
- package/src/client/controls/IconButton.module.css +50 -0
- package/src/client/controls/IconButton.tsx +45 -0
- package/src/client/controls/ModelRouteList.module.css +100 -0
- package/src/client/controls/ModelRouteList.tsx +118 -0
- package/src/client/controls/MultilineField.module.css +26 -0
- package/src/client/controls/MultilineField.tsx +19 -0
- package/src/client/controls/SearchInput.module.css +11 -0
- package/src/client/controls/SearchInput.tsx +22 -0
- package/src/client/controls/SearchSelect.module.css +34 -0
- package/src/client/controls/SearchSelect.tsx +138 -0
- package/src/client/controls/SeatButton.module.css +53 -0
- package/src/client/controls/SeatButton.tsx +40 -0
- package/src/client/controls/Spinner.module.css +18 -0
- package/src/client/controls/Spinner.tsx +14 -0
- package/src/client/controls/Tabs.module.css +48 -0
- package/src/client/controls/Tabs.tsx +57 -0
- package/src/client/controls/TagInput.module.css +105 -0
- package/src/client/controls/TagInput.tsx +152 -0
- package/src/client/controls/TimeLabel.module.css +16 -0
- package/src/client/controls/TimeLabel.tsx +15 -0
- package/src/client/controls/VisuallyHidden.module.css +9 -0
- package/src/client/controls/VisuallyHidden.tsx +16 -0
- package/src/client/controls/classes.ts +7 -0
- package/src/client/controls/index.ts +30 -0
- package/src/client/controls/tags.ts +24 -0
- package/src/client/index.ts +10 -2
- package/src/client/layout/Row.tsx +86 -0
- package/src/client/layout/Stack.tsx +79 -0
- package/src/client/layout/Text.tsx +54 -0
- package/src/client/layout/index.ts +16 -0
- package/src/client/layout/layout.module.css +217 -0
- package/src/client/layout/scales.ts +91 -0
- package/src/client/schema-form/Editor.tsx +48 -65
- package/src/client/schema-form/SchemaForm.tsx +4 -4
- package/src/client/schema-form/controller.ts +5 -2
- package/src/client/schema-form/geometry.ts +3 -0
- package/src/client/schema-form/schema-form.module.css +335 -0
- package/src/client/schema-form/value.tsx +10 -9
- package/src/client/settings-form/SettingsFieldRow.module.css +93 -0
- package/src/client/settings-form/SettingsFieldRow.tsx +103 -0
- package/src/client/settings-form/index.ts +5 -18
- package/src/css-modules.d.ts +6 -0
- package/src/client/schema-form/styles.ts +0 -321
- package/src/client/settings-form/SettingsForm.styles.ts +0 -58
- package/src/client/settings-form/SettingsForm.tsx +0 -68
- package/src/client/settings-form/fields.styles.ts +0 -158
- package/src/client/settings-form/fields.tsx +0 -184
- package/src/client/settings-form/form-model.ts +0 -339
- package/src/client/styling/css.ts +0 -27
- package/src/client/styling/focus.ts +0 -9
- package/src/client/styling/index.ts +0 -6
- package/src/client/styling/styled.tsx +0 -54
- package/src/client/styling/styling.ts +0 -142
- package/src/client/styling/token.ts +0 -275
- package/src/client/styling/toolkit.ts +0 -37
- package/src/client/theme.generated.ts +0 -846
- package/src/client/theme.ts +0 -5
|
@@ -7,7 +7,8 @@ import { useState, type ReactNode } from "react";
|
|
|
7
7
|
import { IconChevronDownOutlineRegular, Menu } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
8
8
|
import type { SelectOption } from "./hints.ts";
|
|
9
9
|
import type { SchemaFieldOwnerProps } from "./slot-contract.ts";
|
|
10
|
-
import { ICON_SIZE
|
|
10
|
+
import { ICON_SIZE } from "./geometry.ts";
|
|
11
|
+
import css from "./schema-form.module.css";
|
|
11
12
|
import { TruncatedTooltip } from "./TruncatedTooltip.tsx";
|
|
12
13
|
|
|
13
14
|
// 编辑态文本:字符串就是原文(换行照旧),其余按 JSON 写。
|
|
@@ -46,18 +47,18 @@ export function InlineValue({ owner }: { owner: SchemaFieldOwnerProps }): ReactN
|
|
|
46
47
|
// secret 不回显值:这里显示的是「已配置」的 `••••••`,它不会截断,`owner.hint` 也不是「被截断文本的全文」
|
|
47
48
|
// ——按行内浮层的口径它不该弹气泡,所以回到非侵入的原行为:挂原生 `title`。
|
|
48
49
|
return (
|
|
49
|
-
<
|
|
50
|
+
<span className={css.lineValue} data-tone="empty" title={owner.hint}>
|
|
50
51
|
{owner.secretConfigured ? "••••••" : ""}
|
|
51
|
-
</
|
|
52
|
+
</span>
|
|
52
53
|
);
|
|
53
54
|
}
|
|
54
55
|
// 值长了会在行里截断(值槽可收缩):确实截断时整份内容由官方 `Tooltip` 承载,放得下就不挂。
|
|
55
56
|
const text = tokenText(owner.value);
|
|
56
57
|
return (
|
|
57
58
|
<TruncatedTooltip label={text}>
|
|
58
|
-
<
|
|
59
|
+
<span className={css.lineValue} data-tone={owner.node.readOnly === null ? tokenTone(owner.value) : "empty"}>
|
|
59
60
|
{text}
|
|
60
|
-
</
|
|
61
|
+
</span>
|
|
61
62
|
</TruncatedTooltip>
|
|
62
63
|
);
|
|
63
64
|
}
|
|
@@ -101,7 +102,7 @@ function OptionSelect({
|
|
|
101
102
|
setOpen(false);
|
|
102
103
|
}}
|
|
103
104
|
anchor={
|
|
104
|
-
<
|
|
105
|
+
<button className={css.valueTrigger}
|
|
105
106
|
type="button"
|
|
106
107
|
disabled={owner.disabled}
|
|
107
108
|
aria-label={owner.label}
|
|
@@ -109,13 +110,13 @@ function OptionSelect({
|
|
|
109
110
|
setOpen(true);
|
|
110
111
|
}}
|
|
111
112
|
>
|
|
112
|
-
<
|
|
113
|
+
<span className={css.lineValue}
|
|
113
114
|
data-tone={current === undefined ? tokenTone(owner.value) : tokenTone(current.value)}
|
|
114
115
|
>
|
|
115
116
|
{label}
|
|
116
|
-
</
|
|
117
|
+
</span>
|
|
117
118
|
<IconChevronDownOutlineRegular size={ICON_SIZE} />
|
|
118
|
-
</
|
|
119
|
+
</button>
|
|
119
120
|
}
|
|
120
121
|
/>
|
|
121
122
|
);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* 字段行(`SettingsFieldRow`):官方 `SettingsValueField` 的留白口径(上下各 12px),分隔线由调用方按
|
|
2
|
+
「不是这一组的第一个字段」显式声明——两种排法与官方控件的留白因此完全一致。 */
|
|
3
|
+
.row {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.row[data-divider='true'] {
|
|
9
|
+
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* 上下排(输入类):标签 / 控件 / 说明同列。 */
|
|
13
|
+
.body {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: 6px;
|
|
17
|
+
padding: 12px 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* 右置排(切换类):左列标签与说明,控件贴最右。 */
|
|
21
|
+
.inline {
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: row;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: space-between;
|
|
26
|
+
gap: 16px;
|
|
27
|
+
padding: 12px 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.text {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
gap: 2px;
|
|
34
|
+
min-width: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.label {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: 8px;
|
|
41
|
+
font-size: 13px;
|
|
42
|
+
font-weight: 500;
|
|
43
|
+
line-height: 1.5;
|
|
44
|
+
color: var(--dsw-alias-label-primary);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* 覆盖标记(「已覆盖」):照官方 badge 的 neutral 那一档。 */
|
|
48
|
+
.badge {
|
|
49
|
+
display: inline-flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
padding: 1px 8px;
|
|
52
|
+
border-radius: 999px;
|
|
53
|
+
corner-shape: round;
|
|
54
|
+
font-size: 11px;
|
|
55
|
+
line-height: 17px;
|
|
56
|
+
font-weight: 500;
|
|
57
|
+
color: var(--dsw-alias-label-secondary);
|
|
58
|
+
background: var(--dsw-alias-bg-module-platform);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* 恢复默认的入口:文字按钮,没有边框与底色。 */
|
|
62
|
+
.reset {
|
|
63
|
+
padding: 0;
|
|
64
|
+
border: none;
|
|
65
|
+
background: none;
|
|
66
|
+
font: inherit;
|
|
67
|
+
font-size: 12px;
|
|
68
|
+
line-height: 1.5;
|
|
69
|
+
color: var(--dsw-alias-label-secondary);
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.reset:hover:not(:disabled) {
|
|
74
|
+
color: var(--dsw-alias-label-primary);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.reset:disabled {
|
|
78
|
+
cursor: default;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.hint {
|
|
82
|
+
margin: 0;
|
|
83
|
+
font-size: 12px;
|
|
84
|
+
line-height: 1.5;
|
|
85
|
+
color: var(--dsw-alias-label-tertiary);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.invalid {
|
|
89
|
+
margin: 0;
|
|
90
|
+
font-size: 12px;
|
|
91
|
+
line-height: 1.5;
|
|
92
|
+
color: var(--dsw-alias-state-error-primary);
|
|
93
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// 字段行:设置面上"一个字段"的容器——标签、控件、说明、非法提示与"已覆盖"徽标都由它摆位置。
|
|
2
|
+
// 两种排法按控件的宽度选:输入类(stack)是标签 / 控件 / 说明同列,切换类(inline)把控件贴最右。
|
|
3
|
+
// 留白与分隔线的口径与官方 `SettingsValueField` 一致(上下各 12px、相邻 0.5px 细线)。
|
|
4
|
+
|
|
5
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
6
|
+
import { classes } from "../controls/classes.ts";
|
|
7
|
+
import css from "./SettingsFieldRow.module.css";
|
|
8
|
+
|
|
9
|
+
export interface SettingsFieldRowProps
|
|
10
|
+
extends Omit<HTMLAttributes<HTMLDivElement>, "children" | "color"> {
|
|
11
|
+
// 字段名。
|
|
12
|
+
label: string;
|
|
13
|
+
// 字段下面那句说明("这一项到底管什么")。
|
|
14
|
+
hint: string;
|
|
15
|
+
// 控件的摆法:`stack`(同列,输入类,缺省)或 `inline`(控件贴右,切换类)。
|
|
16
|
+
layout?: "stack" | "inline";
|
|
17
|
+
// 与上一个字段之间画一条细分隔线(这一组里不是第一个字段时为 true)。
|
|
18
|
+
divider?: boolean;
|
|
19
|
+
// 字段被用户层覆盖时的徽标文案;不给就不显示。
|
|
20
|
+
overriddenLabel?: string;
|
|
21
|
+
// 恢复默认的入口:给了它与 `resetLabel` 才显示(覆盖用户层那一项)。
|
|
22
|
+
resetLabel?: string;
|
|
23
|
+
onReset?: () => void;
|
|
24
|
+
// 非法提示;不给就只说合法的那些。
|
|
25
|
+
invalid?: string;
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function SettingsFieldRow({
|
|
30
|
+
label,
|
|
31
|
+
hint,
|
|
32
|
+
layout = "stack",
|
|
33
|
+
divider = false,
|
|
34
|
+
overriddenLabel,
|
|
35
|
+
resetLabel,
|
|
36
|
+
onReset,
|
|
37
|
+
invalid,
|
|
38
|
+
children,
|
|
39
|
+
className,
|
|
40
|
+
...rest
|
|
41
|
+
}: SettingsFieldRowProps): ReactNode {
|
|
42
|
+
const labelNode = (
|
|
43
|
+
<span className={css.label}>
|
|
44
|
+
{label}
|
|
45
|
+
{overriddenLabel === undefined ? null : (
|
|
46
|
+
<>
|
|
47
|
+
<span className={css.badge}>{overriddenLabel}</span>
|
|
48
|
+
{resetLabel === undefined || onReset === undefined ? null : (
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
51
|
+
className={css.reset}
|
|
52
|
+
onClick={() => {
|
|
53
|
+
onReset();
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
{resetLabel}
|
|
57
|
+
</button>
|
|
58
|
+
)}
|
|
59
|
+
</>
|
|
60
|
+
)}
|
|
61
|
+
</span>
|
|
62
|
+
);
|
|
63
|
+
const invalidNode =
|
|
64
|
+
invalid === undefined ? null : (
|
|
65
|
+
<p className={css.invalid} role="alert">
|
|
66
|
+
{invalid}
|
|
67
|
+
</p>
|
|
68
|
+
);
|
|
69
|
+
if (layout === "inline") {
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
className={classes(css.row, className)}
|
|
73
|
+
data-role="field-row"
|
|
74
|
+
data-divider={divider ? "true" : "false"}
|
|
75
|
+
{...rest}
|
|
76
|
+
>
|
|
77
|
+
<div className={css.inline}>
|
|
78
|
+
<div className={css.text}>
|
|
79
|
+
{labelNode}
|
|
80
|
+
<p className={css.hint}>{hint}</p>
|
|
81
|
+
</div>
|
|
82
|
+
{children}
|
|
83
|
+
</div>
|
|
84
|
+
{invalidNode}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return (
|
|
89
|
+
<div
|
|
90
|
+
className={classes(css.row, className)}
|
|
91
|
+
data-role="field-row"
|
|
92
|
+
data-divider={divider ? "true" : "false"}
|
|
93
|
+
{...rest}
|
|
94
|
+
>
|
|
95
|
+
<div className={css.body}>
|
|
96
|
+
{labelNode}
|
|
97
|
+
{children}
|
|
98
|
+
<p className={css.hint}>{hint}</p>
|
|
99
|
+
</div>
|
|
100
|
+
{invalidNode}
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -1,19 +1,6 @@
|
|
|
1
|
-
// 设置表单原语的 client
|
|
2
|
-
//
|
|
1
|
+
// 设置表单原语的 client 出口:**直接用上游那份**(同名同形的 `SettingsForm` / 两个字段控件 / 表单模型,样式
|
|
2
|
+
// 本来就是上游的 `.module.css`),这里只留本包自造的那一个字段行——上游的 `SettingsValueField` 摆不下
|
|
3
|
+
// 「标签 + 控件 + 说明 + 覆盖标记」这一整行。
|
|
3
4
|
|
|
4
|
-
export {
|
|
5
|
-
export type {
|
|
6
|
-
SettingsFieldSpec,
|
|
7
|
-
SettingsFieldState,
|
|
8
|
-
SettingsFieldWrite,
|
|
9
|
-
SettingsFormActions,
|
|
10
|
-
SettingsFormPathOp,
|
|
11
|
-
SettingsFormScope,
|
|
12
|
-
SettingsFormScopeSnapshot,
|
|
13
|
-
SettingsFormShell,
|
|
14
|
-
SettingsSecretSpec,
|
|
15
|
-
} from "./form-model.ts";
|
|
16
|
-
export { SettingsSecretField, SettingsValueField } from "./fields.tsx";
|
|
17
|
-
export type { SettingsFieldProps } from "./fields.tsx";
|
|
18
|
-
export { SettingsForm } from "./SettingsForm.tsx";
|
|
19
|
-
export type { SettingsFormLabels, SettingsFormProps } from "./SettingsForm.tsx";
|
|
5
|
+
export { SettingsFieldRow } from "./SettingsFieldRow.tsx";
|
|
6
|
+
export type { SettingsFieldRowProps } from "./SettingsFieldRow.tsx";
|
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
// 行式编辑器的样式:等宽、行号、语法色、悬停行为——全部消费官方 `--dsw-*` token。
|
|
2
|
-
//
|
|
3
|
-
// 几何只有一套:行高、行内间距、动作按钮尺寸都由下面几个常量给,行与行之间不各自决定。
|
|
4
|
-
|
|
5
|
-
import { styled } from "../styling/index.ts";
|
|
6
|
-
import { dsw } from "../theme.ts";
|
|
7
|
-
|
|
8
|
-
// 等宽字体栈:配置值的观感与代码一致。
|
|
9
|
-
const MONO = 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace';
|
|
10
|
-
|
|
11
|
-
// 行高:行内容、行内输入、折叠箭头、按钮排都在这一档上对齐。
|
|
12
|
-
const ROW = "22px";
|
|
13
|
-
|
|
14
|
-
// 行内元素之间的间距。
|
|
15
|
-
const GAP = "6px";
|
|
16
|
-
|
|
17
|
-
// 动作按钮:方形图标键,尺寸与行高同一档的紧凑版。
|
|
18
|
-
const ACTION = "20px";
|
|
19
|
-
|
|
20
|
-
// 动作按钮之间的间距(比行内间距紧一档)。
|
|
21
|
-
const ACTION_GAP = "4px";
|
|
22
|
-
|
|
23
|
-
// 图标边长(行内图标与动作键同一档)。
|
|
24
|
-
export const ICON_SIZE = 14;
|
|
25
|
-
|
|
26
|
-
// 折叠列(chevron 与它的占位)宽度。
|
|
27
|
-
const FOLD = "14px";
|
|
28
|
-
|
|
29
|
-
// 行号列宽度。
|
|
30
|
-
const NUMBER = "32px";
|
|
31
|
-
|
|
32
|
-
// 行尾留白。
|
|
33
|
-
const PAD = "8px";
|
|
34
|
-
|
|
35
|
-
// 细线:theme 给的 0.5px 描边(`0 0 0 0.5px var(--dsw-elevation-stroke-color)`)。
|
|
36
|
-
const HAIRLINE = dsw.elevation.stroke;
|
|
37
|
-
|
|
38
|
-
// 描边色:theme 里给描边用的那一档(与官方 `Menu` 面板同源)。
|
|
39
|
-
const LINE = dsw.elevation.stroke.color;
|
|
40
|
-
|
|
41
|
-
// 行左侧那道「改过」的标记宽度。
|
|
42
|
-
const EDGE = "2px";
|
|
43
|
-
|
|
44
|
-
// 出错那一行的底色:theme 的红掺进透明里(半透明红),比整行实红轻、又比只有一道条明显。
|
|
45
|
-
const ERROR_WASH = `color-mix(in srgb, ${String(dsw.alias.state.error.secondary)} 12%, transparent)`;
|
|
46
|
-
|
|
47
|
-
// 编辑器的根:纵向排列的行。
|
|
48
|
-
export const EditorRoot = styled("div")({
|
|
49
|
-
display: "flex",
|
|
50
|
-
flexDirection: "column",
|
|
51
|
-
width: "100%",
|
|
52
|
-
fontFamily: MONO,
|
|
53
|
-
fontSize: "14px",
|
|
54
|
-
lineHeight: ROW,
|
|
55
|
-
color: dsw.alias.label.primary,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// 一行:行号 + 折进 + 行内容。悬停给一层浅底。
|
|
59
|
-
export const LineRow = styled("div")({
|
|
60
|
-
display: "flex",
|
|
61
|
-
// 顶部对齐:行里出现输入框或多行值时,内容不跟着上下跳。
|
|
62
|
-
alignItems: "flex-start",
|
|
63
|
-
gap: GAP,
|
|
64
|
-
minHeight: ROW,
|
|
65
|
-
paddingRight: PAD,
|
|
66
|
-
whiteSpace: "nowrap",
|
|
67
|
-
"&:hover": { background: dsw.alias.bg.layer["2"] },
|
|
68
|
-
// 选中一行(点行号):整行留一层底色,便于对着行号找内容。
|
|
69
|
-
"&[data-selected='true']": { background: dsw.alias.bg.layer["3"] },
|
|
70
|
-
// 本页改过还没保存:左边一道强调色,和「已经存进用户层」的浅灰区分开。
|
|
71
|
-
"&[data-dirty='true']": { boxShadow: `inset ${EDGE} 0 0 ${String(dsw.alias.brand.primary)}` },
|
|
72
|
-
"&[data-overridden='true']": { boxShadow: `inset ${EDGE} 0 0 ${String(LINE)}` },
|
|
73
|
-
// 改过的字段名用强调色(存没存看左边那道条);出错时红优先。
|
|
74
|
-
"&[data-dirty='true'] [data-role='key']": { color: dsw.alias.brand.primary },
|
|
75
|
-
"&[data-overridden='true'] [data-role='key']": { color: dsw.alias.brand.primary },
|
|
76
|
-
// 有问题的行:半透明红底 + 左侧一道实红条,字段名与注释位那句都是红字。
|
|
77
|
-
"&[data-invalid='true']": {
|
|
78
|
-
background: ERROR_WASH,
|
|
79
|
-
boxShadow: `inset ${EDGE} 0 0 ${String(dsw.alias.state.error.primary)}`,
|
|
80
|
-
},
|
|
81
|
-
"&[data-invalid='true'] [data-role='key']": { color: dsw.alias.state.error.primary },
|
|
82
|
-
// 行内的行为按钮(撤回 / 恢复默认 / 复制 / 移除):平时透明,悬停这一行才显形。
|
|
83
|
-
"&:hover [data-role='actions']": { opacity: 1 },
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// 一行的内容区:**缩进只作用在这里**——行号列固定在左边,缩进不推它。
|
|
87
|
-
export const LineBody = styled("div")({
|
|
88
|
-
display: "flex",
|
|
89
|
-
alignItems: "center",
|
|
90
|
-
gap: GAP,
|
|
91
|
-
flex: 1,
|
|
92
|
-
minWidth: 0,
|
|
93
|
-
minHeight: ROW,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// 行号列:右对齐、不可选、淡色。
|
|
97
|
-
export const LineNumber = styled("span")({
|
|
98
|
-
flex: "none",
|
|
99
|
-
width: NUMBER,
|
|
100
|
-
lineHeight: ROW,
|
|
101
|
-
textAlign: "right",
|
|
102
|
-
color: dsw.alias.label.caption,
|
|
103
|
-
userSelect: "none",
|
|
104
|
-
cursor: "pointer",
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// 折叠 chevron(官方箭头图标,尺寸与别的行内图标同源)。
|
|
108
|
-
export const LineFold = styled("button")({
|
|
109
|
-
display: "inline-flex",
|
|
110
|
-
alignItems: "center",
|
|
111
|
-
justifyContent: "center",
|
|
112
|
-
flex: "none",
|
|
113
|
-
width: FOLD,
|
|
114
|
-
height: ROW,
|
|
115
|
-
padding: 0,
|
|
116
|
-
border: 0,
|
|
117
|
-
background: "none",
|
|
118
|
-
color: dsw.alias.label.caption,
|
|
119
|
-
cursor: "pointer",
|
|
120
|
-
"&:hover": { color: dsw.alias.label.secondary },
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// 叶子行的折叠占位(保持列对齐)。
|
|
124
|
-
export const LineFoldSpacer = styled("span")({
|
|
125
|
-
flex: "none",
|
|
126
|
-
width: FOLD,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// 键名(配置键原样;数组下标淡一些)。
|
|
130
|
-
export const LineKey = styled("span")({
|
|
131
|
-
flex: "none",
|
|
132
|
-
color: dsw.alias.label.primary,
|
|
133
|
-
"&[data-index='true']": { color: dsw.alias.label.caption },
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// 结构符与标点(`{` `}` `[` `]` `:`)。
|
|
137
|
-
export const LineToken = styled("span")({
|
|
138
|
-
flex: "none",
|
|
139
|
-
color: dsw.alias.label.caption,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// 值(token):字符串 / 数字 / 布尔各有语法色。
|
|
143
|
-
//
|
|
144
|
-
// **必须是块级盒**:`overflow` / `text-overflow` 对 inline 盒不生效,inline 的 token 无论多长都不会截断,
|
|
145
|
-
// 只会把行撑宽(`min-width` / `overflow` 那两条声明在 inline 盒上是死代码)。
|
|
146
|
-
export const LineValue = styled("span")({
|
|
147
|
-
display: "block",
|
|
148
|
-
minWidth: 0,
|
|
149
|
-
overflow: "hidden",
|
|
150
|
-
textOverflow: "ellipsis",
|
|
151
|
-
cursor: "text",
|
|
152
|
-
color: dsw.alias.state.business.primary,
|
|
153
|
-
"&[data-tone='number']": { color: dsw.alias.state.warn.primary },
|
|
154
|
-
"&[data-tone='boolean']": { color: dsw.alias.state.success.primary },
|
|
155
|
-
"&[data-tone='empty']": { color: dsw.alias.label.caption },
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// 值在行里的位置(字段槽的宿主):键名与结构符都是 `flex: none`,它是行里**唯一可收缩**的那一项。
|
|
159
|
-
//
|
|
160
|
-
// 这里不能放一个裸 `<span>`:flex item 的自动最小尺寸(`min-width: auto`)加上行上的 `white-space: nowrap`,
|
|
161
|
-
// 等于「整段值的宽度」——值长了它一步不让,直接把整行(进而整个表单壳)撑宽。给它 `min-width: 0` 之后,
|
|
162
|
-
// 值要么按自然宽度摆着,要么收缩到行尾并自己截断,长值因此不再有上限之外的影响。
|
|
163
|
-
export const ValueSlot = styled("span")({
|
|
164
|
-
display: "block",
|
|
165
|
-
// 不生长:短值保持自然宽度(不会被拉成一整行),长值才收缩。
|
|
166
|
-
flex: "0 1 auto",
|
|
167
|
-
minWidth: 0,
|
|
168
|
-
overflow: "hidden",
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
// 值上的下拉触发:**它自己就是值的呈现**(不再是「值 + 一个下拉」两遍),点开才出菜单。
|
|
172
|
-
export const ValueTrigger = styled("button")({
|
|
173
|
-
display: "inline-flex",
|
|
174
|
-
alignItems: "center",
|
|
175
|
-
gap: "2px",
|
|
176
|
-
minWidth: 0,
|
|
177
|
-
padding: 0,
|
|
178
|
-
border: 0,
|
|
179
|
-
background: "none",
|
|
180
|
-
font: "inherit",
|
|
181
|
-
textAlign: "left",
|
|
182
|
-
cursor: "pointer",
|
|
183
|
-
color: dsw.alias.label.caption,
|
|
184
|
-
"&:disabled": { cursor: "default" },
|
|
185
|
-
"&:focus-visible": { outline: "none" },
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// 行内输入的外壳:官方 `Input` 压到与值同一行高,并**吃掉剩下的宽度**。
|
|
189
|
-
//
|
|
190
|
-
// 行内编辑与容器闭合行的添加输入共用它——两处的输入框因此长得一模一样。
|
|
191
|
-
export const CompactInput = styled("span")({
|
|
192
|
-
display: "inline-flex",
|
|
193
|
-
flex: "1 1 auto",
|
|
194
|
-
minWidth: 0,
|
|
195
|
-
"& > span": {
|
|
196
|
-
width: "100%",
|
|
197
|
-
height: ROW,
|
|
198
|
-
padding: "0 6px",
|
|
199
|
-
borderRadius: "4px",
|
|
200
|
-
},
|
|
201
|
-
"& input": {
|
|
202
|
-
fontSize: "14px",
|
|
203
|
-
lineHeight: ROW,
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// 多行值的行内输入:官方没有多行原子,所以这里用同一套观感的字段壳(同边框、同圆角、同焦点色),
|
|
208
|
-
// 高度跟着内容长、宽度同样撑满剩下的位置。
|
|
209
|
-
export const CompactTextField = styled("span")({
|
|
210
|
-
display: "inline-flex",
|
|
211
|
-
flex: "1 1 auto",
|
|
212
|
-
minWidth: 0,
|
|
213
|
-
padding: "0 6px",
|
|
214
|
-
borderRadius: "4px",
|
|
215
|
-
background: dsw.alias.bg.layer["1"],
|
|
216
|
-
// 细线走 theme 的描边(0.5px 与颜色都由 token 给)。
|
|
217
|
-
border: 0,
|
|
218
|
-
boxShadow: HAIRLINE,
|
|
219
|
-
"&:focus-within": { "--dsw-elevation-stroke-color": dsw.alias.brand.primary },
|
|
220
|
-
"& > textarea": {
|
|
221
|
-
flex: 1,
|
|
222
|
-
width: "100%",
|
|
223
|
-
minHeight: ACTION,
|
|
224
|
-
maxHeight: "240px",
|
|
225
|
-
padding: 0,
|
|
226
|
-
border: 0,
|
|
227
|
-
outline: "none",
|
|
228
|
-
background: "transparent",
|
|
229
|
-
resize: "none",
|
|
230
|
-
fieldSizing: "content",
|
|
231
|
-
fontFamily: "inherit",
|
|
232
|
-
fontSize: "14px",
|
|
233
|
-
lineHeight: ROW,
|
|
234
|
-
color: dsw.alias.label.primary,
|
|
235
|
-
},
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
// 行内注释(schema 说明与业务文案):比配置值小一档、tertiary 色,占满这一行剩下的宽度,长了就截断——**真的被
|
|
239
|
-
// 截断时**才由官方 `Tooltip` 给全文(`Editor.tsx` 里的 `TruncatedTooltip` 包着它),放得下就一个浮层也不挂。
|
|
240
|
-
export const LineComment = styled("span")({
|
|
241
|
-
flex: "1 1 auto",
|
|
242
|
-
minWidth: 0,
|
|
243
|
-
overflow: "hidden",
|
|
244
|
-
textOverflow: "ellipsis",
|
|
245
|
-
color: dsw.alias.label.tertiary,
|
|
246
|
-
fontFamily: "inherit",
|
|
247
|
-
fontSize: "12px",
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// 行内的校验消息(与注释放同一档字号):注释位装的也可能是整段校验或解析失败的长消息,所以与注释同一套约束
|
|
251
|
-
// ——可收缩、自己截断、**截断了**才由官方 `Tooltip` 给全文。`flex: 0 1 auto`(不生长):短消息别铺满整行,添加
|
|
252
|
-
// 入口那一行里也不跟输入框抢剩下的宽度。红字先说清。
|
|
253
|
-
export const LineInvalid = styled("span")({
|
|
254
|
-
flex: "0 1 auto",
|
|
255
|
-
minWidth: 0,
|
|
256
|
-
overflow: "hidden",
|
|
257
|
-
textOverflow: "ellipsis",
|
|
258
|
-
color: dsw.alias.state.error.primary,
|
|
259
|
-
fontFamily: "inherit",
|
|
260
|
-
fontSize: "12px",
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
// 行尾的动作组:方形图标键(默认透明,悬停时显形)。
|
|
264
|
-
export const LineActions = styled("span")({
|
|
265
|
-
display: "inline-flex",
|
|
266
|
-
alignItems: "center",
|
|
267
|
-
gap: ACTION_GAP,
|
|
268
|
-
flex: "none",
|
|
269
|
-
opacity: 0,
|
|
270
|
-
"& > button": {
|
|
271
|
-
display: "inline-flex",
|
|
272
|
-
alignItems: "center",
|
|
273
|
-
justifyContent: "center",
|
|
274
|
-
width: ACTION,
|
|
275
|
-
height: ACTION,
|
|
276
|
-
padding: 0,
|
|
277
|
-
border: 0,
|
|
278
|
-
borderRadius: "4px",
|
|
279
|
-
background: "none",
|
|
280
|
-
color: dsw.alias.label.secondary,
|
|
281
|
-
cursor: "pointer",
|
|
282
|
-
"&:hover": { background: dsw.alias.bg.layer["3"], color: dsw.alias.label.primary },
|
|
283
|
-
"&:disabled": { cursor: "not-allowed", color: dsw.alias.label.dimmed },
|
|
284
|
-
},
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
// 悬停行时才显示这一行的动作按钮:显形规则写在行上(`[data-role='actions']`),组件只需要带这个属性。
|
|
288
|
-
export const HoverActions = styled(LineActions)({
|
|
289
|
-
// 编辑态的两个按钮常显,而且**要有底色与描边**:行底色上只有淡色图标是看不见的,
|
|
290
|
-
// 所以这里给实体按钮(theme 的描边 + 一层底),确认用成功档、取消用中性档。
|
|
291
|
-
"&[data-editing='true']": {
|
|
292
|
-
opacity: 1,
|
|
293
|
-
gap: GAP,
|
|
294
|
-
"& > button": {
|
|
295
|
-
boxShadow: HAIRLINE,
|
|
296
|
-
background: dsw.alias.bg.layer["1"],
|
|
297
|
-
color: dsw.alias.label.primary,
|
|
298
|
-
"&:first-of-type": { color: dsw.alias.state.success.primary },
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
// 添加输入:**跟在容器的闭合括号同一行**(菜单锚在它上面),宽度吃掉剩下的位置。
|
|
304
|
-
export const AddWrap = styled("span")({
|
|
305
|
-
position: "relative",
|
|
306
|
-
display: "inline-flex",
|
|
307
|
-
alignItems: "center",
|
|
308
|
-
gap: GAP,
|
|
309
|
-
flex: "1 1 auto",
|
|
310
|
-
minWidth: 0,
|
|
311
|
-
marginLeft: GAP,
|
|
312
|
-
"&[data-invalid='true'] input": { borderColor: dsw.alias.state.error.primary },
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
// 一行说明(不可用 / 没有可生成项时用)。
|
|
316
|
-
export const Hint = styled("p")({
|
|
317
|
-
margin: 0,
|
|
318
|
-
fontSize: "12px",
|
|
319
|
-
lineHeight: 1.5,
|
|
320
|
-
color: dsw.alias.label.tertiary,
|
|
321
|
-
});
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// 样式搬自 vendor/deepseek-harness/packages/client/ui-primitives/src/settings-form/SettingsForm.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-label-error 不在官方主题定义集内(token 树里没有这个变量),
|
|
9
|
-
// 照抄引用而不是换一个近义 token——官方主题哪天定义它,这里自动跟着生效。
|
|
10
|
-
const failedColor = "var(--dsw-alias-label-error)";
|
|
11
|
-
|
|
12
|
-
export const styles = {
|
|
13
|
-
form: {
|
|
14
|
-
display: "flex",
|
|
15
|
-
flexDirection: "column",
|
|
16
|
-
},
|
|
17
|
-
// 上游 readOnly 与 unavailable 同一条规则:两处用同一个样式对象。
|
|
18
|
-
notice: {
|
|
19
|
-
margin: "0 0 12px",
|
|
20
|
-
fontSize: "12px",
|
|
21
|
-
lineHeight: 1.5,
|
|
22
|
-
color: dsw.alias.label.tertiary,
|
|
23
|
-
},
|
|
24
|
-
footer: {
|
|
25
|
-
display: "flex",
|
|
26
|
-
alignItems: "center",
|
|
27
|
-
gap: "8px",
|
|
28
|
-
paddingTop: "16px",
|
|
29
|
-
},
|
|
30
|
-
failed: {
|
|
31
|
-
flex: 1,
|
|
32
|
-
minWidth: 0,
|
|
33
|
-
margin: 0,
|
|
34
|
-
fontSize: "12px",
|
|
35
|
-
lineHeight: 1.5,
|
|
36
|
-
color: failedColor,
|
|
37
|
-
},
|
|
38
|
-
save: {
|
|
39
|
-
appearance: "none",
|
|
40
|
-
border: "1px solid transparent",
|
|
41
|
-
borderRadius: "8px",
|
|
42
|
-
padding: "5px 14px",
|
|
43
|
-
font: "inherit",
|
|
44
|
-
fontSize: "13px",
|
|
45
|
-
lineHeight: 1.5,
|
|
46
|
-
cursor: "pointer",
|
|
47
|
-
background: dsw.alias.label.primary,
|
|
48
|
-
color: dsw.alias.bg.layer["3"],
|
|
49
|
-
"&:disabled": {
|
|
50
|
-
opacity: 0.4,
|
|
51
|
-
cursor: "default",
|
|
52
|
-
},
|
|
53
|
-
"&:focus-visible": {
|
|
54
|
-
outline: focusRing,
|
|
55
|
-
outlineOffset: "1px",
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
} satisfies Record<string, CSSProps>;
|