@lucasvu/scope-ui 0.0.1 → 0.0.3

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 ADDED
@@ -0,0 +1,101 @@
1
+ # AI Setup
2
+
3
+ Mục tiêu của file này là để AI của project dùng `@lucasvu/scope-ui` đúng cách, không import lung tung, không hardcode màu sai chỗ và không chọn nhầm component.
4
+
5
+ Nếu project consumer đã cài package, cách nhanh nhất là chạy:
6
+
7
+ ```bash
8
+ npx scope-ui-init
9
+ ```
10
+
11
+ CLI này sẽ tạo `AGENTS.md` và `src/styles/ui-theme.css` theo đúng convention bên dưới.
12
+
13
+ ## 1. Import style đúng thứ tự
14
+
15
+ Import package CSS trước, rồi mới import file override theme của project.
16
+
17
+ ```ts
18
+ import '@lucasvu/scope-ui/styles.css'
19
+ import './styles/ui-theme.css'
20
+ ```
21
+
22
+ `ui-theme.css` nên là nơi duy nhất override màu, surface, border, shadow và gradient của thư viện.
23
+
24
+ ## 2. Tạo file theme của project
25
+
26
+ Đặt file tại `src/styles/ui-theme.css`.
27
+
28
+ ```css
29
+ :root {
30
+ --tw-primary: 12 88% 56%;
31
+ --tw-accent: 24 95% 52%;
32
+ --primary-grad-from: 24 95% 52%;
33
+ --primary-grad-to: 12 88% 56%;
34
+ --surface: rgba(255, 248, 240, 0.92);
35
+ }
36
+
37
+ .dark {
38
+ --tw-primary: 18 100% 62%;
39
+ --tw-accent: 35 100% 58%;
40
+ --surface: rgba(24, 24, 27, 0.82);
41
+ }
42
+ ```
43
+
44
+ Không nên override màu trực tiếp trong từng component nếu chỉ đang đổi theme.
45
+
46
+ ## 3. Rule cho AI của project
47
+
48
+ Copy phần này vào system prompt, repo instructions, `AGENTS.md`, Cursor rules, hoặc bất kỳ chỗ nào project đang dùng để ép AI theo convention:
49
+
50
+ ```md
51
+ Use `@lucasvu/scope-ui` as the default UI library.
52
+
53
+ Always:
54
+ - import `@lucasvu/scope-ui/styles.css` once at the app entry
55
+ - import the project theme override file after the package stylesheet
56
+ - use root exports from `@lucasvu/scope-ui`
57
+ - read `uiAiManifest` to choose the right component by intent
58
+ - read `uiThemeContract` before changing colors, surfaces, borders, or shadows
59
+
60
+ Do not:
61
+ - import from `MainFe` unless the task explicitly targets a legacy screen
62
+ - hardcode brand colors inside page components when a theme token already exists
63
+ - create duplicate Button/Input/Select wrappers unless a project-specific behavior is required
64
+
65
+ Component choice:
66
+ - `Input` for short text
67
+ - `Textarea` for multiline text
68
+ - `Select` for small fixed options
69
+ - `SearchableSelect` for larger local option sets
70
+ - `Combobox` for type-and-pick
71
+ - `AsyncCombobox` for remote search
72
+ - `MultiSelect` for multi-pick
73
+ - `Field` only for custom controls or grouped content
74
+ - `DataTable` for tabular records
75
+ ```
76
+
77
+ ## 4. Runtime contract cho AI/codegen
78
+
79
+ Trong code, AI có thể đọc trực tiếp:
80
+
81
+ ```ts
82
+ import { uiAiManifest, uiThemeContract, uiProjectAiRules } from '@lucasvu/scope-ui'
83
+ ```
84
+
85
+ - `uiAiManifest`: chọn component theo intent
86
+ - `uiThemeContract`: biết override token nào và override ở đâu
87
+ - `uiProjectAiRules`: rule ngắn gọn để inject vào agent của project
88
+
89
+ ## 5. Chỗ override theme
90
+
91
+ Nếu project có app shell:
92
+
93
+ - import `@lucasvu/scope-ui/styles.css` ở app entry
94
+ - import `src/styles/ui-theme.css` ngay sau đó
95
+ - toggle dark mode bằng class `.dark` trên `html` hoặc `body`, hoặc dùng `[data-ui-theme='dark']`
96
+
97
+ Nếu project có nhiều brand/theme:
98
+
99
+ - giữ `:root` là theme mặc định
100
+ - tạo selector như `[data-ui-theme='brand-a']`, `[data-ui-theme='brand-b']`
101
+ - chỉ override token trong file theme, không fork component
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @base/ui
1
+ # @lucasvu/scope-ui
2
2
 
