@lucasvu/scope-ui 0.0.2 → 0.0.4

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,103 @@
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
+ Nếu chưa cài `@lucasvu/scope-ui`, lệnh trên sẽ báo `E404` vì npm sẽ cố tải package tên `scope-ui-init`.
12
+
13
+ CLI này sẽ tạo `AGENTS.md` và `src/styles/ui-theme.css` theo đúng convention bên dưới.
14
+
15
+ ## 1. Import style đúng thứ tự
16
+
17
+ Import package CSS trước, rồi mới import file override theme của project.
18
+
19
+ ```ts
20
+ import '@lucasvu/scope-ui/styles.css'
21
+ import './styles/ui-theme.css'
22
+ ```
23
+
24
+ `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
+
26
+ ## 2. Tạo file theme của project
27
+
28
+ Đặt file tại `src/styles/ui-theme.css`.
29
+
30
+ ```css
31
+ :root {
32
+ --tw-primary: 12 88% 56%;
33
+ --tw-accent: 24 95% 52%;
34
+ --primary-grad-from: 24 95% 52%;
35
+ --primary-grad-to: 12 88% 56%;
36
+ --surface: rgba(255, 248, 240, 0.92);
37
+ }
38
+
39
+ .dark {
40
+ --tw-primary: 18 100% 62%;
41
+ --tw-accent: 35 100% 58%;
42
+ --surface: rgba(24, 24, 27, 0.82);
43
+ }
44
+ ```
45
+
46
+ Không nên override màu trực tiếp trong từng component nếu chỉ đang đổi theme.
47
+
48
+ ## 3. Rule cho AI của project
49
+
50
+ 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:
51
+
52
+ ```md
53
+ Use `@lucasvu/scope-ui` as the default UI library.
54
+
55
+ Always:
56
+ - import `@lucasvu/scope-ui/styles.css` once at the app entry
57
+ - import the project theme override file after the package stylesheet
58
+ - use root exports from `@lucasvu/scope-ui`
59
+ - read `uiAiManifest` to choose the right component by intent
60
+ - read `uiThemeContract` before changing colors, surfaces, borders, or shadows
61
+
62
+ Do not:
63
+ - import from `MainFe` unless the task explicitly targets a legacy screen
64
+ - hardcode brand colors inside page components when a theme token already exists
65
+ - create duplicate Button/Input/Select wrappers unless a project-specific behavior is required
66
+
67
+ Component choice:
68
+ - `Input` for short text
69
+ - `Textarea` for multiline text
70
+ - `Select` for small fixed options
71
+ - `SearchableSelect` for larger local option sets
72
+ - `Combobox` for type-and-pick
73
+ - `AsyncCombobox` for remote search
74
+ - `MultiSelect` for multi-pick
75
+ - `Field` only for custom controls or grouped content
76
+ - `DataTable` for tabular records
77
+ ```
78
+
79
+ ## 4. Runtime contract cho AI/codegen
80
+
81
+ Trong code, AI có thể đọc trực tiếp:
82
+
83
+ ```ts
84
+ import { uiAiManifest, uiThemeContract, uiProjectAiRules } from '@lucasvu/scope-ui'
85
+ ```
86
+
87
+ - `uiAiManifest`: chọn component theo intent
88
+ - `uiThemeContract`: biết override token nào và override ở đâu
89
+ - `uiProjectAiRules`: rule ngắn gọn để inject vào agent của project
90
+
91
+ ## 5. Chỗ override theme
92
+
93
+ Nếu project có app shell:
94
+
95
+ - import `@lucasvu/scope-ui/styles.css` ở app entry
96
+ - import `src/styles/ui-theme.css` ngay sau đó
97
+ - toggle dark mode bằng class `.dark` trên `html` hoặc `body`, hoặc dùng `[data-ui-theme='dark']`
98
+
99
+ Nếu project có nhiều brand/theme:
100
+
101
+ - giữ `:root` là theme mặc định
102
+ - tạo selector như `[data-ui-theme='brand-a']`, `[data-ui-theme='brand-b']`
103
+ - chỉ override token trong file theme, không fork component
package/README.md CHANGED
@@ -2,48 +2,31 @@
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
 
5
- ## Publish lên npm
6
-
7
- Nếu muốn publish public để người khác chỉ cần cài và import:
5
+ ## Cách dùng nhanh
8
6
 
9
- 1. Đổi `name` trong `package.json` sang package name hoặc scope bạn thực sự sở hữu trên npm.
10
- 2. Chạy `npm install` để cài thêm tool build mới nếu máy chưa có lockfile/deps tương ứng.
11
- 3. Build package:
7
+ 1. Cài package:
12
8
 
13
9
  ```bash
