@lucasvu/scope-ui 0.0.4 → 0.0.6
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/AI_SETUP.md +29 -6
- package/README.md +34 -7
- package/bin/scope-ui-init.mjs +341 -20
- package/dist/index.cjs +447 -15
- package/dist/index.d.cts +690 -5
- package/dist/index.d.ts +690 -5
- package/dist/index.js +444 -16
- package/package.json +1 -1
package/AI_SETUP.md
CHANGED
|
@@ -5,12 +5,19 @@ Mục tiêu của file này là để AI của project dùng `@lucasvu/scope-ui`
|
|
|
5
5
|
Nếu project consumer đã cài package, cách nhanh nhất là chạy:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
npm install @lucasvu/scope-ui
|
|
9
|
+
npx scope-ui-init --theme forest
|
|
9
10
|
```
|
|
10
11
|
|
|
11
|
-
Nếu chưa
|
|
12
|
+
Nếu chưa biết chọn preset nào thì chạy thêm:
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
```bash
|
|
15
|
+
npx scope-ui-init --list-themes
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Nếu chưa cài `@lucasvu/scope-ui`, các lệnh trên sẽ báo `E404` vì npm sẽ cố tải package tên `scope-ui-init`.
|
|
19
|
+
|
|
20
|
+
CLI này sẽ tạo `AGENTS.md` và `src/styles/ui-theme.css` theo đúng convention bên dưới. `AGENTS.md` sẽ chứa luôn workflow step-by-step để agent dựng UI đúng thứ tự. Nếu không truyền `--theme`, preset mặc định là `ocean`.
|
|
14
21
|
|
|
15
22
|
## 1. Import style đúng thứ tự
|
|
16
23
|
|
|
@@ -23,10 +30,19 @@ import './styles/ui-theme.css'
|
|
|
23
30
|
|
|
24
31
|
`ui-theme.css` nên là nơi duy nhất override màu, surface, border, shadow và gradient của thư viện.
|
|
25
32
|
|
|
26
|
-
## 2.
|
|
33
|
+
## 2. Chọn preset theme của project
|
|
27
34
|
|
|
28
35
|
Đặt file tại `src/styles/ui-theme.css`.
|
|
29
36
|
|
|
37
|
+
Dùng `--list-themes` để xem preset được duyệt, rồi generate preset đã chọn:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx scope-ui-init --list-themes
|
|
41
|
+
npx scope-ui-init --theme graphite
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
File generate ra sẽ là source of truth cho palette, surface, radius và shadow của project. Agent phải bám vào file này thay vì tự nghĩ màu mới.
|
|
45
|
+
|
|
30
46
|
```css
|
|
31
47
|
:root {
|
|
32
48
|
--tw-primary: 12 88% 56%;
|
|
@@ -56,11 +72,15 @@ Always:
|
|
|
56
72
|
- import `@lucasvu/scope-ui/styles.css` once at the app entry
|
|
57
73
|
- import the project theme override file after the package stylesheet
|
|
58
74
|
- use root exports from `@lucasvu/scope-ui`
|
|
75
|
+
- stay inside the approved preset declared by `src/styles/ui-theme.css`
|
|
76
|
+
- follow the step-by-step UI build workflow from `AGENTS.md` before writing JSX
|
|
77
|
+
- fill the screen brief first: `routeUrl`, `sidebarItems`, `activeSidebarItemId`, `pageTitle`, `actions`, and the page-specific schema
|
|
59
78
|
- read `uiAiManifest` to choose the right component by intent
|
|
60
|
-
- read `uiThemeContract` before changing colors, surfaces, borders, or shadows
|
|
79
|
+
- read `uiScreenBlueprint` and `uiThemeContract` before changing layout, colors, surfaces, borders, or shadows
|
|
61
80
|
|
|
62
81
|
Do not:
|
|
63
82
|
- import from `MainFe` unless the task explicitly targets a legacy screen
|
|
83
|
+
- invent a second palette outside the preset file
|
|
64
84
|
- hardcode brand colors inside page components when a theme token already exists
|
|
65
85
|
- create duplicate Button/Input/Select wrappers unless a project-specific behavior is required
|
|
66
86
|
|
|
@@ -81,11 +101,13 @@ Component choice:
|
|
|
81
101
|
Trong code, AI có thể đọc trực tiếp:
|
|
82
102
|
|
|
83
103
|
```ts
|
|
84
|
-
import { uiAiManifest, uiThemeContract, uiProjectAiRules } from '@lucasvu/scope-ui'
|
|
104
|
+
import { uiAiManifest, uiScreenBlueprint, uiThemeContract, uiThemePresets, uiProjectAiRules } from '@lucasvu/scope-ui'
|
|
85
105
|
```
|
|
86
106
|
|
|
87
107
|
- `uiAiManifest`: chọn component theo intent
|
|
108
|
+
- `uiScreenBlueprint`: brief input và khung layout chuẩn cho `list | form | detail | dashboard`
|
|
88
109
|
- `uiThemeContract`: biết override token nào và override ở đâu
|
|
110
|
+
- `uiThemePresets`: danh sách preset được duyệt để nhiều project dùng chung một visual language
|
|
89
111
|
- `uiProjectAiRules`: rule ngắn gọn để inject vào agent của project
|
|
90
112
|
|
|
91
113
|
## 5. Chỗ override theme
|
|
@@ -100,4 +122,5 @@ Nếu project có nhiều brand/theme:
|
|
|
100
122
|
|
|
101
123
|
- giữ `:root` là theme mặc định
|
|
102
124
|
- tạo selector như `[data-ui-theme='brand-a']`, `[data-ui-theme='brand-b']`
|
|
125
|
+
- vẫn nên xuất phát từ một preset duyệt sẵn rồi mới override token cần thiết
|
|
103
126
|
- chỉ override token trong file theme, không fork component
|
package/README.md
CHANGED
|
@@ -19,12 +19,19 @@ import '@lucasvu/scope-ui/styles.css';
|
|
|
19
19
|
3. Nếu muốn bootstrap rule/theme cho AI trong project consumer:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
|
|
22
|
+
npm install @lucasvu/scope-ui
|
|
23
|
+
npx scope-ui-init --theme sunset
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Nếu chưa biết chọn preset nào thì mới cần xem danh sách trước:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx scope-ui-init --list-themes
|
|
23
30
|
```
|
|
24
31
|
|
|
25
|
-
Lưu ý: lệnh này chỉ chạy được sau khi project đã cài `@lucasvu/scope-ui`. Nếu chưa cài package, `npx` sẽ đi tìm một package riêng tên `scope-ui-init` trên npm và báo `E404`.
|
|
32
|
+
Lưu ý: các lệnh này chỉ chạy được sau khi project đã cài `@lucasvu/scope-ui`. Nếu chưa cài package, `npx` sẽ đi tìm một package riêng tên `scope-ui-init` trên npm và báo `E404`.
|
|
26
33
|
|
|
27
|
-
Lệnh này sẽ tạo `AGENTS.md` và `src/styles/ui-theme.css` trong repo hiện tại. Dùng `--force` nếu muốn overwrite file đã tồn tại.
|
|
34
|
+
Lệnh này sẽ tạo `AGENTS.md` và `src/styles/ui-theme.css` trong repo hiện tại theo preset đã chọn. `AGENTS.md` giờ có thêm playbook step-by-step để agent dựng màn hình đúng layout/component/theme chung. Nếu không truyền `--theme`, preset mặc định là `ocean`. Dùng `--force` nếu muốn overwrite file đã tồn tại.
|
|
28
35
|
|
|
29
36
|
4. Dùng component:
|
|
30
37
|
|
|
@@ -103,22 +110,33 @@ Nếu muốn AI render UI đúng và ổn định theo thư viện này, đừng
|
|
|
103
110
|
|
|
104
111
|
- Import CSS global một lần: `@lucasvu/scope-ui/styles.css`
|
|
105
112
|
- Import file theme override của project ngay sau package CSS, ví dụ `./styles/ui-theme.css`
|
|
106
|
-
-
|
|
113
|
+
- Cài package trước khi bootstrap: `npm install @lucasvu/scope-ui`
|
|
114
|
+
- Chạy `npx scope-ui-init --list-themes` để xem preset được duyệt
|
|
115
|
+
- Chạy `npx scope-ui-init --theme <preset>` ở project consumer sau khi đã cài `@lucasvu/scope-ui` để tạo `AGENTS.md` và `src/styles/ui-theme.css`
|
|
116
|
+
- Khoá project vào một preset thay vì tự bịa palette mới cho từng page
|
|
117
|
+
- Để agent đọc luôn playbook trong `AGENTS.md` để đi đúng thứ tự: kiểm tra import, chọn component, dựng layout, rồi mới tinh chỉnh token
|
|
118
|
+
- Buộc agent điền screen brief trước khi code: `routeUrl`, `sidebarItems`, `activeSidebarItemId`, `pageTitle`, `actions`, `filters`, `fields`, `tableColumns`
|
|
107
119
|
- Chỉ dùng các component canonical ở root package; tránh `MainFe` nếu không phải legacy screen
|
|
108
120
|
- Cho agent đọc `uiAiManifest` để biết component nào dùng cho intent nào, props quan trọng là gì, và khi nào không nên dùng
|
|
109
|
-
- Cho agent đọc `uiThemeContract` để biết
|
|
121
|
+
- Cho agent đọc `uiScreenBlueprint`, `uiThemeContract` và `uiThemePresets` để biết screen recipe, preset và token nào được phép override
|
|
110
122
|
|
|
111
123
|
```ts
|
|
112
124
|
import {
|
|
113
125
|
uiAiManifest,
|
|
114
126
|
uiProjectAiRules,
|
|
127
|
+
uiScreenBlueprint,
|
|
115
128
|
uiThemeContract,
|
|
129
|
+
uiThemePresets,
|
|
116
130
|
} from '@lucasvu/scope-ui';
|
|
117
131
|
```
|
|
118
132
|
|
|
119
133
|
`uiAiManifest` mô tả luật chọn component theo intent như: text input, textarea, fixed select, searchable select, async combobox, multi select, data table, alert, card và field wrapper.
|
|
120
134
|
|
|
121
|
-
`
|
|
135
|
+
`uiScreenBlueprint` mô tả input brief bắt buộc cho từng màn hình như `routeUrl`, `sidebarItems`, `activeSidebarItemId`, `pageTitle`, `actions`, `filters`, `fields`, `tableColumns` và frame recipe cho `list | form | detail | dashboard`.
|
|
136
|
+
|
|
137
|
+
`uiThemeContract` mô tả token màu, surface, border, shadow, radius, gradient và selector theme (`:root`, `.dark`, `[data-ui-theme='*']`) để AI không hardcode màu sai chỗ.
|
|
138
|
+
|
|
139
|
+
`uiThemePresets` là danh sách preset được duyệt sẵn để nhiều project có thể chọn cùng một visual language mà không cần tự dựng theme từ đầu.
|
|
122
140
|
|
|
123
141
|
`AI_SETUP.md` và CLI `scope-ui-init` giúp bootstrap rule/theme cho repo consumer mà không cần copy tay.
|
|
124
142
|
|
|
@@ -126,6 +144,14 @@ import {
|
|
|
126
144
|
|
|
127
145
|
Nên để toàn bộ override theme của project vào một file riêng, ví dụ `src/styles/ui-theme.css`, rồi import file này sau `@lucasvu/scope-ui/styles.css`.
|
|
128
146
|
|
|
147
|
+
Cách nhanh nhất là generate file này từ preset:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
npm install @lucasvu/scope-ui
|
|
151
|
+
npx scope-ui-init --list-themes
|
|
152
|
+
npx scope-ui-init --theme ocean
|
|
153
|
+
```
|
|
154
|
+
|
|
129
155
|
```ts
|
|
130
156
|
import '@lucasvu/scope-ui/styles.css';
|
|
131
157
|
import './styles/ui-theme.css';
|
|
@@ -437,7 +463,8 @@ import { LineClampTooltip } from '@lucasvu/scope-ui';
|
|
|
437
463
|
|
|
438
464
|
## Tuỳ chỉnh theme
|
|
439
465
|
|
|
440
|
-
-
|
|
466
|
+
- Ưu tiên chọn một preset duyệt sẵn qua `npx scope-ui-init --theme <preset>` để giữ UI đồng bộ giữa các project.
|
|
467
|
+
- Nếu cần tinh chỉnh thêm, chỉ sửa token trong `src/styles/ui-theme.css`, không restyle từng component riêng lẻ.
|
|
441
468
|
- Các lớp `ui-*` đã được namespaced để tránh va chạm với CSS hiện tại.
|
|
442
469
|
|
|
443
470
|
## Gợi ý tích hợp
|
package/bin/scope-ui-init.mjs
CHANGED
|
@@ -5,30 +5,264 @@ import { dirname, resolve } from 'node:path'
|
|
|
5
5
|
import process from 'node:process'
|
|
6
6
|
|
|
7
7
|
const PACKAGE_NAME = '@lucasvu/scope-ui'
|
|
8
|
+
const DEFAULT_THEME_PRESET = 'ocean'
|
|
9
|
+
|
|
10
|
+
const THEME_PRESETS = {
|
|
11
|
+
ocean: {
|
|
12
|
+
id: 'ocean',
|
|
13
|
+
label: 'Ocean Glass',
|
|
14
|
+
description:
|
|
15
|
+
'Blue-cyan preset with clean glass surfaces. Matches the current default visual language.',
|
|
16
|
+
recommendedFor: [
|
|
17
|
+
'Admin dashboards',
|
|
18
|
+
'SaaS CRUD screens',
|
|
19
|
+
'Projects that want the existing package look',
|
|
20
|
+
],
|
|
21
|
+
tokens: {
|
|
22
|
+
light: {
|
|
23
|
+
'--tw-background': '0 0% 100%',
|
|
24
|
+
'--tw-foreground': '222.2 47.4% 11.2%',
|
|
25
|
+
'--tw-primary': '221.2 83.2% 53.3%',
|
|
26
|
+
'--tw-accent': '199 89% 48%',
|
|
27
|
+
'--tw-success': '142.1 76.2% 36.3%',
|
|
28
|
+
'--tw-destructive': '0 84.2% 60.2%',
|
|
29
|
+
'--tw-border': '214.3 31.8% 91.4%',
|
|
30
|
+
'--radius': '0.75rem',
|
|
31
|
+
'--surface': 'rgba(255, 255, 255, 0.92)',
|
|
32
|
+
'--surface-strong': 'rgba(241, 245, 249, 0.96)',
|
|
33
|
+
'--grey': 'rgba(248, 250, 252, 0.96)',
|
|
34
|
+
'--grey-strong': 'rgba(241, 245, 249, 0.98)',
|
|
35
|
+
'--shadow-sm': '0 10px 28px -18px rgba(15, 23, 42, 0.22)',
|
|
36
|
+
'--shadow': '0 24px 60px -28px rgba(15, 23, 42, 0.3)',
|
|
37
|
+
'--primary-grad-from': '199 89% 48%',
|
|
38
|
+
'--primary-grad-to': '221.2 83.2% 53.3%',
|
|
39
|
+
},
|
|
40
|
+
dark: {
|
|
41
|
+
'--tw-background': '222.2 84% 4.9%',
|
|
42
|
+
'--tw-foreground': '210 40% 98%',
|
|
43
|
+
'--tw-primary': '217.2 91.2% 59.8%',
|
|
44
|
+
'--tw-accent': '199 89% 48%',
|
|
45
|
+
'--tw-success': '142.1 70.6% 45.3%',
|
|
46
|
+
'--tw-destructive': '0 62.8% 30.6%',
|
|
47
|
+
'--tw-border': '217.2 32.6% 17.5%',
|
|
48
|
+
'--radius': '0.75rem',
|
|
49
|
+
'--surface': 'rgba(15, 23, 42, 0.78)',
|
|
50
|
+
'--surface-strong': 'rgba(30, 41, 59, 0.92)',
|
|
51
|
+
'--grey': 'rgba(30, 41, 59, 0.88)',
|
|
52
|
+
'--grey-strong': 'rgba(51, 65, 85, 0.92)',
|
|
53
|
+
'--shadow-sm': '0 16px 32px -18px rgba(2, 6, 23, 0.48)',
|
|
54
|
+
'--shadow': '0 28px 80px -32px rgba(2, 6, 23, 0.7)',
|
|
55
|
+
'--primary-grad-from': '198.6 88.7% 48.4%',
|
|
56
|
+
'--primary-grad-to': '221.2 83.2% 53.3%',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
sunset: {
|
|
61
|
+
id: 'sunset',
|
|
62
|
+
label: 'Sunset Ember',
|
|
63
|
+
description:
|
|
64
|
+
'Warm orange-coral preset with softer cream surfaces for branded landing or growth products.',
|
|
65
|
+
recommendedFor: [
|
|
66
|
+
'Growth products',
|
|
67
|
+
'Commerce backoffices',
|
|
68
|
+
'Projects that want a warmer visual tone',
|
|
69
|
+
],
|
|
70
|
+
tokens: {
|
|
71
|
+
light: {
|
|
72
|
+
'--tw-background': '30 100% 98%',
|
|
73
|
+
'--tw-foreground': '20 24% 14%',
|
|
74
|
+
'--tw-primary': '14 90% 56%',
|
|
75
|
+
'--tw-accent': '29 100% 58%',
|
|
76
|
+
'--tw-success': '145 63% 38%',
|
|
77
|
+
'--tw-destructive': '0 78% 58%',
|
|
78
|
+
'--tw-border': '24 45% 89%',
|
|
79
|
+
'--radius': '0.85rem',
|
|
80
|
+
'--surface': 'rgba(255, 247, 240, 0.92)',
|
|
81
|
+
'--surface-strong': 'rgba(255, 237, 223, 0.96)',
|
|
82
|
+
'--grey': 'rgba(255, 243, 231, 0.96)',
|
|
83
|
+
'--grey-strong': 'rgba(255, 232, 214, 0.98)',
|
|
84
|
+
'--shadow-sm': '0 12px 30px -18px rgba(194, 65, 12, 0.18)',
|
|
85
|
+
'--shadow': '0 28px 64px -30px rgba(154, 52, 18, 0.24)',
|
|
86
|
+
'--primary-grad-from': '29 100% 58%',
|
|
87
|
+
'--primary-grad-to': '14 90% 56%',
|
|
88
|
+
},
|
|
89
|
+
dark: {
|
|
90
|
+
'--tw-background': '20 24% 8%',
|
|
91
|
+
'--tw-foreground': '40 33% 96%',
|
|
92
|
+
'--tw-primary': '18 100% 62%',
|
|
93
|
+
'--tw-accent': '35 100% 58%',
|
|
94
|
+
'--tw-success': '145 60% 47%',
|
|
95
|
+
'--tw-destructive': '0 73% 52%',
|
|
96
|
+
'--tw-border': '18 24% 22%',
|
|
97
|
+
'--radius': '0.85rem',
|
|
98
|
+
'--surface': 'rgba(41, 24, 18, 0.84)',
|
|
99
|
+
'--surface-strong': 'rgba(59, 34, 24, 0.9)',
|
|
100
|
+
'--grey': 'rgba(70, 42, 29, 0.88)',
|
|
101
|
+
'--grey-strong': 'rgba(92, 54, 38, 0.9)',
|
|
102
|
+
'--shadow-sm': '0 18px 36px -20px rgba(67, 20, 7, 0.48)',
|
|
103
|
+
'--shadow': '0 30px 84px -34px rgba(67, 20, 7, 0.62)',
|
|
104
|
+
'--primary-grad-from': '35 100% 58%',
|
|
105
|
+
'--primary-grad-to': '18 100% 62%',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
forest: {
|
|
110
|
+
id: 'forest',
|
|
111
|
+
label: 'Forest Mist',
|
|
112
|
+
description:
|
|
113
|
+
'Emerald-teal preset with soft botanical surfaces for calmer productivity products.',
|
|
114
|
+
recommendedFor: [
|
|
115
|
+
'Operations tools',
|
|
116
|
+
'Internal platforms',
|
|
117
|
+
'Products that want a calmer green tone',
|
|
118
|
+
],
|
|
119
|
+
tokens: {
|
|
120
|
+
light: {
|
|
121
|
+
'--tw-background': '138 40% 98%',
|
|
122
|
+
'--tw-foreground': '160 25% 14%',
|
|
123
|
+
'--tw-primary': '158 64% 40%',
|
|
124
|
+
'--tw-accent': '173 58% 44%',
|
|
125
|
+
'--tw-success': '145 63% 36%',
|
|
126
|
+
'--tw-destructive': '0 78% 58%',
|
|
127
|
+
'--tw-border': '143 21% 88%',
|
|
128
|
+
'--radius': '0.8rem',
|
|
129
|
+
'--surface': 'rgba(245, 252, 249, 0.92)',
|
|
130
|
+
'--surface-strong': 'rgba(232, 245, 239, 0.96)',
|
|
131
|
+
'--grey': 'rgba(240, 248, 244, 0.96)',
|
|
132
|
+
'--grey-strong': 'rgba(225, 240, 232, 0.98)',
|
|
133
|
+
'--shadow-sm': '0 12px 28px -18px rgba(5, 86, 66, 0.18)',
|
|
134
|
+
'--shadow': '0 26px 62px -30px rgba(6, 78, 59, 0.24)',
|
|
135
|
+
'--primary-grad-from': '173 58% 44%',
|
|
136
|
+
'--primary-grad-to': '158 64% 40%',
|
|
137
|
+
},
|
|
138
|
+
dark: {
|
|
139
|
+
'--tw-background': '164 35% 8%',
|
|
140
|
+
'--tw-foreground': '144 35% 96%',
|
|
141
|
+
'--tw-primary': '160 70% 46%',
|
|
142
|
+
'--tw-accent': '174 72% 45%',
|
|
143
|
+
'--tw-success': '145 68% 46%',
|
|
144
|
+
'--tw-destructive': '0 70% 52%',
|
|
145
|
+
'--tw-border': '160 20% 20%',
|
|
146
|
+
'--radius': '0.8rem',
|
|
147
|
+
'--surface': 'rgba(16, 36, 31, 0.84)',
|
|
148
|
+
'--surface-strong': 'rgba(21, 51, 44, 0.9)',
|
|
149
|
+
'--grey': 'rgba(24, 61, 52, 0.88)',
|
|
150
|
+
'--grey-strong': 'rgba(31, 77, 65, 0.9)',
|
|
151
|
+
'--shadow-sm': '0 18px 34px -20px rgba(1, 44, 34, 0.48)',
|
|
152
|
+
'--shadow': '0 30px 82px -34px rgba(1, 44, 34, 0.6)',
|
|
153
|
+
'--primary-grad-from': '174 72% 45%',
|
|
154
|
+
'--primary-grad-to': '160 70% 46%',
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
graphite: {
|
|
159
|
+
id: 'graphite',
|
|
160
|
+
label: 'Graphite Pulse',
|
|
161
|
+
description:
|
|
162
|
+
'Neutral slate preset with restrained blue accents for products that need a steadier enterprise tone.',
|
|
163
|
+
recommendedFor: [
|
|
164
|
+
'Enterprise admin panels',
|
|
165
|
+
'B2B internal tools',
|
|
166
|
+
'Projects that want a more neutral interface',
|
|
167
|
+
],
|
|
168
|
+
tokens: {
|
|
169
|
+
light: {
|
|
170
|
+
'--tw-background': '220 18% 97%',
|
|
171
|
+
'--tw-foreground': '222 24% 14%',
|
|
172
|
+
'--tw-primary': '221 24% 32%',
|
|
173
|
+
'--tw-accent': '198 83% 44%',
|
|
174
|
+
'--tw-success': '160 56% 38%',
|
|
175
|
+
'--tw-destructive': '0 72% 54%',
|
|
176
|
+
'--tw-border': '218 17% 86%',
|
|
177
|
+
'--radius': '0.7rem',
|
|
178
|
+
'--surface': 'rgba(248, 250, 252, 0.94)',
|
|
179
|
+
'--surface-strong': 'rgba(226, 232, 240, 0.96)',
|
|
180
|
+
'--grey': 'rgba(241, 245, 249, 0.98)',
|
|
181
|
+
'--grey-strong': 'rgba(226, 232, 240, 0.99)',
|
|
182
|
+
'--shadow-sm': '0 12px 30px -20px rgba(15, 23, 42, 0.18)',
|
|
183
|
+
'--shadow': '0 28px 66px -30px rgba(15, 23, 42, 0.24)',
|
|
184
|
+
'--primary-grad-from': '198 83% 44%',
|
|
185
|
+
'--primary-grad-to': '221 24% 32%',
|
|
186
|
+
},
|
|
187
|
+
dark: {
|
|
188
|
+
'--tw-background': '222 32% 8%',
|
|
189
|
+
'--tw-foreground': '210 25% 96%',
|
|
190
|
+
'--tw-primary': '210 24% 82%',
|
|
191
|
+
'--tw-accent': '192 92% 52%',
|
|
192
|
+
'--tw-success': '158 64% 45%',
|
|
193
|
+
'--tw-destructive': '0 72% 56%',
|
|
194
|
+
'--tw-border': '217 19% 24%',
|
|
195
|
+
'--radius': '0.7rem',
|
|
196
|
+
'--surface': 'rgba(15, 23, 42, 0.82)',
|
|
197
|
+
'--surface-strong': 'rgba(30, 41, 59, 0.92)',
|
|
198
|
+
'--grey': 'rgba(30, 41, 59, 0.9)',
|
|
199
|
+
'--grey-strong': 'rgba(51, 65, 85, 0.92)',
|
|
200
|
+
'--shadow-sm': '0 18px 38px -22px rgba(2, 6, 23, 0.48)',
|
|
201
|
+
'--shadow': '0 32px 88px -34px rgba(2, 6, 23, 0.68)',
|
|
202
|
+
'--primary-grad-from': '192 92% 52%',
|
|
203
|
+
'--primary-grad-to': '210 24% 82%',
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function exitWithMissingValue(optionName) {
|
|
210
|
+
console.error(`Missing value for ${optionName}`)
|
|
211
|
+
process.exit(1)
|
|
212
|
+
}
|
|
8
213
|
|
|
9
214
|
function parseArgs(argv) {
|
|
10
215
|
const args = {
|
|
11
216
|
force: false,
|
|
12
217
|
agentsFile: 'AGENTS.md',
|
|
13
218
|
themeFile: 'src/styles/ui-theme.css',
|
|
219
|
+
theme: DEFAULT_THEME_PRESET,
|
|
220
|
+
listThemes: false,
|
|
14
221
|
}
|
|
15
222
|
|
|
16
223
|
for (let index = 0; index < argv.length; index += 1) {
|
|
17
224
|
const arg = argv[index]
|
|
225
|
+
|
|
18
226
|
if (arg === '--force') {
|
|
19
227
|
args.force = true
|
|
20
228
|
continue
|
|
21
229
|
}
|
|
230
|
+
|
|
22
231
|
if (arg === '--agents-file') {
|
|
23
|
-
|
|
232
|
+
const value = argv[index + 1]
|
|
233
|
+
if (!value || value.startsWith('--')) {
|
|
234
|
+
exitWithMissingValue('--agents-file')
|
|
235
|
+
}
|
|
236
|
+
args.agentsFile = value
|
|
24
237
|
index += 1
|
|
25
238
|
continue
|
|
26
239
|
}
|
|
240
|
+
|
|
27
241
|
if (arg === '--theme-file') {
|
|
28
|
-
|
|
242
|
+
const value = argv[index + 1]
|
|
243
|
+
if (!value || value.startsWith('--')) {
|
|
244
|
+
exitWithMissingValue('--theme-file')
|
|
245
|
+
}
|
|
246
|
+
args.themeFile = value
|
|
29
247
|
index += 1
|
|
30
248
|
continue
|
|
31
249
|
}
|
|
250
|
+
|
|
251
|
+
if (arg === '--theme') {
|
|
252
|
+
const value = argv[index + 1]
|
|
253
|
+
if (!value || value.startsWith('--')) {
|
|
254
|
+
exitWithMissingValue('--theme')
|
|
255
|
+
}
|
|
256
|
+
args.theme = value
|
|
257
|
+
index += 1
|
|
258
|
+
continue
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (arg === '--list-themes') {
|
|
262
|
+
args.listThemes = true
|
|
263
|
+
continue
|
|
264
|
+
}
|
|
265
|
+
|
|
32
266
|
if (arg === '--help' || arg === '-h') {
|
|
33
267
|
printHelp()
|
|
34
268
|
process.exit(0)
|
|
@@ -47,38 +281,118 @@ Usage:
|
|
|
47
281
|
npx scope-ui-init
|
|
48
282
|
Run this after @lucasvu/scope-ui has been installed in the consumer project.
|
|
49
283
|
|
|
50
|
-
npx scope-ui-init --
|
|
284
|
+
npx scope-ui-init --theme sunset
|
|
285
|
+
Fast path when you already know the preset you want.
|
|
286
|
+
|
|
287
|
+
npx scope-ui-init --list-themes
|
|
288
|
+
Optional: inspect approved presets before choosing one.
|
|
289
|
+
|
|
290
|
+
npx scope-ui-init --theme forest --force
|
|
51
291
|
npx scope-ui-init --agents-file AGENTS.md --theme-file src/styles/ui-theme.css
|
|
52
292
|
|
|
53
293
|
Options:
|
|
54
294
|
--force overwrite existing files
|
|
55
295
|
--agents-file target path for the generated AGENTS.md file
|
|
56
296
|
--theme-file target path for the generated theme override file
|
|
297
|
+
--theme theme preset id (${Object.keys(THEME_PRESETS).join(', ')})
|
|
298
|
+
--list-themes show the approved preset list
|
|
57
299
|
--help, -h show this help
|
|
58
300
|
`)
|
|
59
301
|
}
|
|
60
302
|
|
|
61
|
-
function
|
|
303
|
+
function printThemeList() {
|
|
304
|
+
console.log('Approved theme presets:\n')
|
|
305
|
+
|
|
306
|
+
for (const themePreset of Object.values(THEME_PRESETS)) {
|
|
307
|
+
console.log(`${themePreset.id} - ${themePreset.label}`)
|
|
308
|
+
console.log(` ${themePreset.description}`)
|
|
309
|
+
console.log(` Recommended for: ${themePreset.recommendedFor.join(', ')}`)
|
|
310
|
+
console.log('')
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function getThemePreset(themeId) {
|
|
315
|
+
return THEME_PRESETS[themeId]
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function resolveThemePreset(themeId) {
|
|
319
|
+
const themePreset = getThemePreset(themeId)
|
|
320
|
+
|
|
321
|
+
if (themePreset) {
|
|
322
|
+
return themePreset
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
console.error(`Unknown theme preset: ${themeId}\n`)
|
|
326
|
+
printThemeList()
|
|
327
|
+
process.exit(1)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function renderTokenBlock(tokens) {
|
|
331
|
+
return Object.entries(tokens)
|
|
332
|
+
.map(([token, value]) => ` ${token}: ${value};`)
|
|
333
|
+
.join('\n')
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function createAgentsTemplate({ themeFile, themePreset }) {
|
|
62
337
|
return `# Agent Rules
|
|
63
338
|
|
|
64
339
|
This repo uses \`${PACKAGE_NAME}\` as the default UI library.
|
|
340
|
+
This repo is locked to the \`${themePreset.label}\` theme preset (\`${themePreset.id}\`).
|
|
65
341
|
|
|
66
342
|
Primary references:
|
|
67
343
|
- \`node_modules/${PACKAGE_NAME}/README.md\`
|
|
68
344
|
- \`node_modules/${PACKAGE_NAME}/AI_SETUP.md\`
|
|
345
|
+
- \`uiScreenBlueprint\` and \`uiAiManifest\` from \`${PACKAGE_NAME}\`
|
|
69
346
|
- \`${themeFile}\`
|
|
70
347
|
|
|
348
|
+
Theme preset summary:
|
|
349
|
+
- ${themePreset.description}
|
|
350
|
+
- Recommended for: ${themePreset.recommendedFor.join(', ')}
|
|
351
|
+
|
|
352
|
+
UI build workflow:
|
|
353
|
+
1. Read \`${themeFile}\`, \`node_modules/${PACKAGE_NAME}/README.md\`, \`node_modules/${PACKAGE_NAME}/AI_SETUP.md\`, and the exported \`uiScreenBlueprint\` contract before generating UI.
|
|
354
|
+
2. Fill the screen brief first. If route url, sidebar items, active sidebar item, title, actions, filters, fields, or columns are missing, ask for them or leave clear TODO placeholders instead of inventing them.
|
|
355
|
+
3. Verify the app entry imports \`${PACKAGE_NAME}/styles.css\` first and the project theme file right after it.
|
|
356
|
+
4. Identify the page kind (\`list | form | detail | dashboard\`), then map each block to canonical components from \`uiAiManifest\` before writing JSX.
|
|
357
|
+
5. Build the shell in this order: \`Sidebar\` -> \`Breadcrumb\` -> \`PageTitle\` -> top actions -> page body cards/grids.
|
|
358
|
+
6. For list pages, build filters first and then the \`DataTable\`. For form pages, build section cards and fields. For detail pages, build summary cards and related tables. For dashboard pages, build stat cards and then supporting tables/cards.
|
|
359
|
+
7. Keep all colors, radius, surface, and shadow decisions inside \`${themeFile}\`. If the preset is insufficient, update tokens there instead of styling one page ad hoc.
|
|
360
|
+
8. Before finishing, self-check: no \`MainFe\`, no hardcoded palette, correct import order, complete brief coverage, consistent spacing, and the screen still matches the shared preset.
|
|
361
|
+
|
|
362
|
+
Screen brief template:
|
|
363
|
+
- pageKind: list | form | detail | dashboard
|
|
364
|
+
- routeUrl:
|
|
365
|
+
- sidebarItems: [{ id, title, href }]
|
|
366
|
+
- activeSidebarItemId:
|
|
367
|
+
- breadcrumbs:
|
|
368
|
+
- pageTitle:
|
|
369
|
+
- pageSubtitle:
|
|
370
|
+
- primaryAction:
|
|
371
|
+
- secondaryActions:
|
|
372
|
+
- summaryStats:
|
|
373
|
+
- filters:
|
|
374
|
+
- tableColumns:
|
|
375
|
+
- rowActions:
|
|
376
|
+
- formSections:
|
|
377
|
+
- fields:
|
|
378
|
+
- detailSections:
|
|
379
|
+
- emptyState:
|
|
380
|
+
- permissions:
|
|
381
|
+
|
|
71
382
|
Always:
|
|
72
383
|
- import \`${PACKAGE_NAME}/styles.css\` once at the app entry
|
|
73
384
|
- import the project theme override file after the package stylesheet
|
|
74
385
|
- use root exports from \`${PACKAGE_NAME}\`
|
|
386
|
+
- use \`${themeFile}\` as the only source of truth for colors, radius, surfaces, and shadows
|
|
387
|
+
- keep layouts and component choices aligned with the shared preset-driven UI used across projects
|
|
75
388
|
- read \`uiAiManifest\` before choosing components
|
|
76
|
-
- read \`uiThemeContract\` before changing colors, surfaces, borders, or shadows
|
|
389
|
+
- read \`uiThemeContract\` before changing colors, surfaces, borders, radius, or shadows
|
|
77
390
|
- keep UI token-driven and theme-driven
|
|
78
391
|
|
|
79
392
|
Do not:
|
|
80
393
|
- import from \`MainFe\` unless the task explicitly targets a legacy screen
|
|
81
|
-
-
|
|
394
|
+
- invent a second palette or one-off brand colors outside \`${themeFile}\`
|
|
395
|
+
- restyle individual pages if the preset tokens can solve it globally
|
|
82
396
|
- create duplicate Button/Input/Select wrappers unless a project-specific behavior is required
|
|
83
397
|
|
|
84
398
|
Component choice:
|
|
@@ -94,20 +408,18 @@ Component choice:
|
|
|
94
408
|
`
|
|
95
409
|
}
|
|
96
410
|
|
|
97
|
-
function createThemeTemplate() {
|
|
98
|
-
return
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
/* --surface: rgba(255, 255, 255, 0.92); */
|
|
411
|
+
function createThemeTemplate({ themePreset }) {
|
|
412
|
+
return `/* Generated by scope-ui-init. */
|
|
413
|
+
/* Theme preset: ${themePreset.label} (${themePreset.id}) */
|
|
414
|
+
/* Re-run \`npx scope-ui-init --theme <preset> --force\` to switch preset. */
|
|
415
|
+
|
|
416
|
+
:root {
|
|
417
|
+
${renderTokenBlock(themePreset.tokens.light)}
|
|
105
418
|
}
|
|
106
419
|
|
|
107
|
-
.dark
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
/* --surface: rgba(15, 23, 42, 0.78); */
|
|
420
|
+
.dark,
|
|
421
|
+
[data-ui-theme='dark'] {
|
|
422
|
+
${renderTokenBlock(themePreset.tokens.dark)}
|
|
111
423
|
}
|
|
112
424
|
`
|
|
113
425
|
}
|
|
@@ -127,19 +439,27 @@ function writeFile(targetPath, content, force) {
|
|
|
127
439
|
|
|
128
440
|
const options = parseArgs(process.argv.slice(2))
|
|
129
441
|
|
|
442
|
+
if (options.listThemes) {
|
|
443
|
+
printThemeList()
|
|
444
|
+
process.exit(0)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const themePreset = resolveThemePreset(options.theme)
|
|
448
|
+
|
|
130
449
|
const agentsResult = writeFile(
|
|
131
450
|
options.agentsFile,
|
|
132
|
-
createAgentsTemplate({ themeFile: options.themeFile }),
|
|
451
|
+
createAgentsTemplate({ themeFile: options.themeFile, themePreset }),
|
|
133
452
|
options.force,
|
|
134
453
|
)
|
|
135
454
|
|
|
136
455
|
const themeResult = writeFile(
|
|
137
456
|
options.themeFile,
|
|
138
|
-
createThemeTemplate(),
|
|
457
|
+
createThemeTemplate({ themePreset }),
|
|
139
458
|
options.force,
|
|
140
459
|
)
|
|
141
460
|
|
|
142
461
|
console.log(`Initialized ${PACKAGE_NAME}`)
|
|
462
|
+
console.log(`- theme preset: ${themePreset.label} (${themePreset.id})`)
|
|
143
463
|
console.log(`- ${agentsResult.path}: ${agentsResult.status}`)
|
|
144
464
|
console.log(`- ${themeResult.path}: ${themeResult.status}`)
|
|
145
465
|
console.log('')
|
|
@@ -147,3 +467,4 @@ console.log('Next steps:')
|
|
|
147
467
|
console.log(`1. Import \`${PACKAGE_NAME}/styles.css\` once at your app entry.`)
|
|
148
468
|
console.log(`2. Import \`${options.themeFile}\` right after the package stylesheet.`)
|
|
149
469
|
console.log(`3. Let your agent read \`${options.agentsFile}\` before generating UI.`)
|
|
470
|
+
console.log(`4. Re-run this command with \`--theme <preset> --force\` if you want another approved preset.`)
|