@james_sww/metaform-asset-distribution 0.1.1 → 0.1.2

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.
Files changed (101) hide show
  1. package/README.md +8 -6
  2. package/codebase/codebase.json +5 -4
  3. package/codebase/template/.metaform/template-overrides.json +24 -0
  4. package/codebase/template/index.html +1 -1
  5. package/codebase/template/src/layouts/role-workspace-shell.tsx +136 -48
  6. package/codebase/template/src/workspace/default-workspace.tsx +1 -1
  7. package/dist/application-source.d.ts +5 -3
  8. package/dist/application-source.js +43 -32
  9. package/dist/contracts.d.ts +6 -1
  10. package/dist/contracts.js +1 -1
  11. package/dist/export-source.js +3 -3
  12. package/dist/prepare.d.ts +1 -1
  13. package/dist/prepare.js +41 -25
  14. package/dist/source-map.d.ts +2 -1
  15. package/dist/source-map.js +10 -8
  16. package/package.json +2 -2
  17. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/catalog.json +1 -0
  18. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/common-semantic-region-scaffold.json +1 -0
  19. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-business-role-tasks-icons.json +1 -0
  20. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-business-role-tasks-types.json +1 -0
  21. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-common-semantic-region-contract.json +1 -0
  22. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-account.json +1 -0
  23. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-icons.json +1 -0
  24. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-routes.json +1 -0
  25. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-types.json +1 -0
  26. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json +1 -0
  27. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/quality-role-task-groups.json +1 -0
  28. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/quality-role-task-overview.json +1 -0
  29. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/role-workspace-shell.json +1 -0
  30. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/styles-styles.json +1 -0
  31. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/styles-theme-tokens.json +1 -0
  32. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-avatar.json +1 -0
  33. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-badge.json +1 -0
  34. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-button.json +1 -0
  35. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-collapsible.json +1 -0
  36. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-dropdown-menu.json +1 -0
  37. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-empty.json +1 -0
  38. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-sheet.json +1 -0
  39. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-skeleton.json +1 -0
  40. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-tooltip.json +1 -0
  41. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/registry.json +1 -0
  42. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/release.json +1 -0
  43. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/catalog.json +1 -0
  44. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/common-semantic-region-scaffold.json +1 -0
  45. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/components-business-role-tasks-icons.json +1 -0
  46. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/components-business-role-tasks-types.json +1 -0
  47. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/components-common-semantic-region-contract.json +1 -0
  48. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/layouts-role-workspace-account.json +1 -0
  49. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/layouts-role-workspace-icons.json +1 -0
  50. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/layouts-role-workspace-routes.json +1 -0
  51. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/layouts-role-workspace-types.json +1 -0
  52. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/lib-utils.json +1 -0
  53. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/quality-role-task-groups.json +1 -0
  54. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/quality-role-task-overview.json +1 -0
  55. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/role-workspace-shell.json +1 -0
  56. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/styles-styles.json +1 -0
  57. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/styles-theme-tokens.json +1 -0
  58. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-avatar.json +1 -0
  59. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-badge.json +1 -0
  60. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-button.json +1 -0
  61. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-collapsible.json +1 -0
  62. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-dropdown-menu.json +1 -0
  63. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-empty.json +1 -0
  64. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-sheet.json +1 -0
  65. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-skeleton.json +1 -0
  66. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-tooltip.json +1 -0
  67. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/registry.json +1 -0
  68. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/release.json +1 -0
  69. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/catalog.json +1 -0
  70. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/common-semantic-region-scaffold.json +1 -0
  71. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/components-business-role-tasks-icons.json +1 -0
  72. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/components-business-role-tasks-types.json +1 -0
  73. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/components-common-semantic-region-contract.json +1 -0
  74. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/lib-utils.json +1 -0
  75. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/quality-role-task-groups.json +1 -0
  76. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/quality-role-task-overview.json +1 -0
  77. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/styles-styles.json +1 -0
  78. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/styles-theme-tokens.json +1 -0
  79. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/ui-badge.json +1 -0
  80. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/ui-button.json +1 -0
  81. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/ui-collapsible.json +1 -0
  82. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/ui-empty.json +1 -0
  83. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/registry.json +1 -0
  84. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/release.json +1 -0
  85. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/catalog.json +1 -0
  86. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/common-semantic-region-scaffold.json +1 -0
  87. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/components-business-role-tasks-icons.json +1 -0
  88. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/components-business-role-tasks-types.json +1 -0
  89. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/components-common-semantic-region-contract.json +1 -0
  90. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/lib-utils.json +1 -0
  91. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/quality-role-task-groups.json +1 -0
  92. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/quality-role-task-overview.json +1 -0
  93. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/styles-styles.json +1 -0
  94. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/styles-theme-tokens.json +1 -0
  95. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/ui-badge.json +1 -0
  96. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/ui-button.json +1 -0
  97. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/ui-collapsible.json +1 -0
  98. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/ui-empty.json +1 -0
  99. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/registry.json +1 -0
  100. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/release.json +1 -0
  101. package/registry/release.json +1 -1
