@topogram/cli 0.3.47 → 0.3.49

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.
@@ -37,6 +37,16 @@ import {
37
37
  UI_NAVIGATION_PATTERNS,
38
38
  UI_REGION_KINDS,
39
39
  UI_PATTERN_KINDS,
40
+ UI_APP_SHELL_KINDS,
41
+ UI_WINDOWING_MODES,
42
+ UI_STATE_KINDS,
43
+ UI_DESIGN_DENSITIES,
44
+ UI_DESIGN_TONES,
45
+ UI_DESIGN_RADIUS_SCALES,
46
+ UI_DESIGN_COLOR_ROLES,
47
+ UI_DESIGN_TYPOGRAPHY_ROLES,
48
+ UI_DESIGN_ACTION_ROLES,
49
+ UI_DESIGN_ACCESSIBILITY_VALUES,
40
50
  FIELD_SPECS
41
51
  } from "./kinds.js";
42
52
  import { validateComponent } from "./per-kind/component.js";
@@ -78,6 +88,16 @@ export {
78
88
  UI_NAVIGATION_PATTERNS,
79
89
  UI_REGION_KINDS,
80
90
  UI_PATTERN_KINDS,
91
+ UI_APP_SHELL_KINDS,
92
+ UI_WINDOWING_MODES,
93
+ UI_STATE_KINDS,
94
+ UI_DESIGN_DENSITIES,
95
+ UI_DESIGN_TONES,
96
+ UI_DESIGN_RADIUS_SCALES,
97
+ UI_DESIGN_COLOR_ROLES,
98
+ UI_DESIGN_TYPOGRAPHY_ROLES,
99
+ UI_DESIGN_ACTION_ROLES,
100
+ UI_DESIGN_ACCESSIBILITY_VALUES,
81
101
  FIELD_SPECS
82
102
  } from "./kinds.js";
83
103
 
@@ -279,7 +299,7 @@ function validateFieldShapes(errors, statement, fieldMap) {
279
299
  ensureSingleValueField(errors, statement, fieldMap, key, ["list"]);
280
300
  }
281
301
 
