@lucasvu/scope-ui 0.0.8 → 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.
package/AI_SETUP.md CHANGED
@@ -17,7 +17,7 @@ npx scope-ui-init --list-themes
17
17
 
18
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
19
 
20
- CLI này sẽ tạo `AGENTS.md` `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`.
20
+ CLI này sẽ tạo `AGENTS.md`, `src/styles/ui-theme.css`, và `src/layout-presets/workspace-admin-v1.ts` theo đúng convention bên dưới. `AGENTS.md` giờ khóa luôn flow hỏi input -> đọc layout preset -> dựng shell -> mới tới page body. Nếu không truyền `--theme`, preset mặc định là `ocean`.
21
21
 
22
22
  ## 1. Import style đúng thứ tự
23
23
 
@@ -43,7 +43,9 @@ npx scope-ui-init --theme graphite
43
43
 
44
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
45
 
46
- `AGENTS.md` sẽ có thêm block brief dạng YAML để agent điền các giá trị như `routeUrl`, `sidebarItems`, `pageTitle`, `filters`, `fields`, `tableColumns` trước khi bắt đầu viết JSX.
46
+ `AGENTS.md` sẽ có thêm shell brief + screen brief dạng YAML để agent điền các giá trị như `workspaceLabel`, `pageTabs`, `searchPlaceholder`, `routeUrl`, `sidebarItems`, `pageTitle`, `filters`, `fields`, `tableColumns` trước khi bắt đầu viết JSX.
47
+
48
+ `src/layout-presets/workspace-admin-v1.ts` là source of truth cho shared shell: dark sidebar, gradient topbar, intro card, optional segmented tabs, toolbar card, rồi mới tới body card/table/form.
47
49
 
48
50
  ```css
49
51
  :root {
@@ -63,6 +65,8 @@ File generate ra sẽ là source of truth cho palette, surface, radius và shado
63
65
 
64
66
  Không nên override màu trực tiếp trong từng component nếu chỉ đang đổi theme.
65
67
 
68
+ `src/layout-presets/workspace-admin-v1.ts` không cần import vào runtime. Đây là layout contract cho agent/codegen đọc để giữ dark sidebar, gradient topbar, intro card, tab row, và toolbar card nhất quán giữa các màn.
69
+
66
70
  ## 3. Rule cho AI của project
67
71
 
68
72
  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:
@@ -75,14 +79,16 @@ Always:
75
79
  - import the project theme override file after the package stylesheet
76
80
  - use root exports from `@lucasvu/scope-ui`
77
81
  - stay inside the approved preset declared by `src/styles/ui-theme.css`
82
+ - read `src/layout-presets/workspace-admin-v1.ts` before changing the app shell or page frame
78
83
  - follow the step-by-step UI build workflow from `AGENTS.md` before writing JSX
79
- - fill the screen brief first: `routeUrl`, `sidebarItems`, `activeSidebarItemId`, `pageTitle`, `actions`, and the page-specific schema
84
+ - fill the shell + screen brief first: `workspaceLabel`, `pageTabs`, `searchPlaceholder`, `routeUrl`, `sidebarItems`, `activeSidebarItemId`, `pageTitle`, `actions`, and the page-specific schema
80
85
  - read `uiAiManifest` to choose the right component by intent
81
86
  - read `uiScreenBlueprint` and `uiThemeContract` before changing layout, colors, surfaces, borders, or shadows
82
87
 
83
88
  Do not:
84
89
  - import from `MainFe` unless the task explicitly targets a legacy screen
85
90
  - invent a second palette outside the preset file
91
+ - replace the shared dark-sidebar + gradient-topbar shell unless the task explicitly asks for a new shell
86
92
  - hardcode brand colors inside page components when a theme token already exists
87
93
  - create duplicate Button/Input/Select wrappers unless a project-specific behavior is required
88
94
 
@@ -107,7 +113,7 @@ import { uiAiManifest, uiScreenBlueprint, uiThemeContract, uiThemePresets, uiPro
107
113
  ```
108
114
 
109
115
  - `uiAiManifest`: chọn component theo intent
110
- - `uiScreenBlueprint`: brief input và khung layout chuẩn cho `list | form | detail | dashboard`
116
+ - `uiScreenBlueprint`: brief input và khung layout chuẩn cho `list | form | detail | dashboard`, gồm cả shell fields như `layoutPreset`, `workspaceLabel`, `pageTabs`, `searchPlaceholder`
111
117
  - `uiThemeContract`: biết override token nào và override ở đâu
112
118
  - `uiThemePresets`: danh sách preset được duyệt để nhiều project dùng chung một visual language
113
119
  - `uiProjectAiRules`: rule ngắn gọn để inject vào agent của project
@@ -118,6 +124,7 @@ Nếu project có app shell:
118
124
 
119
125
  - import `@lucasvu/scope-ui/styles.css` ở app entry
120
126
  - import `src/styles/ui-theme.css` ngay sau đó
127
+ - để agent đọc `src/layout-presets/workspace-admin-v1.ts` trước khi build page shell
121
128
  - toggle dark mode bằng class `.dark` trên `html` hoặc `body`, hoặc dùng `[data-ui-theme='dark']`
122
129
 
123
130
  Nếu project có nhiều brand/theme:
package/README.md CHANGED
@@ -16,7 +16,7 @@ npm install @lucasvu/scope-ui
16
16
  import '@lucasvu/scope-ui/styles.css';
17
17
  ```
18
18
 
19
- 3. Nếu muốn bootstrap rule/theme cho AI trong project consumer:
19
+ 3. Nếu muốn bootstrap rule/theme/layout cho AI trong project consumer:
20
20
 
21
21
  ```bash
22
22
  npm install @lucasvu/scope-ui
@@ -31,7 +31,7 @@ npx scope-ui-init --list-themes
31
31
 
32
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`.
33
33
 
34
- Lệnh này sẽ tạo `AGENTS.md` `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.
34
+ Lệnh này sẽ tạo `AGENTS.md`, `src/styles/ui-theme.css`, và `src/layout-presets/workspace-admin-v1.ts` trong repo hiện tại theo preset đã chọn. `AGENTS.md` giờ có thêm playbook step-by-step để agent hỏi đủ input, đọc layout preset, rồi 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.
35
35
 
36
36
  4. Dùng component:
37
37
 
@@ -112,10 +112,10 @@ Nếu muốn AI render UI đúng và ổn định theo thư viện này, đừng
112
112
  - Import file theme override của project ngay sau package CSS, ví dụ `./styles/ui-theme.css`
113
113
  - Cài package trước khi bootstrap: `npm install @lucasvu/scope-ui`
114
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` `src/styles/ui-theme.css`
115
+ - Chạy `npx scope-ui-init --theme <preset>` ở project consumer sau khi đã cài `@lucasvu/scope-ui` để tạo `AGENTS.md`, `src/styles/ui-theme.css`, và `src/layout-presets/workspace-admin-v1.ts`
116
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ự: điền brief, 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`
117
+ - Để agent đọc luôn playbook trong `AGENTS.md` và `src/layout-presets/workspace-admin-v1.ts` để đi đúng thứ tự: hỏi input, khóa shell, chọn component, dựng layout, rồi mới tinh chỉnh token
118
+ - Buộc agent điền shell + screen brief trước khi code: `workspaceLabel`, `pageTabs`, `searchPlaceholder`, `routeUrl`, `sidebarItems`, `activeSidebarItemId`, `pageTitle`, `actions`, `filters`, `fields`, `tableColumns`
119
119
  - `AGENTS.md` mới sẽ có sẵn block brief dạng YAML để agent hoặc dev điền trực tiếp trước khi code
120
120
  - Chỉ dùng các component canonical ở root package; tránh `MainFe` nếu không phải legacy screen
121
121
  - 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
