@dsh-plugin/dsh-ui-components 0.0.1 → 0.1.0-dev.43613492370

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 (65) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +21 -21
  3. package/README.md +136 -136
  4. package/README.zh_CN.md +136 -136
  5. package/docs/tokens.md +67 -67
  6. package/lib/basic/form.js +49 -49
  7. package/lib/basic/styles.js +391 -391
  8. package/lib/composites/styles.js +427 -427
  9. package/lib/data/styles.js +257 -257
  10. package/lib/display/styles.js +310 -310
  11. package/lib/feedback/styles.js +228 -228
  12. package/lib/foundation/bridge.js +203 -203
  13. package/lib/navigation/styles.js +417 -417
  14. package/package.json +104 -104
  15. package/scripts/check-size.mjs +69 -69
  16. package/scripts/dump-npm-latest.mjs +59 -59
  17. package/scripts/gen-host-tokens.mjs +61 -61
  18. package/scripts/gen-icons.mjs +188 -178
  19. package/scripts/verify-no-literal-colors.mjs +63 -63
  20. package/scripts/verify-tokens.mjs +135 -135
  21. package/src/basic/button.tsx +76 -76
  22. package/src/basic/form.tsx +187 -187
  23. package/src/basic/icon-button.tsx +66 -66
  24. package/src/basic/index.ts +38 -38
  25. package/src/basic/input.tsx +140 -140
  26. package/src/basic/select.tsx +120 -120
  27. package/src/basic/styles.ts +412 -412
  28. package/src/basic/toggle.tsx +116 -116
  29. package/src/composites/data-view.tsx +189 -189
  30. package/src/composites/index.ts +11 -11
  31. package/src/composites/indicator.tsx +90 -90
  32. package/src/composites/menu-select.tsx +230 -230
  33. package/src/composites/panel.tsx +72 -72
  34. package/src/composites/settings.tsx +80 -80
  35. package/src/composites/styles.ts +450 -450
  36. package/src/data/collection.tsx +71 -71
  37. package/src/data/index.ts +39 -39
  38. package/src/data/info.tsx +89 -89
  39. package/src/data/picker.tsx +113 -113
  40. package/src/data/styles.ts +278 -278
  41. package/src/display/container.tsx +67 -67
  42. package/src/display/feedback.tsx +92 -92
  43. package/src/display/index.ts +44 -44
  44. package/src/display/label.tsx +165 -165
  45. package/src/display/overlay.tsx +103 -103
  46. package/src/display/styles.ts +332 -332
  47. package/src/feedback/global.tsx +110 -110
  48. package/src/feedback/index.ts +19 -19
  49. package/src/feedback/inline.tsx +50 -50
  50. package/src/feedback/panel.tsx +93 -93
  51. package/src/feedback/styles.ts +250 -250
  52. package/src/foundation/bridge.ts +318 -318
  53. package/src/foundation/icons.tsx +315 -315
  54. package/src/foundation/index.ts +33 -33
  55. package/src/foundation/palette.ts +153 -153
  56. package/src/foundation/provider.tsx +62 -62
  57. package/src/foundation/style.ts +63 -63
  58. package/src/foundation/tokens.ts +62 -62
  59. package/src/index.ts +18 -18
  60. package/src/navigation/hierarchy.tsx +82 -82
  61. package/src/navigation/index.ts +21 -21
  62. package/src/navigation/indicator.tsx +125 -125
  63. package/src/navigation/menu.tsx +94 -94
  64. package/src/navigation/segmented.tsx +136 -136
  65. package/src/navigation/styles.ts +438 -438