14
- npm run build
10
+ npm install @lucasvu/scope-ui
15
11
  ```
16
12
 
17
- 4. Kiểm tra package trước khi publish:
13
+ 2. Import global style một lần ở entry (ví dụ `main.tsx`):
18
14
 
19
- ```bash
20
- npm pack --dry-run
15
+ ```ts
16
+ import '@lucasvu/scope-ui/styles.css';
21
17
  ```
22
18
 
23
- 5. Đăng nhập npm publish:
19
+ 3. Nếu muốn bootstrap rule/theme cho AI trong project consumer:
24
20
 
25
21
  ```bash
26
- npm login
27
- npm publish
22
+ npx scope-ui-init
28
23
  ```
29
24
 
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
-
32
- ## Cách dùng nhanh
33
-
34
- 1) Cài package:
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`.
35
26
 
36
- ```bash
37
- npm install @lucasvu/scope-ui
38
- ```
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.
39
28
 
40
- 2) Import global style một lần ở entry (ví dụ `main.tsx`):
41
-
42
- ```ts
43
- import '@lucasvu/scope-ui/styles.css'
44
- ```
45
-
46
- 3) Dùng component:
29
+ 4. Dùng component:
47
30
 
48
31
  ```tsx
49
32
  import {
@@ -56,7 +39,7 @@ import {
56
39
  Field,
57
40
  Input,
58
41
  Select,
59
- } from '@lucasvu/scope-ui'
42
+ } from '@lucasvu/scope-ui';
60
43
 
61
44
  export function Example() {
62
45
  return (
@@ -89,7 +72,7 @@ export function Example() {
89
72
  </CardContent>
90
73
  <Button block>Tiếp tục</Button>
91
74
  </Card>
92
- )
75
+ );
93
76
  }
94
77
  ```
95
78
 
@@ -120,27 +103,32 @@ Nếu muốn AI render UI đúng và ổn định theo thư viện này, đừng
120
103
 
121
104
  - Import CSS global một lần: `@lucasvu/scope-ui/styles.css`
122
105
  - Import file theme override của project ngay sau package CSS, ví dụ `./styles/ui-theme.css`
106
+ - Chạy `npx scope-ui-init` ở project consumer sau khi đã cài `@lucasvu/scope-ui` để tạo `AGENTS.md` và `src/styles/ui-theme.css`
123
107
  - Chỉ dùng các component canonical ở root package; tránh `MainFe` nếu không phải legacy screen
124
108
  - 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
125
109
  - Cho agent đọc `uiThemeContract` để biết màu/token phải override ở đâu
126
110
 
127
111
  ```ts
128
- import { uiAiManifest, uiProjectAiRules, uiThemeContract } from '@lucasvu/scope-ui'
112
+ import {
113
+ uiAiManifest,
114
+ uiProjectAiRules,
115
+ uiThemeContract,
116
+ } from '@lucasvu/scope-ui';
129
117
  ```
130
118
 
131
119
  `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.
132
120
 
133
121
  `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ỗ.
134
122
 
135
- `AI_SETUP.md` chứa sẵn đoạn rule/prompt để copy vào settings của agent trong project.
123
+ `AI_SETUP.md` CLI `scope-ui-init` giúp bootstrap rule/theme cho repo consumer không cần copy tay.
136
124
 
137
125
  ## Theme Override
138
126
 
139
127
  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`.
140
128
 
141
129
  ```ts
142
- import '@lucasvu/scope-ui/styles.css'
143
- import './styles/ui-theme.css'
130
+ import '@lucasvu/scope-ui/styles.css';
131
+ import './styles/ui-theme.css';
144
132
  ```
145
133
 
146
134
  Ví dụ:
@@ -204,12 +192,7 @@ type NumericInputProps = {
204
192
  import { NumericInput } from '@lucasvu/scope-ui';
205
193
 
206
194
  // 1) Integer qty
207
- <NumericInput
208
- mode="integer"
209
- min={0}
210
- value={qty}
211
- onValueChange={setQty}
212
- />;
195
+ <NumericInput mode="integer" min={0} value={qty} onValueChange={setQty} />;
213
196
 
214
197
  // 2) Money
215
198
  <NumericInput
@@ -237,7 +220,7 @@ import { NumericInput } from '@lucasvu/scope-ui';
237
220
  ## DataTable
238
221
 
239
222
  ```tsx
240
- import type { DataTableColumn, DataTableSortState } from '@lucasvu/scope-ui'
223
+ import type { DataTableColumn, DataTableSortState } from '@lucasvu/scope-ui';
241
224
  ```
242
225
 
243
226
  ### Tối thiểu
@@ -280,13 +263,13 @@ type DataTableProps<T> = {
280
263
 
281
264
  ```tsx
282
265
  type DataTableColumn<T> = {
283
- key: string; // khóa duy nhất của cột
284
- title: ReactNode; // tiêu đề hiển thị ở header
285
- dataIndex?: keyof T; // map dữ liệu (mặc định lấy theo key)
286
- width?: number | string; // độ rộng ưu tiên (vd: 180, '180px', '20%')
266
+ key: string; // khóa duy nhất của cột
267
+ title: ReactNode; // tiêu đề hiển thị ở header
268
+ dataIndex?: keyof T; // map dữ liệu (mặc định lấy theo key)
269
+ width?: number | string; // độ rộng ưu tiên (vd: 180, '180px', '20%')
287
270
  render?: (value, record, index) => ReactNode; // custom cell
288
- sortable?: boolean; // bật/tắt sort cho cột (ưu tiên cao nhất)
289
- sorter?: (a, b) => number; // so sánh asc/desc
271
+ sortable?: boolean; // bật/tắt sort cho cột (ưu tiên cao nhất)
272
+ sorter?: (a, b) => number; // so sánh asc/desc
290
273
  sortValue?: (record) => string | number | Date | null;
291
274
  };
292
275
  ```
@@ -362,7 +345,7 @@ Checkbox chỉ hiện khi truyền `rowSelection`. Không truyền thì sẽ ẩ
362
345
  selectedRowKeys,
363
346
  onChange: setSelectedRowKeys,
364
347
  }}
365
- />;
348
+ />
366
349
  ```
367
350
 
368
351
  ### Pagination + chọn page size
@@ -400,7 +383,7 @@ const [pageSize, setPageSize] = useState(20);
400
383
  rowKey="id"
401
384
  onRowClick={(row) => console.log(row)}
402
385
  renderActions={(row) => <ActionMenu row={row} />}
403
- />;
386
+ />
404
387
  ```
405
388
 
406
389
  ### Loading + empty
@@ -412,7 +395,7 @@ const [pageSize, setPageSize] = useState(20);
412
395
  rowKey="id"
413
396
  loading={isLoading}
414
397
  emptyText="No data"
415
- />;
398
+ />
416
399
  ```
417
400
 
418
401
  ### Sticky header khi scroll
@@ -440,7 +423,7 @@ DataTable tự hỗ trợ scroll ngang khi nội dung vượt chiều rộng (đ
440
423
  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.
441
424
 
442
425
  ```tsx
443
- import { LineClampTooltip } from '@lucasvu/scope-ui'
426
+ import { LineClampTooltip } from '@lucasvu/scope-ui';
444
427
 
445
428
  <LineClampTooltip
446
429
  text={record.apiEndpoint}
@@ -449,7 +432,7 @@ import { LineClampTooltip } from '@lucasvu/scope-ui'
449
432
  side="top"
450
433
  wrap
451
434
  portal
