dsh-config-manager 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/README.md +216 -0
  2. package/README.zh-CN.md +218 -0
  3. package/cordis.patch.yml +45 -0
  4. package/dsh.bundle.patch +21 -0
  5. package/dsh.client +39 -0
  6. package/lib/adapters/agent-presets.d.ts +13 -0
  7. package/lib/adapters/agent-presets.js +14 -0
  8. package/lib/adapters/agent-presets.js.map +1 -0
  9. package/lib/adapters/credentials.d.ts +36 -0
  10. package/lib/adapters/credentials.js +121 -0
  11. package/lib/adapters/credentials.js.map +1 -0
  12. package/lib/adapters/file-collection.d.ts +14 -0
  13. package/lib/adapters/file-collection.js +102 -0
  14. package/lib/adapters/file-collection.js.map +1 -0
  15. package/lib/adapters/index.d.ts +38 -0
  16. package/lib/adapters/index.js +46 -0
  17. package/lib/adapters/index.js.map +1 -0
  18. package/lib/adapters/mcp.d.ts +27 -0
  19. package/lib/adapters/mcp.js +175 -0
  20. package/lib/adapters/mcp.js.map +1 -0
  21. package/lib/adapters/plugin-files.d.ts +15 -0
  22. package/lib/adapters/plugin-files.js +91 -0
  23. package/lib/adapters/plugin-files.js.map +1 -0
  24. package/lib/adapters/plugins.d.ts +13 -0
  25. package/lib/adapters/plugins.js +177 -0
  26. package/lib/adapters/plugins.js.map +1 -0
  27. package/lib/adapters/prompts.d.ts +29 -0
  28. package/lib/adapters/prompts.js +228 -0
  29. package/lib/adapters/prompts.js.map +1 -0
  30. package/lib/adapters/providers.d.ts +33 -0
  31. package/lib/adapters/providers.js +143 -0
  32. package/lib/adapters/providers.js.map +1 -0
  33. package/lib/adapters/sessions.d.ts +14 -0
  34. package/lib/adapters/sessions.js +15 -0
  35. package/lib/adapters/sessions.js.map +1 -0
  36. package/lib/adapters/settings.d.ts +23 -0
  37. package/lib/adapters/settings.js +142 -0
  38. package/lib/adapters/settings.js.map +1 -0
  39. package/lib/adapters/skills.d.ts +13 -0
  40. package/lib/adapters/skills.js +14 -0
  41. package/lib/adapters/skills.js.map +1 -0
  42. package/lib/adapters/test-helpers.d.ts +99 -0
  43. package/lib/adapters/test-helpers.js +192 -0
  44. package/lib/adapters/test-helpers.js.map +1 -0
  45. package/lib/adapters/ui.d.ts +26 -0
  46. package/lib/adapters/ui.js +88 -0
  47. package/lib/adapters/ui.js.map +1 -0
  48. package/lib/adapters/workspaces.d.ts +12 -0
  49. package/lib/adapters/workspaces.js +92 -0
  50. package/lib/adapters/workspaces.js.map +1 -0
  51. package/lib/client.d.ts +377 -0
  52. package/lib/client.js +2598 -0
  53. package/lib/client.js.map +1 -0
  54. package/lib/core/analyzer.d.ts +35 -0
  55. package/lib/core/analyzer.js +563 -0
  56. package/lib/core/analyzer.js.map +1 -0
  57. package/lib/core/backup.d.ts +29 -0
  58. package/lib/core/backup.js +184 -0
  59. package/lib/core/backup.js.map +1 -0
  60. package/lib/core/exporter.d.ts +37 -0
  61. package/lib/core/exporter.js +222 -0
  62. package/lib/core/exporter.js.map +1 -0
  63. package/lib/core/importer.d.ts +42 -0
  64. package/lib/core/importer.js +25 -0
  65. package/lib/core/importer.js.map +1 -0
  66. package/lib/core/index.d.ts +12 -0
  67. package/lib/core/index.js +11 -0
  68. package/lib/core/index.js.map +1 -0
  69. package/lib/core/rollback.d.ts +9 -0
  70. package/lib/core/rollback.js +136 -0
  71. package/lib/core/rollback.js.map +1 -0
  72. package/lib/core/types.d.ts +331 -0
  73. package/lib/core/types.js +18 -0
  74. package/lib/core/types.js.map +1 -0
  75. package/lib/core/validator.d.ts +18 -0
  76. package/lib/core/validator.js +77 -0
  77. package/lib/core/validator.js.map +1 -0
  78. package/lib/index.d.ts +51 -0
  79. package/lib/index.js +875 -0
  80. package/lib/index.js.map +1 -0
  81. package/lib/migrations/index.d.ts +29 -0
  82. package/lib/migrations/index.js +56 -0
  83. package/lib/migrations/index.js.map +1 -0
  84. package/lib/migrations/v1-to-v2.d.ts +9 -0
  85. package/lib/migrations/v1-to-v2.js +10 -0
  86. package/lib/migrations/v1-to-v2.js.map +1 -0
  87. package/lib/profiles/index.d.ts +4 -0
  88. package/lib/profiles/index.js +5 -0
  89. package/lib/profiles/index.js.map +1 -0
  90. package/lib/profiles/profile-manager.d.ts +102 -0
  91. package/lib/profiles/profile-manager.js +522 -0
  92. package/lib/profiles/profile-manager.js.map +1 -0
  93. package/lib/schema/config.d.ts +28 -0
  94. package/lib/schema/config.js +136 -0
  95. package/lib/schema/config.js.map +1 -0
  96. package/lib/schema/manifest.d.ts +28 -0
  97. package/lib/schema/manifest.js +109 -0
  98. package/lib/schema/manifest.js.map +1 -0
  99. package/lib/schema/types.d.ts +169 -0
  100. package/lib/schema/types.js +8 -0
  101. package/lib/schema/types.js.map +1 -0
  102. package/lib/schema/versions.d.ts +27 -0
  103. package/lib/schema/versions.js +50 -0
  104. package/lib/schema/versions.js.map +1 -0
  105. package/lib/security/encryption.d.ts +41 -0
  106. package/lib/security/encryption.js +152 -0
  107. package/lib/security/encryption.js.map +1 -0
  108. package/lib/security/index.d.ts +16 -0
  109. package/lib/security/index.js +17 -0
  110. package/lib/security/index.js.map +1 -0
  111. package/lib/security/integrity.d.ts +30 -0
  112. package/lib/security/integrity.js +78 -0
  113. package/lib/security/integrity.js.map +1 -0
  114. package/lib/security/redaction.d.ts +26 -0
  115. package/lib/security/redaction.js +95 -0
  116. package/lib/security/redaction.js.map +1 -0
  117. package/lib/security/secret-scanner.d.ts +48 -0
  118. package/lib/security/secret-scanner.js +253 -0
  119. package/lib/security/secret-scanner.js.map +1 -0
  120. package/lib/security/zip-security.d.ts +27 -0
  121. package/lib/security/zip-security.js +154 -0
  122. package/lib/security/zip-security.js.map +1 -0
  123. package/lib/ui/conflict-view.d.ts +28 -0
  124. package/lib/ui/conflict-view.js +49 -0
  125. package/lib/ui/conflict-view.js.map +1 -0
  126. package/lib/ui/errors.d.ts +17 -0
  127. package/lib/ui/errors.js +76 -0
  128. package/lib/ui/errors.js.map +1 -0
  129. package/lib/ui/export-flow.d.ts +62 -0
  130. package/lib/ui/export-flow.js +79 -0
  131. package/lib/ui/export-flow.js.map +1 -0
  132. package/lib/ui/import-wizard.d.ts +67 -0
  133. package/lib/ui/import-wizard.js +169 -0
  134. package/lib/ui/import-wizard.js.map +1 -0
  135. package/lib/ui/path-mapping.d.ts +38 -0
  136. package/lib/ui/path-mapping.js +75 -0
  137. package/lib/ui/path-mapping.js.map +1 -0
  138. package/lib/ui/progress.d.ts +27 -0
  139. package/lib/ui/progress.js +76 -0
  140. package/lib/ui/progress.js.map +1 -0
  141. package/lib/ui/report.d.ts +21 -0
  142. package/lib/ui/report.js +146 -0
  143. package/lib/ui/report.js.map +1 -0
  144. package/lib/ui/test-helpers.d.ts +62 -0
  145. package/lib/ui/test-helpers.js +147 -0
  146. package/lib/ui/test-helpers.js.map +1 -0
  147. package/lib/ui/types.d.ts +129 -0
  148. package/lib/ui/types.js +21 -0
  149. package/lib/ui/types.js.map +1 -0
  150. package/lib/utils/hashing.d.ts +17 -0
  151. package/lib/utils/hashing.js +41 -0
  152. package/lib/utils/hashing.js.map +1 -0
  153. package/lib/utils/json.d.ts +24 -0
  154. package/lib/utils/json.js +76 -0
  155. package/lib/utils/json.js.map +1 -0
  156. package/lib/utils/logger.d.ts +39 -0
  157. package/lib/utils/logger.js +82 -0
  158. package/lib/utils/logger.js.map +1 -0
  159. package/lib/utils/paths.d.ts +31 -0
  160. package/lib/utils/paths.js +132 -0
  161. package/lib/utils/paths.js.map +1 -0
  162. package/lib/utils/zip.d.ts +54 -0
  163. package/lib/utils/zip.js +285 -0
  164. package/lib/utils/zip.js.map +1 -0
  165. package/package.json +109 -0
  166. package/src/adapters/agent-presets.ts +14 -0
  167. package/src/adapters/credentials.test.ts +90 -0
  168. package/src/adapters/credentials.ts +141 -0
  169. package/src/adapters/file-collection.ts +110 -0
  170. package/src/adapters/files.test.ts +113 -0
  171. package/src/adapters/index.ts +69 -0
  172. package/src/adapters/mcp.test.ts +96 -0
  173. package/src/adapters/mcp.ts +181 -0
  174. package/src/adapters/plugin-files.ts +96 -0
  175. package/src/adapters/plugins.test.ts +72 -0
  176. package/src/adapters/plugins.ts +176 -0
  177. package/src/adapters/prompts.test.ts +110 -0
  178. package/src/adapters/prompts.ts +226 -0
  179. package/src/adapters/providers.test.ts +125 -0
  180. package/src/adapters/providers.ts +161 -0
  181. package/src/adapters/roundtrip.test.ts +188 -0
  182. package/src/adapters/sessions.ts +15 -0
  183. package/src/adapters/settings.test.ts +93 -0
  184. package/src/adapters/settings.ts +165 -0
  185. package/src/adapters/skills.ts +14 -0
  186. package/src/adapters/test-helpers.ts +207 -0
  187. package/src/adapters/ui.test.ts +56 -0
  188. package/src/adapters/ui.ts +106 -0
  189. package/src/adapters/workspaces.test.ts +79 -0
  190. package/src/adapters/workspaces.ts +95 -0
  191. package/src/client/CLIENT_DEPENDENCIES.md +101 -0
  192. package/src/client/ConfigManagerSection.tsx +60 -0
  193. package/src/client/api.ts +270 -0
  194. package/src/client/client-types.ts +27 -0
  195. package/src/client/common/ErrorBanner.tsx +68 -0
  196. package/src/client/common/ProgressBar.tsx +49 -0
  197. package/src/client/common/ReportView.tsx +117 -0
  198. package/src/client/common/ui.tsx +165 -0
  199. package/src/client/config-manager.module.css +759 -0
  200. package/src/client/css-modules.d.ts +8 -0
  201. package/src/client/export/ExportView.tsx +222 -0
  202. package/src/client/import/ConflictList.tsx +72 -0
  203. package/src/client/import/ImportWizardView.tsx +406 -0
  204. package/src/client/import/PathMappingForm.tsx +77 -0
  205. package/src/client/index.ts +65 -0
  206. package/src/client/locales.ts +182 -0
  207. package/src/core/analyzer.ts +631 -0
  208. package/src/core/backup.ts +212 -0
  209. package/src/core/exporter.ts +246 -0
  210. package/src/core/importer.ts +66 -0
  211. package/src/core/index.ts +39 -0
  212. package/src/core/rollback.ts +140 -0
  213. package/src/core/smoke.test.ts +717 -0
  214. package/src/core/types.ts +343 -0
  215. package/src/core/validator.ts +84 -0
  216. package/src/index.ts +942 -0
  217. package/src/migrations/index.ts +71 -0
  218. package/src/migrations/v1-to-v2.ts +17 -0
  219. package/src/profiles/index.ts +8 -0
  220. package/src/profiles/profile-manager.test.ts +280 -0
  221. package/src/profiles/profile-manager.ts +606 -0
  222. package/src/schema/config.ts +137 -0
  223. package/src/schema/manifest.ts +124 -0
  224. package/src/schema/types.ts +179 -0
  225. package/src/schema/versions.ts +55 -0
  226. package/src/security/encryption.ts +180 -0
  227. package/src/security/index.ts +16 -0
  228. package/src/security/integrity.ts +102 -0
  229. package/src/security/redaction.ts +101 -0
  230. package/src/security/secret-scanner.ts +275 -0
  231. package/src/security/security.test.ts +796 -0
  232. package/src/security/zip-security.ts +180 -0
  233. package/src/ui/conflict-view.test.ts +62 -0
  234. package/src/ui/conflict-view.ts +65 -0
  235. package/src/ui/errors.test.ts +44 -0
  236. package/src/ui/errors.ts +95 -0
  237. package/src/ui/export-flow.test.ts +75 -0
  238. package/src/ui/export-flow.ts +128 -0
  239. package/src/ui/import-wizard.test.ts +128 -0
  240. package/src/ui/import-wizard.ts +208 -0
  241. package/src/ui/path-mapping.test.ts +67 -0
  242. package/src/ui/path-mapping.ts +103 -0
  243. package/src/ui/progress.test.ts +42 -0
  244. package/src/ui/progress.ts +90 -0
  245. package/src/ui/report.test.ts +93 -0
  246. package/src/ui/report.ts +140 -0
  247. package/src/ui/test-helpers.ts +177 -0
  248. package/src/ui/types.ts +193 -0
  249. package/src/utils/hashing.ts +53 -0
  250. package/src/utils/json.ts +77 -0
  251. package/src/utils/logger.ts +114 -0
  252. package/src/utils/paths.ts +122 -0
  253. package/src/utils/zip.ts +332 -0