282
- for (const key of ["fields", "props", "events", "slots", "behaviors", "keys", "relations", "invariants", "rename", "overrides", "http", "http_errors", "http_fields", "http_responses", "http_preconditions", "http_idempotency", "http_cache", "http_delete", "http_async", "http_status", "http_download", "http_authz", "http_callbacks", "ui_screens", "ui_collections", "ui_actions", "ui_visibility", "ui_lookups", "ui_routes", "ui_web", "ui_ios", "ui_app_shell", "ui_navigation", "ui_screen_regions", "ui_components", "db_tables", "db_columns", "db_keys", "db_indexes", "db_relations", "db_lifecycle", "generator_defaults"]) {
302
+ for (const key of ["fields", "props", "events", "slots", "behaviors", "keys", "relations", "invariants", "rename", "overrides", "http", "http_errors", "http_fields", "http_responses", "http_preconditions", "http_idempotency", "http_cache", "http_delete", "http_async", "http_status", "http_download", "http_authz", "http_callbacks", "ui_screens", "ui_collections", "ui_actions", "ui_visibility", "ui_lookups", "ui_routes", "ui_web", "ui_ios", "ui_app_shell", "ui_navigation", "ui_screen_regions", "ui_components", "ui_design", "db_tables", "db_columns", "db_keys", "db_indexes", "db_relations", "db_lifecycle", "generator_defaults"]) {
283
303
  ensureSingleValueField(errors, statement, fieldMap, key, ["block"]);
284
304
  }
285
305
 
@@ -2320,18 +2340,113 @@ function validateProjectionUiAppShell(errors, statement, fieldMap) {
2320
2340
  }
2321
2341
  seenKeys.add(key);
2322
2342
 
2323
- if (key === "shell" && !["topbar", "sidebar", "dual_nav", "workspace", "wizard", "bottom_tabs", "split_view", "menu_bar"].includes(value)) {
2343
+ if (key === "shell" && !UI_APP_SHELL_KINDS.has(value)) {
2324
2344
  pushError(errors, `Projection ${statement.id} ui_app_shell has invalid shell '${value}'`, entry.loc);
2325
2345
  }
2326
2346
  if (["global_search", "notifications", "account_menu", "workspace_switcher"].includes(key) && !["true", "false"].includes(value)) {
2327
2347
  pushError(errors, `Projection ${statement.id} ui_app_shell '${key}' must be true or false`, entry.loc);
2328
2348
  }
2329
- if (key === "windowing" && !["single_window", "multi_window"].includes(value)) {
2349
+ if (key === "windowing" && !UI_WINDOWING_MODES.has(value)) {
2330
2350
  pushError(errors, `Projection ${statement.id} ui_app_shell has invalid windowing '${value}'`, entry.loc);
2331
2351
  }
2332
2352
  }
2333
2353
  }
2334
2354
 
2355
+ function validateProjectionUiDesign(errors, statement, fieldMap) {
2356
+ if (statement.kind !== "projection") {
2357
+ return;
2358
+ }
2359
+
2360
+ const designField = fieldMap.get("ui_design")?.[0];
2361
+ if (!designField || designField.value.type !== "block") {
2362
+ return;
2363
+ }
2364
+
2365
+ if (symbolValue(getFieldValue(statement, "platform")) !== "ui_shared") {
2366
+ pushError(errors, `Projection ${statement.id} ui_design belongs on shared UI projections; concrete UI projections inherit semantic design intent through 'realizes'`, designField.loc);
2367
+ }
2368
+
2369
+ for (const entry of designField.value.entries) {
2370
+ const tokens = blockSymbolItems(entry).map((item) => item.value);
2371
+ const [key, value, extra] = tokens;
2372
+
2373
+ if (key === "density") {
2374
+ if (!UI_DESIGN_DENSITIES.has(value || "")) {
2375
+ pushError(errors, `Projection ${statement.id} ui_design density has invalid value '${value}'`, entry.loc);
2376
+ }
2377
+ if (tokens.length !== 2) {
2378
+ pushError(errors, `Projection ${statement.id} ui_design density accepts exactly one value`, entry.loc);
2379
+ }
2380
+ continue;
2381
+ }
2382
+
2383
+ if (key === "tone") {
2384
+ if (!UI_DESIGN_TONES.has(value || "")) {
2385
+ pushError(errors, `Projection ${statement.id} ui_design tone has invalid value '${value}'`, entry.loc);
2386
+ }
2387
+ if (tokens.length !== 2) {
2388
+ pushError(errors, `Projection ${statement.id} ui_design tone accepts exactly one value`, entry.loc);
2389
+ }
2390
+ continue;
2391
+ }
2392
+
2393
+ if (key === "radius_scale") {
2394
+ if (!UI_DESIGN_RADIUS_SCALES.has(value || "")) {
2395
+ pushError(errors, `Projection ${statement.id} ui_design radius_scale has invalid value '${value}'`, entry.loc);
2396
+ }
2397
+ if (tokens.length !== 2) {
2398
+ pushError(errors, `Projection ${statement.id} ui_design radius_scale accepts exactly one value`, entry.loc);
2399
+ }
2400
+ continue;
2401
+ }
2402
+
2403
+ if (key === "color_role") {
2404
+ if (!UI_DESIGN_COLOR_ROLES.has(value || "")) {
2405
+ pushError(errors, `Projection ${statement.id} ui_design color_role has invalid role '${value}'`, entry.loc);
2406
+ }
2407
+ if (tokens.length !== 3) {
2408
+ pushError(errors, `Projection ${statement.id} ui_design color_role must use 'color_role <role> <semantic-token>'`, entry.loc);
2409
+ }
2410
+ continue;
2411
+ }
2412
+
2413
+ if (key === "typography_role") {
2414
+ if (!UI_DESIGN_TYPOGRAPHY_ROLES.has(value || "")) {
2415
+ pushError(errors, `Projection ${statement.id} ui_design typography_role has invalid role '${value}'`, entry.loc);
2416
+ }
2417
+ if (tokens.length !== 3) {
2418
+ pushError(errors, `Projection ${statement.id} ui_design typography_role must use 'typography_role <role> <semantic-token>'`, entry.loc);
2419
+ }
2420
+ continue;
2421
+ }
2422
+
2423
+ if (key === "action_role") {
2424
+ if (!UI_DESIGN_ACTION_ROLES.has(value || "")) {
2425
+ pushError(errors, `Projection ${statement.id} ui_design action_role has invalid role '${value}'`, entry.loc);
2426
+ }
2427
+ if (tokens.length !== 3) {
2428
+ pushError(errors, `Projection ${statement.id} ui_design action_role must use 'action_role <role> <semantic-token>'`, entry.loc);
2429
+ }
2430
+ continue;
2431
+ }
2432
+
2433
+ if (key === "accessibility") {
2434
+ const values = UI_DESIGN_ACCESSIBILITY_VALUES[value];
2435
+ if (tokens.length !== 3) {
2436
+ pushError(errors, `Projection ${statement.id} ui_design accessibility must use 'accessibility <setting> <value>'`, entry.loc);
2437
+ }
2438
+ if (!values) {
2439
+ pushError(errors, `Projection ${statement.id} ui_design accessibility has invalid setting '${value}'`, entry.loc);
2440
+ } else if (!values.has(extra || "")) {
2441
+ pushError(errors, `Projection ${statement.id} ui_design accessibility '${value}' has invalid value '${extra}'`, entry.loc);
2442
+ }
2443
+ continue;
2444
+ }
2445
+
2446
+ pushError(errors, `Projection ${statement.id} ui_design has unknown key '${key}'`, entry.loc);
2447
+ }
2448
+ }
2449
+
2335
2450
  function validateProjectionUiNavigation(errors, statement, fieldMap, registry) {
2336
2451
  if (statement.kind !== "projection") {
2337
2452
  return;
@@ -2458,7 +2573,7 @@ function validateProjectionUiScreenRegions(errors, statement, fieldMap, registry
2458
2573
  if (directives.has("placement") && !["primary", "secondary", "supporting"].includes(directives.get("placement"))) {
2459
2574
  pushError(errors, `Projection ${statement.id} ui_screen_regions for '${screenId}' has invalid placement '${directives.get("placement")}'`, entry.loc);
2460
2575
  }
2461
- if (directives.has("state") && !["loading", "empty", "error", "unauthorized", "not_found", "success"].includes(directives.get("state"))) {
2576
+ if (directives.has("state") && !UI_STATE_KINDS.has(directives.get("state"))) {
2462
2577
  pushError(errors, `Projection ${statement.id} ui_screen_regions for '${screenId}' has invalid state '${directives.get("state")}'`, entry.loc);
2463
2578
  }
2464
2579
  }
@@ -2474,6 +2589,10 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2474
2589
  return;
2475
2590
  }
2476
2591
 
2592
+ if (symbolValue(getFieldValue(statement, "platform")) !== "ui_shared") {
2593
+ pushError(errors, `Projection ${statement.id} ui_components belongs on shared UI projections; concrete UI projections inherit component placement through 'realizes'`, componentsField.loc);
2594
+ }
2595
+
2477
2596
  const availableScreens = collectAvailableUiScreenIds(statement, fieldMap, registry);
2478
2597
  const availableRegions = collectAvailableUiRegionKeys(statement, registry);
2479
2598
  const availableRegionPatterns = collectAvailableUiRegionPatterns(statement, registry);
@@ -3387,6 +3506,7 @@ export function validateWorkspace(workspaceAst) {
3387
3506
  validateProjectionUiLookups(errors, statement, fieldMap, registry);
3388
3507
  validateProjectionUiRoutes(errors, statement, fieldMap, registry);
3389
3508
  validateProjectionUiAppShell(errors, statement, fieldMap);
3509
+ validateProjectionUiDesign(errors, statement, fieldMap);
3390
3510
  validateProjectionUiNavigation(errors, statement, fieldMap, registry);
3391
3511
  validateProjectionUiScreenRegions(errors, statement, fieldMap, registry);
3392
3512
  validateProjectionUiComponents(errors, statement, fieldMap, registry);
@@ -74,58 +74,26 @@ export const STATUS_SETS_BY_KIND = {
74
74
  bug: BUG_STATUSES
75
75
  };
76
76
  export const VERIFICATION_METHODS = new Set(["smoke", "runtime", "contract", "journey", "manual"]);
77
- export const UI_SCREEN_KINDS = new Set(["list", "detail", "form", "dashboard", "job_status", "board", "calendar", "feed", "inbox", "settings", "wizard", "report", "analytics"]);
78
- export const UI_COLLECTION_PRESENTATIONS = new Set(["table", "data_grid", "cards", "list", "board", "calendar", "gallery"]);
79
- export const UI_NAVIGATION_PATTERNS = new Set([
80
- "primary",
81
- "tabs",
82
- "stack_navigation",
83
- "bottom_tabs",
84
- "segmented_control",
85
- "command_palette",
86
- "split_view",
87
- "navigation_rail"
88
- ]);
89
- export const UI_REGION_KINDS = new Set([
90
- "hero",
91
- "toolbar",
92
- "filters",
93
- "search",
94
- "results",
95
- "summary",
96
- "metadata",
97
- "aside",
98
- "related",
99
- "activity",
100
- "comments",
101
- "timeline",
102
- "tabs",
103
- "bulk_actions",
104
- "footer_actions"
105
- ]);
106
- export const UI_PATTERN_KINDS = new Set([
107
- "resource_table",
108
- "data_grid_view",
109
- "resource_cards",
110
- "detail_panel",
111
- "edit_form",
112
- "lookup_select",
113
- "action_bar",
114
- "status_badge",
115
- "summary_stats",
116
- "activity_feed",
117
- "comment_thread",
118
- "timeline_view",
119
- "board_view",
120
- "calendar_view",
121
- "settings_section",
122
- "wizard_stepper",
123
- "audit_log",
124
- "search_results",
125
- "empty_state_panel",
126
- "inspector_pane",
127
- "master_detail"
128
- ]);
77
+
78
+ export {
79
+ UI_APP_SHELL_KINDS,
80
+ UI_WINDOWING_MODES,
81
+ UI_SCREEN_KINDS,
82
+ UI_COLLECTION_PRESENTATIONS,
83
+ UI_NAVIGATION_PATTERNS,
84
+ UI_REGION_KINDS,
85
+ UI_PATTERN_KINDS,
86
+ UI_ACTION_PRESENTATIONS,
87
+ UI_STATE_KINDS,
88
+ UI_PLATFORM_PATTERNS,
89
+ UI_DESIGN_DENSITIES,
90
+ UI_DESIGN_TONES,
91
+ UI_DESIGN_RADIUS_SCALES,
92
+ UI_DESIGN_COLOR_ROLES,
93
+ UI_DESIGN_TYPOGRAPHY_ROLES,
94
+ UI_DESIGN_ACTION_ROLES,
95
+ UI_DESIGN_ACCESSIBILITY_VALUES
96
+ } from "../ui/taxonomy.js";
129
97
 
130
98
  // Kinds that may carry an optional singular `domain dom_x` field. Keep this
131
99
  // set in sync with the `allowed[]` arrays in FIELD_SPECS below; the cross-kind
@@ -221,6 +189,7 @@ export const FIELD_SPECS = {
221
189
  "ui_navigation",
222
190
  "ui_screen_regions",
223
191
  "ui_components",
192
+ "ui_design",
224
193
  "db_tables",
225
194
  "db_columns",
226
195
  "db_keys",