@demokit-ui/demokit 0.1.1 → 0.2.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.
package/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  - 常用基础组件和页面拼装所需的通用能力
8
8
  - `DemoKitProvider`
9
9
  - 主题与全局上下文能力
10
+ - 自适配主题 token、Agent 调研产物模型和 Tweak 微调入口
10
11
  - 样式入口 `@demokit-ui/demokit/styles.css`
11
12
 
12
13
  ## 安装
@@ -34,6 +35,48 @@ import { DemoKitProvider, Button, Card } from '@demokit-ui/demokit';
34
35
 
35
36
  `@demokit-ui/demokit/styles.css` 提供主题变量、基础 body 背景色、组件 CSS、`card-surface` 和 demo 布局 CSS。`DemoKitProvider` 会把当前主题同步到 `<html data-theme="...">`。
36
37
 
38
+ ## 自适配主题
39
+
40
+ `DemoKitProvider` 支持通过 `initialSettings` 注入项目级自适配主题。典型使用方式是由 Agent 在创建项目时根据 PRD、客户、行业和 DESIGN.md 参考生成 `AdaptiveThemeResearch`,再转成 `adaptiveTheme` token。
41
+
42
+ ```tsx
43
+ import {
44
+ createAdaptiveThemeFromResearch,
45
+ DemoKitProvider,
46
+ type AdaptiveThemeResearch,
47
+ type SettingsProviderInitialSettings,
48
+ } from '@demokit-ui/demokit';
49
+
50
+ const research: AdaptiveThemeResearch = {
51
+ source: 'agent',
52
+ query: '创建一个星巴克的订单管理后台',
53
+ customer: '星巴克',
54
+ project: '订单管理后台',
55
+ industry: 'retail',
56
+ tone: 'calm',
57
+ brandColor: '#006241',
58
+ confidence: 0.82,
59
+ evidence: ['使用品牌绿作为运营后台主色,暖中性画布降低长时间值守疲劳。'],
60
+ };
61
+
62
+ const adaptiveTheme = createAdaptiveThemeFromResearch(research).profile;
63
+
64
+ const initialSettings: SettingsProviderInitialSettings = {
65
+ theme: 'adaptive',
66
+ adaptiveTheme,
67
+ };
68
+
69
+ export function Root() {
70
+ return (
71
+ <DemoKitProvider initialSettings={initialSettings}>
72
+ <App />
73
+ </DemoKitProvider>
74
+ );
75
+ }
76
+ ```
77
+
78
+ 生成后,Tweak 面板会显示自适配主题的颜色编辑入口。用户在 Tweak 里的调整应视为人工 override,不需要回退 Agent 的原始调研产物。
79
+
37
80
  ## Tailwind v4 扫描
38
81
 
39
82
  当前样式入口是 Tailwind v4 CSS。消费项目需要让 Tailwind 扫描 `@demokit-ui/demokit` 的构建产物,否则包内部组件和 demo shell 里的 utility class 不会生成。
@@ -96,6 +139,7 @@ Showcase 和 starter 页面里的指标卡、图表、表格、Tabs、假数据
96
139
  - `DemoKitProvider`
97
140
  - `Demo1Shell`
98
141
  - 主题、设置和 Tooltip 相关 Provider
142
+ - 自适配主题工具:`createAdaptiveThemeFromResearch`、`suggestAdaptiveThemeFromPrompt`、`applyAdaptiveTheme`、`updateAdaptiveThemeColor`
99
143
  - `theme-toggle`
100
144
  - `chart-palette`
101
145
  - `APP_SETTINGS`、配置类型和 `cn` 等辅助导出
@@ -15,8 +15,8 @@ export interface TweakPanelProps {
15
15
  showDensity?: boolean;
16
16
  showRadius?: boolean;
17
17
  showContainer?: boolean;
18
+ showAdaptiveThemeEditor?: boolean;
18
19
  }
19
- export interface TweakScopeProps extends ComponentPropsWithoutRef<'div'> {
20
- }
20
+ export type TweakScopeProps = ComponentPropsWithoutRef<'div'>;
21
21
  export declare function TweakScope({ className, ...props }: TweakScopeProps): import("react/jsx-runtime").JSX.Element;