3
3
  Bộ component phong cách **shadcn** dùng chung cho các app/micro-frontend trong workspace. Tất cả đều là React component thuần, kèm sẵn theme tối và hiệu ứng kính mờ.
4
4
 
@@ -27,26 +27,44 @@ npm login
27
27
  npm publish
28
28
  ```
29
29
 
30
- `publishConfig.access: public` đã được bật sẵn, nên với package dạng scope như `@your-scope/ui` không cần nhớ thêm `--access public`.
30
+ `publishConfig.access: public` đã được bật sẵn, nên với package dạng scope như `@lucasvu/scope-ui` không cần nhớ thêm `--access public`.
31
31
 
32
32
  ## Cách dùng nhanh
33
33
 
34
34
  1) Cài package:
35
35
 
36
36
  ```bash
37
- npm install @your-scope/ui
37
+ npm install @lucasvu/scope-ui
38
38
  ```
39
39
 
40
40
  2) Import global style một lần ở entry (ví dụ `main.tsx`):
41
41
 
42
42
  ```ts
43
- import '@your-scope/ui/styles.css'
43
+ import '@lucasvu/scope-ui/styles.css'
44
44
  ```
45
45
 
46
- 3) Dùng component:
46
+ 3) Nếu muốn bootstrap rule/theme cho AI trong project consumer:
47
+
48
+ ```bash
49
+ npx scope-ui-init
50
+ ```
51
+
52
+ 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.
53
+
54
+ 4) Dùng component:
47
55
 
48
56
  ```tsx
49
- import { Button, Card, CardHeader, CardTitle, CardContent, DataTable, Field, Input } from '@your-scope/ui'
57
+ import {
58
+ Button,
59
+ Card,
60
+ CardContent,
61
+ CardHeader,
62
+ CardTitle,
63
+ DataTable,
64
+ Field,
65
+ Input,
66
+ Select,
67
+ } from '@lucasvu/scope-ui'
50
68
 