452
- />
435
+ />;
453
436
  ```
454
437
 
455
438
  ## Tuỳ chỉnh theme
@@ -0,0 +1,149 @@
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
+ Run this after @lucasvu/scope-ui has been installed in the consumer project.
49
+
50
+ npx scope-ui-init --force
51
+ npx scope-ui-init --agents-file AGENTS.md --theme-file src/styles/ui-theme.css
52
+
53
+ Options:
54
+ --force overwrite existing files
55
+ --agents-file target path for the generated AGENTS.md file
56
+ --theme-file target path for the generated theme override file
57
+ --help, -h show this help
58
+ `)
59
+ }
60
+
61
+ function createAgentsTemplate({ themeFile }) {
62
+ return `# Agent Rules
63
+
64
+ This repo uses \`${PACKAGE_NAME}\` as the default UI library.
65
+
66
+ Primary references:
67
+ - \`node_modules/${PACKAGE_NAME}/README.md\`
68
+ - \`node_modules/${PACKAGE_NAME}/AI_SETUP.md\`
69
+ - \`${themeFile}\`
70
+
71
+ Always:
72
+ - import \`${PACKAGE_NAME}/styles.css\` once at the app entry
73
+ - import the project theme override file after the package stylesheet
74
+ - use root exports from \`${PACKAGE_NAME}\`
75
+ - read \`uiAiManifest\` before choosing components
76
+ - read \`uiThemeContract\` before changing colors, surfaces, borders, or shadows
77
+ - keep UI token-driven and theme-driven
78
+
79
+ Do not:
80
+ - import from \`MainFe\` unless the task explicitly targets a legacy screen
81
+ - hardcode brand colors inside page components when a theme token already exists
82
+ - create duplicate Button/Input/Select wrappers unless a project-specific behavior is required
83
+
84
+ Component choice:
85
+ - \`Input\` for short text
86
+ - \`Textarea\` for multiline text
87
+ - \`Select\` for small fixed options
88
+ - \`SearchableSelect\` for larger local option sets
89
+ - \`Combobox\` for type-and-pick
90
+ - \`AsyncCombobox\` for remote search
91
+ - \`MultiSelect\` for multi-pick
92
+ - \`Field\` only for custom controls or grouped content
93
+ - \`DataTable\` for tabular records
94
+ `
95
+ }
96
+
97
+ function createThemeTemplate() {
98
+ return `:root {
99
+ /* Override only the tokens you need for the project brand. */
100
+ /* --tw-primary: 221.2 83.2% 53.3%; */
101
+ /* --tw-accent: 199 89% 48%; */
102
+ /* --primary-grad-from: 199 89% 48%; */
103
+ /* --primary-grad-to: 221.2 83.2% 53.3%; */
104
+ /* --surface: rgba(255, 255, 255, 0.92); */
105
+ }
106
+
107
+ .dark {
108
+ /* --tw-primary: 217.2 91.2% 59.8%; */
109
+ /* --tw-accent: 199 89% 48%; */
110
+ /* --surface: rgba(15, 23, 42, 0.78); */
111
+ }
112
+ `
113
+ }
114
+
115
+ function writeFile(targetPath, content, force) {
116
+ const absolutePath = resolve(process.cwd(), targetPath)
117
+ const alreadyExists = existsSync(absolutePath)
118
+
119
+ if (alreadyExists && !force) {
120
+ return { path: targetPath, status: 'skipped' }
121
+ }
122
+
123
+ mkdirSync(dirname(absolutePath), { recursive: true })
124
+ writeFileSync(absolutePath, content, 'utf8')
125
+ return { path: targetPath, status: alreadyExists ? 'overwritten' : 'created' }
126
+ }
127
+
128
+ const options = parseArgs(process.argv.slice(2))
129
+
130
+ const agentsResult = writeFile(
131
+ options.agentsFile,
132
+ createAgentsTemplate({ themeFile: options.themeFile }),
133
+ options.force,
134
+ )
135
+
136
+ const themeResult = writeFile(
137
+ options.themeFile,
138
+ createThemeTemplate(),
139
+ options.force,
140
+ )
141
+
142
+ console.log(`Initialized ${PACKAGE_NAME}`)
143
+ console.log(`- ${agentsResult.path}: ${agentsResult.status}`)
144
+ console.log(`- ${themeResult.path}: ${themeResult.status}`)
145
+ console.log('')
146
+ console.log('Next steps:')
147
+ console.log(`1. Import \`${PACKAGE_NAME}/styles.css\` once at your app entry.`)
148
+ console.log(`2. Import \`${options.themeFile}\` right after the package stylesheet.`)
149
+ console.log(`3. Let your agent read \`${options.agentsFile}\` before generating UI.`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucasvu/scope-ui",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -14,13 +14,19 @@
14
14
  "./styles.css": "./dist/styles.css",
15
15
  "./package.json": "./package.json"
16
16
  },
17
+ "bin": {
18
+ "scope-ui-init": "./bin/scope-ui-init.mjs"
19
+ },
17
20
  "files": [
18
- "dist"
21
+ "dist",
22
+ "bin",
23
+ "AI_SETUP.md"
19
24
  ],
20
25
  "scripts": {
21
26
  "build": "npm run build:js && npm run build:css",
22
27
  "build:js": "tsup",
23
28
  "build:css": "tailwindcss -c ./tailwind.config.js -i ./src/styles.css -o ./dist/styles.css --minify",
29
+ "init-ai": "node ./bin/scope-ui-init.mjs",
24
30
  "prepublishOnly": "npm run build"
25
31
  },
26
32
  "publishConfig": {
@@ -47,4 +53,4 @@
47
53
  "tsup": "^8.5.0",
48
54
  "typescript": "^5.8.3"
49
55
  }
50
- }
56
+ }