@@ -0,0 +1,117 @@
1
+ /**
2
+ * 结果报告(规范 §21 导出 / §22 导入 / §17 回滚,绑 src/ui/report.ts)。
3
+ *
4
+ * 直接调用 report.ts 的纯文本渲染器(renderExportReport / renderImportReport /
5
+ * renderRollbackReport / suggestedActions / importSectionStats),React 只做外壳:
6
+ * 标题 + 结构化统计徽章 + 文本详情 + 建议动作按钮。
7
+ *
8
+ * 安全约束:所有渲染文本展示前再过 `redact()` 兜底,Secret 不进入 UI。
9
+ */
10
+ import type { ExportReport, ImportResult, RollbackReport } from '../../core/types.ts'
11
+ import {
12
+ importSectionStats,
13
+ renderExportReport,
14
+ renderImportReport,
15
+ renderRollbackReport,
16
+ suggestedActions,
17
+ } from '../../ui/report.ts'
18
+ import type { ImportResultAction } from '../../ui/types.ts'
19
+ import { redact } from '../../security/redaction.ts'
20
+ import { Badge, Button, type BadgeKind } from './ui.tsx'
21
+ import css from '../config-manager.module.css'
22
+
23
+ export type ReportViewKind = 'export' | 'import'
24
+
25
+ export interface ReportViewProps {
26
+ kind: ReportViewKind
27
+ exportReport?: ExportReport
28
+ importResult?: ImportResult
29
+ /** 结果页动作回调(Fix Issues / View Details / Done) */
30
+ onAction?: (action: ImportResultAction) => void
31
+ /** 下载导出文件的回调(导出报告场景) */
32
+ onDownload?: () => void
33
+ }
34
+
35
+ /** 导入分区的统计徽章(由 report.importSectionStats 计算) */
36
+ function SectionStatBadges({ result }: { result: ImportResult }) {
37
+ const stats = importSectionStats(result.executed)
38
+ return (
39
+ <div className={css.statRow}>
40
+ {stats.map((s) => {
41
+ let kind: BadgeKind = 'ok'
42
+ if (s.failed > 0) kind = 'error'
43
+ else if (s.skipped > 0) kind = 'warn'
44
+ return (
45
+ <Badge key={s.section} kind={kind}>
46
+ {s.section}: {s.ok}✓{s.skipped > 0 ? ` ${s.skipped}≈` : ''}{s.failed > 0 ? ` ${s.failed}✗` : ''}
47
+ </Badge>
48
+ )
49
+ })}
50
+ </div>
51
+ )
52
+ }
53
+
54
+ /** 导出报告的安全摘要(Included / Excluded / Security 徽章) */
55
+ function ExportSummary({ report }: { report: ExportReport }) {
56
+ const included = report.included.length
57
+ const excluded = report.excluded.length
58
+ return (
59
+ <div className={css.statRow}>
60
+ <Badge kind="ok">{included} included</Badge>
61
+ {excluded > 0 && <Badge kind="warn">{excluded} excluded</Badge>}
62
+ {report.security.containsSecrets && <Badge kind="warn">encrypted</Badge>}
63
+ {!report.security.containsSecrets && <Badge kind="ok">no secrets</Badge>}
64
+ {report.security.redactedHits > 0 && (
65
+ <Badge kind="error">{report.security.redactedHits} redacted</Badge>
66
+ )}
67
+ </div>
68
+ )
69
+ }
70
+
71
+ /**
72
+ * 结果报告视图。
73
+ * 文本详情 = report.ts 渲染器的输出(已脱敏),展示前再过 redact() 双保险;
74
+ * 以 <pre> 等宽块呈现保持对齐。
75
+ */
76
+ export function ReportView({ kind, exportReport, importResult, onAction, onDownload }: ReportViewProps) {
77
+ const actions = kind === 'import' && importResult !== undefined
78
+ ? suggestedActions(importResult)
79
+ : []
80
+
81
+ return (
82
+ <div className={css.reportView}>
83
+ {kind === 'export' && exportReport !== undefined && (
84
+ <>
85
+ <ExportSummary report={exportReport} />
86
+ <pre className={css.reportText}>{redact(renderExportReport(exportReport))}</pre>
87
+ {onDownload !== undefined && (
88
+ <div className={css.reportFooter}>
89
+ <Button variant="primary" onClick={onDownload}>Download</Button>
90
+ </div>
91
+ )}
92
+ </>
93
+ )}
94
+ {kind === 'import' && importResult !== undefined && (
95
+ <>
96
+ <SectionStatBadges result={importResult} />
97
+ <pre className={css.reportText}>{redact(renderImportReport(importResult))}</pre>
98
+ {importResult.rollback !== null && (
99
+ <div className={css.rollbackBox}>
100
+ <strong>Rollback</strong>
101
+ <pre className={css.reportText}>{redact(renderRollbackReport(importResult.rollback as RollbackReport))}</pre>
102
+ </div>
103
+ )}
104
+ {actions.length > 0 && (
105
+ <div className={css.reportFooter}>
106
+ {actions.map((a) => (
107
+ <Button key={a} variant={a === 'done' ? 'primary' : 'ghost'} onClick={() => onAction?.(a)}>
108
+ {a}
109
+ </Button>
110
+ ))}
111
+ </div>
112
+ )}
113
+ </>
114
+ )}
115
+ </div>
116
+ )
117
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Config Manager 基础 UI 原语(dsh-ssh 风格,全部走 DSH Design System 的 --dsw-* token,
3
+ * 不另造视觉体系)。仅作薄封装:类名来自 config-manager.module.css,无业务逻辑。
4
+ */
5
+ import type { ChangeEvent, ReactNode } from 'react'
6
+ import css from '../config-manager.module.css'
7
+
8
+ /* ---------------- Button ---------------- */
9
+
10
+ export type ButtonVariant = 'primary' | 'ghost' | 'danger'
11
+
12
+ export interface ButtonProps {
13
+ variant?: ButtonVariant
14
+ disabled?: boolean
15
+ onClick?: () => void
16
+ children: ReactNode
17
+ title?: string
18
+ className?: string
19
+ }
20
+
21
+ /** 统一按钮(primary=主操作 / ghost=次操作 / danger=危险操作) */
22
+ export function Button({ variant = 'ghost', disabled, onClick, children, title, className }: ButtonProps) {
23
+ const cls =
24
+ variant === 'primary' ? css.primaryButton
25
+ : variant === 'danger' ? css.dangerButton
26
+ : css.ghostButton
27
+ return (
28
+ <button
29
+ type="button"
30
+ className={className !== undefined ? `${cls} ${className}` : cls}
31
+ disabled={disabled}
32
+ title={title}
33
+ onClick={onClick}
34
+ >
35
+ {children}
36
+ </button>
37
+ )
38
+ }
39
+
40
+ /* ---------------- Badge ---------------- */
41
+
42
+ export type BadgeKind = 'info' | 'ok' | 'warn' | 'error'
43
+
44
+ export interface BadgeProps {
45
+ kind?: BadgeKind
46
+ children: ReactNode
47
+ }
48
+
49
+ /** 状态徽章(info=业务色 / ok=成功 / warn=警告 / error=错误) */
50
+ export function Badge({ kind = 'info', children }: BadgeProps) {
51
+ return <span className={`${css.badge} ${css[`badge${kind[0]!.toUpperCase()}${kind.slice(1)}`] ?? ''}`}>{children}</span>
52
+ }
53
+
54
+ /* ---------------- Banner ---------------- */
55
+
56
+ export type BannerKind = 'ok' | 'error' | 'info' | 'warn'
57
+
58
+ export interface BannerProps {
59
+ kind?: BannerKind
60
+ children: ReactNode
61
+ }
62
+
63
+ /** 说明横幅(ok/error/info/warn 四态) */
64
+ export function Banner({ kind = 'info', children }: BannerProps) {
65
+ return <div className={css.banner} data-kind={kind}>{children}</div>
66
+ }
67
+
68
+ /* ---------------- Card ---------------- */
69
+
70
+ export interface CardProps {
71
+ children: ReactNode
72
+ className?: string
73
+ }
74
+
75
+ /** 卡片容器(bg-layer-2 + 圆角 + 细边框) */
76
+ export function Card({ children, className }: CardProps) {
77
+ return <div className={className !== undefined ? `${css.card} ${className}` : css.card}>{children}</div>
78
+ }
79
+
80
+ /* ---------------- Spinner ---------------- */
81
+
82
+ export interface SpinnerProps {
83
+ label?: string
84
+ }
85
+
86
+ /** 加载指示(旋转环 + 可选文案) */
87
+ export function Spinner({ label }: SpinnerProps) {
88
+ return (
89
+ <span className={css.spinnerWrap}>
90
+ <span className={css.spinner} aria-hidden="true" />
91
+ {label !== undefined && <span className={css.spinnerLabel}>{label}</span>}
92
+ </span>
93
+ )
94
+ }
95
+
96
+ /* ---------------- Field ---------------- */
97
+
98
+ export interface FieldProps {
99
+ label: string
100
+ hint?: string
101
+ children: ReactNode
102
+ }
103
+
104
+ /** 表单字段(标签 + 控件 + 说明) */
105
+ export function Field({ label, hint, children }: FieldProps) {
106
+ return (
107
+ <label className={css.field}>
108
+ <span className={css.fieldLabel}>{label}</span>
109
+ {children}
110
+ {hint !== undefined && <span className={css.hint}>{hint}</span>}
111
+ </label>
112
+ )
113
+ }
114
+
115
+ /* ---------------- SectionTitle ---------------- */
116
+
117
+ export interface SectionTitleProps {
118
+ title: string
119
+ subtitle?: string
120
+ }
121
+
122
+ /** 区块标题(页面内二级标题 + 可选副标题) */
123
+ export function SectionTitle({ title, subtitle }: SectionTitleProps) {
124
+ return (
125
+ <div className={css.sectionTitleBlock}>
126
+ <h3 className={css.sectionTitle}>{title}</h3>
127
+ {subtitle !== undefined && <p className={css.sectionSubtitle}>{subtitle}</p>}
128
+ </div>
129
+ )
130
+ }
131
+
132
+ /* ---------------- Empty / Loading ---------------- */
133
+
134
+ export interface EmptyProps {
135
+ children: ReactNode
136
+ }
137
+
138
+ /** 空状态占位 */
139
+ export function Empty({ children }: EmptyProps) {
140
+ return <div className={css.empty}>{children}</div>
141
+ }
142
+
143
+ /* ---------------- Checkbox ---------------- */
144
+
145
+ export interface CheckboxProps {
146
+ checked: boolean
147
+ onChange: (checked: boolean) => void
148
+ label: ReactNode
149
+ disabled?: boolean
150
+ }
151
+
152
+ /** 复选框行(勾选 + 标签) */
153
+ export function Checkbox({ checked, onChange, label, disabled }: CheckboxProps) {
154
+ return (
155
+ <label className={css.checkboxRow}>
156
+ <input
157
+ type="checkbox"
158
+ checked={checked}
159
+ disabled={disabled}
160
+ onChange={(event: ChangeEvent<HTMLInputElement>) => { onChange(event.target.checked) }}
161
+ />
162
+ <span>{label}</span>
163
+ </label>
164
+ )
165
+ }