@@ -133,19 +133,19 @@ import {
133
133
 
134
134
  `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.
135
135
 
136
- `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
+ `uiScreenBlueprint` mô tả input brief bắt buộc cho từng màn hình như `layoutPreset`, `workspaceLabel`, `routeUrl`, `sidebarItems`, `activeSidebarItemId`, `pageTitle`, `pageTabs`, `searchPlaceholder`, `actions`, `filters`, `fields`, `tableColumns` và frame recipe cho `list | form | detail | dashboard`.
137
137
 
138
138
  `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ỗ.
139
139
 
140
140
  `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.
141
141
 
142
- `AI_SETUP.md` và CLI `scope-ui-init` giúp bootstrap rule/theme cho repo consumer mà không cần copy tay.
142
+ `AI_SETUP.md` và CLI `scope-ui-init` giúp bootstrap rule/theme/layout cho repo consumer mà không cần copy tay.
143
143
 
144
144
  ## Theme Override
145
145
 
146
146
  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`.
147
147
 
148
- Cách nhanh nhất là generate file này từ preset:
148
+ Cách nhanh nhất là generate bộ file này từ preset:
149
149
 
150
150
  ```bash
151
151
  npm install @lucasvu/scope-ui
@@ -158,6 +158,8 @@ import '@lucasvu/scope-ui/styles.css';
158
158
  import './styles/ui-theme.css';
159
159
  ```
160
160
 
161
+ `src/layout-presets/workspace-admin-v1.ts` không cần import ở runtime. File này dùng để agent/codegen đọc và giữ shell layout ổn định giữa các màn.
162
+
161
163
  Ví dụ:
162
164
 
163
165
  ```css
@@ -6,6 +6,44 @@ import process from 'node:process'
6
6
 
7
7
  const PACKAGE_NAME = '@lucasvu/scope-ui'
8
8
  const DEFAULT_THEME_PRESET = 'ocean'
9
+ const DEFAULT_LAYOUT_FILE = 'src/layout-presets/workspace-admin-v1.ts'
10
+
11
+ const LAYOUT_PRESET = {
12
+ id: 'workspace-admin-v1',
13
+ label: 'Workspace Admin v1',
14
+ description:
15
+ 'Shared admin shell with a dark sidebar, gradient topbar, soft intro card, optional segmented tabs, and card-based content blocks inspired by the Campaigns screen.',
16
+ recommendedFor: [
17
+ 'Admin workspaces',
18
+ 'Operations dashboards',
19
+ 'CRUD-heavy backoffice screens that should share one shell',
20
+ ],
21
+ shellRecipe: [
22
+ 'Dark left sidebar with icon + label navigation and one active item',
23
+ 'Gradient workspace topbar with workspace label on the left and timezone/user slots on the right',
24
+ 'Large page intro card with breadcrumb, title, and optional subtitle',
25
+ 'Optional segmented tabs row directly under the intro card',
26
+ 'Toolbar card with search/filters on the left and actions on the right',
27
+ 'Main body rendered as one or more content cards instead of a flat page',
28
+ ],
29
+ requiredFields: [
30
+ 'layoutPreset',
31
+ 'workspaceLabel',
32
+ 'pageKind',
33
+ 'routeUrl',
34
+ 'sidebarItems',
35
+ 'activeSidebarItemId',
36
+ 'pageTitle',
37
+ ],
38
+ askBeforeCoding: [
39
+ 'What workspace label should appear in the topbar?',
40
+ 'What timezone or top-right meta label should appear beside the user badge?',
41
+ 'What sidebar items exist and which one is active on this screen?',
42
+ 'Does this screen need segmented tabs? If yes, what are the tab labels and which tab is active?',
43
+ 'What search placeholder, filters, and primary action belong in the toolbar card?',
44
+ 'What columns, fields, or summary blocks should be rendered in the page body?',
45
+ ],
46
+ }
9
47
 
10
48
  const THEME_PRESETS = {
11
49
  ocean: {
@@ -216,6 +254,7 @@ function parseArgs(argv) {
216
254
  force: false,
217
255
  agentsFile: 'AGENTS.md',
218
256
  themeFile: 'src/styles/ui-theme.css',
257
+ layoutFile: DEFAULT_LAYOUT_FILE,
219
258
  theme: DEFAULT_THEME_PRESET,
220
259
  listThemes: false,
221
260
  }
@@ -248,6 +287,16 @@ function parseArgs(argv) {
248
287
  continue
249
288
  }
250
289
 
290
+ if (arg === '--layout-file') {
291
+ const value = argv[index + 1]
292
+ if (!value || value.startsWith('--')) {
293
+ exitWithMissingValue('--layout-file')
294
+ }
295
+ args.layoutFile = value
296
+ index += 1
297
+ continue
298
+ }
299
+
251
300
  if (arg === '--theme') {
252
301
  const value = argv[index + 1]
253
302
  if (!value || value.startsWith('--')) {
@@ -288,12 +337,13 @@ Usage:
288
337
  Optional: inspect approved presets before choosing one.
289
338
 
290
339
  npx scope-ui-init --theme forest --force
291
- npx scope-ui-init --agents-file AGENTS.md --theme-file src/styles/ui-theme.css
340
+ npx scope-ui-init --agents-file AGENTS.md --theme-file src/styles/ui-theme.css --layout-file ${DEFAULT_LAYOUT_FILE}
292
341
 
293
342
  Options:
294
343
  --force overwrite existing files
295
344
  --agents-file target path for the generated AGENTS.md file
296
345
  --theme-file target path for the generated theme override file
346
+ --layout-file target path for the generated layout preset file
297
347
  --theme theme preset id (${Object.keys(THEME_PRESETS).join(', ')})
298
348
  --list-themes show the approved preset list
299
349
  --help, -h show this help
@@ -333,38 +383,72 @@ function renderTokenBlock(tokens) {
333
383
  .join('\n')
334
384
  }
335
385
 
336
- function createAgentsTemplate({ themeFile, themePreset }) {
386
+ function renderMarkdownList(items) {
387
+ return items.map((item) => `- ${item}`).join('\n')
388
+ }
389
+
390
+ function createAgentsTemplate({ themeFile, layoutFile, themePreset, layoutPreset }) {
337
391
  return `# Agent Rules
338
392
 
339
393
  This repo uses \`${PACKAGE_NAME}\` as the default UI library.
340
394
  This repo is locked to the \`${themePreset.label}\` theme preset (\`${themePreset.id}\`).
395
+ This repo uses the \`${layoutPreset.label}\` shell preset (\`${layoutPreset.id}\`) as the shared layout baseline.
341
396
 
342
397
  Primary references:
343
398
  - \`node_modules/${PACKAGE_NAME}/README.md\`
344
399
  - \`node_modules/${PACKAGE_NAME}/AI_SETUP.md\`
345
400
  - \`uiScreenBlueprint\` and \`uiAiManifest\` from \`${PACKAGE_NAME}\`
346
401
  - \`${themeFile}\`
402
+ - \`${layoutFile}\`
347
403
 
348
404
  Theme preset summary:
349
405
  - ${themePreset.description}
350
406
  - Recommended for: ${themePreset.recommendedFor.join(', ')}
351
407
 
408
+ Layout preset summary:
409
+ ${renderMarkdownList(layoutPreset.shellRecipe)}
410
+
352
411
  ## Required Flow
353
412
 
354
413
  When asked to build or edit a screen, follow these steps in order:
355
414
 
356
- 1. Read \`${themeFile}\`, \`node_modules/${PACKAGE_NAME}/README.md\`, \`node_modules/${PACKAGE_NAME}/AI_SETUP.md\`, and the exported \`uiScreenBlueprint\` contract before generating UI.
357
- 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.
415
+ 1. Read \`${themeFile}\`, \`${layoutFile}\`, \`node_modules/${PACKAGE_NAME}/README.md\`, \`node_modules/${PACKAGE_NAME}/AI_SETUP.md\`, and the exported \`uiScreenBlueprint\` contract before generating UI.
416
+ 2. Fill the shell brief and screen brief first. If a required field is missing, stop and ask for that exact value before writing JSX. Only leave TODO placeholders when the user explicitly asks for an incomplete scaffold.
358
417
  3. Verify the app entry imports \`${PACKAGE_NAME}/styles.css\` first and the project theme file right after it.
359
- 4. Identify the page kind (\`list | form | detail | dashboard\`), then map each block to canonical components from \`uiAiManifest\` before writing JSX.
360
- 5. Build the shell in this exact order: \`Sidebar\` -> \`Breadcrumb\` -> \`PageTitle\` -> top actions -> page body cards/grids.
361
- 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 cards/tables.
362
- 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.
363
- 8. Before finishing, run the output checklist in this file.
418
+ 4. Treat \`${layoutFile}\` as the source of truth for the app shell. Keep the dark sidebar, gradient topbar, intro card, and card-based body structure aligned with that preset unless the user explicitly asks for a different shell.
419
+ 5. Identify the page kind (\`list | form | detail | dashboard\`), then map each block to canonical components from \`uiAiManifest\` before writing JSX.
420
+ 6. Build the shell in this exact order: \`Sidebar\` -> workspace topbar -> intro card (\`Breadcrumb\` + \`PageTitle\`) -> optional segmented tabs -> toolbar card -> page body cards/grids.
421
+ 7. For list pages, build the toolbar card first and then the \`DataTable\`. For form pages, keep the same shell and swap the body into section cards and a bottom action row. For detail pages, keep the shell and render summary/detail cards plus related tables. For dashboard pages, keep the shell and render stat cards plus supporting cards/tables.
422
+ 8. If \`${layoutFile}\` says a slot is optional, omit it cleanly when the brief does not request it. Do not invent tabs, workspace labels, filters, or toolbar actions.
423
+ 9. 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.
424
+ 10. Before finishing, run the output checklist in this file.
425
+
426
+ ## Ask For Missing Input
427
+
428
+ If any of these are missing, ask the user directly before writing JSX:
429
+
430
+ ${renderMarkdownList(layoutPreset.askBeforeCoding)}
431
+
432
+ ## Shell Brief
433
+
434
+ Complete this shell brief before writing JSX:
435
+
436
+ \`\`\`yaml
437
+ layoutPreset: ${layoutPreset.id}
438
+ workspaceLabel:
439
+ timezoneLabel:
440
+ userBadgeLabel:
441
+ pageTabs:
442
+ - value:
443
+ label:
444
+ activePageTab:
445
+ searchPlaceholder:
446
+ toolbarNote:
447
+ \`\`\`
364
448
 
365
449
  ## Screen Brief
366
450
 
367
- Complete this brief before writing JSX:
451
+ Complete this screen brief before writing JSX:
368
452
 
369
453
  \`\`\`yaml
370
454
  pageKind: list | form | detail | dashboard
@@ -373,6 +457,7 @@ sidebarItems:
373
457
  - id:
374
458
  title:
375
459
  href:
460
+ icon:
376
461
  activeSidebarItemId:
377
462
  breadcrumbs:
378
463
  - label:
@@ -421,12 +506,18 @@ emptyState:
421
506
  permissions: []
422
507
  \`\`\`
423
508
 
509
+ ## Shell Lock
510
+
511
+ Keep the layout aligned with \`${layoutPreset.id}\`:
512
+
513
+ ${renderMarkdownList(layoutPreset.shellRecipe)}
514
+
424
515
  ## Page Frame Recipes
425
516
 
426
- - \`list\`: \`Sidebar\` -> \`Breadcrumb\` -> \`PageTitle\` -> header actions -> optional stats -> filter \`Card\` -> \`DataTable\` \`Card\`
427
- - \`form\`: \`Sidebar\` -> \`Breadcrumb\` -> \`PageTitle\` -> primary/cancel actions -> sectioned form \`Card\` blocks -> bottom action row
428
- - \`detail\`: \`Sidebar\` -> \`Breadcrumb\` -> \`PageTitle\` -> status/actions -> summary metadata \`Card\` -> detail \`Card\` blocks -> related \`DataTable\`
429
- - \`dashboard\`: \`Sidebar\` -> \`Breadcrumb\` -> \`PageTitle\` -> header actions -> \`Stat\` row -> filters -> insight \`Card\` blocks and \`DataTable\`
517
+ - \`list\`: dark \`Sidebar\` -> workspace topbar -> intro card -> optional segmented tabs -> toolbar \`Card\` -> \`DataTable\` \`Card\`
518
+ - \`form\`: dark \`Sidebar\` -> workspace topbar -> intro card -> optional segmented tabs -> top actions -> sectioned form \`Card\` blocks -> bottom action row
519
+ - \`detail\`: dark \`Sidebar\` -> workspace topbar -> intro card -> optional segmented tabs -> status/actions -> summary metadata \`Card\` -> detail \`Card\` blocks -> related \`DataTable\`
520
+ - \`dashboard\`: dark \`Sidebar\` -> workspace topbar -> intro card -> optional segmented tabs -> toolbar \`Card\` -> \`Stat\` row -> insight \`Card\` blocks and \`DataTable\`
430
521
 
431
522
  ## Hard Rules
432
523
 
@@ -434,6 +525,7 @@ permissions: []
434
525
  - import the project theme override file after the package stylesheet
435
526
  - use root exports from \`${PACKAGE_NAME}\`
436
527
  - use \`${themeFile}\` as the only source of truth for colors, radius, surfaces, and shadows
528
+ - use \`${layoutFile}\` as the source of truth for the shell layout and shared page frame
437
529
  - keep layouts and component choices aligned with the shared preset-driven UI used across projects
438
530
  - read \`uiAiManifest\` before choosing components
439
531
  - read \`uiThemeContract\` before changing colors, surfaces, borders, radius, or shadows
@@ -442,6 +534,7 @@ permissions: []
442
534
  Do not:
443
535
  - import from \`MainFe\` unless the task explicitly targets a legacy screen
444
536
  - invent a second palette or one-off brand colors outside \`${themeFile}\`
537
+ - replace the shared dark-sidebar + gradient-topbar shell with a flat blank layout unless the user explicitly requests it
445
538
  - restyle individual pages if the preset tokens can solve it globally
446
539
  - create duplicate Button/Input/Select wrappers unless a project-specific behavior is required
447
540
 
@@ -461,13 +554,15 @@ Do not:
461
554
 
462
555
  Before finishing, confirm all of these are true:
463
556
 
464
- - The screen brief is complete or unresolved values are clearly marked as TODO.
465
- - The shell contains \`Sidebar\`, \`Breadcrumb\`, and \`PageTitle\` in the correct order.
557
+ - The shell brief and screen brief are complete.
558
+ - Any missing required input was explicitly asked for before coding.
559
+ - The shell matches \`${layoutPreset.id}\` with a dark sidebar, gradient topbar, intro card, and card-based body structure.
466
560
  - The chosen components match the page kind and the brief.
467
561
  - No \`MainFe\` imports were used.
468
562
  - No hardcoded brand colors were added in page components.
469
563
  - Theme decisions live in \`${themeFile}\`, not in ad hoc JSX styling.
470
564
  - Spacing, card structure, and actions are consistent with the shared preset.
565
+ - The layout shell stayed stable even if the page body changed.
471
566
  `
472
567
  }
473
568
 
@@ -487,6 +582,90 @@ ${renderTokenBlock(themePreset.tokens.dark)}
487
582
  `
488
583
  }
489
584
 
585
+ function createLayoutTemplate({ themeFile, layoutPreset }) {
586
+ return `/* Generated by scope-ui-init. */
587
+ /* Layout preset: ${layoutPreset.label} (${layoutPreset.id}) */
588
+ /* Use this file as the shared app-shell source of truth for AI/codegen. */
589
+
590
+ export const uiLayoutPreset = {
591
+ id: '${layoutPreset.id}',
592
+ label: '${layoutPreset.label}',
593
+ description:
594
+ '${layoutPreset.description}',
595
+ themeSource: '${themeFile}',
596
+ recommendedFor: [
597
+ ${layoutPreset.recommendedFor.map((item) => ` '${item}',`).join('\n')}
598
+ ],
599
+ shellRecipe: [
600
+ ${layoutPreset.shellRecipe.map((item) => ` '${item}',`).join('\n')}
601
+ ],
602
+ requiredFields: [
603
+ ${layoutPreset.requiredFields.map((item) => ` '${item}',`).join('\n')}
604
+ ],
605
+ askBeforeCoding: [
606
+ ${layoutPreset.askBeforeCoding.map((item) => ` '${item}',`).join('\n')}
607
+ ],
608
+ shell: {
609
+ sidebar: {
610
+ tone: 'dark',
611
+ width: '240px',
612
+ content: ['workspace badge', 'icon + label navigation', 'active state'],
613
+ },
614
+ topbar: {
615
+ background: 'theme gradient from --primary-grad-from to --primary-grad-to',
616
+ leftSlot: 'workspaceLabel',
617
+ rightSlots: ['timezoneLabel', 'userBadgeLabel'],
618
+ },
619
+ introCard: {
620
+ content: ['breadcrumbs', 'pageTitle', 'pageSubtitle'],
621
+ surface: 'soft elevated card',
622
+ },
623
+ pageTabs: {
624
+ optional: true,
625
+ style: 'segmented pills',
626
+ fields: ['pageTabs', 'activePageTab'],
627
+ },
628
+ toolbarCard: {
629
+ optional: true,
630
+ leftSlots: ['searchPlaceholder', 'filters'],
631
+ rightSlots: ['secondaryActions', 'primaryAction'],
632
+ },
633
+ },
634
+ pageBodies: {
635
+ list: ['toolbar card', 'DataTable card', 'rowActions', 'emptyState'],
636
+ form: ['sectioned form cards', 'fields', 'bottom action row'],
637
+ detail: ['summary metadata card', 'detailSections', 'related tables'],
638
+ dashboard: ['summaryStats', 'filters', 'insight cards', 'DataTable'],
639
+ },
640
+ hardRules: [
641
+ 'Keep the dark sidebar and gradient topbar shell stable across screens.',
642
+ 'Do not replace the intro card with a bare page title row.',
643
+ 'Use the shared theme tokens from the themeSource file for color, border, surface, and shadow decisions.',
644
+ 'Only omit tabs or toolbar sections when the brief explicitly leaves them out.',
645
+ 'Ask for missing required fields before generating JSX instead of guessing from the screenshot.',
646
+ ],
647
+ exampleBrief: {
648
+ layoutPreset: '${layoutPreset.id}',
649
+ workspaceLabel: 'Workspace',
650
+ timezoneLabel: 'UTC+07:00',
651
+ userBadgeLabel: 'L',
652
+ pageKind: 'list',
653
+ routeUrl: '/campaigns/new',
654
+ activeSidebarItemId: 'campaigns',
655
+ pageTabs: [
656
+ { value: 'overview', label: 'Overview' },
657
+ { value: 'coupons', label: 'Coupons' },
658
+ { value: 'redemption', label: 'Redemption' },
659
+ ],
660
+ activePageTab: 'coupons',
661
+ searchPlaceholder: 'Search by coupon code',
662
+ },
663
+ } as const
664
+
665
+ export type UiLayoutPreset = typeof uiLayoutPreset
666
+ `
667
+ }
668
+
490
669
  function writeFile(targetPath, content, force) {
491
670
  const absolutePath = resolve(process.cwd(), targetPath)
492
671
  const alreadyExists = existsSync(absolutePath)
@@ -511,7 +690,12 @@ const themePreset = resolveThemePreset(options.theme)
511
690
 
512
691
  const agentsResult = writeFile(
513
692
  options.agentsFile,
514
- createAgentsTemplate({ themeFile: options.themeFile, themePreset }),
693
+ createAgentsTemplate({
694
+ themeFile: options.themeFile,
695
+ layoutFile: options.layoutFile,
696
+ themePreset,
697
+ layoutPreset: LAYOUT_PRESET,
698
+ }),
515
699
  options.force,
516
700
  )
517
701
 
@@ -521,13 +705,21 @@ const themeResult = writeFile(
521
705
  options.force,
522
706
  )
523
707
 
708
+ const layoutResult = writeFile(
709
+ options.layoutFile,
710
+ createLayoutTemplate({ themeFile: options.themeFile, layoutPreset: LAYOUT_PRESET }),
711
+ options.force,
712
+ )
713
+
524
714
  console.log(`Initialized ${PACKAGE_NAME}`)
525
715
  console.log(`- theme preset: ${themePreset.label} (${themePreset.id})`)
716
+ console.log(`- layout preset: ${LAYOUT_PRESET.label} (${LAYOUT_PRESET.id})`)
526
717
  console.log(`- ${agentsResult.path}: ${agentsResult.status}`)
527
718
  console.log(`- ${themeResult.path}: ${themeResult.status}`)
719
+ console.log(`- ${layoutResult.path}: ${layoutResult.status}`)
528
720
  console.log('')
529
721
  console.log('Next steps:')
530
722
  console.log(`1. Import \`${PACKAGE_NAME}/styles.css\` once at your app entry.`)
531
723
  console.log(`2. Import \`${options.themeFile}\` right after the package stylesheet.`)
532
- console.log(`3. Let your agent read \`${options.agentsFile}\` before generating UI.`)
724
+ console.log(`3. Let your agent read \`${options.agentsFile}\` and \`${options.layoutFile}\` before generating UI.`)
533
725
  console.log(`4. Re-run this command with \`--theme <preset> --force\` if you want another approved preset.`)
package/dist/index.cjs CHANGED
@@ -6647,7 +6647,8 @@ var uiAiManifest = {
6647
6647
  rules: [
6648
6648
  "Import the stylesheet once at the app entry before rendering any component.",
6649
6649
  "If the project declares an approved theme preset in AGENTS.md or ui-theme.css, stay inside that preset and do not invent a second palette.",
6650
- "Collect the screen brief first: route url, sidebar items, active sidebar item, page title, actions, and the page-specific content schema.",
6650
+ "Collect the shell + screen brief first: layout preset, workspace label, route url, sidebar items, active sidebar item, page title, tabs/actions, and the page-specific content schema.",
6651
+ "If the project ships a generated layout preset file, keep the shell aligned with it instead of inventing a new page frame.",
6651
6652
  "Prefer the canonical component for each intent instead of mixing legacy MainFe components.",
6652
6653
  "Use Input/Textarea/Select label props directly for simple fields; use Field only to wrap custom controls or grouped content.",
6653
6654
  "Use Select for small fixed option lists, SearchableSelect for larger local lists, Combobox for type-and-pick flows, and AsyncCombobox for remote search.",
@@ -6865,6 +6866,30 @@ var uiAiManifest = {
6865
6866
 
6866
6867
  // src/screen-blueprint.ts
6867
6868
  var uiScreenBriefFields = [
6869
+ {
6870
+ id: "layoutPreset",
6871
+ label: "Layout preset",
6872
+ description: "Shared shell preset id that locks the sidebar/topbar/page-card structure.",
6873
+ required: false,
6874
+ appliesTo: ["all"],
6875
+ example: "workspace-admin-v1"
6876
+ },
6877
+ {
6878
+ id: "workspaceLabel",
6879
+ label: "Workspace label",
6880
+ description: "Short label shown in the sidebar or workspace topbar.",
6881
+ required: false,
6882
+ appliesTo: ["all"],
6883
+ example: "Workspace"
6884
+ },
6885
+ {
6886
+ id: "timezoneLabel",
6887
+ label: "Timezone/meta label",
6888
+ description: "Short meta label shown in the top-right shell area, such as a timezone or environment.",
6889
+ required: false,
6890
+ appliesTo: ["all"],
6891
+ example: "UTC+07:00"
6892
+ },
6868
6893
  {
6869
6894
  id: "pageKind",
6870
6895
  label: "Page kind",
@@ -6921,6 +6946,22 @@ var uiScreenBriefFields = [
6921
6946
  appliesTo: ["all"],
6922
6947
  example: "Manage internal user accounts and permissions."
6923
6948
  },
6949
+ {
6950
+ id: "pageTabs",
6951
+ label: "Page tabs",
6952
+ description: "Optional segmented tabs rendered under the intro card.",
6953
+ required: false,
6954
+ appliesTo: ["all"],
6955
+ example: '[{ value: "overview", label: "Overview" }, { value: "coupons", label: "Coupons" }]'
6956
+ },
6957
+ {
6958
+ id: "activePageTab",
6959
+ label: "Active page tab",
6960
+ description: "The selected segmented tab value when tabs are present.",
6961
+ required: false,
6962
+ appliesTo: ["all"],
6963
+ example: "coupons"
6964
+ },
6924
6965
  {
6925
6966
  id: "primaryAction",
6926
6967
  label: "Primary action",
@@ -6953,6 +6994,14 @@ var uiScreenBriefFields = [
6953
6994
  appliesTo: ["list", "dashboard"],
6954
6995
  example: '[{ type: "search", label: "Search user" }, { type: "select", label: "Role" }]'
6955
6996
  },
6997
+ {
6998
+ id: "searchPlaceholder",
6999
+ label: "Search placeholder",
7000
+ description: "Placeholder text for the main toolbar search input when the shell includes one.",
7001
+ required: false,
7002
+ appliesTo: ["list", "dashboard"],
7003
+ example: "Search by coupon code"
7004
+ },
6956
7005
  {
6957
7006
  id: "tableColumns",
6958
7007
  label: "Table columns",
@@ -7017,10 +7066,11 @@ var uiScreenBlueprint = {
7017
7066
  label: "List page",
7018
7067
  frame: [
7019
7068
  "Sidebar shell",
7020
- "Breadcrumb",
7021
- "PageTitle with actions",
7069
+ "Workspace topbar",
7070
+ "Page intro card with Breadcrumb and PageTitle",
7071
+ "Optional segmented tabs",
7072
+ "Toolbar card",
7022
7073
  "Optional stats",
7023
- "Filter card",
7024
7074
  "DataTable card"
7025
7075
  ]
7026
7076
  },
@@ -7029,8 +7079,10 @@ var uiScreenBlueprint = {
7029
7079
  label: "Form page",
7030
7080
  frame: [
7031
7081
  "Sidebar shell",
7032
- "Breadcrumb",
7033
- "PageTitle with primary and cancel actions",
7082
+ "Workspace topbar",
7083
+ "Page intro card with Breadcrumb and PageTitle",
7084
+ "Optional segmented tabs",
7085
+ "Header actions",
7034
7086
  "Sectioned form cards",
7035
7087
  "Bottom action row"
7036
7088
  ]
@@ -7040,7 +7092,9 @@ var uiScreenBlueprint = {
7040
7092
  label: "Detail page",
7041
7093
  frame: [
7042
7094
  "Sidebar shell",
7043
- "Breadcrumb",
7095
+ "Workspace topbar",
7096
+ "Page intro card with Breadcrumb and PageTitle",
7097
+ "Optional segmented tabs",
7044
7098
  "PageTitle with status/actions",
7045
7099
  "Summary stats or metadata card",
7046
7100
  "Detail cards and related tables"
@@ -7051,17 +7105,18 @@ var uiScreenBlueprint = {
7051
7105
  label: "Dashboard page",
7052
7106
  frame: [
7053
7107
  "Sidebar shell",
7054
- "Breadcrumb",
7055
- "PageTitle with actions",
7108
+ "Workspace topbar",
7109
+ "Page intro card with Breadcrumb and PageTitle",
7110
+ "Optional segmented tabs",
7111
+ "Toolbar card with actions",
7056
7112
  "Stat cards row",
7057
- "Filters",
7058
7113
  "Main insight cards and tables"
7059
7114
  ]
7060
7115
  }
7061
7116
  ],
7062
7117
  workflow: [
7063
- "Collect the screen brief before coding. Do not invent sidebar items, route urls, filters, fields, or table columns if the brief is missing.",
7064
- "Use Sidebar plus Breadcrumb and PageTitle to lock the shell before building the page body.",
7118
+ "Collect the shell + screen brief before coding. Do not invent layout presets, workspace labels, sidebar items, route urls, tabs, filters, fields, or table columns if the brief is missing.",
7119
+ "Use Sidebar plus the workspace topbar and intro card to lock the shell before building the page body.",
7065
7120
  "Choose only canonical root exports from @lucasvu/scope-ui.",
7066
7121
  "Use Card and ui-grid utilities to create the frame first, then place controls and data components inside.",
7067
7122
  "Keep all palette, radius, surface, and shadow decisions in the shared ui-theme.css preset file.",
package/dist/index.d.cts CHANGED
@@ -875,7 +875,7 @@ type UiAiComponentDescriptor = {
875
875
  declare const uiAiManifest: {
876
876
  readonly packageName: "@lucasvu/scope-ui";
877
877
  readonly styleImport: "@lucasvu/scope-ui/styles.css";
878
- readonly rules: readonly ["Import the stylesheet once at the app entry before rendering any component.", "If the project declares an approved theme preset in AGENTS.md or ui-theme.css, stay inside that preset and do not invent a second palette.", "Collect the screen brief first: route url, sidebar items, active sidebar item, page title, actions, and the page-specific content schema.", "Prefer the canonical component for each intent instead of mixing legacy MainFe components.", "Use Input/Textarea/Select label props directly for simple fields; use Field only to wrap custom controls or grouped content.", "Use Select for small fixed option lists, SearchableSelect for larger local lists, Combobox for type-and-pick flows, and AsyncCombobox for remote search.", "Always provide a stable rowKey to DataTable and use sortMode=\"server\" when sorting happens on the backend.", "Prefer Card as the outer layout section and keep alerts, stats, and tables inside CardContent when building dashboards or forms.", "Do not import MainFe components unless the target explicitly asks for legacy main-fe styling."];
878
+ readonly rules: readonly ["Import the stylesheet once at the app entry before rendering any component.", "If the project declares an approved theme preset in AGENTS.md or ui-theme.css, stay inside that preset and do not invent a second palette.", "Collect the shell + screen brief first: layout preset, workspace label, route url, sidebar items, active sidebar item, page title, tabs/actions, and the page-specific content schema.", "If the project ships a generated layout preset file, keep the shell aligned with it instead of inventing a new page frame.", "Prefer the canonical component for each intent instead of mixing legacy MainFe components.", "Use Input/Textarea/Select label props directly for simple fields; use Field only to wrap custom controls or grouped content.", "Use Select for small fixed option lists, SearchableSelect for larger local lists, Combobox for type-and-pick flows, and AsyncCombobox for remote search.", "Always provide a stable rowKey to DataTable and use sortMode=\"server\" when sorting happens on the backend.", "Prefer Card as the outer layout section and keep alerts, stats, and tables inside CardContent when building dashboards or forms.", "Do not import MainFe components unless the target explicitly asks for legacy main-fe styling."];
879
879
  readonly components: ({
880
880
  name: string;
881
881
  importName: string;
@@ -921,6 +921,27 @@ type UiScreenBriefField = {
921
921
  example: string;
922
922
  };
923
923
  declare const uiScreenBriefFields: readonly [{
924
+ readonly id: "layoutPreset";
925
+ readonly label: "Layout preset";
926
+ readonly description: "Shared shell preset id that locks the sidebar/topbar/page-card structure.";
927
+ readonly required: false;
928
+ readonly appliesTo: ["all"];
929
+ readonly example: "workspace-admin-v1";
930
+ }, {
931
+ readonly id: "workspaceLabel";
932
+ readonly label: "Workspace label";
933
+ readonly description: "Short label shown in the sidebar or workspace topbar.";
934
+ readonly required: false;
935
+ readonly appliesTo: ["all"];
936
+ readonly example: "Workspace";
937
+ }, {
938
+ readonly id: "timezoneLabel";
939
+ readonly label: "Timezone/meta label";
940
+ readonly description: "Short meta label shown in the top-right shell area, such as a timezone or environment.";
941
+ readonly required: false;
942
+ readonly appliesTo: ["all"];
943
+ readonly example: "UTC+07:00";
944
+ }, {
924
945
  readonly id: "pageKind";
925
946
  readonly label: "Page kind";
926
947
  readonly description: "Choose the base recipe for the screen.";
@@ -969,6 +990,20 @@ declare const uiScreenBriefFields: readonly [{
969
990
  readonly required: false;
970
991
  readonly appliesTo: ["all"];
971
992
  readonly example: "Manage internal user accounts and permissions.";
993
+ }, {
994
+ readonly id: "pageTabs";
995
+ readonly label: "Page tabs";
996
+ readonly description: "Optional segmented tabs rendered under the intro card.";
997
+ readonly required: false;
998
+ readonly appliesTo: ["all"];
999
+ readonly example: "[{ value: \"overview\", label: \"Overview\" }, { value: \"coupons\", label: \"Coupons\" }]";
1000
+ }, {
1001
+ readonly id: "activePageTab";
1002
+ readonly label: "Active page tab";
1003
+ readonly description: "The selected segmented tab value when tabs are present.";
1004
+ readonly required: false;
1005
+ readonly appliesTo: ["all"];
1006
+ readonly example: "coupons";
972
1007
  }, {
973
1008
  readonly id: "primaryAction";
974
1009
  readonly label: "Primary action";
@@ -997,6 +1032,13 @@ declare const uiScreenBriefFields: readonly [{
997
1032
  readonly required: false;
998
1033
  readonly appliesTo: ["list", "dashboard"];
999
1034
  readonly example: "[{ type: \"search\", label: \"Search user\" }, { type: \"select\", label: \"Role\" }]";
1035
+ }, {
1036
+ readonly id: "searchPlaceholder";
1037
+ readonly label: "Search placeholder";
1038
+ readonly description: "Placeholder text for the main toolbar search input when the shell includes one.";
1039
+ readonly required: false;
1040
+ readonly appliesTo: ["list", "dashboard"];
1041
+ readonly example: "Search by coupon code";
1000
1042
  }, {
1001
1043
  readonly id: "tableColumns";
1002
1044
  readonly label: "Table columns";
@@ -1051,22 +1093,43 @@ declare const uiScreenBlueprint: {
1051
1093
  readonly pageKinds: readonly [{
1052
1094
  readonly id: "list";
1053
1095
  readonly label: "List page";
1054
- readonly frame: readonly ["Sidebar shell", "Breadcrumb", "PageTitle with actions", "Optional stats", "Filter card", "DataTable card"];
1096
+ readonly frame: readonly ["Sidebar shell", "Workspace topbar", "Page intro card with Breadcrumb and PageTitle", "Optional segmented tabs", "Toolbar card", "Optional stats", "DataTable card"];
1055
1097
  }, {
1056
1098
  readonly id: "form";
1057
1099
  readonly label: "Form page";
1058
- readonly frame: readonly ["Sidebar shell", "Breadcrumb", "PageTitle with primary and cancel actions", "Sectioned form cards", "Bottom action row"];
1100
+ readonly frame: readonly ["Sidebar shell", "Workspace topbar", "Page intro card with Breadcrumb and PageTitle", "Optional segmented tabs", "Header actions", "Sectioned form cards", "Bottom action row"];
1059
1101
  }, {
1060
1102
  readonly id: "detail";
1061
1103
  readonly label: "Detail page";
1062
- readonly frame: readonly ["Sidebar shell", "Breadcrumb", "PageTitle with status/actions", "Summary stats or metadata card", "Detail cards and related tables"];
1104
+ readonly frame: readonly ["Sidebar shell", "Workspace topbar", "Page intro card with Breadcrumb and PageTitle", "Optional segmented tabs", "PageTitle with status/actions", "Summary stats or metadata card", "Detail cards and related tables"];
1063
1105
  }, {
1064
1106
  readonly id: "dashboard";
1065
1107
  readonly label: "Dashboard page";
1066
- readonly frame: readonly ["Sidebar shell", "Breadcrumb", "PageTitle with actions", "Stat cards row", "Filters", "Main insight cards and tables"];
1108
+ readonly frame: readonly ["Sidebar shell", "Workspace topbar", "Page intro card with Breadcrumb and PageTitle", "Optional segmented tabs", "Toolbar card with actions", "Stat cards row", "Main insight cards and tables"];
1067
1109
  }];
1068
- readonly workflow: readonly ["Collect the screen brief before coding. Do not invent sidebar items, route urls, filters, fields, or table columns if the brief is missing.", "Use Sidebar plus Breadcrumb and PageTitle to lock the shell before building the page body.", "Choose only canonical root exports from @lucasvu/scope-ui.", "Use Card and ui-grid utilities to create the frame first, then place controls and data components inside.", "Keep all palette, radius, surface, and shadow decisions in the shared ui-theme.css preset file.", "End with a consistency check against the selected preset and the screen brief."];
1110
+ readonly workflow: readonly ["Collect the shell + screen brief before coding. Do not invent layout presets, workspace labels, sidebar items, route urls, tabs, filters, fields, or table columns if the brief is missing.", "Use Sidebar plus the workspace topbar and intro card to lock the shell before building the page body.", "Choose only canonical root exports from @lucasvu/scope-ui.", "Use Card and ui-grid utilities to create the frame first, then place controls and data components inside.", "Keep all palette, radius, surface, and shadow decisions in the shared ui-theme.css preset file.", "End with a consistency check against the selected preset and the screen brief."];
1069
1111
  readonly briefFields: readonly [{
1112
+ readonly id: "layoutPreset";
1113
+ readonly label: "Layout preset";
1114
+ readonly description: "Shared shell preset id that locks the sidebar/topbar/page-card structure.";
1115
+ readonly required: false;
1116
+ readonly appliesTo: ["all"];
1117
+ readonly example: "workspace-admin-v1";
1118
+ }, {
1119
+ readonly id: "workspaceLabel";
1120
+ readonly label: "Workspace label";
1121
+ readonly description: "Short label shown in the sidebar or workspace topbar.";
1122
+ readonly required: false;
1123
+ readonly appliesTo: ["all"];
1124
+ readonly example: "Workspace";
1125
+ }, {
1126
+ readonly id: "timezoneLabel";
1127
+ readonly label: "Timezone/meta label";
1128
+ readonly description: "Short meta label shown in the top-right shell area, such as a timezone or environment.";
1129
+ readonly required: false;
1130
+ readonly appliesTo: ["all"];
1131
+ readonly example: "UTC+07:00";
1132
+ }, {
1070
1133
  readonly id: "pageKind";
1071
1134
  readonly label: "Page kind";
1072
1135
  readonly description: "Choose the base recipe for the screen.";
@@ -1115,6 +1178,20 @@ declare const uiScreenBlueprint: {
1115
1178
  readonly required: false;
1116
1179
  readonly appliesTo: ["all"];
1117
1180
  readonly example: "Manage internal user accounts and permissions.";
1181
+ }, {
1182
+ readonly id: "pageTabs";
1183
+ readonly label: "Page tabs";
1184
+ readonly description: "Optional segmented tabs rendered under the intro card.";
1185
+ readonly required: false;
1186
+ readonly appliesTo: ["all"];
1187
+ readonly example: "[{ value: \"overview\", label: \"Overview\" }, { value: \"coupons\", label: \"Coupons\" }]";
1188
+ }, {
1189
+ readonly id: "activePageTab";
1190
+ readonly label: "Active page tab";
1191
+ readonly description: "The selected segmented tab value when tabs are present.";
1192
+ readonly required: false;
1193
+ readonly appliesTo: ["all"];
1194
+ readonly example: "coupons";
1118
1195
  }, {
1119
1196
  readonly id: "primaryAction";
1120
1197
  readonly label: "Primary action";
@@ -1143,6 +1220,13 @@ declare const uiScreenBlueprint: {
1143
1220
  readonly required: false;
1144
1221
  readonly appliesTo: ["list", "dashboard"];
1145
1222
  readonly example: "[{ type: \"search\", label: \"Search user\" }, { type: \"select\", label: \"Role\" }]";
1223
+ }, {
1224
+ readonly id: "searchPlaceholder";
1225
+ readonly label: "Search placeholder";
1226
+ readonly description: "Placeholder text for the main toolbar search input when the shell includes one.";
1227
+ readonly required: false;
1228
+ readonly appliesTo: ["list", "dashboard"];
1229
+ readonly example: "Search by coupon code";
1146
1230
  }, {
1147
1231
  readonly id: "tableColumns";
1148
1232
  readonly label: "Table columns";
package/dist/index.d.ts CHANGED
@@ -875,7 +875,7 @@ type UiAiComponentDescriptor = {
875
875
  declare const uiAiManifest: {
876
876
  readonly packageName: "@lucasvu/scope-ui";
877
877
  readonly styleImport: "@lucasvu/scope-ui/styles.css";
878
- readonly rules: readonly ["Import the stylesheet once at the app entry before rendering any component.", "If the project declares an approved theme preset in AGENTS.md or ui-theme.css, stay inside that preset and do not invent a second palette.", "Collect the screen brief first: route url, sidebar items, active sidebar item, page title, actions, and the page-specific content schema.", "Prefer the canonical component for each intent instead of mixing legacy MainFe components.", "Use Input/Textarea/Select label props directly for simple fields; use Field only to wrap custom controls or grouped content.", "Use Select for small fixed option lists, SearchableSelect for larger local lists, Combobox for type-and-pick flows, and AsyncCombobox for remote search.", "Always provide a stable rowKey to DataTable and use sortMode=\"server\" when sorting happens on the backend.", "Prefer Card as the outer layout section and keep alerts, stats, and tables inside CardContent when building dashboards or forms.", "Do not import MainFe components unless the target explicitly asks for legacy main-fe styling."];
878
+ readonly rules: readonly ["Import the stylesheet once at the app entry before rendering any component.", "If the project declares an approved theme preset in AGENTS.md or ui-theme.css, stay inside that preset and do not invent a second palette.", "Collect the shell + screen brief first: layout preset, workspace label, route url, sidebar items, active sidebar item, page title, tabs/actions, and the page-specific content schema.", "If the project ships a generated layout preset file, keep the shell aligned with it instead of inventing a new page frame.", "Prefer the canonical component for each intent instead of mixing legacy MainFe components.", "Use Input/Textarea/Select label props directly for simple fields; use Field only to wrap custom controls or grouped content.", "Use Select for small fixed option lists, SearchableSelect for larger local lists, Combobox for type-and-pick flows, and AsyncCombobox for remote search.", "Always provide a stable rowKey to DataTable and use sortMode=\"server\" when sorting happens on the backend.", "Prefer Card as the outer layout section and keep alerts, stats, and tables inside CardContent when building dashboards or forms.", "Do not import MainFe components unless the target explicitly asks for legacy main-fe styling."];
879
879
  readonly components: ({
880
880
  name: string;
881
881
  importName: string;
@@ -921,6 +921,27 @@ type UiScreenBriefField = {
921
921
  example: string;
922
922
  };
923
923
  declare const uiScreenBriefFields: readonly [{
924
+ readonly id: "layoutPreset";
925
+ readonly label: "Layout preset";
926
+ readonly description: "Shared shell preset id that locks the sidebar/topbar/page-card structure.";
927
+ readonly required: false;
928
+ readonly appliesTo: ["all"];
929
+ readonly example: "workspace-admin-v1";
930
+ }, {
931
+ readonly id: "workspaceLabel";
932
+ readonly label: "Workspace label";
933
+ readonly description: "Short label shown in the sidebar or workspace topbar.";
934
+ readonly required: false;
935
+ readonly appliesTo: ["all"];
936
+ readonly example: "Workspace";
937
+ }, {
938
+ readonly id: "timezoneLabel";
939
+ readonly label: "Timezone/meta label";
940
+ readonly description: "Short meta label shown in the top-right shell area, such as a timezone or environment.";
941
+ readonly required: false;
942
+ readonly appliesTo: ["all"];
943
+ readonly example: "UTC+07:00";
944
+ }, {
924
945
  readonly id: "pageKind";
925
946
  readonly label: "Page kind";
926
947
  readonly description: "Choose the base recipe for the screen.";
@@ -969,6 +990,20 @@ declare const uiScreenBriefFields: readonly [{
969
990
  readonly required: false;
970
991
  readonly appliesTo: ["all"];
971
992
  readonly example: "Manage internal user accounts and permissions.";
993
+ }, {
994
+ readonly id: "pageTabs";
995
+ readonly label: "Page tabs";
996
+ readonly description: "Optional segmented tabs rendered under the intro card.";
997
+ readonly required: false;
998
+ readonly appliesTo: ["all"];
999
+ readonly example: "[{ value: \"overview\", label: \"Overview\" }, { value: \"coupons\", label: \"Coupons\" }]";
1000
+ }, {
1001
+ readonly id: "activePageTab";
1002
+ readonly label: "Active page tab";
1003
+ readonly description: "The selected segmented tab value when tabs are present.";
1004
+ readonly required: false;
1005
+ readonly appliesTo: ["all"];
1006
+ readonly example: "coupons";
972
1007
  }, {
973
1008
  readonly id: "primaryAction";
974
1009
  readonly label: "Primary action";
@@ -997,6 +1032,13 @@ declare const uiScreenBriefFields: readonly [{
997
1032
  readonly required: false;
998
1033
  readonly appliesTo: ["list", "dashboard"];
999
1034
  readonly example: "[{ type: \"search\", label: \"Search user\" }, { type: \"select\", label: \"Role\" }]";
1035
+ }, {
1036
+ readonly id: "searchPlaceholder";
1037
+ readonly label: "Search placeholder";
1038
+ readonly description: "Placeholder text for the main toolbar search input when the shell includes one.";
1039
+ readonly required: false;
1040
+ readonly appliesTo: ["list", "dashboard"];
1041
+ readonly example: "Search by coupon code";
1000
1042
  }, {
1001
1043
  readonly id: "tableColumns";
1002
1044
  readonly label: "Table columns";
@@ -1051,22 +1093,43 @@ declare const uiScreenBlueprint: {
1051
1093
  readonly pageKinds: readonly [{
1052
1094
  readonly id: "list";
1053
1095
  readonly label: "List page";
1054
- readonly frame: readonly ["Sidebar shell", "Breadcrumb", "PageTitle with actions", "Optional stats", "Filter card", "DataTable card"];
1096
+ readonly frame: readonly ["Sidebar shell", "Workspace topbar", "Page intro card with Breadcrumb and PageTitle", "Optional segmented tabs", "Toolbar card", "Optional stats", "DataTable card"];
1055
1097
  }, {
1056
1098
  readonly id: "form";
1057
1099
  readonly label: "Form page";
1058
- readonly frame: readonly ["Sidebar shell", "Breadcrumb", "PageTitle with primary and cancel actions", "Sectioned form cards", "Bottom action row"];
1100
+ readonly frame: readonly ["Sidebar shell", "Workspace topbar", "Page intro card with Breadcrumb and PageTitle", "Optional segmented tabs", "Header actions", "Sectioned form cards", "Bottom action row"];
1059
1101
  }, {
1060
1102
  readonly id: "detail";
1061
1103
  readonly label: "Detail page";
1062
- readonly frame: readonly ["Sidebar shell", "Breadcrumb", "PageTitle with status/actions", "Summary stats or metadata card", "Detail cards and related tables"];
1104
+ readonly frame: readonly ["Sidebar shell", "Workspace topbar", "Page intro card with Breadcrumb and PageTitle", "Optional segmented tabs", "PageTitle with status/actions", "Summary stats or metadata card", "Detail cards and related tables"];
1063
1105
  }, {
1064
1106
  readonly id: "dashboard";
1065
1107
  readonly label: "Dashboard page";
1066
- readonly frame: readonly ["Sidebar shell", "Breadcrumb", "PageTitle with actions", "Stat cards row", "Filters", "Main insight cards and tables"];
1108
+ readonly frame: readonly ["Sidebar shell", "Workspace topbar", "Page intro card with Breadcrumb and PageTitle", "Optional segmented tabs", "Toolbar card with actions", "Stat cards row", "Main insight cards and tables"];
1067
1109
  }];
1068
- readonly workflow: readonly ["Collect the screen brief before coding. Do not invent sidebar items, route urls, filters, fields, or table columns if the brief is missing.", "Use Sidebar plus Breadcrumb and PageTitle to lock the shell before building the page body.", "Choose only canonical root exports from @lucasvu/scope-ui.", "Use Card and ui-grid utilities to create the frame first, then place controls and data components inside.", "Keep all palette, radius, surface, and shadow decisions in the shared ui-theme.css preset file.", "End with a consistency check against the selected preset and the screen brief."];
1110
+ readonly workflow: readonly ["Collect the shell + screen brief before coding. Do not invent layout presets, workspace labels, sidebar items, route urls, tabs, filters, fields, or table columns if the brief is missing.", "Use Sidebar plus the workspace topbar and intro card to lock the shell before building the page body.", "Choose only canonical root exports from @lucasvu/scope-ui.", "Use Card and ui-grid utilities to create the frame first, then place controls and data components inside.", "Keep all palette, radius, surface, and shadow decisions in the shared ui-theme.css preset file.", "End with a consistency check against the selected preset and the screen brief."];
1069
1111
  readonly briefFields: readonly [{
1112
+ readonly id: "layoutPreset";
1113
+ readonly label: "Layout preset";
1114
+ readonly description: "Shared shell preset id that locks the sidebar/topbar/page-card structure.";
1115
+ readonly required: false;
1116
+ readonly appliesTo: ["all"];
1117
+ readonly example: "workspace-admin-v1";
1118
+ }, {
1119
+ readonly id: "workspaceLabel";
1120
+ readonly label: "Workspace label";
1121
+ readonly description: "Short label shown in the sidebar or workspace topbar.";
1122
+ readonly required: false;
1123
+ readonly appliesTo: ["all"];
1124
+ readonly example: "Workspace";
1125
+ }, {
1126
+ readonly id: "timezoneLabel";
1127
+ readonly label: "Timezone/meta label";
1128
+ readonly description: "Short meta label shown in the top-right shell area, such as a timezone or environment.";
1129
+ readonly required: false;
1130
+ readonly appliesTo: ["all"];
1131
+ readonly example: "UTC+07:00";
1132
+ }, {
1070
1133
  readonly id: "pageKind";
1071
1134
  readonly label: "Page kind";
1072
1135
  readonly description: "Choose the base recipe for the screen.";
@@ -1115,6 +1178,20 @@ declare const uiScreenBlueprint: {
1115
1178
  readonly required: false;
1116
1179
  readonly appliesTo: ["all"];
1117
1180
  readonly example: "Manage internal user accounts and permissions.";
1181
+ }, {
1182
+ readonly id: "pageTabs";
1183
+ readonly label: "Page tabs";
1184
+ readonly description: "Optional segmented tabs rendered under the intro card.";
1185
+ readonly required: false;
1186
+ readonly appliesTo: ["all"];
1187
+ readonly example: "[{ value: \"overview\", label: \"Overview\" }, { value: \"coupons\", label: \"Coupons\" }]";
1188
+ }, {
1189
+ readonly id: "activePageTab";
1190
+ readonly label: "Active page tab";
1191
+ readonly description: "The selected segmented tab value when tabs are present.";
1192
+ readonly required: false;
1193
+ readonly appliesTo: ["all"];
1194
+ readonly example: "coupons";
1118
1195
  }, {
1119
1196
  readonly id: "primaryAction";
1120
1197
  readonly label: "Primary action";
@@ -1143,6 +1220,13 @@ declare const uiScreenBlueprint: {
1143
1220
  readonly required: false;
1144
1221
  readonly appliesTo: ["list", "dashboard"];
1145
1222
  readonly example: "[{ type: \"search\", label: \"Search user\" }, { type: \"select\", label: \"Role\" }]";
1223
+ }, {
1224
+ readonly id: "searchPlaceholder";
1225
+ readonly label: "Search placeholder";
1226
+ readonly description: "Placeholder text for the main toolbar search input when the shell includes one.";
1227
+ readonly required: false;
1228
+ readonly appliesTo: ["list", "dashboard"];
1229
+ readonly example: "Search by coupon code";
1146
1230
  }, {
1147
1231
  readonly id: "tableColumns";
1148
1232
  readonly label: "Table columns";
package/dist/index.js CHANGED
@@ -6625,7 +6625,8 @@ var uiAiManifest = {
6625
6625
  rules: [
6626
6626
  "Import the stylesheet once at the app entry before rendering any component.",
6627
6627
  "If the project declares an approved theme preset in AGENTS.md or ui-theme.css, stay inside that preset and do not invent a second palette.",
6628
- "Collect the screen brief first: route url, sidebar items, active sidebar item, page title, actions, and the page-specific content schema.",
6628
+ "Collect the shell + screen brief first: layout preset, workspace label, route url, sidebar items, active sidebar item, page title, tabs/actions, and the page-specific content schema.",
6629
+ "If the project ships a generated layout preset file, keep the shell aligned with it instead of inventing a new page frame.",
6629
6630
  "Prefer the canonical component for each intent instead of mixing legacy MainFe components.",
6630
6631
  "Use Input/Textarea/Select label props directly for simple fields; use Field only to wrap custom controls or grouped content.",
6631
6632
  "Use Select for small fixed option lists, SearchableSelect for larger local lists, Combobox for type-and-pick flows, and AsyncCombobox for remote search.",
@@ -6843,6 +6844,30 @@ var uiAiManifest = {
6843
6844
 
6844
6845
  // src/screen-blueprint.ts
6845
6846
  var uiScreenBriefFields = [
6847
+ {
6848
+ id: "layoutPreset",
6849
+ label: "Layout preset",
6850
+ description: "Shared shell preset id that locks the sidebar/topbar/page-card structure.",
6851
+ required: false,
6852
+ appliesTo: ["all"],
6853
+ example: "workspace-admin-v1"
6854
+ },
6855
+ {
6856
+ id: "workspaceLabel",
6857
+ label: "Workspace label",
6858
+ description: "Short label shown in the sidebar or workspace topbar.",
6859
+ required: false,
6860
+ appliesTo: ["all"],
6861
+ example: "Workspace"
6862
+ },
6863
+ {
6864
+ id: "timezoneLabel",
6865
+ label: "Timezone/meta label",
6866
+ description: "Short meta label shown in the top-right shell area, such as a timezone or environment.",
6867
+ required: false,
6868
+ appliesTo: ["all"],
6869
+ example: "UTC+07:00"
6870
+ },
6846
6871
  {
6847
6872
  id: "pageKind",
6848
6873
  label: "Page kind",
@@ -6899,6 +6924,22 @@ var uiScreenBriefFields = [
6899
6924
  appliesTo: ["all"],
6900
6925
  example: "Manage internal user accounts and permissions."
6901
6926
  },
6927
+ {
6928
+ id: "pageTabs",
6929
+ label: "Page tabs",
6930
+ description: "Optional segmented tabs rendered under the intro card.",
6931
+ required: false,
6932
+ appliesTo: ["all"],
6933
+ example: '[{ value: "overview", label: "Overview" }, { value: "coupons", label: "Coupons" }]'
6934
+ },
6935
+ {
6936
+ id: "activePageTab",
6937
+ label: "Active page tab",
6938
+ description: "The selected segmented tab value when tabs are present.",
6939
+ required: false,
6940
+ appliesTo: ["all"],
6941
+ example: "coupons"
6942
+ },
6902
6943
  {
6903
6944
  id: "primaryAction",
6904
6945
  label: "Primary action",
@@ -6931,6 +6972,14 @@ var uiScreenBriefFields = [
6931
6972
  appliesTo: ["list", "dashboard"],
6932
6973
  example: '[{ type: "search", label: "Search user" }, { type: "select", label: "Role" }]'
6933
6974
  },
6975
+ {
6976
+ id: "searchPlaceholder",
6977
+ label: "Search placeholder",
6978
+ description: "Placeholder text for the main toolbar search input when the shell includes one.",
6979
+ required: false,
6980
+ appliesTo: ["list", "dashboard"],
6981
+ example: "Search by coupon code"
6982
+ },
6934
6983
  {
6935
6984
  id: "tableColumns",
6936
6985
  label: "Table columns",
@@ -6995,10 +7044,11 @@ var uiScreenBlueprint = {
6995
7044
  label: "List page",
6996
7045
  frame: [
6997
7046
  "Sidebar shell",
6998
- "Breadcrumb",
6999
- "PageTitle with actions",
7047
+ "Workspace topbar",
7048
+ "Page intro card with Breadcrumb and PageTitle",
7049
+ "Optional segmented tabs",
7050
+ "Toolbar card",
7000
7051
  "Optional stats",
7001
- "Filter card",
7002
7052
  "DataTable card"
7003
7053
  ]
7004
7054
  },
@@ -7007,8 +7057,10 @@ var uiScreenBlueprint = {
7007
7057
  label: "Form page",
7008
7058
  frame: [
7009
7059
  "Sidebar shell",
7010
- "Breadcrumb",
7011
- "PageTitle with primary and cancel actions",
7060
+ "Workspace topbar",
7061
+ "Page intro card with Breadcrumb and PageTitle",
7062
+ "Optional segmented tabs",
7063
+ "Header actions",
7012
7064
  "Sectioned form cards",
7013
7065
  "Bottom action row"
7014
7066
  ]
@@ -7018,7 +7070,9 @@ var uiScreenBlueprint = {
7018
7070
  label: "Detail page",
7019
7071
  frame: [
7020
7072
  "Sidebar shell",
7021
- "Breadcrumb",
7073
+ "Workspace topbar",
7074
+ "Page intro card with Breadcrumb and PageTitle",
7075
+ "Optional segmented tabs",
7022
7076
  "PageTitle with status/actions",
7023
7077
  "Summary stats or metadata card",
7024
7078
  "Detail cards and related tables"
@@ -7029,17 +7083,18 @@ var uiScreenBlueprint = {
7029
7083
  label: "Dashboard page",
7030
7084
  frame: [
7031
7085
  "Sidebar shell",
7032
- "Breadcrumb",
7033
- "PageTitle with actions",
7086
+ "Workspace topbar",
7087
+ "Page intro card with Breadcrumb and PageTitle",
7088
+ "Optional segmented tabs",
7089
+ "Toolbar card with actions",
7034
7090
  "Stat cards row",
7035
- "Filters",
7036
7091
  "Main insight cards and tables"
7037
7092
  ]
7038
7093
  }
7039
7094
  ],
7040
7095
  workflow: [
7041
- "Collect the screen brief before coding. Do not invent sidebar items, route urls, filters, fields, or table columns if the brief is missing.",
7042
- "Use Sidebar plus Breadcrumb and PageTitle to lock the shell before building the page body.",
7096
+ "Collect the shell + screen brief before coding. Do not invent layout presets, workspace labels, sidebar items, route urls, tabs, filters, fields, or table columns if the brief is missing.",
7097
+ "Use Sidebar plus the workspace topbar and intro card to lock the shell before building the page body.",
7043
7098
  "Choose only canonical root exports from @lucasvu/scope-ui.",
7044
7099
  "Use Card and ui-grid utilities to create the frame first, then place controls and data components inside.",
7045
7100
  "Keep all palette, radius, surface, and shadow decisions in the shared ui-theme.css preset file.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucasvu/scope-ui",
3
- "version": "0.0.8",
3
+ "version": "0.1.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -50,7 +50,7 @@
50
50
  "autoprefixer": "^10.4.23",
51
51
  "postcss": "^8.5.6",
52
52
  "tailwindcss": "3.4.17",
53
- "tsup": "^8.5.0",
53
+ "tsup": "^8.5.1",
54
54
  "typescript": "^5.8.3"
55
55
  }
56
- }
56
+ }