@@ -1,135 +1,135 @@
1
- #!/usr/bin/env node
2
- /**
3
- * 校验源码里出现的每个 DSH 变量名都真实存在于宿主样式表中。
4
- *
5
- * 宿主升级后若重命名某个 `--dsw-*` 令牌,本脚本会在离线阶段直接报错,而不是
6
- * 等到运行时样式静默失效。宿主样式表位置按顺序探测:
7
- *
8
- * 1. `DSH_UI_THEME_DIR` 环境变量指向的目录;
9
- * 2. 工作区内的 DSH 源码副本 `.dsh/temp/session-tree-harness/...`;
10
- * 3. `node_modules/@deepseek-ai/dsh-client-ui-theme/` 之类已安装布局。
11
- *
12
- * 全部探测失败时默认跳过本次校验并以 0 退出:宿主样式表不属于本包依赖,缺了
13
- * 它只说明当前机器没装 DSH 源码,不该阻断发布。设置 `DSH_UI_REQUIRE_TOKENS=1`
14
- * 可以让缺失变成失败,供需要强校验的 CI 使用。
15
- */
16
- import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs'
17
- import { dirname, join, resolve } from 'node:path'
18
- import { fileURLToPath } from 'node:url'
19
-
20
- const root = join(dirname(fileURLToPath(import.meta.url)), '..')
21
-
22
- /** 宿主样式表文件相对于 ui-theme 包的路径。 */
23
- const STYLE_FILES = ['base.css', 'design-platform.css', 'gradient-shadow-text.css', 'focus.css', 'scrollbar.css', 'corner-shape.css']
24
-
25
- /**
26
- * 按候选顺序定位宿主样式目录。
27
- *
28
- * @returns 样式目录绝对路径。
29
- */
30
- function locateThemeDir() {
31
- const candidates = []
32
- if (process.env.DSH_UI_THEME_DIR) candidates.push(process.env.DSH_UI_THEME_DIR)
33
- candidates.push(resolve(root, '..', '.dsh', 'temp', 'session-tree-harness', 'packages', 'client', 'ui-theme', 'src', 'styles'))
34
- candidates.push(resolve(root, 'node_modules', '@deepseek-ai', 'dsh-client-ui-theme', 'src', 'styles'))
35
- candidates.push(resolve(root, 'node_modules', '@deepseek-ai', 'dsh-client-ui-theme', 'lib', 'styles'))
36
- for (const candidate of candidates) {
37
- if (existsSync(candidate) && statSync(candidate).isDirectory()) return candidate
38
- }
39
- return null
40
- }
41
-
42
- const themeDir = locateThemeDir()
43
-
44
- if (themeDir === null) {
45
- const message = [
46
- '未找到 DSH 宿主样式目录,跳过令牌校验。',
47
- '需要该校验时设置 DSH_UI_THEME_DIR,或在工作区保留 .dsh/temp/session-tree-harness 源码副本。',
48
- ].join('\n')
49
- if (process.env.DSH_UI_REQUIRE_TOKENS === '1') {
50
- console.error(message.replace('跳过令牌校验', '且已设置 DSH_UI_REQUIRE_TOKENS=1'))
51
- process.exit(2)
52
- }
53
- console.log(message)
54
- process.exit(0)
55
- }
56
-
57
- /** 从宿主样式表解析出的全部变量名。 */
58
- const hostTokens = new Set()
59
- for (const file of STYLE_FILES) {
60
- const path = join(themeDir, file)
61
- if (!existsSync(path)) continue
62
- const css = readFileSync(path, 'utf8')
63
- for (const match of css.matchAll(/(--ds[wh]?-[a-z0-9-]*[a-z0-9])\s*:/g)) hostTokens.add(match[1])
64
- }
65
-
66
- if (hostTokens.size === 0) {
67
- console.error(`未能从 ${themeDir} 解析出任何 --dsw-* 变量`)
68
- process.exit(2)
69
- }
70
-
71
- /**
72
- * 匹配源码中一个完整的宿主令牌名。
73
- *
74
- * 覆盖宿主的三类前缀:`--dsw-*`(主题令牌)、`--dsh-*`(主题运行时写入的
75
- * 变量)与 `--ds-*`(字体与动效)。令牌必须以字母或数字结尾,且后面不能
76
- * 紧跟 `-`、`*` 或标识符字符,这样注释里的通配写法(`--dsw-alias-*`)与
77
- * 半截前缀不会被当成真实令牌名。
78
- */
79
- const TOKEN_PATTERN = /--ds[wh]?-[a-z0-9-]*[a-z0-9](?![-\w*])/g
80
-
81
- /**
82
- * 递归列出目录下的 TypeScript 源文件。
83
- *
84
- * @param dir - 起始目录。
85
- * @returns 文件绝对路径列表。
86
- */
87
- function listSources(dir) {
88
- const out = []
89
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
90
- const path = join(dir, entry.name)
91
- if (entry.isDirectory()) out.push(...listSources(path))
92
- else if (/\.tsx?$/.test(entry.name)) out.push(path)
93
- }
94
- return out
95
- }
96
-
97
- /**
98
- * 匹配本包自己定义局部变量的两种写法:CSS 里的 `--name:` 声明,以及 TSX
99
- * inline style 里的 `'--name': value` 键。分段控件的位置变量就是这样注入的。
100
- */
101
- const DECLARATION_PATTERNS = [
102
- /(--ds[wh]?-[a-z0-9-]*[a-z0-9])\s*:/g,
103
- /['"](--ds[wh]?-[a-z0-9-]*[a-z0-9])['"]\s*:/g,
104
- ]
105
-
106
- const sources = listSources(join(root, 'src')).filter(file => !file.endsWith('icons-index.ts'))
107
-
108
- // 本包自己声明的局部变量(如分段控件的位置变量)不要求宿主定义。
109
- const localTokens = new Set()
110
- for (const file of sources) {
111
- const text = readFileSync(file, 'utf8')
112
- for (const pattern of DECLARATION_PATTERNS) {
113
- for (const match of text.matchAll(pattern)) localTokens.add(match[1])
114
- }
115
- }
116
-
117
- const problems = []
118
- const referenced = new Set()
119
- for (const file of sources) {
120
- const relative = file.slice(root.length + 1).replace(/\\/g, '/')
121
- const text = readFileSync(file, 'utf8')
122
- for (const match of text.matchAll(TOKEN_PATTERN)) {
123
- const name = match[0]
124
- referenced.add(name)
125
- if (!hostTokens.has(name) && !localTokens.has(name)) problems.push(`${relative} 引用了不存在的令牌 ${name}`)
126
- }
127
- }
128
-
129
- if (problems.length > 0) {
130
- console.error([...new Set(problems)].join('\n'))
131
- console.error('对照宿主样式表修正上述令牌名')
132
- process.exit(1)
133
- }
134
-
135
- console.log(`令牌校验通过:引用 ${referenced.size} 个令牌(其中本包声明 ${localTokens.size} 个),宿主定义 ${hostTokens.size} 个`)
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 校验源码里出现的每个 DSH 变量名都真实存在于宿主样式表中。
4
+ *
5
+ * 宿主升级后若重命名某个 `--dsw-*` 令牌,本脚本会在离线阶段直接报错,而不是
6
+ * 等到运行时样式静默失效。宿主样式表位置按顺序探测:
7
+ *
8
+ * 1. `DSH_UI_THEME_DIR` 环境变量指向的目录;
9
+ * 2. 工作区内的 DSH 源码副本 `.dsh/temp/session-tree-harness/...`;
10
+ * 3. `node_modules/@deepseek-ai/dsh-client-ui-theme/` 之类已安装布局。
11
+ *
12
+ * 全部探测失败时默认跳过本次校验并以 0 退出:宿主样式表不属于本包依赖,缺了
13
+ * 它只说明当前机器没装 DSH 源码,不该阻断发布。设置 `DSH_UI_REQUIRE_TOKENS=1`
14
+ * 可以让缺失变成失败,供需要强校验的 CI 使用。
15
+ */
16
+ import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs'
17
+ import { dirname, join, resolve } from 'node:path'
18
+ import { fileURLToPath } from 'node:url'
19
+
20
+ const root = join(dirname(fileURLToPath(import.meta.url)), '..')
21
+
22
+ /** 宿主样式表文件相对于 ui-theme 包的路径。 */
23
+ const STYLE_FILES = ['base.css', 'design-platform.css', 'gradient-shadow-text.css', 'focus.css', 'scrollbar.css', 'corner-shape.css']
24
+
25
+ /**
26
+ * 按候选顺序定位宿主样式目录。
27
+ *
28
+ * @returns 样式目录绝对路径。
29
+ */
30
+ function locateThemeDir() {
31
+ const candidates = []
32
+ if (process.env.DSH_UI_THEME_DIR) candidates.push(process.env.DSH_UI_THEME_DIR)
33
+ candidates.push(resolve(root, '..', '.dsh', 'temp', 'session-tree-harness', 'packages', 'client', 'ui-theme', 'src', 'styles'))
34
+ candidates.push(resolve(root, 'node_modules', '@deepseek-ai', 'dsh-client-ui-theme', 'src', 'styles'))
35
+ candidates.push(resolve(root, 'node_modules', '@deepseek-ai', 'dsh-client-ui-theme', 'lib', 'styles'))
36
+ for (const candidate of candidates) {
37
+ if (existsSync(candidate) && statSync(candidate).isDirectory()) return candidate
38
+ }
39
+ return null
40
+ }
41
+
42
+ const themeDir = locateThemeDir()
43
+
44
+ if (themeDir === null) {
45
+ const message = [
46
+ '未找到 DSH 宿主样式目录,跳过令牌校验。',
47
+ '需要该校验时设置 DSH_UI_THEME_DIR,或在工作区保留 .dsh/temp/session-tree-harness 源码副本。',
48
+ ].join('\n')
49
+ if (process.env.DSH_UI_REQUIRE_TOKENS === '1') {
50
+ console.error(message.replace('跳过令牌校验', '且已设置 DSH_UI_REQUIRE_TOKENS=1'))
51
+ process.exit(2)
52
+ }
53
+ console.log(message)
54
+ process.exit(0)
55
+ }
56
+
57
+ /** 从宿主样式表解析出的全部变量名。 */
58
+ const hostTokens = new Set()
59
+ for (const file of STYLE_FILES) {
60
+ const path = join(themeDir, file)
61
+ if (!existsSync(path)) continue
62
+ const css = readFileSync(path, 'utf8')
63
+ for (const match of css.matchAll(/(--ds[wh]?-[a-z0-9-]*[a-z0-9])\s*:/g)) hostTokens.add(match[1])
64
+ }
65
+
66
+ if (hostTokens.size === 0) {
67
+ console.error(`未能从 ${themeDir} 解析出任何 --dsw-* 变量`)
68
+ process.exit(2)
69
+ }
70
+
71
+ /**
72
+ * 匹配源码中一个完整的宿主令牌名。
73
+ *
74
+ * 覆盖宿主的三类前缀:`--dsw-*`(主题令牌)、`--dsh-*`(主题运行时写入的
75
+ * 变量)与 `--ds-*`(字体与动效)。令牌必须以字母或数字结尾,且后面不能
76
+ * 紧跟 `-`、`*` 或标识符字符,这样注释里的通配写法(`--dsw-alias-*`)与
77
+ * 半截前缀不会被当成真实令牌名。
78
+ */
79
+ const TOKEN_PATTERN = /--ds[wh]?-[a-z0-9-]*[a-z0-9](?![-\w*])/g
80
+
81
+ /**
82
+ * 递归列出目录下的 TypeScript 源文件。
83
+ *
84
+ * @param dir - 起始目录。
85
+ * @returns 文件绝对路径列表。
86
+ */
87
+ function listSources(dir) {
88
+ const out = []
89
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
90
+ const path = join(dir, entry.name)
91
+ if (entry.isDirectory()) out.push(...listSources(path))
92
+ else if (/\.tsx?$/.test(entry.name)) out.push(path)
93
+ }
94
+ return out
95
+ }
96
+
97
+ /**
98
+ * 匹配本包自己定义局部变量的两种写法:CSS 里的 `--name:` 声明,以及 TSX
99
+ * inline style 里的 `'--name': value` 键。分段控件的位置变量就是这样注入的。
100
+ */
101
+ const DECLARATION_PATTERNS = [
102
+ /(--ds[wh]?-[a-z0-9-]*[a-z0-9])\s*:/g,
103
+ /['"](--ds[wh]?-[a-z0-9-]*[a-z0-9])['"]\s*:/g,
104
+ ]
105
+
106
+ const sources = listSources(join(root, 'src')).filter(file => !file.endsWith('icons-index.ts'))
107
+
108
+ // 本包自己声明的局部变量(如分段控件的位置变量)不要求宿主定义。
109
+ const localTokens = new Set()
110
+ for (const file of sources) {
111
+ const text = readFileSync(file, 'utf8')
112
+ for (const pattern of DECLARATION_PATTERNS) {
113
+ for (const match of text.matchAll(pattern)) localTokens.add(match[1])
114
+ }
115
+ }
116
+
117
+ const problems = []
118
+ const referenced = new Set()
119
+ for (const file of sources) {
120
+ const relative = file.slice(root.length + 1).replace(/\\/g, '/')
121
+ const text = readFileSync(file, 'utf8')
122
+ for (const match of text.matchAll(TOKEN_PATTERN)) {
123
+ const name = match[0]
124
+ referenced.add(name)
125
+ if (!hostTokens.has(name) && !localTokens.has(name)) problems.push(`${relative} 引用了不存在的令牌 ${name}`)
126
+ }
127
+ }
128
+
129
+ if (problems.length > 0) {
130
+ console.error([...new Set(problems)].join('\n'))
131
+ console.error('对照宿主样式表修正上述令牌名')
132
+ process.exit(1)
133
+ }
134
+
135
+ console.log(`令牌校验通过:引用 ${referenced.size} 个令牌(其中本包声明 ${localTokens.size} 个),宿主定义 ${hostTokens.size} 个`)
@@ -1,76 +1,76 @@
1
- /**
2
- * 按钮。
3
- *
4
- * 语义与键盘行为来自 Arco 的 `Button`,外观由桥接样式表与 `basic/styles`
5
- * 决定。DSH 的按钮尺寸是 36(标准)与 28(紧凑),主按钮是中性近黑色而非
6
- * 品牌蓝,这一层已经由桥接规则处理。
7
- *
8
- * @module @dsh-plugin/dsh-ui-components/basic/button
9
- */
10
- import * as React from 'react'
11
- import { Arco } from '@dsh-plugin/dsh-loader/client'
12
- import { injectBasicStyles } from './styles.js'
13
-
14
- /** 按钮的视觉层级。 */
15
- export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'text'
16
-
17
- /** 按钮尺寸,`md` 高 36,`sm` 高 28。 */
18
- export type ButtonSize = 'md' | 'sm' | 'lg'
19
-
20
- /** {@link Button} 的 props。 */
21
- export type ButtonProps = Omit<React.ComponentPropsWithoutRef<typeof Arco.Button>, 'type' | 'size' | 'status'> & {
22
- /** 视觉层级,默认 `secondary`。 */
23
- variant?: ButtonVariant
24
- /** 尺寸,默认 `md`。 */
25
- size?: ButtonSize
26
- /** 渲染为危险操作(删除、清空)。 */
27
- danger?: boolean
28
- /** 前置图标。 */
29
- icon?: React.ReactNode
30
- /** 撑满父容器宽度。 */
31
- block?: boolean
32
- }
33
-
34
- /** Arco 的 `type` 取值。 */
35
- const ARCO_TYPE: Record<ButtonVariant, 'primary' | 'secondary' | 'outline' | 'text'> = {
36
- primary: 'primary',
37
- secondary: 'secondary',
38
- outline: 'outline',
39
- text: 'text',
40
- }
41
-
42
- /** DSH 尺寸到 Arco 尺寸的映射。 */
43
- const ARCO_SIZE: Record<ButtonSize, 'default' | 'small' | 'large'> = {
44
- md: 'default',
45
- sm: 'small',
46
- lg: 'large',
47
- }
48
-
49
- /**
50
- * 一个按钮。
51
- *
52
- * @param props - 见 {@link ButtonProps}。
53
- * @param ref - 指向原生 `button` 元素,供焦点管理与浮层定位使用。
54
- * @returns 按钮元素。
55
- */
56
- export const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>> =
57
- React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
58
- { variant = 'secondary', size = 'md', danger, icon, block, children, htmlType, ...rest },
59
- ref,
60
- ) {
61
- injectBasicStyles()
62
- return (
63
- <Arco.Button
64
- {...rest}
65
- ref={ref}
66
- type={ARCO_TYPE[variant]}
67
- size={ARCO_SIZE[size]}
68
- status={danger === true ? 'danger' : undefined}
69
- long={block === true}
70
- htmlType={htmlType ?? 'button'}
71
- >
72
- {icon === undefined || icon === null ? null : icon}
73
- {children}
74
- </Arco.Button>
75
- )
76
- })
1
+ /**
2
+ * 按钮。
3
+ *
4
+ * 语义与键盘行为来自 Arco 的 `Button`,外观由桥接样式表与 `basic/styles`
5
+ * 决定。DSH 的按钮尺寸是 36(标准)与 28(紧凑),主按钮是中性近黑色而非
6
+ * 品牌蓝,这一层已经由桥接规则处理。
7
+ *
8
+ * @module @dsh-plugin/dsh-ui-components/basic/button
9
+ */
10
+ import * as React from 'react'
11
+ import { Arco } from '@dsh-plugin/dsh-loader/client'
12
+ import { injectBasicStyles } from './styles.js'
13
+
14
+ /** 按钮的视觉层级。 */
15
+ export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'text'
16
+
17
+ /** 按钮尺寸,`md` 高 36,`sm` 高 28。 */
18
+ export type ButtonSize = 'md' | 'sm' | 'lg'
19
+
20
+ /** {@link Button} 的 props。 */
21
+ export type ButtonProps = Omit<React.ComponentPropsWithoutRef<typeof Arco.Button>, 'type' | 'size' | 'status'> & {
22
+ /** 视觉层级,默认 `secondary`。 */
23
+ variant?: ButtonVariant
24
+ /** 尺寸,默认 `md`。 */
25
+ size?: ButtonSize
26
+ /** 渲染为危险操作(删除、清空)。 */
27
+ danger?: boolean
28
+ /** 前置图标。 */
29
+ icon?: React.ReactNode
30
+ /** 撑满父容器宽度。 */
31
+ block?: boolean
32
+ }
33
+
34
+ /** Arco 的 `type` 取值。 */
35
+ const ARCO_TYPE: Record<ButtonVariant, 'primary' | 'secondary' | 'outline' | 'text'> = {
36
+ primary: 'primary',
37
+ secondary: 'secondary',
38
+ outline: 'outline',
39
+ text: 'text',
40
+ }
41
+
42
+ /** DSH 尺寸到 Arco 尺寸的映射。 */
43
+ const ARCO_SIZE: Record<ButtonSize, 'default' | 'small' | 'large'> = {
44
+ md: 'default',
45
+ sm: 'small',
46
+ lg: 'large',
47
+ }
48
+
49
+ /**
50
+ * 一个按钮。
51
+ *
52
+ * @param props - 见 {@link ButtonProps}。
53
+ * @param ref - 指向原生 `button` 元素,供焦点管理与浮层定位使用。
54
+ * @returns 按钮元素。
55
+ */
56
+ export const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>> =
57
+ React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
58
+ { variant = 'secondary', size = 'md', danger, icon, block, children, htmlType, ...rest },
59
+ ref,
60
+ ) {
61
+ injectBasicStyles()
62
+ return (
63
+ <Arco.Button
64
+ {...rest}
65
+ ref={ref}
66
+ type={ARCO_TYPE[variant]}
67
+ size={ARCO_SIZE[size]}
68
+ status={danger === true ? 'danger' : undefined}
69
+ long={block === true}
70
+ htmlType={htmlType ?? 'button'}
71
+ >
72
+ {icon === undefined || icon === null ? null : icon}
73
+ {children}
74
+ </Arco.Button>
75
+ )
76
+ })