22
- export declare function TweakPanel({ triggerLabel, triggerTitle, title, description, resetLabel, side, align, sideOffset, triggerClassName, contentClassName, showTheme, showDensity, showRadius, showContainer, }: TweakPanelProps): import("react/jsx-runtime").JSX.Element;
22
+ export declare function TweakPanel({ triggerLabel, triggerTitle, title, description, resetLabel, side, align, sideOffset, triggerClassName, contentClassName, showTheme, showDensity, showRadius, showContainer, showAdaptiveThemeEditor, }: TweakPanelProps): import("react/jsx-runtime").JSX.Element;
@@ -1,12 +1,14 @@
1
1
  import { jsxs as r, jsx as e } from "react/jsx-runtime";
2
- import { SlidersHorizontal as I } from "lucide-react";
3
- import { APP_SETTINGS as n } from "../config/settings.config.js";
4
- import { cn as d } from "../lib/utils.js";
5
- import { useSettings as O } from "../providers/settings-provider.js";
6
- import { Popover as z, PopoverTrigger as _, PopoverContent as E } from "../kit/components/feedback/popover/popover.js";
7
- import { Button as g } from "../kit/components/form/button/button.js";
8
- import { ToggleGroup as j, ToggleGroupItem as D } from "../kit/components/form/toggle-group/toggle-group.js";
9
- const G = [
2
+ import { SlidersHorizontal as z } from "lucide-react";
3
+ import { APP_SETTINGS as i } from "../config/settings.config.js";
4
+ import { updateAdaptiveThemeColor as D } from "../kit/themes/adaptive-theme.js";
5
+ import { cn as p } from "../lib/utils.js";
6
+ import { useSettings as j } from "../providers/settings-provider.js";
7
+ import { Popover as G, PopoverTrigger as H, PopoverContent as L } from "../kit/components/feedback/popover/popover.js";
8
+ import { Button as h } from "../kit/components/form/button/button.js";
9
+ import { ToggleGroup as B, ToggleGroupItem as R } from "../kit/components/form/toggle-group/toggle-group.js";
10
+ import { Input as U } from "../kit/components/form/input/input.js";
11
+ const b = [
10
12
  {
11
13
  value: "glass",
12
14
  label: "活力",
@@ -19,123 +21,158 @@ const G = [
19
21
  description: "高对比业务界面",
20
22
  dot: "#2563eb"
21
23
  }
22
- ], H = [
24
+ ], F = [
23
25
  { value: "compact", label: "紧凑", description: "高频操作台" },
24
26
  { value: "standard", label: "标准", description: "默认后台节奏" },
25
27
  { value: "relaxed", label: "宽松", description: "演示与大屏预览" }
26
- ], A = [
28
+ ], M = [
27
29
  { value: "sharp", label: "直角" },
28
30
  { value: "sm", label: "小" },
29
31
  { value: "md", label: "中" },
30
32
  { value: "lg", label: "大" }
31
- ], B = [
33
+ ], W = [
32
34
  { value: "fluid", label: "流式", description: "跟随窗口铺满" },
33
35
  { value: "fixed", label: "固定", description: "限制最大宽度" }
36
+ ], Y = [
37
+ { key: "primary", label: "主色", getValue: (a) => a.tokens.primary },
38
+ { key: "success", label: "成功", getValue: (a) => a.tokens.success },
39
+ { key: "warning", label: "警告", getValue: (a) => a.tokens.warning },
40
+ { key: "info", label: "信息", getValue: (a) => a.tokens.info },
41
+ {
42
+ key: "background",
43
+ label: "背景",
44
+ getValue: (a) => a.tokens.background
45
+ },
46
+ { key: "border", label: "边框", getValue: (a) => a.tokens.border }
34
47
  ];
35
- function K({ className: l, ...o }) {
48
+ function re({ className: a, ...d }) {
36
49
  return /* @__PURE__ */ e(
37
50
  "div",
38
51
  {
39
52
  "data-slot": "tweak-scope",
40
- className: d("demokit-tweak-scope min-w-0", l),
41
- ...o
53
+ className: p("demokit-tweak-scope min-w-0", a),
54
+ ...d
42
55
  }
43
56
  );
44
57
  }
45
- function L({
46
- triggerLabel: l = "Tweak",
47
- triggerTitle: o = "调整界面外观",
48
- title: u = "界面设置",
49
- description: i = "调整主题、密度、圆角和内容宽度。",
50
- resetLabel: p = "重置",
51
- side: h = "top",
52
- align: a = "end",
53
- sideOffset: v = 10,
54
- triggerClassName: f,
55
- contentClassName: x,
56
- showTheme: b = !0,
57
- showDensity: N = !0,
58
+ function le({
59
+ triggerLabel: a = "Tweak",
60
+ triggerTitle: d = "调整界面外观",
61
+ title: c = "界面设置",
62
+ description: s = "调整主题、密度、圆角和内容宽度。",
63
+ resetLabel: m = "重置",
64
+ side: g = "top",
65
+ align: t = "end",
66
+ sideOffset: f = 10,
67
+ triggerClassName: x,
68
+ contentClassName: k,
69
+ showTheme: N = !0,
70
+ showDensity: y = !0,
58
71
  showRadius: T = !0,
59
- showContainer: y = !0
72
+ showContainer: w = !0,
73
+ showAdaptiveThemeEditor: C = !0
60
74
  }) {
61
- const { settings: c, storeOption: t } = O(), w = c.theme ?? n.theme, k = c.appearance?.density ?? n.appearance.density, S = c.appearance?.radius ?? n.appearance.radius, C = c.container ?? n.container, P = () => {
62
- t("theme", n.theme), t("appearance.density", n.appearance.density), t("appearance.radius", n.appearance.radius), t("container", n.container);
75
+ const { settings: l, storeOption: n } = j(), I = l.theme ?? i.theme, v = l.adaptiveTheme ? [
76
+ ...b,
77
+ {
78
+ value: "adaptive",
79
+ label: "自适配",
80
+ description: "客户专属 token",
81
+ dot: l.adaptiveTheme.tokens.primary
82
+ }
83
+ ] : b, S = l.appearance?.density ?? i.appearance.density, O = l.appearance?.radius ?? i.appearance.radius, P = l.container ?? i.container, _ = () => {
84
+ n("theme", i.theme), n("appearance.density", i.appearance.density), n("appearance.radius", i.appearance.radius), n("container", i.container), n("adaptiveTheme", null);
85
+ }, A = (o, E) => {
86
+ l.adaptiveTheme && (n(
87
+ "adaptiveTheme",
88
+ D(l.adaptiveTheme, o, E)
89
+ ), n("theme", "adaptive"));
90
+ }, V = () => {
91
+ n("adaptiveTheme", null), l.theme === "adaptive" && n("theme", i.theme);
63
92
  };
64
- return /* @__PURE__ */ r(z, { children: [
65
- /* @__PURE__ */ e(_, { asChild: !0, children: /* @__PURE__ */ r(
66
- g,
93
+ return /* @__PURE__ */ r(G, { children: [
94
+ /* @__PURE__ */ e(H, { asChild: !0, children: /* @__PURE__ */ r(
95
+ h,
67
96
  {
68
97
  type: "button",
69
98
  variant: "ghost",
70
99
  size: "sm",
71
100
  shape: "circle",
72
- title: o,
73
- className: d(
101
+ title: d,
102
+ className: p(
74
103
  "h-7 gap-1.5 rounded-full px-3 text-xs text-foreground/80 hover:bg-muted hover:text-foreground",
75
- f
104
+ x
76
105
  ),
77
106
  children: [
78
- /* @__PURE__ */ e(I, { className: "size-3.5" }),
79
- l
107
+ /* @__PURE__ */ e(z, { className: "size-3.5" }),
108
+ a
80
109
  ]
81
110
  }
82
111
  ) }),
83
112
  /* @__PURE__ */ e(
84
- E,
113
+ L,
85
114
  {
86
- align: a,
87
- side: h,
88
- sideOffset: v,
89
- className: d(
90
- "w-[340px] rounded-[var(--dk-radius-card)] p-3",
91
- x
115
+ align: t,
116
+ side: g,
117
+ sideOffset: f,
118
+ className: p(
119
+ "w-[380px] rounded-[var(--dk-radius-card)] p-3",
120
+ k
92
121
  ),
93
122
  children: /* @__PURE__ */ r("div", { className: "space-y-4", children: [
94
123
  /* @__PURE__ */ r("div", { className: "flex items-center justify-between gap-3", children: [
95
124
  /* @__PURE__ */ r("div", { className: "min-w-0", children: [
96
- /* @__PURE__ */ e("div", { className: "text-sm font-semibold text-foreground", children: u }),
97
- i && /* @__PURE__ */ e("div", { className: "text-xs leading-5 text-muted-foreground", children: i })
125
+ /* @__PURE__ */ e("div", { className: "text-sm font-semibold text-foreground", children: c }),
126
+ s && /* @__PURE__ */ e("div", { className: "text-xs leading-5 text-muted-foreground", children: s })
98
127
  ] }),
99
- /* @__PURE__ */ e(g, { type: "button", variant: "ghost", size: "sm", onClick: P, children: p })
128
+ /* @__PURE__ */ e(h, { type: "button", variant: "ghost", size: "sm", onClick: _, children: m })
100
129
  ] }),
101
- b && /* @__PURE__ */ e(
102
- m,
130
+ N && /* @__PURE__ */ e(
131
+ u,
103
132
  {
104
- columns: 2,
105
133
  label: "主题",
106
- value: w,
107
- options: G,
108
- onChange: (s) => t("theme", s)
134
+ value: I,
135
+ columns: v.length === 3 ? 3 : 2,
136
+ options: v,
137
+ onChange: (o) => n("theme", o)
109
138
  }
110
139
  ),
111
- N && /* @__PURE__ */ e(
112
- m,
140
+ y && /* @__PURE__ */ e(
141
+ u,
113
142
  {
114
143
  columns: 3,
115
144
  label: "密度",
116
- value: k,
117
- options: H,
118
- onChange: (s) => t("appearance.density", s)
145
+ value: S,
146
+ options: F,
147
+ onChange: (o) => n("appearance.density", o)
119
148
  }
120
149
  ),
121
150
  T && /* @__PURE__ */ e(
122
- m,
151
+ u,
123
152
  {
124
153
  columns: 4,
125
154
  label: "圆角",
126
- value: S,
127
- options: A,
128
- onChange: (s) => t("appearance.radius", s)
155
+ value: O,
156
+ options: M,
157
+ onChange: (o) => n("appearance.radius", o)
129
158
  }
130
159
  ),
131
- y && /* @__PURE__ */ e(
132
- m,
160
+ w && /* @__PURE__ */ e(
161
+ u,
133
162
  {
134
163
  columns: 2,
135
164
  label: "容器",
136
- value: C,
137
- options: B,
138
- onChange: (s) => t("container", s)
165
+ value: P,
166
+ options: W,
167
+ onChange: (o) => n("container", o)
168
+ }
169
+ ),
170
+ C && l.adaptiveTheme && /* @__PURE__ */ e(
171
+ q,
172
+ {
173
+ theme: l.adaptiveTheme,
174
+ onColorChange: A,
175
+ onClear: V
139
176
  }
140
177
  )
141
178
  ] })
@@ -143,56 +180,91 @@ function L({
143
180
  )
144
181
  ] });
145
182
  }
146
- function m({
147
- label: l,
148
- value: o,
149
- options: u,
150
- columns: i,
151
- onChange: p
183
+ function q({
184
+ theme: a,
185
+ onColorChange: d,
186
+ onClear: c
187
+ }) {
188
+ return /* @__PURE__ */ r("div", { className: "space-y-3 border-t border-border pt-3", children: [
189
+ /* @__PURE__ */ r("div", { className: "flex items-start justify-between gap-3", children: [
190
+ /* @__PURE__ */ r("div", { className: "min-w-0", children: [
191
+ /* @__PURE__ */ e("div", { className: "text-xs font-medium text-muted-foreground", children: "客户主题微调" }),
192
+ /* @__PURE__ */ e("div", { className: "mt-1 truncate text-xs font-semibold text-foreground", children: a.name })
193
+ ] }),
194
+ /* @__PURE__ */ e(h, { type: "button", variant: "ghost", size: "sm", onClick: c, children: "清除" })
195
+ ] }),
196
+ /* @__PURE__ */ e("div", { className: "grid grid-cols-3 gap-2", children: Y.map((s) => /* @__PURE__ */ r(
197
+ "label",
198
+ {
199
+ className: "space-y-1 text-[11px] text-muted-foreground",
200
+ children: [
201
+ /* @__PURE__ */ e("span", { children: s.label }),
202
+ /* @__PURE__ */ e(
203
+ U,
204
+ {
205
+ type: "color",
206
+ variant: "sm",
207
+ value: s.getValue(a),
208
+ className: "h-8 w-full p-1",
209
+ onChange: (m) => d(s.key, m.target.value)
210
+ }
211
+ )
212
+ ]
213
+ },
214
+ s.key
215
+ )) })
216
+ ] });
217
+ }
218
+ function u({
219
+ label: a,
220
+ value: d,
221
+ options: c,
222
+ columns: s,
223
+ onChange: m
152
224
  }) {
153
225
  return /* @__PURE__ */ r("div", { className: "space-y-2", children: [
154
- /* @__PURE__ */ e("div", { className: "text-xs font-medium text-muted-foreground", children: l }),
226
+ /* @__PURE__ */ e("div", { className: "text-xs font-medium text-muted-foreground", children: a }),
155
227
  /* @__PURE__ */ e(
156
- j,
228
+ B,
157
229
  {
158
230
  type: "single",
159
- value: o,
160
- onValueChange: (a) => {
161
- a && p(a);
231
+ value: d,
232
+ onValueChange: (t) => {
233
+ t && m(t);
162
234
  },
163
- className: d(
235
+ className: p(
164
236
  "grid rounded-md border border-border bg-muted/50 p-1",
165
- i === 4 ? "grid-cols-4" : i === 3 ? "grid-cols-3" : "grid-cols-2"
237
+ s === 4 ? "grid-cols-4" : s === 3 ? "grid-cols-3" : "grid-cols-2"
166
238
  ),
167
- children: u.map((a) => /* @__PURE__ */ r(
168
- D,
239
+ children: c.map((t) => /* @__PURE__ */ r(
240
+ R,
169
241
  {
170
- value: a.value,
171
- className: d(
242
+ value: t.value,
243
+ className: p(
172
244
  "h-auto min-h-10 min-w-0 flex-col items-start rounded-[var(--dk-radius-control)] px-2.5 py-2 text-left data-[state=on]:bg-card data-[state=on]:text-foreground data-[state=on]:shadow-xs",
173
- a.description && "min-h-12"
245
+ t.description && "min-h-12"
174
246
  ),
175
247
  children: [
176
248
  /* @__PURE__ */ r("span", { className: "flex min-w-0 items-center gap-1.5 text-xs font-semibold leading-4", children: [
177
- a.dot && /* @__PURE__ */ e(
249
+ t.dot && /* @__PURE__ */ e(
178
250
  "span",
179
251
  {
180
252
  className: "size-2.5 shrink-0 rounded-full",
181
- style: { background: a.dot }
253
+ style: { background: t.dot }
182
254
  }
183
255
  ),
184
- /* @__PURE__ */ e("span", { className: "truncate", children: a.label })
256
+ /* @__PURE__ */ e("span", { className: "truncate", children: t.label })
185
257
  ] }),
186
- a.description && /* @__PURE__ */ e("span", { className: "mt-0.5 line-clamp-2 text-[10px] font-normal leading-4 text-muted-foreground", children: a.description })
258
+ t.description && /* @__PURE__ */ e("span", { className: "mt-0.5 line-clamp-2 text-[10px] font-normal leading-4 text-muted-foreground", children: t.description })
187
259
  ]
188
260
  },
189
- a.value
261
+ t.value
190
262
  ))
191
263
  }
192
264
  )
193
265
  ] });
194
266
  }
195
267
  export {
196
- L as TweakPanel,
197
- K as TweakScope
268
+ le as TweakPanel,
269
+ re as TweakScope
198
270
  };
@@ -6,6 +6,7 @@ const e = {
6
6
  layout: "",
7
7
  container: "fluid",
8
8
  theme: "glass",
9
+ adaptiveTheme: null,
9
10
  layouts: {
10
11
  demo1: {
11
12
  sidebarCollapse: !1
@@ -15,9 +15,65 @@ export interface MenuItem {
15
15
  separator?: boolean;
16
16
  }
17
17
  export type MenuConfig = MenuItem[];
18
- export type ThemePreset = 'glass' | 'navy';
18
+ export type ThemePreset = 'glass' | 'navy' | 'adaptive';
19
19
  export type DensityPreset = 'compact' | 'standard' | 'relaxed';
20
20
  export type RadiusPreset = 'sharp' | 'sm' | 'md' | 'lg';
21
+ export type AdaptiveThemeIndustry = 'generic' | 'finance' | 'healthcare' | 'manufacturing' | 'energy' | 'government' | 'education' | 'retail';
22
+ export type AdaptiveThemeTone = 'professional' | 'calm' | 'vivid' | 'premium';
23
+ export interface AdaptiveThemeTokens {
24
+ background: string;
25
+ foreground: string;
26
+ card: string;
27
+ cardForeground: string;
28
+ popover: string;
29
+ popoverForeground: string;
30
+ primary: string;
31
+ primaryHover: string;
32
+ primaryLight: string;
33
+ primaryForeground: string;
34
+ secondary: string;
35
+ secondaryForeground: string;
36
+ muted: string;
37
+ mutedForeground: string;
38
+ accent: string;
39
+ accentForeground: string;
40
+ destructive: string;
41
+ destructiveLight: string;
42
+ destructiveForeground: string;
43
+ success: string;
44
+ successLight: string;
45
+ successForeground: string;
46
+ warning: string;
47
+ warningLight: string;
48
+ warningForeground: string;
49
+ info: string;
50
+ infoLight: string;
51
+ infoForeground: string;
52
+ mono: string;
53
+ monoForeground: string;
54
+ border: string;
55
+ input: string;
56
+ ring: string;
57
+ chart: [string, string, string, string, string, string];
58
+ surfaceBg: string;
59
+ surfaceBorder: string;
60
+ surfaceBlur: string;
61
+ surfaceShadow: string;
62
+ decorOpacity: string;
63
+ decorNavyOpacity: string;
64
+ }
65
+ export interface AdaptiveThemeProfile {
66
+ name: string;
67
+ customer?: string;
68
+ project?: string;
69
+ industry: AdaptiveThemeIndustry;
70
+ tone: AdaptiveThemeTone;
71
+ brandColor: string;
72
+ description?: string;
73
+ generatedAt: string;
74
+ tokens: AdaptiveThemeTokens;
75
+ notes: string[];
76
+ }
21
77
  export interface Settings {
22
78
  appearance: {
23
79
  density: DensityPreset;
@@ -26,6 +82,7 @@ export interface Settings {
26
82
  container: 'fixed' | 'fluid';
27
83
  layout: string;
28
84
  theme: ThemePreset;
85
+ adaptiveTheme: AdaptiveThemeProfile | null;
29
86
  layouts: {
30
87
  demo1: {
31
88
  sidebarCollapse: boolean;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export * from './kit/components';
2
2
  export * from './kit/themes/theme-toggle';
3
+ export * from './kit/themes/adaptive-theme';
4
+ export * from './kit/themes/adaptive-theme-panel';
3
5
  export * from './kit/tokens/chart-palette';
4
6
  export * from './app/container';
5
7
  export * from './app/background-decor';