51
69
  export function Example() {
52
70
  return (
@@ -55,10 +73,18 @@ export function Example() {
55
73
  <CardTitle>Form đăng ký</CardTitle>
56
74
  </CardHeader>
57
75
  <CardContent className="ui-grid ui-grid--two">
58
- <Field label="Email" required>
59
- <Input type="email" placeholder="you@example.com" />
76
+ <Field label="Email" required htmlFor="email">
77
+ <Input id="email" type="email" placeholder="you@example.com" />
60
78
  </Field>
61
- <Field label="Gói sử dụng">
79
+ <Select
80
+ label="Gói sử dụng"
81
+ placeholder="Chọn gói"
82
+ options={[
83
+ { label: 'Starter', value: 'starter' },
84
+ { label: 'Pro', value: 'pro' },
85
+ ]}
86
+ />
87
+ <Field label="Bảng giá theo gói" helperText="Ví dụ DataTable cơ bản">
62
88
  <DataTable
63
89
  data={[{ name: 'Starter', price: '$19' }]}
64
90
  rowKey="name"
@@ -77,10 +103,10 @@ export function Example() {
77
103
 
78
104
  ## Thành phần có sẵn
79
105
 
80
- - Nút: `Button` (variant `primary | secondary | ghost | outline | destructive | link | accent`, size `sm | md | lg | icon`, hỗ trợ `block`)
106
+ - Nút: `Button` (variant `default | secondary | ghost | outline | destructive | link | confirm | create`, size `sm | md | lg | icon`, hỗ trợ `block`)
81
107
  - Badge: `Badge` (solid, outline, success, warning)
82
108
  - Thẻ: `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`
83
- - Form: `Field`, `Label`, `Input`, `Select`, `Textarea`, `Combobox` (searchable input + dropdown giống shadcn)
109
+ - Form: `Form`, `Field`, `Item`, `Label`, `Control`, `Description`, `Message`, `Input`, `Textarea`, `Select`, `SearchableSelect`, `Combobox`, `AsyncCombobox`, `MultiSelect`
84
110
  - Numeric: `NumericInput` (hỗ trợ integer/decimal, parse an toàn, format/clamp khi blur, dùng cho price/amount)
85
111
  - Tabs: `Tabs` với mảng `items` `{ value, label, content, badge? }`
86
112
  - Thông báo: `Alert` (tone info/success/warning/danger)
@@ -88,7 +114,59 @@ export function Example() {
88
114
  - Bảng: `DataTable<T>` với `columns` và `render` tuỳ biến
89
115
  - Tooltip: `Tooltip`, `OverflowTooltip`, `LineClampTooltip` (hiển thị tooltip khi bị cắt theo dòng)
90
116
  - Lưới tiện dụng: class `ui-grid`, `ui-grid--two`, `ui-grid--three` để xếp block nhanh
91
- - `Showcase`: một view mẫu kết hợp stats + tabs + form + bảng (import từ `@base/ui` để demo)
117
+ - `MainFe`: nhóm component dành cho main frontend vẫn được export riêng để tương thích ngược
118
+
119
+ ## Ghi chú DX
120
+
121
+ - `Input`, `Textarea`, `Select`, `SearchableSelect`, `Combobox` đã hỗ trợ trực tiếp `label`, `helperText`, `errorMessage`.
122
+ - `Field` phù hợp khi bạn cần bọc một control custom hoặc gom nhiều control dưới cùng một nhãn.
123
+ - Nếu bạn đổi tên package trước khi publish, thay `@lucasvu/scope-ui` trong các ví dụ bằng package name mới.
124
+
125
+ ## Dùng cho AI/codegen
126
+
127
+ Nếu muốn AI render UI đúng và ổn định theo thư viện này, đừng chỉ đưa mỗi tên package. Hãy để agent bám vào một surface chuẩn:
128
+
129
+ - Import CSS global một lần: `@lucasvu/scope-ui/styles.css`
130
+ - Import file theme override của project ngay sau package CSS, ví dụ `./styles/ui-theme.css`
131
+ - Chạy `npx scope-ui-init` ở project consumer để tạo `AGENTS.md` và `src/styles/ui-theme.css`
132
+ - Chỉ dùng các component canonical ở root package; tránh `MainFe` nếu không phải legacy screen
133
+ - 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
134
+ - Cho agent đọc `uiThemeContract` để biết màu/token phải override ở đâu
135
+
136
+ ```ts
137
+ import { uiAiManifest, uiProjectAiRules, uiThemeContract } from '@lucasvu/scope-ui'
138
+ ```
139
+
140
+ `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.
141
+
142
+ `uiThemeContract` mô tả token màu, surface, border, shadow, gradient và selector theme (`:root`, `.dark`, `[data-ui-theme='*']`) để AI không hardcode màu sai chỗ.
143
+
144
+ `AI_SETUP.md` và CLI `scope-ui-init` giúp bootstrap rule/theme cho repo consumer mà không cần copy tay.
145
+
146
+ ## Theme Override
147
+
148
+ 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`.
149
+
150
+ ```ts
151
+ import '@lucasvu/scope-ui/styles.css'
152
+ import './styles/ui-theme.css'
153
+ ```
154
+
155
+ Ví dụ:
156
+
157
+ ```css
158
+ :root {
159
+ --tw-primary: 12 88% 56%;
160
+ --tw-accent: 24 95% 52%;
161
+ --primary-grad-from: 24 95% 52%;
162
+ --primary-grad-to: 12 88% 56%;
163
+ }
164
+
165
+ .dark {
166
+ --tw-primary: 18 100% 62%;
167
+ --tw-accent: 35 100% 58%;
168
+ }
169
+ ```
92
170
 
93
171
  ## NumericInput
94
172
 
@@ -132,7 +210,7 @@ type NumericInputProps = {
132
210
  ### Ví dụ dùng
133
211
 
134
212
  ```tsx
135
- import { NumericInput } from '@base/ui';
213
+ import { NumericInput } from '@lucasvu/scope-ui';
136
214
 
137
215
  // 1) Integer qty
138
216
  <NumericInput
@@ -168,7 +246,7 @@ import { NumericInput } from '@base/ui';
168
246
  ## DataTable
169
247
 
170
248
  ```tsx
171
- import type { DataTableColumn, DataTableSortState } from '@base/ui'
249
+ import type { DataTableColumn, DataTableSortState } from '@lucasvu/scope-ui'
172
250
  ```
173
251
 
174
252
  ### Tối thiểu
@@ -371,7 +449,7 @@ DataTable tự hỗ trợ scroll ngang khi nội dung vượt chiều rộng (đ
371
449
  Dùng khi cần hiển thị tối đa N dòng, nếu text bị cắt sẽ hiện tooltip đầy đủ khi hover.
372
450
 
373
451
  ```tsx
374
- import { LineClampTooltip } from '@base/ui'
452
+ import { LineClampTooltip } from '@lucasvu/scope-ui'
375
453
 
376
454
  <LineClampTooltip
377
455
  text={record.apiEndpoint}
@@ -390,5 +468,5 @@ import { LineClampTooltip } from '@base/ui'
390
468
 
391
469
  ## Gợi ý tích hợp
392
470
 
393
- - Import `@base/ui/styles.css` ở host để các remote có thể tái sử dụng cùng theme.
471
+ - Import `@lucasvu/scope-ui/styles.css` ở host để các remote có thể tái sử dụng cùng theme.
394
472
  - Nếu muốn kết hợp với Tailwind/shadcn gốc, giữ nguyên class `ui-*` và thêm `content` trỏ tới `packages/ui/src/**/*` trong `tailwind.config`.
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
4
+ import { dirname, resolve } from 'node:path'
5
+ import process from 'node:process'
6
+
7
+ const PACKAGE_NAME = '@lucasvu/scope-ui'
8
+
9
+ function parseArgs(argv) {
10
+ const args = {
11
+ force: false,
12
+ agentsFile: 'AGENTS.md',
13
+ themeFile: 'src/styles/ui-theme.css',
14
+ }
15
+
16
+ for (let index = 0; index < argv.length; index += 1) {
17
+ const arg = argv[index]
18
+ if (arg === '--force') {
19
+ args.force = true
20
+ continue
21
+ }
22
+ if (arg === '--agents-file') {
23
+ args.agentsFile = argv[index + 1] ?? args.agentsFile
24
+ index += 1
25
+ continue
26
+ }
27
+ if (arg === '--theme-file') {
28
+ args.themeFile = argv[index + 1] ?? args.themeFile
29
+ index += 1
30
+ continue
31
+ }
32
+ if (arg === '--help' || arg === '-h') {
33
+ printHelp()
34
+ process.exit(0)
35
+ }
36
+ }
37
+
38
+ return args
39
+ }
40
+
41
+ function printHelp() {
42
+ console.log(`scope-ui-init
43
+
44
+ Create bootstrap files for using ${PACKAGE_NAME} with AI/codegen.
45
+
46
+ Usage:
47
+ npx scope-ui-init
48
+ npx scope-ui-init --force
49
+ npx scope-ui-init --agents-file AGENTS.md --theme-file src/styles/ui-theme.css
50
+
51
+ Options:
52
+ --force overwrite existing files
53
+ --agents-file target path for the generated AGENTS.md file
54
+ --theme-file target path for the generated theme override file
55
+ --help, -h show this help
56
+ `)
57
+ }
58
+
59
+ function createAgentsTemplate({ themeFile }) {
60
+ return `# Agent Rules
61
+
62
+ This repo uses \`${PACKAGE_NAME}\` as the default UI library.
63
+
64
+ Primary references:
65
+ - \`node_modules/${PACKAGE_NAME}/README.md\`
66
+ - \`node_modules/${PACKAGE_NAME}/AI_SETUP.md\`
67
+ - \`${themeFile}\`
68
+
69
+ Always:
70
+ - import \`${PACKAGE_NAME}/styles.css\` once at the app entry
71
+ - import the project theme override file after the package stylesheet
72
+ - use root exports from \`${PACKAGE_NAME}\`
73
+ - read \`uiAiManifest\` before choosing components
74
+ - read \`uiThemeContract\` before changing colors, surfaces, borders, or shadows
75
+ - keep UI token-driven and theme-driven
76
+
77
+ Do not:
78
+ - import from \`MainFe\` unless the task explicitly targets a legacy screen
79
+ - hardcode brand colors inside page components when a theme token already exists
80
+ - create duplicate Button/Input/Select wrappers unless a project-specific behavior is required
81
+
82
+ Component choice:
83
+ - \`Input\` for short text
84
+ - \`Textarea\` for multiline text
85
+ - \`Select\` for small fixed options
86
+ - \`SearchableSelect\` for larger local option sets
87
+ - \`Combobox\` for type-and-pick
88
+ - \`AsyncCombobox\` for remote search
89
+ - \`MultiSelect\` for multi-pick
90
+ - \`Field\` only for custom controls or grouped content
91
+ - \`DataTable\` for tabular records
92
+ `
93
+ }
94
+
95
+ function createThemeTemplate() {
96
+ return `:root {
97
+ /* Override only the tokens you need for the project brand. */
98
+ /* --tw-primary: 221.2 83.2% 53.3%; */
99
+ /* --tw-accent: 199 89% 48%; */
100
+ /* --primary-grad-from: 199 89% 48%; */
101
+ /* --primary-grad-to: 221.2 83.2% 53.3%; */
102
+ /* --surface: rgba(255, 255, 255, 0.92); */
103
+ }
104
+
105
+ .dark {
106
+ /* --tw-primary: 217.2 91.2% 59.8%; */
107
+ /* --tw-accent: 199 89% 48%; */
108
+ /* --surface: rgba(15, 23, 42, 0.78); */
109
+ }
110
+ `
111
+ }
112
+
113
+ function writeFile(targetPath, content, force) {
114
+ const absolutePath = resolve(process.cwd(), targetPath)
115
+ const alreadyExists = existsSync(absolutePath)
116
+
117
+ if (alreadyExists && !force) {
118
+ return { path: targetPath, status: 'skipped' }
119
+ }
120
+
121
+ mkdirSync(dirname(absolutePath), { recursive: true })
122
+ writeFileSync(absolutePath, content, 'utf8')
123
+ return { path: targetPath, status: alreadyExists ? 'overwritten' : 'created' }
124
+ }
125
+
126
+ const options = parseArgs(process.argv.slice(2))
127
+
128
+ const agentsResult = writeFile(
129
+ options.agentsFile,
130
+ createAgentsTemplate({ themeFile: options.themeFile }),
131
+ options.force,
132
+ )
133
+
134
+ const themeResult = writeFile(
135
+ options.themeFile,
136
+ createThemeTemplate(),
137
+ options.force,
138
+ )
139
+
140
+ console.log(`Initialized ${PACKAGE_NAME}`)
141
+ console.log(`- ${agentsResult.path}: ${agentsResult.status}`)
142
+ console.log(`- ${themeResult.path}: ${themeResult.status}`)
143
+ console.log('')
144
+ console.log('Next steps:')
145
+ console.log(`1. Import \`${PACKAGE_NAME}/styles.css\` once at your app entry.`)
146
+ console.log(`2. Import \`${options.themeFile}\` right after the package stylesheet.`)
147
+ console.log(`3. Let your agent read \`${options.agentsFile}\` before generating UI.`)