@@ -0,0 +1 @@
1
+ {"$schema":"https://ui.shadcn.com/schema/registry.json","homepage":"http://localhost:4318/","items":[{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":[],"description":"components.common.semantic-region.contract","files":[{"content":"export type DecisionStatus = 'pending' | 'confirmed' | 'deferred';\nexport interface SemanticAction {\n id: string;\n label: string;\n intent: 'navigate' | 'query' | 'command';\n inputs: string[];\n preconditions: string[];\n result: string;\n feedback: string;\n enabled: boolean;\n blockedReason?: string;\n targetPageId?: string;\n capabilityRef?: string;\n}\nexport interface SemanticRegionContract {\n id: string;\n pageId: string;\n parentRegionId?: string;\n title: string;\n responsibility: string;\n sourceRefs: { ref: string; version?: string }[];\n inputs: string[];\n outputs: string[];\n bindings: {\n id: string;\n label: string;\n logicalTarget: string;\n status: DecisionStatus;\n concreteRef?: string;\n }[];\n actions: SemanticAction[];\n businessStates: string[];\n navigation: string[];\n replacement: { inputs: string[]; events: string[]; stateOwner: string };\n questions: {\n id: string;\n question: string;\n status: DecisionStatus;\n answer?: string;\n evidence?: string;\n }[];\n progress: {\n design: 'proposed' | 'confirmed';\n implementation: 'scaffold' | 'asset' | 'custom';\n binding: 'pending' | 'ready';\n verification: 'not-run' | 'passed' | 'failed';\n };\n}\nexport interface ContractIssue {\n path: string;\n message: string;\n}\n\nexport function isJsonData(value: unknown, ancestors = new Set<object>(), depth = 0): boolean {\n if (depth > 48) return false;\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return true;\n if (typeof value === 'number') return Number.isFinite(value);\n if (typeof value !== 'object' || ancestors.has(value)) return false;\n if (\n !Array.isArray(value) &&\n Object.getPrototypeOf(value) !== Object.prototype &&\n Object.getPrototypeOf(value) !== null\n )\n return false;\n ancestors.add(value);\n const valid = Object.entries(value).every(\n ([key, item]) =>\n !['__proto__', 'constructor', 'prototype'].includes(key) &&\n isJsonData(item, ancestors, depth + 1),\n );\n ancestors.delete(value);\n return valid;\n}\n\nexport function validateSemanticRegion(value: unknown): {\n valid: boolean;\n issues: ContractIssue[];\n} {\n const issues: ContractIssue[] = [];\n const issue = (path: string, message: string) => {\n issues.push({ path, message });\n };\n if (!isJsonData(value))\n return { valid: false, issues: [{ path: '', message: '只接受普通 JSON 数据' }] };\n function object(v: unknown, path: string): Record<string, unknown> {\n if (!v || typeof v !== 'object' || Array.isArray(v)) {\n issue(path, '需要对象');\n return {};\n }\n return v as Record<string, unknown>;\n }\n function text(v: unknown, path: string) {\n if (typeof v !== 'string' || !v.trim()) issue(path, '需要非空文字');\n }\n function choice(v: unknown, path: string, values: string[]) {\n if (!values.includes(v as string)) issue(path, '状态不在允许范围');\n }\n function array(v: unknown, path: string): unknown[] {\n if (!Array.isArray(v)) {\n issue(path, '需要数组');\n return [];\n }\n return v;\n }\n function strings(v: unknown, path: string) {\n array(v, path).forEach((x, i) => text(x, `${path}.${i}`));\n }\n function rows(\n v: unknown,\n path: string,\n visit: (row: Record<string, unknown>, path: string) => void,\n ) {\n const ids = new Set<string>();\n array(v, path).forEach((x, i) => {\n const p = `${path}.${i}`,\n row = object(x, p);\n text(row.id, p + '.id');\n if (typeof row.id === 'string') {\n if (!/^[a-z][a-z0-9-]*$/.test(row.id) || ids.has(row.id))\n issue(p + '.id', '标识无效或重复');\n ids.add(row.id);\n }\n visit(row, p);\n });\n }\n const r = object(value, '');\n for (const key of ['id', 'pageId', 'title', 'responsibility']) text(r[key], key);\n if (typeof r.id === 'string' && !/^[a-z][a-z0-9-]*$/.test(r.id)) issue('id', '区域标识无效');\n for (const key of ['inputs', 'outputs', 'businessStates', 'navigation']) strings(r[key], key);\n array(r.sourceRefs, 'sourceRefs').forEach((x, i) => {\n const row = object(x, `sourceRefs.${i}`);\n text(row.ref, `sourceRefs.${i}.ref`);\n if (row.version !== undefined) text(row.version, `sourceRefs.${i}.version`);\n });\n rows(r.bindings, 'bindings', (b, p) => {\n for (const key of ['label', 'logicalTarget']) text(b[key], p + '.' + key);\n choice(b.status, p + '.status', ['pending', 'confirmed', 'deferred']);\n if (b.status === 'confirmed') text(b.concreteRef, p + '.concreteRef');\n });\n rows(r.actions, 'actions', (a, p) => {\n for (const key of ['label', 'result', 'feedback']) text(a[key], p + '.' + key);\n strings(a.inputs, p + '.inputs');\n strings(a.preconditions, p + '.preconditions');\n choice(a.intent, p + '.intent', ['navigate', 'query', 'command']);\n if (typeof a.enabled !== 'boolean') issue(p + '.enabled', '需要明确可用性');\n if (a.enabled === false) text(a.blockedReason, p + '.blockedReason');\n if (a.enabled === true)\n text(a.intent === 'navigate' ? a.targetPageId : a.capabilityRef, p + '.target');\n });\n const replacement = object(r.replacement, 'replacement');\n strings(replacement.inputs, 'replacement.inputs');\n strings(replacement.events, 'replacement.events');\n text(replacement.stateOwner, 'replacement.stateOwner');\n rows(r.questions, 'questions', (q, p) => {\n text(q.question, p + '.question');\n choice(q.status, p + '.status', ['pending', 'confirmed', 'deferred']);\n if (q.status === 'confirmed') {\n text(q.answer, p + '.answer');\n text(q.evidence, p + '.evidence');\n }\n });\n const progress = object(r.progress, 'progress');\n choice(progress.design, 'progress.design', ['proposed', 'confirmed']);\n choice(progress.implementation, 'progress.implementation', ['scaffold', 'asset', 'custom']);\n choice(progress.binding, 'progress.binding', ['pending', 'ready']);\n choice(progress.verification, 'progress.verification', ['not-run', 'passed', 'failed']);\n if (\n progress.binding === 'ready' &&\n array(r.bindings, 'bindings').some((b) => object(b, 'bindings').status !== 'confirmed')\n )\n issue('progress.binding', '仍有未确认的数据需求');\n return { valid: issues.length === 0, issues };\n}\n","path":"src/components/common/semantic-region/contract.ts","target":"~/src/components/common/semantic-region/contract.ts","type":"registry:file"}],"meta":{"assetId":"components.common.semantic-region.contract","contractRef":"layout/src/semantic-region/contract.ts","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"components-common-semantic-region-contract","registryDependencies":[],"title":"components.common.semantic-region.contract","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["clsx@2.1.1","tailwind-merge@3.6.0"],"description":"lib.utils","files":[{"content":"import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","path":"src/lib/utils.ts","target":"~/src/lib/utils.ts","type":"registry:file"}],"meta":{"assetId":"lib.utils","contractRef":"vendor/design-system/src/lib/utils.ts","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"lib-utils","registryDependencies":[],"title":"lib.utils","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":[],"description":"styles.theme-tokens","files":[{"content":"/* Platform console's always-dark navigation, shared by light and dark modes. */\n[data-sidebar-theme='platform'] {\n --sidebar-background: #17263c;\n --sidebar-foreground: #aebed0;\n --sidebar-border: #2e4055;\n --sidebar-user-foreground: #fff;\n --sidebar-avatar-background: #e4d9c9;\n --sidebar-avatar-foreground: #7a6044;\n --foreground: #fff;\n --muted-foreground: #859bb2;\n --control-hover: #263950;\n --selection: #294c54;\n --selection-foreground: #58d5bd;\n --selection-border: #294c54;\n --ring: #58d5bd;\n}\n\n/*\n * Metaform theme source of truth.\n *\n * Palette tokens never change meaning. Theme role tokens are the only values\n * overridden by a preset, so application code never depends on a brand hex.\n */\n:root {\n --radius: 0.5rem;\n\n /* Fixed palette */\n --palette-ink: oklch(21.01% 0.0318 264.66deg);\n --palette-ink-hover: oklch(27.81% 0.0296 256.85deg);\n --palette-ink-active: oklch(12.96% 0.0274 261.69deg);\n --palette-cobalt: oklch(54.78% 0.1724 265.67deg);\n --palette-cobalt-hover: oklch(47.44% 0.1459 265.64deg);\n --palette-cobalt-active: oklch(39.72% 0.1074 262.56deg);\n --palette-cyan: oklch(71.5% 0.143 215.22deg);\n --palette-teal: oklch(49.5% 0.102 181.5deg);\n --palette-teal-hover: oklch(43.5% 0.092 181.5deg);\n --palette-teal-active: oklch(36% 0.075 181.5deg);\n --palette-steel: oklch(48% 0.065 245deg);\n --palette-steel-hover: oklch(41% 0.06 245deg);\n --palette-steel-active: oklch(33% 0.052 245deg);\n --palette-safety: oklch(66% 0.17 58deg);\n --palette-safety-hover: oklch(57% 0.15 58deg);\n --palette-safety-active: oklch(48% 0.13 58deg);\n --palette-ocean: oklch(49% 0.11 210deg);\n --palette-ocean-hover: oklch(42% 0.1 210deg);\n --palette-ocean-active: oklch(35% 0.082 210deg);\n --palette-white: oklch(98.5% 0.003 247.86deg);\n\n /* Theme roles: neutral is the default preset. */\n --theme-accent: var(--palette-ink);\n --theme-accent-hover: var(--palette-ink-hover);\n --theme-accent-active: var(--palette-ink-active);\n --theme-accent-soft: oklch(96.83% 0.0069 247.9deg);\n --theme-accent-border: oklch(86.9% 0.0198 252.89deg);\n --theme-accent-foreground: var(--palette-white);\n\n /* Auxiliary brand roles: every preset owns the complete family. */\n --theme-support: var(--palette-cobalt);\n --theme-support-strong: var(--palette-teal);\n --theme-support-soft: oklch(96.5% 0.012 230deg);\n --theme-display-gradient-start: var(--palette-cobalt-active);\n --theme-display-gradient-highlight: var(--palette-cyan);\n --theme-display-gradient-end: var(--palette-teal);\n\n /* Stable semantic colors never follow the selected brand theme. */\n --semantic-success: oklch(65.59% 0.1686 146.19deg);\n --semantic-success-soft: oklch(96.3% 0.032 146.19deg);\n --semantic-success-foreground: oklch(40.5% 0.115 146.19deg);\n --semantic-warning: oklch(79.98% 0.1706 72.75deg);\n --semantic-warning-soft: oklch(97.3% 0.035 72.75deg);\n --semantic-warning-foreground: oklch(43% 0.12 72.75deg);\n --semantic-danger: oklch(63.07% 0.201 25.03deg);\n --semantic-danger-soft: oklch(95.8% 0.025 25.03deg);\n --semantic-danger-foreground: oklch(44% 0.16 25.03deg);\n\n /* Surfaces and content */\n --surface-canvas: oklch(97.89% 0.0029 264.54deg);\n --surface-panel: oklch(99.5% 0.002 247.86deg);\n --surface-raised: oklch(100% 0.001 247.86deg);\n --surface-muted: oklch(96.7% 0.004 264.54deg);\n --surface-muted-foreground: var(--text-regular);\n --text-primary: var(--palette-ink);\n --text-regular: oklch(37.3% 0.034 259.73deg);\n --text-secondary: oklch(55.44% 0.0407 257.42deg);\n\n /* Navigation links stay blue independently of the selected brand theme. */\n --link-foreground: var(--palette-cobalt);\n --link-hover-foreground: var(--palette-cobalt-hover);\n --border-base: oklch(92.88% 0.0126 255.51deg);\n --border-soft: oklch(95.5% 0.008 255.51deg);\n --border-input: oklch(84.5% 0.016 255.51deg);\n --control-hover: var(--surface-muted);\n --control-active: var(--selection);\n --control-active-foreground: var(--selection-foreground);\n --control-active-border: var(--selection-border);\n\n /* shadcn focus ring: neutral gray, not brand accent */\n --ring-muted: oklch(70.4% 0.04 256.79deg);\n\n /* Application roles derived from the active theme preset. */\n --brand-accent: var(--theme-accent);\n --brand-accent-hover: var(--theme-accent-hover);\n --brand-accent-active: var(--theme-accent-active);\n --brand-accent-soft: var(--theme-accent-soft);\n --brand-accent-border: var(--theme-accent-border);\n --brand-accent-foreground: var(--theme-accent-foreground);\n --brand-support: var(--theme-support);\n --brand-support-strong: var(--theme-support-strong);\n --brand-support-soft: var(--theme-support-soft);\n --brand-gradient-start: var(--theme-display-gradient-start);\n --brand-gradient-highlight: var(--theme-display-gradient-highlight);\n --brand-gradient-end: var(--theme-display-gradient-end);\n --action-primary: var(--theme-accent);\n --action-primary-hover: var(--theme-accent-hover);\n --action-primary-active: var(--theme-accent-active);\n --action-primary-foreground: var(--theme-accent-foreground);\n --selection: var(--theme-accent-soft);\n --selection-foreground: var(--theme-accent);\n --selection-border: var(--theme-accent-border);\n --selection-strong: var(--theme-accent-active);\n --focus-ring: var(--theme-accent);\n --sidebar-background: var(--surface-panel);\n --sidebar-foreground: var(--text-primary);\n --sidebar-active: var(--selection);\n --sidebar-active-foreground: var(--selection-foreground);\n --sidebar-border: var(--border-base);\n\n /* Data visualization colors are fixed and independent from branding. */\n --data-blue: var(--palette-cobalt);\n --data-cyan: var(--palette-cyan);\n --data-violet: oklch(60.6% 0.196 286.48deg);\n --data-green: var(--semantic-success);\n --data-amber: var(--semantic-warning);\n --data-red: var(--semantic-danger);\n --data-business: var(--palette-teal);\n --data-equipment: var(--semantic-danger);\n --data-process: var(--semantic-warning);\n --data-metric: var(--data-violet);\n --data-strategy: var(--palette-cyan);\n --data-business-soft: color-mix(in oklch, var(--data-business) 10%, var(--surface-panel));\n --data-equipment-soft: color-mix(in oklch, var(--data-equipment) 10%, var(--surface-panel));\n --data-process-soft: color-mix(in oklch, var(--data-process) 10%, var(--surface-panel));\n --data-metric-soft: color-mix(in oklch, var(--data-metric) 10%, var(--surface-panel));\n --data-strategy-soft: color-mix(in oklch, var(--data-strategy) 10%, var(--surface-panel));\n\n /* Theme setting swatches */\n --theme-swatch-neutral: var(--palette-ink);\n --theme-swatch-cobalt: var(--palette-cobalt);\n --theme-swatch-teal: var(--palette-teal);\n --theme-swatch-steel: var(--palette-steel);\n --theme-swatch-safety: var(--palette-safety);\n --theme-swatch-ocean: var(--palette-ocean);\n\n /* shadcn roles */\n --background: var(--surface-canvas);\n --foreground: var(--text-primary);\n --card: var(--surface-panel);\n --card-foreground: var(--text-primary);\n --popover: var(--surface-raised);\n --popover-foreground: var(--text-primary);\n --primary: var(--action-primary);\n --primary-foreground: var(--action-primary-foreground);\n --secondary: var(--surface-muted);\n --secondary-foreground: var(--surface-muted-foreground);\n --muted: var(--surface-muted);\n --muted-foreground: var(--text-secondary);\n --accent: var(--selection);\n --accent-foreground: var(--selection-foreground);\n --destructive: var(--semantic-danger);\n --destructive-foreground: var(--palette-white);\n --border: var(--border-base);\n --input: var(--border-input);\n --ring: var(--brand-accent);\n\n /* Legacy aliases. New code should use role tokens above. */\n --el-color-primary: var(--theme-accent);\n --el-color-primary-hover: var(--theme-accent-hover);\n --el-color-primary-active: var(--theme-accent-active);\n --el-color-primary-light-9: var(--theme-accent-soft);\n --el-color-primary-light-7: var(--theme-accent-border);\n --el-color-action: var(--action-primary);\n --el-color-action-hover: var(--action-primary-hover);\n --el-color-action-active: var(--action-primary-active);\n --el-color-success: var(--semantic-success);\n --el-color-success-light-9: var(--semantic-success-soft);\n --el-color-warning: var(--semantic-warning);\n --el-color-warning-light-9: var(--semantic-warning-soft);\n --el-color-danger: var(--semantic-danger);\n --el-color-danger-light-9: var(--semantic-danger-soft);\n --el-bg-color: var(--surface-canvas);\n --el-text-color-primary: var(--text-primary);\n --el-text-color-regular: var(--text-regular);\n --el-text-color-secondary: var(--text-secondary);\n --el-border-color-base: var(--border-base);\n}\n\n:root[data-accent='cobalt'] {\n --theme-accent: var(--palette-cobalt);\n --theme-accent-hover: var(--palette-cobalt-hover);\n --theme-accent-active: var(--palette-cobalt-active);\n --theme-accent-soft: oklch(95.53% 0.0154 269.98deg);\n --theme-accent-border: oklch(86.49% 0.0463 269.14deg);\n --theme-accent-foreground: var(--palette-white);\n --theme-support: var(--palette-cyan);\n --theme-support-strong: var(--palette-teal);\n --theme-support-soft: oklch(95.5% 0.025 215.22deg);\n --theme-display-gradient-start: var(--palette-cobalt-active);\n --theme-display-gradient-highlight: oklch(70% 0.13 265.67deg);\n --theme-display-gradient-end: var(--palette-cobalt);\n}\n\n:root[data-accent='teal'] {\n --theme-accent: var(--palette-teal);\n --theme-accent-hover: var(--palette-teal-hover);\n --theme-accent-active: var(--palette-teal-active);\n --theme-accent-soft: oklch(96% 0.018 181.5deg);\n --theme-accent-border: oklch(84% 0.045 181.5deg);\n --theme-accent-foreground: var(--palette-white);\n --theme-support: var(--palette-cyan);\n --theme-support-strong: var(--palette-cobalt);\n --theme-support-soft: oklch(95.5% 0.025 205deg);\n --theme-display-gradient-start: var(--palette-teal-active);\n --theme-display-gradient-highlight: oklch(72% 0.1 181.5deg);\n --theme-display-gradient-end: var(--palette-teal);\n}\n\n:root[data-accent='steel'] {\n --theme-accent: var(--palette-steel);\n --theme-accent-hover: var(--palette-steel-hover);\n --theme-accent-active: var(--palette-steel-active);\n --theme-accent-soft: oklch(95.5% 0.012 245deg);\n --theme-accent-border: oklch(84% 0.035 245deg);\n --theme-accent-foreground: var(--palette-white);\n --theme-support: var(--palette-cobalt);\n --theme-support-strong: var(--palette-ocean);\n --theme-support-soft: oklch(95.5% 0.02 225deg);\n --theme-display-gradient-start: var(--palette-steel-active);\n --theme-display-gradient-highlight: oklch(75% 0.08 225deg);\n --theme-display-gradient-end: var(--palette-ocean);\n}\n\n:root[data-accent='safety'] {\n --theme-accent: var(--palette-safety);\n --theme-accent-hover: var(--palette-safety-hover);\n --theme-accent-active: var(--palette-safety-active);\n --theme-accent-soft: oklch(96.5% 0.025 75deg);\n --theme-accent-border: oklch(86% 0.08 70deg);\n --theme-accent-foreground: var(--palette-ink-active);\n --theme-support: var(--palette-ocean);\n --theme-support-strong: var(--palette-steel);\n --theme-support-soft: oklch(95.5% 0.02 210deg);\n --theme-display-gradient-start: var(--palette-safety-active);\n --theme-display-gradient-highlight: oklch(79% 0.11 75deg);\n --theme-display-gradient-end: var(--palette-ocean);\n}\n\n:root[data-accent='ocean'] {\n --theme-accent: var(--palette-ocean);\n --theme-accent-hover: var(--palette-ocean-hover);\n --theme-accent-active: var(--palette-ocean-active);\n --theme-accent-soft: oklch(95.5% 0.02 210deg);\n --theme-accent-border: oklch(84% 0.055 210deg);\n --theme-accent-foreground: var(--palette-white);\n --theme-support: var(--palette-cyan);\n --theme-support-strong: var(--palette-steel);\n --theme-support-soft: oklch(95.5% 0.025 215deg);\n --theme-display-gradient-start: var(--palette-ocean-active);\n --theme-display-gradient-highlight: oklch(78% 0.1 210deg);\n --theme-display-gradient-end: var(--palette-cyan);\n}\n\n:root.dark {\n color-scheme: dark;\n\n --link-foreground: oklch(72% 0.12 265.67deg);\n --link-hover-foreground: oklch(78% 0.105 265.67deg);\n --theme-accent: oklch(92.88% 0.0126 255.51deg);\n --theme-accent-hover: oklch(86% 0.015 255.51deg);\n --theme-accent-active: oklch(78% 0.018 255.51deg);\n --theme-accent-soft: oklch(25.5% 0.018 257.42deg);\n --theme-accent-border: oklch(36% 0.02 257.42deg);\n --theme-accent-foreground: oklch(16% 0.02 264.66deg);\n --theme-support: oklch(72% 0.12 215.22deg);\n --theme-support-strong: oklch(72% 0.1 181.5deg);\n --theme-support-soft: oklch(25.5% 0.025 215.22deg);\n --theme-display-gradient-start: oklch(69% 0.02 257.42deg);\n --theme-display-gradient-highlight: oklch(96% 0.008 255.51deg);\n --theme-display-gradient-end: oklch(82% 0.014 255.51deg);\n --surface-canvas: oklch(14.5% 0.014 264.66deg);\n --surface-panel: oklch(17.8% 0.016 264.66deg);\n --surface-raised: oklch(21% 0.018 264.66deg);\n --surface-muted: oklch(24.5% 0.017 264.66deg);\n --surface-muted-foreground: var(--text-regular);\n --text-primary: oklch(95.5% 0.008 255.51deg);\n --text-regular: oklch(82% 0.014 255.51deg);\n --text-secondary: oklch(69% 0.02 257.42deg);\n --border-base: oklch(31% 0.018 257.42deg);\n --border-soft: oklch(25.5% 0.015 257.42deg);\n --border-input: oklch(38% 0.02 257.42deg);\n --ring-muted: oklch(55.1% 0.027 264.36deg);\n --semantic-success: oklch(74% 0.145 146.19deg);\n --semantic-success-soft: oklch(24% 0.045 146.19deg);\n --semantic-success-foreground: oklch(82% 0.12 146.19deg);\n --semantic-warning: oklch(82% 0.15 72.75deg);\n --semantic-warning-soft: oklch(25% 0.045 72.75deg);\n --semantic-warning-foreground: oklch(86% 0.12 72.75deg);\n --semantic-danger: oklch(70.4% 0.191 22.22deg);\n --semantic-danger-soft: oklch(25% 0.045 22.22deg);\n --semantic-danger-foreground: oklch(82% 0.14 22.22deg);\n}\n\n:root.dark[data-accent='cobalt'] {\n --theme-accent: oklch(67.5% 0.145 265.67deg);\n --theme-accent-hover: oklch(73% 0.125 265.67deg);\n --theme-accent-active: oklch(62% 0.135 265.67deg);\n --theme-accent-soft: oklch(25.5% 0.04 265.67deg);\n --theme-accent-border: oklch(42% 0.075 265.67deg);\n --theme-accent-foreground: oklch(16% 0.02 264.66deg);\n --theme-support: oklch(76% 0.11 215.22deg);\n --theme-support-strong: oklch(72% 0.1 181.5deg);\n --theme-support-soft: oklch(25.5% 0.04 215.22deg);\n --theme-display-gradient-start: oklch(62% 0.135 265.67deg);\n --theme-display-gradient-highlight: oklch(82% 0.095 265.67deg);\n --theme-display-gradient-end: oklch(70% 0.13 265.67deg);\n}\n\n:root.dark[data-accent='teal'] {\n --theme-accent: oklch(72% 0.12 181.5deg);\n --theme-accent-hover: oklch(78% 0.105 181.5deg);\n --theme-accent-active: oklch(65% 0.11 181.5deg);\n --theme-accent-soft: oklch(25.5% 0.035 181.5deg);\n --theme-accent-border: oklch(42% 0.065 181.5deg);\n --theme-accent-foreground: oklch(16% 0.02 264.66deg);\n --theme-support: oklch(76% 0.11 215.22deg);\n --theme-support-strong: oklch(72% 0.12 265.67deg);\n --theme-support-soft: oklch(25.5% 0.035 215.22deg);\n --theme-display-gradient-start: oklch(65% 0.11 181.5deg);\n --theme-display-gradient-highlight: oklch(84% 0.08 181.5deg);\n --theme-display-gradient-end: oklch(72% 0.12 181.5deg);\n}\n\n:root.dark[data-accent='steel'],\n:root.dark[data-accent='safety'],\n:root.dark[data-accent='ocean'] {\n --theme-accent-foreground: oklch(16% 0.02 264.66deg);\n --theme-accent-soft: color-mix(in oklch, var(--theme-accent) 22%, transparent);\n --theme-accent-border: color-mix(in oklch, var(--theme-accent) 48%, var(--border-base));\n --theme-support-soft: color-mix(in oklch, var(--theme-support) 22%, transparent);\n}\n\n:root.dark[data-accent='steel'] {\n --theme-accent: oklch(70% 0.075 245deg);\n --theme-accent-hover: oklch(77% 0.065 245deg);\n --theme-accent-active: oklch(62% 0.07 245deg);\n --theme-support: oklch(73% 0.1 210deg);\n --theme-support-strong: oklch(70% 0.08 210deg);\n}\n\n:root.dark[data-accent='safety'] {\n --theme-accent: oklch(76% 0.15 65deg);\n --theme-accent-hover: oklch(82% 0.12 65deg);\n --theme-accent-active: oklch(68% 0.14 65deg);\n --theme-support: oklch(73% 0.1 210deg);\n --theme-support-strong: oklch(70% 0.075 245deg);\n}\n\n:root.dark[data-accent='ocean'] {\n --theme-accent: oklch(72% 0.11 210deg);\n --theme-accent-hover: oklch(79% 0.095 210deg);\n --theme-accent-active: oklch(64% 0.105 210deg);\n --theme-support: oklch(76% 0.11 215.22deg);\n --theme-support-strong: oklch(70% 0.075 245deg);\n}\n","path":"src/styles/theme-tokens.css","target":"~/src/styles/theme-tokens.css","type":"registry:file"}],"meta":{"assetId":"styles.theme-tokens","contractRef":"vendor/design-system/src/theme-tokens.css","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"styles-theme-tokens","registryDependencies":[],"title":"styles.theme-tokens","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["tw-animate-css@1.4.0"],"description":"styles.styles","files":[{"content":"@import 'tw-animate-css';\n@import './theme-tokens.css';\n@custom-variant dark (&:is(.dark *));\n\n@theme inline {\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-link: var(--link-foreground);\n --color-link-hover: var(--link-hover-foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-destructive-foreground: var(--destructive-foreground);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n --color-brand: var(--brand-accent);\n --color-brand-foreground: var(--brand-accent-foreground);\n --color-brand-hover: var(--brand-accent-hover);\n --color-brand-active: var(--brand-accent-active);\n --color-brand-soft: var(--brand-accent-soft);\n --color-brand-support: var(--brand-support);\n --color-brand-support-strong: var(--brand-support-strong);\n --color-brand-support-soft: var(--brand-support-soft);\n --color-action: var(--action-primary);\n --color-action-hover: var(--action-primary-hover);\n --color-action-active: var(--action-primary-active);\n --color-action-foreground: var(--action-primary-foreground);\n --color-selection: var(--selection);\n --color-selection-foreground: var(--selection-foreground);\n --color-surface-muted: var(--surface-muted);\n --color-surface-muted-foreground: var(--surface-muted-foreground);\n --color-control-hover: var(--control-hover);\n --color-control-active: var(--control-active);\n --color-control-active-foreground: var(--control-active-foreground);\n --color-control-active-border: var(--control-active-border);\n --color-sidebar: var(--sidebar-background);\n --color-sidebar-foreground: var(--sidebar-foreground);\n --color-data-blue: var(--data-blue);\n --color-data-cyan: var(--data-cyan);\n --color-data-violet: var(--data-violet);\n --color-data-green: var(--data-green);\n --color-data-amber: var(--data-amber);\n --color-data-red: var(--data-red);\n --color-data-business: var(--data-business);\n --color-data-equipment: var(--data-equipment);\n --color-data-process: var(--data-process);\n --color-data-metric: var(--data-metric);\n --color-data-strategy: var(--data-strategy);\n --color-data-business-soft: var(--data-business-soft);\n --color-data-equipment-soft: var(--data-equipment-soft);\n --color-data-process-soft: var(--data-process-soft);\n --color-data-metric-soft: var(--data-metric-soft);\n --color-data-strategy-soft: var(--data-strategy-soft);\n --color-success: var(--semantic-success);\n --color-success-soft: var(--semantic-success-soft);\n --color-success-foreground: var(--semantic-success-foreground);\n --color-warning: var(--semantic-warning);\n --color-warning-soft: var(--semantic-warning-soft);\n --color-warning-foreground: var(--semantic-warning-foreground);\n --color-danger: var(--semantic-danger);\n --color-danger-soft: var(--semantic-danger-soft);\n --color-danger-foreground: var(--semantic-danger-foreground);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --color-border-soft: var(--border-soft);\n}\n\n/*\n * Match shadcn-admin: default border-color on all elements so width-only\n * utilities (border-b, border, etc.) resolve to --border instead of currentColor.\n */\n@layer base {\n * {\n @apply border-border outline-ring/50;\n }\n\n [data-sonner-toaster] [data-sonner-toast] {\n background: var(--card) !important;\n border-color: var(--border) !important;\n }\n\n [data-sonner-toaster] [data-sonner-toast][data-type='info'],\n [data-sonner-toaster] [data-sonner-toast][data-type='info'] [data-title],\n [data-sonner-toaster] [data-sonner-toast][data-type='info'] [data-content],\n [data-sonner-toaster] [data-sonner-toast]:not([data-type]),\n [data-sonner-toaster] [data-sonner-toast]:not([data-type]) [data-title],\n [data-sonner-toaster] [data-sonner-toast]:not([data-type]) [data-content] {\n color: var(--brand-accent) !important;\n }\n\n [data-sonner-toaster] [data-sonner-toast][data-type='success'],\n [data-sonner-toaster] [data-sonner-toast][data-type='success'] [data-title],\n [data-sonner-toaster] [data-sonner-toast][data-type='success'] [data-content] {\n color: var(--semantic-success) !important;\n }\n\n [data-sonner-toaster] [data-sonner-toast][data-type='warning'],\n [data-sonner-toaster] [data-sonner-toast][data-type='warning'] [data-title],\n [data-sonner-toaster] [data-sonner-toast][data-type='warning'] [data-content] {\n color: var(--semantic-warning) !important;\n }\n\n [data-sonner-toaster] [data-sonner-toast][data-type='error'],\n [data-sonner-toaster] [data-sonner-toast][data-type='error'] [data-title],\n [data-sonner-toaster] [data-sonner-toast][data-type='error'] [data-content] {\n color: var(--semantic-danger) !important;\n }\n\n [data-sonner-toaster] [data-sonner-toast] [data-description] {\n color: var(--muted-foreground) !important;\n }\n}\n","path":"src/styles/styles.css","target":"~/src/styles/styles.css","type":"registry:file"}],"meta":{"assetId":"styles.styles","contractRef":"vendor/design-system/src/styles.css","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"styles-styles","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/styles-theme-tokens.json"],"title":"styles.styles","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8","class-variance-authority@0.7.1","radix-ui@1.6.7"],"description":"ui.badge","files":[{"content":"import * as React from \"react\";\n\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst badgeVariants = cva(\n 'inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3',\n {\n variants: {\n variant: {\n default: 'bg-action text-action-foreground [a&]:hover:bg-action-hover',\n secondary: 'bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',\n destructive:\n 'bg-destructive text-destructive-foreground focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90',\n outline:\n 'border-border text-foreground [a&]:hover:bg-control-hover [a&]:hover:text-foreground',\n ghost: '[a&]:hover:bg-control-hover [a&]:hover:text-foreground',\n link: 'text-brand underline-offset-4 [a&]:hover:text-brand-hover [a&]:hover:underline',\n success: 'bg-success-soft text-success-foreground',\n warning: 'bg-warning-soft text-warning-foreground',\n danger: 'bg-danger-soft text-danger-foreground',\n neutral: 'bg-secondary text-secondary-foreground',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n },\n);\n\nfunction Badge({\n className,\n variant = 'default',\n asChild = false,\n ...props\n}: React.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n const Comp = asChild ? Slot.Root : 'span';\n\n return (\n <Comp\n data-slot=\"badge\"\n data-variant={variant}\n className={cn(badgeVariants({ variant }), className)}\n {...props}\n />\n );\n}\n\nexport { Badge, badgeVariants };\n","path":"src/components/ui/badge.tsx","target":"~/src/components/ui/badge.tsx","type":"registry:file"}],"meta":{"assetId":"ui.badge","contractRef":"vendor/design-system/src/components/ui/badge.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"ui-badge","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json"],"title":"ui.badge","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8","class-variance-authority@0.7.1","radix-ui@1.6.7"],"description":"ui.button","files":[{"content":"import * as React from \"react\";\n\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst buttonVariants = cva(\n \"inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,background-color,border-color,box-shadow,transform,opacity] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 active:translate-y-px disabled:pointer-events-none disabled:cursor-not-allowed disabled:translate-y-0 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: 'bg-action text-action-foreground hover:bg-action-hover active:bg-action-active',\n destructive:\n 'bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40',\n outline:\n 'border border-border bg-transparent text-foreground shadow-xs hover:bg-muted/60 hover:text-foreground active:bg-muted/80',\n secondary:\n 'border border-border bg-transparent text-brand hover:border-border hover:bg-brand-soft hover:text-brand-hover active:border-border active:bg-brand-soft/80 active:text-brand-active',\n ghost:\n 'border border-transparent bg-transparent text-brand hover:border-transparent hover:bg-brand-soft hover:text-brand-hover active:border-transparent active:bg-brand-soft/80 active:text-brand-active',\n 'ghost-neutral':\n 'border border-transparent bg-transparent text-muted-foreground hover:border-transparent hover:bg-control-hover hover:text-foreground active:border-transparent active:bg-control-hover active:text-foreground',\n link: 'text-brand underline-offset-4 hover:text-brand-hover hover:underline',\n },\n size: {\n default: 'h-9 px-4 py-2 has-[>svg]:px-3',\n xs: \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: 'h-8 gap-1.5 rounded-md px-3 text-xs has-[>svg]:px-2.5',\n lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',\n icon: 'size-9',\n 'icon-xs': \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n 'icon-sm': 'size-8',\n 'icon-lg': 'size-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\nfunction Button({\n className,\n variant = 'default',\n size = 'default',\n asChild = false,\n ...props\n}: React.ComponentProps<'button'> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : 'button';\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n","path":"src/components/ui/button.tsx","target":"~/src/components/ui/button.tsx","type":"registry:file"}],"meta":{"assetId":"ui.button","contractRef":"vendor/design-system/src/components/ui/button.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"ui-button","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json"],"title":"ui.button","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["radix-ui@1.6.7"],"description":"ui.collapsible","files":[{"content":"import { Collapsible as CollapsiblePrimitive } from \"radix-ui\";\n\nfunction Collapsible({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {\n return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />;\n}\n\nfunction CollapsibleTrigger({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {\n return <CollapsiblePrimitive.CollapsibleTrigger data-slot=\"collapsible-trigger\" {...props} />;\n}\n\nfunction CollapsibleContent({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {\n return <CollapsiblePrimitive.CollapsibleContent data-slot=\"collapsible-content\" {...props} />;\n}\n\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent };\n","path":"src/components/ui/collapsible.tsx","target":"~/src/components/ui/collapsible.tsx","type":"registry:file"}],"meta":{"assetId":"ui.collapsible","contractRef":"vendor/design-system/src/components/ui/collapsible.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"ui-collapsible","registryDependencies":[],"title":"ui.collapsible","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8"],"description":"common.semantic-region-scaffold","files":[{"content":"import \"../../../styles/styles.css\";\nimport { type ReactNode, useId } from \"react\";\n\nimport { Badge } from \"../../ui/badge\";\nimport { Button } from \"../../ui/button\";\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"../../ui/collapsible\";\nimport { cn } from \"../../../lib/utils\";\n\nimport type { SemanticAction, SemanticRegionContract } from \"./contract\";\n\nexport interface SemanticRegionScaffoldProps {\n contract: SemanticRegionContract;\n commands?: { onAction(action: SemanticAction): void };\n slots?: { authoringActions?: ReactNode };\n className?: string;\n}\nfunction DetailList({ title, items }: { title: string; items: readonly string[] }) {\n return (\n <div className=\"space-y-2\">\n <h3 className=\"font-medium text-foreground\">{title}</h3>\n {items.length ? (\n <ul className=\"space-y-1 text-muted-foreground\">\n {items.map((item, i) => (\n <li key={i}>{item}</li>\n ))}\n </ul>\n ) : (\n <p className=\"text-muted-foreground\">尚未确定</p>\n )}\n </div>\n );\n}\nexport function SemanticRegionScaffold({\n contract,\n commands,\n slots,\n className,\n}: SemanticRegionScaffoldProps) {\n const titleId = useId();\n return (\n <section\n aria-labelledby={titleId}\n data-region-id={contract.id}\n data-region-implementation=\"scaffold\"\n className={cn(\n 'min-w-0 rounded-lg border border-dashed border-border bg-card p-5 text-sm text-foreground',\n className,\n )}\n >\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0 space-y-2\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h2 id={titleId} className=\"text-base font-semibold\">\n {contract.title}\n </h2>\n <Badge variant=\"outline\">待打造</Badge>\n </div>\n <p className=\"max-w-3xl leading-relaxed text-muted-foreground\">\n {contract.responsibility}\n </p>\n </div>\n {slots?.authoringActions}\n </div>\n <div className=\"mt-4 flex flex-wrap gap-x-5 gap-y-2 text-muted-foreground\">\n <span>{contract.progress.binding === 'ready' ? '绑定已确认' : '数据待绑定'}</span>\n <span>{contract.progress.design === 'confirmed' ? '设计已确认' : '设计待确认'}</span>\n </div>\n {contract.actions.length ? (\n <div className=\"mt-5 grid gap-3 sm:grid-cols-2\">\n {contract.actions.map((action) => {\n const reason = !action.enabled\n ? action.blockedReason\n : !commands?.onAction\n ? '尚未接入操作处理'\n : undefined;\n return (\n <div key={action.id} className=\"space-y-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n data-action-id={action.id}\n disabled={Boolean(reason)}\n onClick={() => {\n if (!reason) commands?.onAction(action);\n }}\n >\n {action.label}\n </Button>\n <p className=\"text-xs leading-relaxed text-muted-foreground\">\n {reason ?? action.result}\n </p>\n </div>\n );\n })}\n </div>\n ) : null}\n <Collapsible className=\"mt-5 border-t border-border pt-3\">\n <CollapsibleTrigger asChild>\n <Button type=\"button\" variant=\"ghost-neutral\">\n 查看区域设计详情\n </Button>\n </CollapsibleTrigger>\n <CollapsibleContent className=\"pt-4\">\n <div className=\"grid gap-6 sm:grid-cols-2\">\n <DetailList title=\"输入与预期结果\" items={[...contract.inputs, ...contract.outputs]} />\n <DetailList\n title=\"数据指向\"\n items={contract.bindings.map(\n (b) =>\n `${b.label}:${b.logicalTarget}(${b.status === 'confirmed' ? '已确认' : b.status === 'deferred' ? '已暂缓' : '待确认'})`,\n )}\n />\n <DetailList\n title=\"业务状态与导航\"\n items={[...contract.businessStates, ...contract.navigation]}\n />\n <DetailList\n title=\"后续替换契约\"\n items={[\n ...contract.replacement.inputs.map((x) => `输入:${x}`),\n ...contract.replacement.events.map((x) => `事件:${x}`),\n `状态归属:${contract.replacement.stateOwner}`,\n ]}\n />\n <DetailList\n title=\"操作要求\"\n items={contract.actions.map(\n (a) => `${a.label}:${a.preconditions.join(';') || '前置待确认'};${a.feedback}`,\n )}\n />\n <DetailList\n title=\"待确认问题\"\n items={contract.questions.map(\n (q) => `${q.question}${q.answer ? `:${q.answer}` : ''}`,\n )}\n />\n <DetailList\n title=\"本体来源\"\n items={contract.sourceRefs.map(\n (s) => `${s.ref}${s.version ? ` · ${s.version}` : ' · 版本待核验'}`,\n )}\n />\n </div>\n </CollapsibleContent>\n </Collapsible>\n </section>\n );\n}\n","path":"src/components/common/semantic-region/semantic-region-scaffold.tsx","target":"~/src/components/common/semantic-region/semantic-region-scaffold.tsx","type":"registry:file"}],"meta":{"assetId":"common.semantic-region-scaffold","contractRef":"layout/src/semantic-region/semantic-region-scaffold.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"common-semantic-region-scaffold","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-common-semantic-region-contract.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/styles-styles.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-badge.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-button.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-collapsible.json"],"title":"common.semantic-region-scaffold","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":[],"description":"components.business.role-tasks.icons","files":[{"content":"const paths = {\n clipboard:\n 'M9 5H6a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-3 M9 3h6v4H9z',\n document: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M8 13h8 M8 17h6',\n check:\n 'M9 5H6a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-3 M9 3h6v4H9z M8 14l3 3 5-6',\n play: 'M10 8l6 4-6 4z M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0',\n query: 'M5 3h14v18H5z M9 7h6 M9 11h6 M9 15h3',\n alert: 'M12 8v5 M12 17h.01 M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0',\n history: 'M3 3v6h6 M3 9a9 9 0 1 1 0 6 M12 7v5l3 2',\n database:\n 'M20 6c0 2-4 3-8 3S4 8 4 6s4-3 8-3 8 1 8 3z M4 6v12c0 2 4 3 8 3s8-1 8-3V6 M4 12c0 2 4 3 8 3s8-1 8-3',\n folder: 'M3 7V5a2 2 0 0 1 2-2h5l2 3h7a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z',\n target: 'M21 12a9 9 0 1 1-9-9 M17 12a5 5 0 1 1-5-5 M12 12l9-9 M16 3h5v5',\n arrowRight: 'M5 12h14 M13 6l6 6-6 6',\n arrowUpRight: 'M7 17 17 7 M7 7h10v10',\n arrowLeft: 'M19 12H5 M11 6l-6 6 6 6',\n};\n\nexport function RoleTaskIcon({\n name,\n className = 'size-4',\n}: {\n name: keyof typeof paths;\n className?: string;\n}) {\n return (\n <svg\n aria-hidden=\"true\"\n className={className}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d={paths[name]} />\n </svg>\n );\n}\n","path":"src/components/business/role-tasks/icons.tsx","target":"~/src/components/business/role-tasks/icons.tsx","type":"registry:file"}],"meta":{"assetId":"components.business.role-tasks.icons","contractRef":"business/quality/src/role-tasks/icons.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"components-business-role-tasks-icons","registryDependencies":[],"title":"components.business.role-tasks.icons","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8"],"description":"components.business.role-tasks.types","files":[{"content":"import type { ReactNode } from \"react\";\n\nexport interface RoleTaskAction {\n onSelect: () => void;\n disabled?: boolean;\n}\n\nexport interface RoleTaskOverviewProps {\n model: {\n stageName: string;\n stageDescription: string;\n roleLabel: string;\n taskCount: number | null;\n relatedItemCount: number | null;\n goal: string;\n workItemCount?: number | null;\n icon?: ReactNode;\n scopeLabel?: string;\n };\n commands?: {\n onViewValue?: RoleTaskAction;\n onViewWorkItems?: RoleTaskAction;\n };\n}\n\nexport interface RoleTask {\n id: string;\n title: string;\n typeLabel: string;\n description: string;\n icon?: ReactNode;\n relatedItemCount: number | null;\n pendingItemCount: number | null;\n pendingActionTone?: 'primary' | 'accent' | 'neutral';\n iconTone?: 'brand' | 'support' | 'warning';\n viewPendingDisabled?: boolean;\n openTaskDisabled?: boolean;\n}\n\nexport interface RoleTaskGroup {\n id: string;\n title: string;\n description: string;\n tasks: readonly RoleTask[];\n}\n\nexport interface RoleTaskGroupsProps {\n groups: readonly RoleTaskGroup[];\n commands: {\n onViewPendingItems: (task: RoleTask, group: RoleTaskGroup) => void;\n onOpenTask: (task: RoleTask, group: RoleTaskGroup) => void;\n };\n}\n","path":"src/components/business/role-tasks/types.ts","target":"~/src/components/business/role-tasks/types.ts","type":"registry:file"}],"meta":{"assetId":"components.business.role-tasks.types","contractRef":"business/quality/src/role-tasks/types.ts","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"components-business-role-tasks-types","registryDependencies":[],"title":"components.business.role-tasks.types","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8"],"description":"layouts.role-workspace-types","files":[{"content":"import type { ReactNode } from \"react\";\n\nexport interface WorkspaceUser {\n id: string;\n name: string;\n email?: string;\n avatar?: { src?: string; fallback: string };\n}\n\nexport type WorkspaceSession =\n | { status: 'loading' }\n | { status: 'anonymous' }\n | { status: 'expired' }\n | { status: 'authenticated'; user: WorkspaceUser };\n\nexport interface WorkspaceTheme {\n id: string;\n label: string;\n availability: 'implemented' | 'planned';\n count?: number;\n icon?: ReactNode;\n}\n\nexport interface WorkspaceSlotContext {\n sidebarCollapsed: boolean;\n mobileMenuOpen: boolean;\n toolsOpen: boolean;\n activeThemeId: string;\n session?: WorkspaceSession;\n surface: 'header' | 'mobile-menu' | 'sidebar' | 'content' | 'tools';\n}\n\nexport type WorkspaceSlot = ReactNode | ((context: WorkspaceSlotContext) => ReactNode);\n\nexport interface WorkspaceCommands {\n onSelectTheme(themeId: string): void;\n onSidebarCollapsedChange?(collapsed: boolean): void;\n onMobileMenuOpenChange?(open: boolean): void;\n onToolsOpenChange?(open: boolean): void;\n onSignIn?(): void;\n onSignOut?(): void;\n onReauthenticate?(): void;\n}\n\nexport interface RoleWorkspaceShellProps {\n model: {\n productName: string;\n workspaceTitle: string;\n actorLabel?: string;\n actorAvatar?: { src?: string; fallback: string };\n roleLabel?: string;\n contextLabel?: string;\n workspaceGoal?: string;\n themes?: readonly WorkspaceTheme[];\n pendingItemCount?: number;\n activeThemeId: string;\n session?: WorkspaceSession;\n toolsTitle?: string;\n };\n state?: { sidebarCollapsed?: boolean; mobileMenuOpen?: boolean; toolsOpen?: boolean };\n commands: WorkspaceCommands;\n slots: {\n content: WorkspaceSlot;\n brand?: WorkspaceSlot;\n contextSwitcher?: WorkspaceSlot;\n globalSearch?: WorkspaceSlot;\n globalActions?: WorkspaceSlot;\n account?: WorkspaceSlot;\n accountMenu?: WorkspaceSlot;\n navigationBefore?: WorkspaceSlot;\n navigationAfter?: WorkspaceSlot;\n sidebarFooter?: WorkspaceSlot;\n breadcrumbs?: WorkspaceSlot;\n pageHeader?: WorkspaceSlot;\n banner?: WorkspaceSlot;\n toolsPanel?: WorkspaceSlot;\n footer?: WorkspaceSlot;\n overlays?: WorkspaceSlot;\n headerActions?: ReactNode;\n assistant?: ReactNode;\n };\n}\n","path":"src/layouts/role-workspace-types.ts","target":"~/src/layouts/role-workspace-types.ts","type":"registry:file"}],"meta":{"assetId":"layouts.role-workspace-types","contractRef":"platform/src/platform/role-workspace-types.ts","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"layouts-role-workspace-types","registryDependencies":[],"title":"layouts.role-workspace-types","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8","radix-ui@1.6.7"],"description":"ui.avatar","files":[{"content":"import * as React from \"react\";\n\nimport { Avatar as AvatarPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Avatar({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Root>) {\n return (\n <AvatarPrimitive.Root\n data-slot=\"avatar\"\n className={cn('relative flex size-8 shrink-0 overflow-hidden rounded-full', className)}\n {...props}\n />\n );\n}\n\nfunction AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>) {\n return (\n <AvatarPrimitive.Image\n data-slot=\"avatar-image\"\n className={cn('aspect-square size-full', className)}\n {...props}\n />\n );\n}\n\nfunction AvatarFallback({\n className,\n ...props\n}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {\n return (\n <AvatarPrimitive.Fallback\n data-slot=\"avatar-fallback\"\n className={cn(\n 'flex size-full items-center justify-center rounded-full bg-primary/10 text-xs font-semibold text-primary select-none',\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Avatar, AvatarImage, AvatarFallback };\n","path":"src/components/ui/avatar.tsx","target":"~/src/components/ui/avatar.tsx","type":"registry:file"}],"meta":{"assetId":"ui.avatar","contractRef":"vendor/design-system/src/components/ui/avatar.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"ui-avatar","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json"],"title":"ui.avatar","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8","lucide-react@0.468.0","radix-ui@1.6.7"],"description":"ui.dropdown-menu","files":[{"content":"import * as React from \"react\";\n\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {\n return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />;\n}\n\nfunction DropdownMenuPortal({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {\n return <DropdownMenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />;\n}\n\nfunction DropdownMenuTrigger({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\n return <DropdownMenuPrimitive.Trigger data-slot=\"dropdown-menu-trigger\" {...props} />;\n}\n\nfunction DropdownMenuContent({\n className,\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(\n 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',\n className,\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nfunction DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {\n return <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />;\n}\n\nfunction DropdownMenuItem({\n className,\n inset,\n variant = 'default',\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean;\n variant?: 'default' | 'destructive';\n}) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-xs outline-hidden transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {\n return (\n <DropdownMenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(\n 'relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-xs outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n className,\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n );\n}\n\nfunction DropdownMenuRadioGroup({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {\n return <DropdownMenuPrimitive.RadioGroup data-slot=\"dropdown-menu-radio-group\" {...props} />;\n}\n\nfunction DropdownMenuRadioItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {\n return (\n <DropdownMenuPrimitive.RadioItem\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(\n 'relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-xs outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n className,\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n );\n}\n\nfunction DropdownMenuLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean;\n}) {\n return (\n <DropdownMenuPrimitive.Label\n data-slot=\"dropdown-menu-label\"\n data-inset={inset}\n className={cn('px-2 py-1.5 text-xs font-semibold data-[inset]:pl-8', className)}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn('-mx-1 my-1 h-px bg-border/80', className)}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />;\n}\n\nfunction DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n}) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-xs outline-hidden focus:bg-accent data-[inset]:pl-8 data-[state=open]:bg-accent',\n className,\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto size-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n );\n}\n\nfunction DropdownMenuSubContent({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\n return (\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(\n 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',\n className,\n )}\n {...props}\n />\n );\n}\n\nexport {\n DropdownMenu,\n DropdownMenuPortal,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubTrigger,\n DropdownMenuSubContent,\n};\n","path":"src/components/ui/dropdown-menu.tsx","target":"~/src/components/ui/dropdown-menu.tsx","type":"registry:file"}],"meta":{"assetId":"ui.dropdown-menu","contractRef":"vendor/design-system/src/components/ui/dropdown-menu.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"ui-dropdown-menu","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json"],"title":"ui.dropdown-menu","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8"],"description":"ui.skeleton","files":[{"content":"import type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\nexport function Skeleton({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"skeleton\"\n className={cn('animate-pulse rounded-md bg-muted/60', className)}\n {...props}\n />\n );\n}\n","path":"src/components/ui/skeleton.tsx","target":"~/src/components/ui/skeleton.tsx","type":"registry:file"}],"meta":{"assetId":"ui.skeleton","contractRef":"vendor/design-system/src/components/ui/skeleton.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"ui-skeleton","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json"],"title":"ui.skeleton","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8"],"description":"layouts.role-workspace-account","files":[{"content":"import type { ReactNode } from \"react\";\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"../components/ui/avatar\";\nimport { Button } from \"../components/ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"../components/ui/dropdown-menu\";\nimport { Skeleton } from \"../components/ui/skeleton\";\n\nimport type { RoleWorkspaceShellProps, WorkspaceCommands } from \"./role-workspace-types\";\n\nfunction AccountAvatar({\n name,\n avatar,\n}: {\n name: string;\n avatar?: { src?: string; fallback: string };\n}) {\n return (\n <Avatar className=\"size-8\" role=\"img\" aria-label={name}>\n {avatar?.src ? <AvatarImage src={avatar.src} alt=\"\" /> : null}\n <AvatarFallback className=\"border border-brand/20 bg-brand text-sm font-medium text-brand-foreground\">\n {avatar?.fallback || Array.from(name.trim())[0] || '我'}\n </AvatarFallback>\n </Avatar>\n );\n}\n\nexport function WorkspaceAccount({\n model,\n commands,\n menu,\n}: {\n model: RoleWorkspaceShellProps['model'];\n commands: WorkspaceCommands;\n menu?: ReactNode;\n}) {\n const session = model.session;\n if (session?.status === 'loading')\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n data-session-status=\"loading\"\n className=\"flex items-center gap-2\"\n >\n <Skeleton className=\"h-4 w-20 motion-reduce:animate-none\" />\n <Skeleton className=\"size-8 rounded-full motion-reduce:animate-none\" />\n <span className=\"sr-only\">正在确认身份</span>\n </div>\n );\n if (session?.status === 'anonymous' || (!session && !model.actorLabel))\n return (\n <div data-session-status=\"anonymous\">\n <Button\n type=\"button\"\n variant=\"outline\"\n data-account-action=\"sign-in\"\n disabled={!commands.onSignIn}\n onClick={commands.onSignIn}\n >\n 登录\n </Button>\n </div>\n );\n if (session?.status === 'expired')\n return (\n <div data-session-status=\"expired\" className=\"flex items-center gap-2\">\n <span className=\"hidden text-xs text-muted-foreground sm:inline\">会话已过期</span>\n <Button\n type=\"button\"\n variant=\"outline\"\n data-account-action=\"reauthenticate\"\n disabled={!(commands.onReauthenticate ?? commands.onSignIn)}\n onClick={commands.onReauthenticate ?? commands.onSignIn}\n >\n 重新登录\n </Button>\n </div>\n );\n if (!session)\n return (\n <div className=\"flex items-center gap-3\" data-session-status=\"legacy\">\n {model.roleLabel ? (\n <span className=\"hidden rounded-md border border-border px-3 py-2 text-xs sm:inline\">\n {model.roleLabel}视图\n </span>\n ) : null}\n <span className=\"hidden max-w-32 truncate text-sm sm:inline\">{model.actorLabel}</span>\n <AccountAvatar name={model.actorLabel!} avatar={model.actorAvatar} />\n </div>\n );\n const { user } = session;\n return (\n <div data-session-status=\"authenticated\" className=\"flex min-w-0 items-center gap-3\">\n {model.roleLabel ? (\n <span className=\"hidden rounded-md border border-border px-3 py-2 text-xs sm:inline\">\n {model.roleLabel}视图\n </span>\n ) : null}\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost-neutral\"\n className=\"h-10 max-w-48 px-1.5 text-foreground\"\n aria-label=\"打开账户菜单\"\n >\n <span className=\"hidden max-w-28 truncate sm:inline\">{user.name}</span>\n <AccountAvatar name={user.name} avatar={user.avatar} />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-60\">\n <DropdownMenuLabel>\n <p className=\"truncate\">{user.name}</p>\n {user.email ? (\n <p className=\"mt-1 truncate text-xs font-normal text-muted-foreground\">\n {user.email}\n </p>\n ) : null}\n </DropdownMenuLabel>\n {menu}\n <DropdownMenuSeparator />\n <DropdownMenuItem disabled={!commands.onSignOut} onSelect={commands.onSignOut}>\n 退出登录\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n}\n","path":"src/layouts/role-workspace-account.tsx","target":"~/src/layouts/role-workspace-account.tsx","type":"registry:file"}],"meta":{"assetId":"layouts.role-workspace-account","contractRef":"platform/src/platform/role-workspace-account.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"layouts-role-workspace-account","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-types.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-avatar.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-button.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-dropdown-menu.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-skeleton.json"],"title":"layouts.role-workspace-account","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":[],"description":"layouts.role-workspace-icons","files":[{"content":"const paths = {\n 'value-panorama': 'M4 7h6v6H4z M10 10h5a4 4 0 0 1 4 4v3 M16 14l3 3 3-3 M7 13v4',\n 'my-value': 'M12 2c1 7 3 9 10 10-7 1-9 3-10 10-1-7-3-9-10-10 7-1 9-3 10-10z',\n inbox: 'M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0 M12 6v6h5',\n 'enterprise-tasks': 'M3 3h18v18H3z M3 9h18 M3 15h18 M9 3v18 M15 3v18',\n};\n\nexport function WorkspaceRouteIcon({ id }: { id: keyof typeof paths }) {\n return (\n <svg\n aria-hidden=\"true\"\n className=\"size-4 shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d={paths[id]} />\n </svg>\n );\n}\n\nexport function WorkspaceBrandMark() {\n return (\n <svg\n aria-hidden=\"true\"\n className=\"size-[18px]\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.9\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"4\" y=\"2.5\" width=\"16\" height=\"19\" rx=\"2\" />\n <path d=\"M8 8h8M8 12h5M8.5 16.5l2.2 2.2 4.8-4.8\" />\n </svg>\n );\n}\n\nconst utilityPaths = {\n home: 'M3 10l9-7 9 7 M5 9v12h5v-7h4v7h5V9',\n notification: 'M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9 M10 21h4',\n tools: 'M3 4h18v16H3z M15 4v16 M18 8h.01 M18 12h.01',\n menu: 'M4 6h16 M4 12h16 M4 18h16',\n};\n\nexport function WorkspaceUtilityIcon({ name }: { name: keyof typeof utilityPaths }) {\n return (\n <svg\n aria-hidden=\"true\"\n className=\"size-[18px] shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d={utilityPaths[name]} />\n </svg>\n );\n}\n","path":"src/layouts/role-workspace-icons.tsx","target":"~/src/layouts/role-workspace-icons.tsx","type":"registry:file"}],"meta":{"assetId":"layouts.role-workspace-icons","contractRef":"platform/src/platform/role-workspace-icons.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"layouts-role-workspace-icons","registryDependencies":[],"title":"layouts.role-workspace-icons","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":[],"description":"layouts.role-workspace-routes","files":[{"content":"/** 固定框架入口;URL 前缀、页面内容和权限由消费者负责。 */\nexport const DEFAULT_WORKSPACE_ROUTES = [\n { id: 'value-panorama', label: '价值全景', group: 'value' },\n { id: 'my-value', label: '我的价值', group: 'value' },\n { id: 'inbox', label: '待办事项', group: 'inbox' },\n { id: 'enterprise-tasks', label: '企业任务总览', group: 'enterprise' },\n] as const;\n\nexport type WorkspaceRouteId = (typeof DEFAULT_WORKSPACE_ROUTES)[number]['id'];\n\nexport function isWorkspaceRouteId(id: string): id is WorkspaceRouteId {\n return DEFAULT_WORKSPACE_ROUTES.some((route) => route.id === id);\n}\n","path":"src/layouts/role-workspace-routes.ts","target":"~/src/layouts/role-workspace-routes.ts","type":"registry:file"}],"meta":{"assetId":"layouts.role-workspace-routes","contractRef":"platform/src/platform/role-workspace-routes.ts","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"layouts-role-workspace-routes","registryDependencies":[],"title":"layouts.role-workspace-routes","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8","class-variance-authority@0.7.1"],"description":"ui.empty","files":[{"content":"import type { ComponentProps } from \"react\";\n\nimport { type VariantProps, cva } from \"class-variance-authority\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Empty({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"empty\"\n className={cn(\n 'flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction EmptyHeader({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"empty-header\"\n className={cn('flex max-w-sm flex-col items-center gap-2 text-center', className)}\n {...props}\n />\n );\n}\n\nconst emptyMediaVariants = cva(\n 'mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',\n {\n variants: {\n variant: {\n default: 'bg-transparent',\n icon: \"flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6\",\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n },\n);\n\nfunction EmptyMedia({\n className,\n variant = 'default',\n ...props\n}: ComponentProps<'div'> & VariantProps<typeof emptyMediaVariants>) {\n return (\n <div\n data-slot=\"empty-icon\"\n data-variant={variant}\n className={cn(emptyMediaVariants({ variant, className }))}\n {...props}\n />\n );\n}\n\nfunction EmptyTitle({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"empty-title\"\n className={cn('text-lg font-medium tracking-tight', className)}\n {...props}\n />\n );\n}\n\nfunction EmptyDescription({ className, ...props }: ComponentProps<'p'>) {\n return (\n <div\n data-slot=\"empty-description\"\n className={cn(\n 'text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-brand',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction EmptyContent({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"empty-content\"\n className={cn(\n 'flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance',\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia };\n","path":"src/components/ui/empty.tsx","target":"~/src/components/ui/empty.tsx","type":"registry:file"}],"meta":{"assetId":"ui.empty","contractRef":"vendor/design-system/src/components/ui/empty.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"ui-empty","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json"],"title":"ui.empty","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8"],"description":"quality.role-task-groups","files":[{"content":"import \"../../../styles/styles.css\";\nimport { useId } from \"react\";\n\nimport { Badge } from \"../../ui/badge\";\nimport { Button } from \"../../ui/button\";\nimport { Empty, EmptyTitle } from \"../../ui/empty\";\n\nimport { RoleTaskIcon } from \"./icons\";\nimport type { RoleTask, RoleTaskGroupsProps } from \"./types\";\n\nexport type { RoleTaskGroupsProps } from \"./types\";\n\ntype PendingTone = 'primary' | 'accent' | 'neutral';\n\nfunction resolvePendingTone(task: RoleTask): PendingTone {\n if (task.pendingActionTone) return task.pendingActionTone;\n if (task.pendingItemCount !== null && task.pendingItemCount > 1) return 'primary';\n if (task.pendingItemCount === 1) return 'accent';\n return 'neutral';\n}\n\nconst pendingButtonClass: Record<PendingTone, string> = {\n primary:\n 'bg-link font-semibold text-action-foreground hover:bg-link-hover hover:text-action-foreground',\n accent:\n 'border-link bg-transparent font-semibold text-link shadow-none hover:bg-muted hover:text-link',\n neutral:\n 'border-border bg-muted/40 font-semibold text-muted-foreground shadow-none hover:bg-muted hover:text-muted-foreground',\n};\n\nexport function RoleTaskGroups({ groups, commands }: RoleTaskGroupsProps) {\n const prefix = useId();\n if (groups.length === 0)\n return (\n <Empty>\n <EmptyTitle className=\"text-sm\">暂无岗位任务</EmptyTitle>\n </Empty>\n );\n return (\n <div className=\"space-y-7\">\n {groups.map((group, index) => (\n <section key={group.id} aria-labelledby={`${prefix}-${index}`}>\n <div className=\"mb-2.5 flex flex-wrap items-baseline gap-x-5 gap-y-1\">\n <h2\n id={`${prefix}-${index}`}\n className=\"text-lg leading-7 font-semibold text-foreground\"\n >\n {group.title}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n <span className=\"tabular-nums\">{group.tasks.length}</span> 项任务 ·{' '}\n {group.description}\n </p>\n </div>\n {group.tasks.length === 0 ? (\n <Empty>\n <EmptyTitle className=\"text-sm\">此分组暂无岗位任务</EmptyTitle>\n </Empty>\n ) : (\n <div className=\"grid grid-cols-1 gap-2.5 md:grid-cols-2\">\n {group.tasks.map((task) => {\n const pendingTone = resolvePendingTone(task);\n return (\n <article\n key={task.id}\n aria-label={task.title}\n className=\"flex min-w-0 flex-col rounded-xl border border-border bg-card px-5 py-4 shadow-2xs\"\n >\n <div className=\"flex items-start gap-5\">\n {task.icon ? (\n <span\n aria-hidden=\"true\"\n className={`flex size-11 shrink-0 items-center justify-center rounded-lg [&_svg]:size-5 ${task.iconTone === 'support' ? 'bg-brand-support-soft text-brand-support-strong' : task.iconTone === 'warning' ? 'bg-warning-soft text-warning-foreground' : 'bg-link/10 text-link'}`}\n >\n {task.icon}\n </span>\n ) : null}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <Badge\n variant=\"secondary\"\n className=\"h-6 rounded-md bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground\"\n >\n {task.typeLabel}\n </Badge>\n <p className=\"text-sm text-muted-foreground\">\n 关联事项{' '}\n <span\n className={`ml-1.5 text-lg leading-7 font-semibold tabular-nums ${task.relatedItemCount !== null && task.relatedItemCount > 0 ? 'text-foreground' : 'text-muted-foreground'}`}\n >\n {task.relatedItemCount ?? '—'}\n </span>\n </p>\n </div>\n <h3 className=\"mt-2 text-base leading-6 font-semibold text-foreground\">\n {task.title}\n </h3>\n <p className=\"mt-1 break-words text-sm leading-5 text-muted-foreground\">\n {task.description}\n </p>\n </div>\n </div>\n <div className=\"mt-auto flex flex-wrap items-center justify-between gap-2 pt-3\">\n <Button\n type=\"button\"\n variant={pendingTone === 'primary' ? 'default' : 'outline'}\n className={pendingButtonClass[pendingTone]}\n disabled={task.viewPendingDisabled}\n onClick={() => commands.onViewPendingItems(task, group)}\n aria-label={`${task.title}:查看待办事项(${task.pendingItemCount ?? '—'})`}\n >\n <span>\n 查看待办事项{' '}\n <span className=\"tabular-nums\">({task.pendingItemCount ?? '—'})</span>\n </span>\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost-neutral\"\n className=\"h-9 gap-1.5 px-0 text-sm font-medium hover:bg-transparent has-[>svg]:px-0\"\n disabled={task.openTaskDisabled}\n onClick={() => commands.onOpenTask(task, group)}\n aria-label={`${task.title}:打开任务界面`}\n >\n 打开任务界面 <RoleTaskIcon name=\"arrowUpRight\" />\n </Button>\n </div>\n </article>\n );\n })}\n </div>\n )}\n </section>\n ))}\n </div>\n );\n}\n","path":"src/components/business/role-tasks/role-task-groups.tsx","target":"~/src/components/business/role-tasks/role-task-groups.tsx","type":"registry:file"}],"meta":{"assetId":"quality.role-task-groups","contractRef":"business/quality/src/role-tasks/role-task-groups.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"quality-role-task-groups","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-business-role-tasks-icons.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-business-role-tasks-types.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/styles-styles.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-badge.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-button.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-empty.json"],"title":"quality.role-task-groups","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8"],"description":"quality.role-task-overview","files":[{"content":"import \"../../../styles/styles.css\";\nimport { useId } from \"react\";\n\nimport { Button } from \"../../ui/button\";\n\nimport { RoleTaskIcon } from \"./icons\";\nimport type { RoleTaskOverviewProps } from \"./types\";\n\nexport type { RoleTaskOverviewProps } from \"./types\";\n\nexport function RoleTaskOverview({ model, commands }: RoleTaskOverviewProps) {\n const titleId = useId();\n return (\n <section aria-labelledby={titleId} className=\"space-y-5\">\n <div className=\"rounded-2xl border border-border bg-card px-5 py-4 shadow-2xs\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between lg:gap-8\">\n <div className=\"flex min-w-0 items-center gap-4\">\n {model.icon ? (\n <span\n aria-hidden=\"true\"\n className=\"flex size-12 shrink-0 items-center justify-center rounded-lg bg-link/10 text-link [&_svg]:size-6\"\n >\n {model.icon}\n </span>\n ) : null}\n <div className=\"min-w-0\">\n <h2 id={titleId} className=\"text-[22px] leading-8 font-semibold text-foreground\">\n {model.stageName}\n </h2>\n <p className=\"mt-0.5 text-sm leading-5 text-muted-foreground\">\n {model.stageDescription}\n </p>\n </div>\n </div>\n <dl className=\"flex shrink-0 flex-wrap items-center gap-y-2 text-sm [&>div]:mr-4 [&>div]:border-r [&>div]:border-border [&>div]:pr-4 [&>div:last-of-type]:mr-0 [&>div:last-of-type]:border-r-0 [&>div:last-of-type]:pr-0\">\n <div className=\"flex items-center gap-2\">\n <dt className=\"text-muted-foreground\">当前角色</dt>\n <dd className=\"font-semibold text-foreground\">{model.roleLabel}</dd>\n </div>\n <div className=\"flex items-center gap-2\">\n <dt className=\"text-muted-foreground\">岗位任务</dt>\n <dd className=\"font-semibold tabular-nums text-foreground\">\n {model.taskCount ?? '—'} 项\n </dd>\n </div>\n <div className=\"flex items-center gap-2\">\n <dt className=\"text-muted-foreground\">关联事项</dt>\n <dd className=\"font-semibold tabular-nums text-foreground\">\n {model.relatedItemCount ?? '—'} 项\n </dd>\n </div>\n {model.scopeLabel ? (\n <div className=\"text-muted-foreground\">\n <dt className=\"sr-only\">统计范围</dt>\n <dd>{model.scopeLabel}</dd>\n </div>\n ) : null}\n </dl>\n </div>\n </div>\n <div className=\"flex min-h-12 flex-wrap items-center justify-between gap-3 rounded-xl border border-link/20 bg-link/10 px-4 py-2\">\n <p className=\"flex min-w-0 items-center gap-3 text-sm text-foreground\">\n <span\n aria-hidden=\"true\"\n className=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-card text-link\"\n >\n <RoleTaskIcon name=\"target\" className=\"size-4\" />\n </span>\n <span>\n <span className=\"font-semibold\">岗位目标:</span>\n <span className=\"text-muted-foreground\">{model.goal}</span>\n </span>\n </p>\n <div className=\"flex flex-wrap items-center\">\n {commands?.onViewValue ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"h-9 gap-1 rounded-none px-3 text-sm font-medium text-link hover:bg-transparent hover:text-link-hover\"\n disabled={commands.onViewValue.disabled}\n onClick={commands.onViewValue.onSelect}\n >\n 查看业务价值关联 <RoleTaskIcon name=\"arrowUpRight\" />\n </Button>\n ) : null}\n {commands?.onViewValue && commands?.onViewWorkItems ? (\n <span aria-hidden=\"true\" className=\"h-4 w-px bg-border\" />\n ) : null}\n {commands?.onViewWorkItems ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"h-9 gap-1 rounded-none px-3 text-sm font-medium text-link hover:bg-transparent hover:text-link-hover\"\n disabled={commands.onViewWorkItems.disabled}\n onClick={commands.onViewWorkItems.onSelect}\n >\n <span>\n 我的处理事项\n {model.workItemCount !== undefined ? (\n <span className=\"tabular-nums\"> ({model.workItemCount ?? '—'})</span>\n ) : null}\n </span>\n <RoleTaskIcon name=\"arrowRight\" />\n </Button>\n ) : null}\n </div>\n </div>\n </section>\n );\n}\n","path":"src/components/business/role-tasks/role-task-overview.tsx","target":"~/src/components/business/role-tasks/role-task-overview.tsx","type":"registry:file"}],"meta":{"assetId":"quality.role-task-overview","contractRef":"business/quality/src/role-tasks/role-task-overview.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"quality-role-task-overview","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-business-role-tasks-icons.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-business-role-tasks-types.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/styles-styles.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-button.json"],"title":"quality.role-task-overview","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8","lucide-react@0.468.0","radix-ui@1.6.7"],"description":"ui.sheet","files":[{"content":"import * as React from \"react\";\n\nimport { XIcon } from \"lucide-react\";\nimport { Dialog as SheetPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {\n return <SheetPrimitive.Root data-slot=\"sheet\" {...props} />;\n}\n\nfunction SheetTrigger({ ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {\n return <SheetPrimitive.Trigger data-slot=\"sheet-trigger\" {...props} />;\n}\n\nfunction SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Close>) {\n return <SheetPrimitive.Close data-slot=\"sheet-close\" {...props} />;\n}\n\nfunction SheetPortal({ ...props }: React.ComponentProps<typeof SheetPrimitive.Portal>) {\n return <SheetPrimitive.Portal data-slot=\"sheet-portal\" {...props} />;\n}\n\nfunction SheetOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {\n return (\n <SheetPrimitive.Overlay\n data-slot=\"sheet-overlay\"\n className={cn(\n 'fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction SheetContent({\n className,\n children,\n side = 'right',\n showCloseButton = true,\n overlayClassName,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Content> & {\n side?: 'top' | 'right' | 'bottom' | 'left';\n showCloseButton?: boolean;\n overlayClassName?: string;\n}) {\n return (\n <SheetPortal>\n <SheetOverlay className={overlayClassName} />\n <SheetPrimitive.Content\n data-slot=\"sheet-content\"\n className={cn(\n 'fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500',\n side === 'right' &&\n 'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',\n side === 'left' &&\n 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',\n side === 'top' &&\n 'inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',\n side === 'bottom' &&\n 'inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <SheetPrimitive.Close className=\"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-control-active\">\n <XIcon className=\"size-4\" />\n <span className=\"sr-only\">Close</span>\n </SheetPrimitive.Close>\n )}\n </SheetPrimitive.Content>\n </SheetPortal>\n );\n}\n\nfunction SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn('flex flex-col gap-1.5 p-4', className)}\n {...props}\n />\n );\n}\n\nfunction SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn('mt-auto flex flex-col gap-2 p-4', className)}\n {...props}\n />\n );\n}\n\nfunction SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>) {\n return (\n <SheetPrimitive.Title\n data-slot=\"sheet-title\"\n className={cn('font-semibold text-foreground', className)}\n {...props}\n />\n );\n}\n\nfunction SheetDescription({\n className,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Description>) {\n return (\n <SheetPrimitive.Description\n data-slot=\"sheet-description\"\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n );\n}\n\nexport {\n Sheet,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n};\n","path":"src/components/ui/sheet.tsx","target":"~/src/components/ui/sheet.tsx","type":"registry:file"}],"meta":{"assetId":"ui.sheet","contractRef":"vendor/design-system/src/components/ui/sheet.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"ui-sheet","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json"],"title":"ui.sheet","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8","radix-ui@1.6.7"],"description":"ui.tooltip","files":[{"content":"import * as React from \"react\";\n\nimport { Tooltip as TooltipPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction TooltipProvider({\n delayDuration = 0,\n ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n return (\n <TooltipPrimitive.Provider\n data-slot=\"tooltip-provider\"\n delayDuration={delayDuration}\n {...props}\n />\n );\n}\n\nfunction Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {\n return <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />;\n}\n\nfunction TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\n return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />;\n}\n\nfunction TooltipContent({\n className,\n sideOffset = 0,\n children,\n ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\n return (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n data-slot=\"tooltip-content\"\n sideOffset={sideOffset}\n className={cn(\n 'z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',\n className,\n )}\n {...props}\n >\n {children}\n <TooltipPrimitive.Arrow className=\"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground\" />\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n );\n}\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n","path":"src/components/ui/tooltip.tsx","target":"~/src/components/ui/tooltip.tsx","type":"registry:file"}],"meta":{"assetId":"ui.tooltip","contractRef":"vendor/design-system/src/components/ui/tooltip.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"ui-tooltip","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json"],"title":"ui.tooltip","type":"registry:block"},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","dependencies":["react@19.2.8","lucide-react@0.468.0"],"description":"role-workspace-shell","files":[{"content":"import \"../styles/styles.css\";\nimport { type ReactNode, useId, useRef, useState } from \"react\";\n\nimport { ChevronLeft, ChevronRight, Layers3 } from \"lucide-react\";\n\nimport { Button } from \"../components/ui/button\";\nimport {\n Sheet,\n SheetClose,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from \"../components/ui/sheet\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"../components/ui/tooltip\";\nimport { cn } from \"../lib/utils\";\n\nimport { WorkspaceAccount } from \"./role-workspace-account\";\nimport {\n WorkspaceBrandMark,\n WorkspaceRouteIcon,\n WorkspaceUtilityIcon,\n} from \"./role-workspace-icons\";\nimport { DEFAULT_WORKSPACE_ROUTES, isWorkspaceRouteId } from \"./role-workspace-routes\";\nimport type {\n RoleWorkspaceShellProps,\n WorkspaceSlot,\n WorkspaceSlotContext,\n WorkspaceTheme,\n} from \"./role-workspace-types\";\n\nexport type {\n RoleWorkspaceShellProps,\n WorkspaceTheme,\n WorkspaceSession,\n WorkspaceSlotContext,\n} from \"./role-workspace-types\";\n\nfunction renderSlot(slot: WorkspaceSlot | undefined, context: WorkspaceSlotContext) {\n return typeof slot === 'function' ? slot(context) : slot;\n}\nfunction hasNode(node: ReactNode) {\n return node !== null && node !== undefined && node !== false && node !== '';\n}\n\nfunction NavigationItem({\n theme,\n activeThemeId,\n onSelect,\n collapsed,\n}: {\n theme: WorkspaceTheme;\n activeThemeId: string;\n onSelect(id: string): void;\n collapsed: boolean;\n}) {\n const active = theme.id === activeThemeId;\n const button = (\n <Button\n type=\"button\"\n variant=\"ghost-neutral\"\n data-route-id={theme.id}\n className={cn(\n // 基础样式\n 'group relative h-auto w-full justify-start gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200',\n \n // 激活状态\n active && [\n 'bg-gradient-to-r from-[var(--theme-accent-soft)] via-[var(--theme-accent-soft)]/50 to-transparent',\n 'shadow-sm',\n 'border-l-2 border-[var(--theme-accent)]',\n 'font-semibold text-[var(--theme-accent)]',\n 'hover:from-[var(--theme-accent-soft)] hover:via-[var(--theme-accent-soft)]/60',\n ],\n \n // 非激活状态\n !active && [\n 'text-foreground/80',\n 'hover:bg-accent/50 hover:text-foreground',\n 'hover:translate-x-0.5 hover:shadow-sm',\n ],\n \n // 计划中状态\n theme.availability === 'planned' && [\n 'cursor-not-allowed opacity-50',\n 'hover:translate-x-0 hover:bg-transparent',\n ],\n \n // 折叠状态\n collapsed && 'justify-center px-2 border-l-0',\n )}\n aria-label={collapsed ? theme.label : undefined}\n aria-current={active ? 'page' : undefined}\n disabled={theme.availability === 'planned'}\n onClick={() => onSelect(theme.id)}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n 'relative flex size-5 shrink-0 items-center justify-center transition-transform duration-200',\n active ? 'scale-110 text-[var(--theme-accent)]' : 'text-muted-foreground group-hover:scale-105 group-hover:text-foreground',\n )}\n >\n {theme.icon ?? <Layers3 className=\"size-4\" />}\n {/* 激活状态的微妙发光效果 */}\n {active && (\n <span className=\"absolute inset-0 animate-pulse rounded-full bg-[var(--theme-accent-soft)] blur-sm\" />\n )}\n </span>\n {!collapsed ? <span className=\"flex-1 truncate text-left\">{theme.label}</span> : null}\n {!collapsed && theme.count !== undefined && theme.count > 0 ? (\n <span\n className={cn(\n 'flex h-5 min-w-[20px] items-center justify-center rounded-full px-1.5 text-[11px] font-semibold shadow-sm transition-all duration-200',\n active\n ? 'bg-primary text-primary-foreground ring-1 ring-primary/20'\n : 'bg-muted text-muted-foreground group-hover:bg-primary/10 group-hover:text-primary'\n )}\n >\n {theme.count > 99 ? '99+' : theme.count}\n </span>\n ) : null}\n {!collapsed && theme.availability === 'planned' ? (\n <span className=\"ml-auto text-[10px] font-medium uppercase tracking-wide text-muted-foreground/60\">\n 即将推出\n </span>\n ) : null}\n </Button>\n );\n return collapsed ? (\n <Tooltip>\n <TooltipTrigger asChild>\n <span\n className=\"block\"\n tabIndex={theme.availability === 'planned' ? 0 : undefined}\n aria-label={theme.availability === 'planned' ? `${theme.label}(待开放)` : undefined}\n >\n {button}\n </span>\n </TooltipTrigger>\n <TooltipContent side=\"right\" sideOffset={12}>\n {theme.label}\n {theme.availability === 'planned' ? '(待开放)' : ''}\n </TooltipContent>\n </Tooltip>\n ) : (\n button\n );\n}\n\nfunction ThemeNavigation({\n themes,\n pendingItemCount,\n activeThemeId,\n onSelect,\n collapsed = false,\n}: {\n themes: readonly WorkspaceTheme[];\n pendingItemCount?: number;\n activeThemeId: string;\n onSelect(id: string): void;\n collapsed?: boolean;\n}) {\n const count = pendingItemCount ?? themes.find((theme) => theme.id === 'inbox')?.count;\n const extensions = themes.filter((theme) => !isWorkspaceRouteId(theme.id));\n return (\n <TooltipProvider delayDuration={200}>\n <nav aria-label=\"角色工作主题\" className=\"space-y-1 px-3\">\n {/* 核心视图组 */}\n <div className=\"space-y-1\">\n {!collapsed && (\n <p className=\"mb-2 px-3 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/70\">\n 核心视图\n </p>\n )}\n {DEFAULT_WORKSPACE_ROUTES.filter((route) => route.group === 'value').map((route) => (\n <NavigationItem\n key={route.id}\n theme={{\n ...route,\n availability: 'implemented',\n icon: <WorkspaceRouteIcon id={route.id} />,\n }}\n activeThemeId={activeThemeId}\n onSelect={onSelect}\n collapsed={collapsed}\n />\n ))}\n </div>\n\n {/* 渐变分隔线 */}\n <div className=\"my-3 h-px bg-gradient-to-r from-transparent via-border to-transparent\" />\n\n {/* 待办事项组 */}\n <div className=\"space-y-1\">\n {!collapsed && (\n <p className=\"mb-2 px-3 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/70\">\n 工作台\n </p>\n )}\n {DEFAULT_WORKSPACE_ROUTES.filter((route) => route.group === 'inbox').map((route) => (\n <NavigationItem\n key={route.id}\n theme={{\n ...route,\n availability: 'implemented',\n icon: <WorkspaceRouteIcon id={route.id} />,\n count: route.id === 'inbox' ? count : undefined,\n }}\n activeThemeId={activeThemeId}\n onSelect={onSelect}\n collapsed={collapsed}\n />\n ))}\n </div>\n\n {/* 企业协作组 */}\n {DEFAULT_WORKSPACE_ROUTES.some((route) => route.group === 'enterprise') && (\n <>\n <div className=\"my-3 h-px bg-gradient-to-r from-transparent via-border to-transparent\" />\n <div className=\"space-y-1\">\n {!collapsed && (\n <p className=\"mb-2 px-3 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/70\">\n 协作空间\n </p>\n )}\n {DEFAULT_WORKSPACE_ROUTES.filter((route) => route.group === 'enterprise').map((route) => (\n <NavigationItem\n key={route.id}\n theme={{\n ...route,\n availability: 'implemented',\n icon: <WorkspaceRouteIcon id={route.id} />,\n }}\n activeThemeId={activeThemeId}\n onSelect={onSelect}\n collapsed={collapsed}\n />\n ))}\n </div>\n </>\n )}\n\n {/* 扩展功能 */}\n {extensions.length > 0 && (\n <>\n <div className=\"my-3 h-px bg-gradient-to-r from-transparent via-border to-transparent\" />\n <div className=\"space-y-1\">\n {extensions.map((theme) => (\n <NavigationItem\n key={theme.id}\n theme={theme}\n activeThemeId={activeThemeId}\n onSelect={onSelect}\n collapsed={collapsed}\n />\n ))}\n </div>\n </>\n )}\n </nav>\n </TooltipProvider>\n );\n}\n\nconst NO_THEMES: readonly WorkspaceTheme[] = [];\n\nexport function RoleWorkspaceShell({ model, state, commands, slots }: RoleWorkspaceShellProps) {\n const [internalMenuOpen, setInternalMenuOpen] = useState(false);\n const [internalCollapsed, setInternalCollapsed] = useState(false);\n const [internalToolsOpen, setInternalToolsOpen] = useState(false);\n const menuOpen = state?.mobileMenuOpen ?? internalMenuOpen;\n const collapsed = state?.sidebarCollapsed ?? internalCollapsed;\n const toolsOpen = state?.toolsOpen ?? internalToolsOpen;\n const sidebarId = useId();\n const toolsId = useId();\n const contentId = useId();\n const contentRef = useRef<HTMLElement>(null);\n const themes = model.themes ?? NO_THEMES;\n const context = {\n sidebarCollapsed: collapsed,\n mobileMenuOpen: menuOpen,\n toolsOpen,\n activeThemeId: model.activeThemeId,\n session: model.session,\n };\n const region = (\n name: keyof RoleWorkspaceShellProps['slots'],\n surface: WorkspaceSlotContext['surface'] = 'content',\n ) => renderSlot(slots[name], { ...context, surface });\n const search = region('globalSearch', 'header');\n const globalActions = region('globalActions', 'header');\n const switcher = region('contextSwitcher', 'header');\n const mobileSwitcher = region('contextSwitcher', 'mobile-menu');\n const mobileSearch = region('globalSearch', 'mobile-menu');\n const mobileActions = region('globalActions', 'mobile-menu');\n const toolsPanel = region('toolsPanel', 'tools');\n const banner = region('banner');\n const breadcrumbs = region('breadcrumbs');\n const pageHeader = region('pageHeader');\n const footer = region('footer');\n const sidebarBefore = region('navigationBefore', 'sidebar');\n const sidebarAfter = region('navigationAfter', 'sidebar');\n const sidebarFooter =\n slots.sidebarFooter === undefined ? (\n !collapsed && model.workspaceGoal ? (\n <div className=\"border-t border-border pt-4\">\n <p className=\"text-xs text-muted-foreground\">我的目标</p>\n <p className=\"mt-1 text-sm text-foreground\">{model.workspaceGoal}</p>\n </div>\n ) : null\n ) : (\n region('sidebarFooter', 'sidebar')\n );\n\n function changeMenu(open: boolean) {\n if (state?.mobileMenuOpen === undefined) setInternalMenuOpen(open);\n commands.onMobileMenuOpenChange?.(open);\n }\n function changeCollapsed(value: boolean) {\n if (state?.sidebarCollapsed === undefined) setInternalCollapsed(value);\n commands.onSidebarCollapsedChange?.(value);\n }\n function changeTools(open: boolean) {\n if (state?.toolsOpen === undefined) setInternalToolsOpen(open);\n commands.onToolsOpenChange?.(open);\n }\n function selectTheme(id: string) {\n if (\n !isWorkspaceRouteId(id) &&\n !themes.some((theme) => theme.id === id && theme.availability === 'implemented')\n )\n return;\n commands.onSelectTheme(id);\n changeMenu(false);\n }\n\n return (\n <Sheet open={hasNode(toolsPanel) && toolsOpen} onOpenChange={changeTools}>\n <div className=\"flex h-dvh flex-col overflow-hidden bg-background text-sm text-foreground\">\n <a\n href={`#${contentId}`}\n onClick={(event) => {\n event.preventDefault();\n contentRef.current?.focus();\n }}\n className=\"sr-only z-50 rounded-md bg-card p-3 focus:not-sr-only focus:fixed focus:left-3 focus:top-3\"\n >\n 跳到工作内容\n </a>\n <header className=\"relative z-20 flex h-[60px] shrink-0 items-center justify-between gap-3 border-b border-border bg-card px-4 shadow-xs md:px-[22px]\">\n <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n <Sheet open={menuOpen} onOpenChange={changeMenu}>\n <SheetTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost-neutral\"\n size=\"icon\"\n title=\"打开主题菜单\"\n className=\"lg:hidden\"\n aria-label=\"打开主题菜单\"\n >\n <WorkspaceUtilityIcon name=\"menu\" />\n </Button>\n </SheetTrigger>\n <SheetContent\n side=\"left\"\n showCloseButton={false}\n className=\"gap-0 overflow-hidden p-0 motion-reduce:animate-none motion-reduce:transition-none\"\n >\n <div className=\"flex items-start justify-between gap-3 border-b border-border px-4 py-3\">\n <SheetHeader className=\"min-w-0 gap-1 p-0\">\n <SheetTitle className=\"truncate text-base\">{model.workspaceTitle}</SheetTitle>\n <SheetDescription>选择要进入的工作主题</SheetDescription>\n </SheetHeader>\n <div className=\"flex shrink-0 items-center gap-1\">\n {hasNode(mobileActions) ? mobileActions : null}\n <SheetClose asChild>\n <Button type=\"button\" variant=\"ghost-neutral\" aria-label=\"关闭主题菜单\">\n 关闭\n </Button>\n </SheetClose>\n </div>\n </div>\n {hasNode(mobileSwitcher) ? (\n <div className=\"border-b border-border px-4 py-3\">{mobileSwitcher}</div>\n ) : null}\n {hasNode(mobileSearch) ? (\n <div className=\"border-b border-border px-4 py-3\">{mobileSearch}</div>\n ) : null}\n <div className=\"flex min-h-0 flex-1 flex-col overflow-y-auto py-3\">\n {region('navigationBefore', 'mobile-menu')}\n <ThemeNavigation\n themes={themes}\n pendingItemCount={model.pendingItemCount}\n activeThemeId={model.activeThemeId}\n onSelect={selectTheme}\n />\n {region('navigationAfter', 'mobile-menu')}\n {slots.sidebarFooter !== undefined ? (\n <div className=\"mt-auto px-4 pt-4\">\n {region('sidebarFooter', 'mobile-menu')}\n </div>\n ) : null}\n </div>\n </SheetContent>\n </Sheet>\n {slots.brand === undefined ? (\n <>\n <span\n aria-hidden=\"true\"\n className=\"hidden size-[30px] shrink-0 items-center justify-center rounded-lg bg-brand text-brand-foreground sm:flex\"\n >\n <WorkspaceBrandMark />\n </span>\n <span className=\"truncate text-base font-semibold tracking-tight\">\n {model.productName}\n </span>\n </>\n ) : (\n region('brand', 'header')\n )}\n {hasNode(switcher) ? (\n <div className=\"hidden shrink-0 lg:block\">{switcher}</div>\n ) : slots.contextSwitcher === undefined && model.contextLabel ? (\n <span className=\"hidden truncate border-l border-border pl-3.5 text-[11px] text-muted-foreground xl:block\">\n {model.contextLabel}\n </span>\n ) : null}\n </div>\n {hasNode(search) ? (\n <div className=\"hidden min-w-0 max-w-72 flex-1 lg:block\">{search}</div>\n ) : null}\n <div className=\"flex min-w-0 shrink-0 items-center gap-2 md:gap-3\">\n {slots.globalActions === undefined ? (\n slots.headerActions\n ) : hasNode(globalActions) ? (\n <div className=\"hidden items-center gap-2 lg:flex\">{globalActions}</div>\n ) : null}\n {hasNode(toolsPanel) ? (\n <SheetTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost-neutral\"\n size=\"icon\"\n title=\"打开工具面板\"\n aria-label=\"打开工具面板\"\n aria-controls={toolsId}\n aria-expanded={toolsOpen}\n >\n <WorkspaceUtilityIcon name=\"tools\" />\n </Button>\n </SheetTrigger>\n ) : null}\n {slots.account === undefined ? (\n <WorkspaceAccount\n model={model}\n commands={commands}\n menu={region('accountMenu', 'header')}\n />\n ) : (\n region('account', 'header')\n )}\n </div>\n </header>\n {hasNode(banner) ? (\n <div className=\"shrink-0 border-b border-border bg-card\">{banner}</div>\n ) : null}\n <div className=\"flex min-h-0 flex-1 overflow-hidden\">\n <aside\n id={sidebarId}\n className={cn(\n 'relative z-10 hidden h-full shrink-0 overflow-visible border-r border-border bg-card transition-[width] duration-200 ease-out motion-reduce:transition-none md:flex md:flex-col',\n collapsed ? 'w-[72px]' : 'w-[264px]',\n )}\n >\n <div className=\"flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain py-4\">\n {hasNode(sidebarBefore) ? <div className=\"mb-3 px-3\">{sidebarBefore}</div> : null}\n <ThemeNavigation\n themes={themes}\n pendingItemCount={model.pendingItemCount}\n activeThemeId={model.activeThemeId}\n onSelect={selectTheme}\n collapsed={collapsed}\n />\n {hasNode(sidebarAfter) ? <div className=\"mt-3 px-3\">{sidebarAfter}</div> : null}\n {hasNode(sidebarFooter) ? (\n <div className={cn('mt-auto pt-5', collapsed ? 'px-2' : 'px-[23px]')}>\n {sidebarFooter}\n </div>\n ) : null}\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className=\"absolute right-0 top-1/2 z-30 size-9 translate-x-1/2 -translate-y-1/2 rounded-full bg-card shadow-sm hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 active:-translate-y-1/2\"\n aria-controls={sidebarId}\n aria-expanded={!collapsed}\n aria-label={collapsed ? '展开主题导航' : '收起主题导航'}\n title={collapsed ? '展开侧边栏' : '收起侧边栏'}\n onClick={() => changeCollapsed(!collapsed)}\n >\n {collapsed ? <ChevronRight className=\"size-4\" /> : <ChevronLeft className=\"size-4\" />}\n </Button>\n </aside>\n <main\n id={contentId}\n ref={contentRef}\n tabIndex={-1}\n className=\"min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain bg-background p-4 outline-none [scrollbar-gutter:stable] scroll-pt-4 md:p-7\"\n >\n {hasNode(breadcrumbs) ? <div className=\"mb-4\">{breadcrumbs}</div> : null}\n {hasNode(pageHeader) ? <div className=\"mb-5\">{pageHeader}</div> : null}\n {region('content')}\n </main>\n </div>\n {hasNode(footer) ? (\n <footer className=\"shrink-0 border-t border-border bg-card px-4 py-2\">{footer}</footer>\n ) : null}\n {hasNode(toolsPanel) ? (\n <SheetContent\n id={toolsId}\n side=\"right\"\n showCloseButton={false}\n className=\"overflow-y-auto motion-reduce:animate-none motion-reduce:transition-none\"\n >\n <SheetHeader className=\"pr-20\">\n <SheetTitle>{model.toolsTitle ?? '工具面板'}</SheetTitle>\n <SheetDescription>查看当前工作空间的辅助内容</SheetDescription>\n </SheetHeader>\n <SheetClose asChild>\n <Button\n type=\"button\"\n variant=\"ghost-neutral\"\n className=\"absolute right-3 top-3\"\n aria-label=\"关闭工具面板\"\n >\n 关闭\n </Button>\n </SheetClose>\n <div className=\"px-4 pb-4\">{toolsPanel}</div>\n </SheetContent>\n ) : null}\n {region('overlays')}\n {slots.assistant}\n </div>\n </Sheet>\n );\n}\n","path":"src/layouts/role-workspace-shell.tsx","target":"~/src/layouts/role-workspace-shell.tsx","type":"registry:file"}],"meta":{"assetId":"role-workspace-shell","contractRef":"platform/src/platform/role-workspace-shell.tsx","maturity":"candidate","releaseDigest":"47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9","version":"0.1.0"},"name":"role-workspace-shell","registryDependencies":["http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-account.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-icons.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-routes.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-types.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/styles-styles.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-button.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-sheet.json","http://localhost:4318/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-tooltip.json"],"title":"role-workspace-shell","type":"registry:block"}],"name":"metaform"}