@utopia-studio-design/design-system-cli 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/utopia-ds-mcp.mjs +18 -5
- package/bin/utopia-ds.mjs +16 -3
- package/data/blocks/semantic-pattern-parts/AuthParts.tsx +48 -0
- package/data/blocks/semantic-pattern-parts/ChoiceCards.tsx +7 -0
- package/data/blocks/semantic-pattern-parts/ListFilters.tsx +9 -0
- package/data/blocks/semantic-pattern-parts/OutcomeNotice.tsx +7 -0
- package/data/blocks/semantic-pattern-parts/PatternParts.tsx +7 -0
- package/data/blocks/semantic-pattern-parts/ProductParts.tsx +38 -0
- package/data/blocks/semantic-pattern-parts/ProgressSteps.tsx +6 -0
- package/data/blocks/semantic-pattern-parts/ReviewList.tsx +7 -0
- package/data/blocks/semantic-pattern-parts/ValidatedField.tsx +8 -0
- package/data/blocks/semantic-pattern-parts/WorkflowParts.tsx +339 -0
- package/data/blocks/semantic-pattern-parts/auth-parts.css +27 -0
- package/data/blocks/semantic-pattern-parts/pattern-parts.css +25 -0
- package/data/blocks/semantic-pattern-parts/product-parts.css +25 -0
- package/data/blocks/semantic-pattern-parts/types.ts +3 -0
- package/data/blocks/semantic-pattern-parts/workflow-parts.css +118 -0
- package/data/blocks/ux-pattern-examples/AccessExamples.tsx +46 -0
- package/data/blocks/ux-pattern-examples/AccountExamples.tsx +133 -0
- package/data/blocks/ux-pattern-examples/OnboardingExample.tsx +15 -0
- package/data/blocks/ux-pattern-examples/ProductExamples.tsx +79 -0
- package/data/blocks/ux-pattern-examples/ReviewExample.tsx +14 -0
- package/data/blocks/ux-pattern-examples/SignupExample.tsx +22 -0
- package/data/blocks/ux-pattern-examples/WorkflowExample.tsx +1761 -0
- package/data/blocks/ux-pattern-examples/WorkflowExamples.tsx +190 -0
- package/data/blocks/ux-pattern-examples/copy.ts +6 -0
- package/data/blocks/ux-pattern-examples/ux-examples.css +18 -0
- package/data/blocks/ux-pattern-examples/workflow-copy.ts +76 -0
- package/data/blocks/ux-pattern-examples/workflow-definitions.ts +1291 -0
- package/data/docs/ceramic-release-2026-09-06.md +2 -0
- package/data/docs/create-a-brandbook.md +79 -0
- package/data/docs/dextrum-document-family.md +47 -0
- package/data/docs/guide.md +2 -0
- package/data/docs/presentation-asset-repository.md +5 -0
- package/data/docs/ui-parts-qa.md +172 -0
- package/data/docs/ui-parts.md +105 -0
- package/data/docs/ux-pattern-adaptation-qa.md +38 -0
- package/data/docs/ux-pattern-adaptation.md +41 -0
- package/data/docs/ux-pattern-migration.md +111 -0
- package/data/docs/ux-pattern-release-2026-09-13.md +26 -0
- package/data/docs/ux-source-evidence/account-auth.json +41 -0
- package/data/docs/ux-source-evidence/account-forgot-password.json +36 -0
- package/data/docs/ux-source-evidence/account-invite.json +63 -0
- package/data/docs/ux-source-evidence/account-locked.json +63 -0
- package/data/docs/ux-source-evidence/account-login.json +28 -0
- package/data/docs/ux-source-evidence/account-mfa.json +62 -0
- package/data/docs/ux-source-evidence/account-onboarding-credentials.json +52 -0
- package/data/docs/ux-source-evidence/account-onboarding-legal.json +56 -0
- package/data/docs/ux-source-evidence/account-onboarding-orientation.json +66 -0
- package/data/docs/ux-source-evidence/account-onboarding-profile.json +50 -0
- package/data/docs/ux-source-evidence/account-onboarding.json +69 -0
- package/data/docs/ux-source-evidence/account-reset-password.json +27 -0
- package/data/docs/ux-source-evidence/account-session-expired.json +62 -0
- package/data/docs/ux-source-evidence/account-signup.json +33 -0
- package/data/docs/ux-source-evidence/account-verify-email.json +34 -0
- package/data/docs/ux-source-evidence/account-welcome.json +41 -0
- package/data/docs/ux-source-evidence/admin-analytics.json +54 -0
- package/data/docs/ux-source-evidence/admin-audit.json +31 -0
- package/data/docs/ux-source-evidence/admin-broadcasts-new.json +36 -0
- package/data/docs/ux-source-evidence/admin-broadcasts.json +41 -0
- package/data/docs/ux-source-evidence/admin-closures.json +52 -0
- package/data/docs/ux-source-evidence/admin-devices.json +51 -0
- package/data/docs/ux-source-evidence/admin-escalations.json +35 -0
- package/data/docs/ux-source-evidence/admin-incidents.json +54 -0
- package/data/docs/ux-source-evidence/admin-loading.json +17 -0
- package/data/docs/ux-source-evidence/admin-onboarding-detail.json +80 -0
- package/data/docs/ux-source-evidence/admin-onboarding.json +62 -0
- package/data/docs/ux-source-evidence/admin-payments.json +45 -0
- package/data/docs/ux-source-evidence/admin-providers.json +38 -0
- package/data/docs/ux-source-evidence/admin-settings.json +18 -0
- package/data/docs/ux-source-evidence/admin-subscriptions-detail-allocate.json +42 -0
- package/data/docs/ux-source-evidence/admin-subscriptions-detail.json +48 -0
- package/data/docs/ux-source-evidence/admin-subscriptions-new.json +43 -0
- package/data/docs/ux-source-evidence/admin-subscriptions.json +41 -0
- package/data/docs/ux-source-evidence/admin-transactions.json +41 -0
- package/data/docs/ux-source-evidence/error.json +30 -0
- package/data/docs/ux-source-evidence/loading.json +19 -0
- package/data/docs/ux-source-evidence/marketing-blog.json +48 -0
- package/data/docs/ux-source-evidence/marketing-contact.json +36 -0
- package/data/docs/ux-source-evidence/marketing.json +64 -0
- package/data/docs/ux-source-evidence/not-found.json +28 -0
- package/data/docs/ux-source-evidence/portal-loading.json +17 -0
- package/data/docs/ux-source-evidence/portal-mentor-messages.json +31 -0
- package/data/docs/ux-source-evidence/portal-mentor-profile.json +23 -0
- package/data/docs/ux-source-evidence/portal-mentor-requests-detail.json +54 -0
- package/data/docs/ux-source-evidence/portal-mentor-sessions-detail-assess.json +42 -0
- package/data/docs/ux-source-evidence/portal-mentor-settings.json +18 -0
- package/data/docs/ux-source-evidence/portal-mentor.json +38 -0
- package/data/docs/ux-source-evidence/portal-sessions-detail-live.json +48 -0
- package/data/docs/ux-source-evidence/portal-sponsor-billing.json +28 -0
- package/data/docs/ux-source-evidence/portal-sponsor-profile.json +22 -0
- package/data/docs/ux-source-evidence/portal-sponsor-sessions.json +28 -0
- package/data/docs/ux-source-evidence/portal-sponsor-settings.json +18 -0
- package/data/docs/ux-source-evidence/portal-sponsor-trainees.json +36 -0
- package/data/docs/ux-source-evidence/portal-sponsor.json +52 -0
- package/data/docs/ux-source-evidence/portal-trainee-competency.json +42 -0
- package/data/docs/ux-source-evidence/portal-trainee-credits.json +40 -0
- package/data/docs/ux-source-evidence/portal-trainee-mentors.json +48 -0
- package/data/docs/ux-source-evidence/portal-trainee-messages.json +32 -0
- package/data/docs/ux-source-evidence/portal-trainee-notifications.json +35 -0
- package/data/docs/ux-source-evidence/portal-trainee-profile.json +26 -0
- package/data/docs/ux-source-evidence/portal-trainee-request.json +54 -0
- package/data/docs/ux-source-evidence/portal-trainee-sessions-detail-feedback.json +36 -0
- package/data/docs/ux-source-evidence/portal-trainee-sessions-detail-precheck.json +42 -0
- package/data/docs/ux-source-evidence/portal-trainee-sessions-detail.json +55 -0
- package/data/docs/ux-source-evidence/portal-trainee-sessions.json +36 -0
- package/data/docs/ux-source-evidence/portal-trainee-settings-notifications.json +38 -0
- package/data/docs/ux-source-evidence/portal-trainee-settings.json +18 -0
- package/data/docs/ux-source-evidence/portal-trainee.json +44 -0
- package/data/docs/ux-source-evidence/portal-tv-detail.json +39 -0
- package/data/docs/ux-source-evidence/portal-tv.json +44 -0
- package/data/docs/ux-source-evidence/shared-admin-admin-shell.json +35 -0
- package/data/docs/ux-source-evidence/shared-auth-auth-primitives.json +105 -0
- package/data/docs/ux-source-evidence/shared-auth-auth-shell.json +25 -0
- package/data/docs/ux-source-evidence/shared-auth-onboarding-primitives.json +52 -0
- package/data/docs/ux-source-evidence/shared-auth-onboarding-shell.json +28 -0
- package/data/docs/ux-source-evidence/shared-auth-status-indicator.json +41 -0
- package/data/docs/ux-source-evidence/shared-portal-empty-state.json +24 -0
- package/data/docs/ux-source-evidence/shared-portal-loading-skeleton.json +27 -0
- package/data/docs/ux-source-evidence/shared-portal-notifications-dropdown.json +39 -0
- package/data/docs/ux-source-evidence/shared-portal-portal-shell.json +36 -0
- package/data/docs/ux-source-evidence/shared-portal-profile-form.json +32 -0
- package/data/docs/ux-source-evidence/shared-portal-settings-view.json +81 -0
- package/data/docs/ux-source-evidence/shared-portal-usage-sparkline.json +18 -0
- package/data/docs/ux-source-evidence/shared-portal-user-menu.json +34 -0
- package/data/manifests/blocks.json +230 -21
- package/data/manifests/brandbook-skill.json +33 -0
- package/data/manifests/catalog.json +1 -0
- package/data/manifests/patterns.json +358 -0
- package/data/manifests/theme-dextrum.json +132 -21
- package/data/manifests/themes.json +253 -39
- package/data/manifests/ui-patterns.json +1029 -0
- package/data/manifests/ux-examples.json +2177 -0
- package/data/manifests/ux-patterns.json +1135 -0
- package/data/manifests/ux-source-inventory.json +4669 -0
- package/data/ui-parts/blocks/analytics-dashboard/AnalyticsMetricBlocks.tsx +87 -0
- package/data/ui-parts/blocks/analytics-dashboard/analytics-dashboard.css +92 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/AuthParts.tsx +48 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/ChoiceCards.tsx +7 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/ListFilters.tsx +9 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/OutcomeNotice.tsx +7 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/ProductParts.tsx +38 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/ProgressSteps.tsx +6 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/ReviewList.tsx +7 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/ValidatedField.tsx +8 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/WorkflowParts.tsx +339 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/auth-parts.css +27 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/pattern-parts.css +25 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/product-parts.css +25 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/types.ts +3 -0
- package/data/ui-parts/blocks/semantic-pattern-parts/workflow-parts.css +118 -0
- package/data/ui-parts/blocks/ux-pattern-examples/AccessExamples.tsx +46 -0
- package/data/ui-parts/blocks/ux-pattern-examples/AccountExamples.tsx +133 -0
- package/data/ui-parts/blocks/ux-pattern-examples/ProductExamples.tsx +79 -0
- package/data/ui-parts/blocks/ux-pattern-examples/WorkflowExample.tsx +1761 -0
- package/data/ui-parts/blocks/ux-pattern-examples/WorkflowExamples.tsx +190 -0
- package/data/ui-parts/blocks/ux-pattern-examples/copy.ts +6 -0
- package/data/ui-parts/blocks/ux-pattern-examples/ux-examples.css +18 -0
- package/data/ui-parts/blocks/ux-pattern-examples/workflow-copy.ts +76 -0
- package/data/ui-parts/blocks/ux-pattern-examples/workflow-definitions.ts +1291 -0
- package/data/ux-source-evidence/account-auth.json +41 -0
- package/data/ux-source-evidence/account-forgot-password.json +36 -0
- package/data/ux-source-evidence/account-invite.json +63 -0
- package/data/ux-source-evidence/account-locked.json +63 -0
- package/data/ux-source-evidence/account-login.json +28 -0
- package/data/ux-source-evidence/account-mfa.json +62 -0
- package/data/ux-source-evidence/account-onboarding-credentials.json +52 -0
- package/data/ux-source-evidence/account-onboarding-legal.json +56 -0
- package/data/ux-source-evidence/account-onboarding-orientation.json +66 -0
- package/data/ux-source-evidence/account-onboarding-profile.json +50 -0
- package/data/ux-source-evidence/account-onboarding.json +69 -0
- package/data/ux-source-evidence/account-reset-password.json +27 -0
- package/data/ux-source-evidence/account-session-expired.json +62 -0
- package/data/ux-source-evidence/account-signup.json +33 -0
- package/data/ux-source-evidence/account-verify-email.json +34 -0
- package/data/ux-source-evidence/account-welcome.json +41 -0
- package/data/ux-source-evidence/admin-analytics.json +54 -0
- package/data/ux-source-evidence/admin-audit.json +31 -0
- package/data/ux-source-evidence/admin-broadcasts-new.json +36 -0
- package/data/ux-source-evidence/admin-broadcasts.json +41 -0
- package/data/ux-source-evidence/admin-closures.json +52 -0
- package/data/ux-source-evidence/admin-devices.json +51 -0
- package/data/ux-source-evidence/admin-escalations.json +35 -0
- package/data/ux-source-evidence/admin-incidents.json +54 -0
- package/data/ux-source-evidence/admin-loading.json +17 -0
- package/data/ux-source-evidence/admin-onboarding-detail.json +80 -0
- package/data/ux-source-evidence/admin-onboarding.json +62 -0
- package/data/ux-source-evidence/admin-payments.json +45 -0
- package/data/ux-source-evidence/admin-providers.json +38 -0
- package/data/ux-source-evidence/admin-settings.json +18 -0
- package/data/ux-source-evidence/admin-subscriptions-detail-allocate.json +42 -0
- package/data/ux-source-evidence/admin-subscriptions-detail.json +48 -0
- package/data/ux-source-evidence/admin-subscriptions-new.json +43 -0
- package/data/ux-source-evidence/admin-subscriptions.json +41 -0
- package/data/ux-source-evidence/admin-transactions.json +41 -0
- package/data/ux-source-evidence/error.json +30 -0
- package/data/ux-source-evidence/loading.json +19 -0
- package/data/ux-source-evidence/marketing-blog.json +48 -0
- package/data/ux-source-evidence/marketing-contact.json +36 -0
- package/data/ux-source-evidence/marketing.json +64 -0
- package/data/ux-source-evidence/not-found.json +28 -0
- package/data/ux-source-evidence/portal-loading.json +17 -0
- package/data/ux-source-evidence/portal-mentor-messages.json +31 -0
- package/data/ux-source-evidence/portal-mentor-profile.json +23 -0
- package/data/ux-source-evidence/portal-mentor-requests-detail.json +54 -0
- package/data/ux-source-evidence/portal-mentor-sessions-detail-assess.json +42 -0
- package/data/ux-source-evidence/portal-mentor-settings.json +18 -0
- package/data/ux-source-evidence/portal-mentor.json +38 -0
- package/data/ux-source-evidence/portal-sessions-detail-live.json +48 -0
- package/data/ux-source-evidence/portal-sponsor-billing.json +28 -0
- package/data/ux-source-evidence/portal-sponsor-profile.json +22 -0
- package/data/ux-source-evidence/portal-sponsor-sessions.json +28 -0
- package/data/ux-source-evidence/portal-sponsor-settings.json +18 -0
- package/data/ux-source-evidence/portal-sponsor-trainees.json +36 -0
- package/data/ux-source-evidence/portal-sponsor.json +52 -0
- package/data/ux-source-evidence/portal-trainee-competency.json +42 -0
- package/data/ux-source-evidence/portal-trainee-credits.json +40 -0
- package/data/ux-source-evidence/portal-trainee-mentors.json +48 -0
- package/data/ux-source-evidence/portal-trainee-messages.json +32 -0
- package/data/ux-source-evidence/portal-trainee-notifications.json +35 -0
- package/data/ux-source-evidence/portal-trainee-profile.json +26 -0
- package/data/ux-source-evidence/portal-trainee-request.json +54 -0
- package/data/ux-source-evidence/portal-trainee-sessions-detail-feedback.json +36 -0
- package/data/ux-source-evidence/portal-trainee-sessions-detail-precheck.json +42 -0
- package/data/ux-source-evidence/portal-trainee-sessions-detail.json +55 -0
- package/data/ux-source-evidence/portal-trainee-sessions.json +36 -0
- package/data/ux-source-evidence/portal-trainee-settings-notifications.json +38 -0
- package/data/ux-source-evidence/portal-trainee-settings.json +18 -0
- package/data/ux-source-evidence/portal-trainee.json +44 -0
- package/data/ux-source-evidence/portal-tv-detail.json +39 -0
- package/data/ux-source-evidence/portal-tv.json +44 -0
- package/data/ux-source-evidence/shared-admin-admin-shell.json +35 -0
- package/data/ux-source-evidence/shared-auth-auth-primitives.json +105 -0
- package/data/ux-source-evidence/shared-auth-auth-shell.json +25 -0
- package/data/ux-source-evidence/shared-auth-onboarding-primitives.json +52 -0
- package/data/ux-source-evidence/shared-auth-onboarding-shell.json +28 -0
- package/data/ux-source-evidence/shared-auth-status-indicator.json +41 -0
- package/data/ux-source-evidence/shared-portal-empty-state.json +24 -0
- package/data/ux-source-evidence/shared-portal-loading-skeleton.json +27 -0
- package/data/ux-source-evidence/shared-portal-notifications-dropdown.json +39 -0
- package/data/ux-source-evidence/shared-portal-portal-shell.json +36 -0
- package/data/ux-source-evidence/shared-portal-profile-form.json +32 -0
- package/data/ux-source-evidence/shared-portal-settings-view.json +81 -0
- package/data/ux-source-evidence/shared-portal-usage-sparkline.json +18 -0
- package/data/ux-source-evidence/shared-portal-user-menu.json +34 -0
- package/lib/api.mjs +228 -4
- package/package.json +2 -2
package/lib/api.mjs
CHANGED
|
@@ -58,6 +58,186 @@ export function listTemplates() {
|
|
|
58
58
|
return readJson(join(paths().manifests, 'templates.json')).templates
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
export function getUxPatternRegistry() {
|
|
62
|
+
return readJson(join(paths().manifests, 'ux-patterns.json'))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function listUxPatterns() {
|
|
66
|
+
return getUxPatternRegistry().patterns
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function listUiParts() {
|
|
70
|
+
return readJson(join(paths().manifests, 'ui-patterns.json')).parts
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function listUxExamples() {
|
|
74
|
+
return readJson(join(paths().manifests, 'ux-examples.json')).examples
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Complete source evidence, distinct from runnable Ceramic examples. */
|
|
78
|
+
export function listUxSourcePatterns() {
|
|
79
|
+
const inventory = readJson(join(paths().manifests, 'ux-source-inventory.json'))
|
|
80
|
+
const examples = listUxExamples()
|
|
81
|
+
return inventory.entries.map(({ evidence, ...entry }) => ({ ...entry,
|
|
82
|
+
examples: examples.filter(example => (example.sourcePatterns ?? [example.sourcePattern]).includes(entry.id)).map(example => example.id),
|
|
83
|
+
}))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function findUxSourcePatterns(goal) {
|
|
87
|
+
if (typeof goal !== 'string' || !goal.trim() || goal.length > 2000) return { ok: false, code: 'INVALID_GOAL' }
|
|
88
|
+
const words = goal.toLowerCase().split(/\s+/).filter(word => word.length > 1)
|
|
89
|
+
const candidates = listUxSourcePatterns().map(entry => {
|
|
90
|
+
const text = [entry.id, entry.category, ...Object.values(entry.title), ...(entry.keywords ?? []), ...entry.fragments.map(f => f.id)].join(' ').toLowerCase()
|
|
91
|
+
const aliases = (entry.keywords ?? []).filter(keyword => goal.toLowerCase().includes(keyword.toLowerCase()))
|
|
92
|
+
return { ...entry, score: words.filter(word => text.includes(word)).length + aliases.length }
|
|
93
|
+
}).filter(entry => entry.score > 0).sort((a,b) => b.score - a.score)
|
|
94
|
+
return { ok: true, candidates, guidance: 'AI selects only relevant fragments, without a user selection wizard. Prefer runnable examples when available. source-indexed means original evidence only, NOT an implemented Ceramic component. Use get_ux_source_part for one named fragment. No match means use normal Ceramic components.' }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function getUxSourcePart(input) {
|
|
98
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => !['id', 'part', 'theme'].includes(key))) return { ok: false, code: 'INVALID_SOURCE_PART_INPUT' }
|
|
99
|
+
const inventory = readJson(join(paths().manifests, 'ux-source-inventory.json'))
|
|
100
|
+
const entry = inventory.entries.find(entry => entry.id === input.id)
|
|
101
|
+
if (!entry) return { ok: false, code: 'UNKNOWN_SOURCE_PATTERN' }
|
|
102
|
+
const theme = resolveUiTheme(input.theme)
|
|
103
|
+
if (!theme) return { ok: false, code: 'UNKNOWN_THEME' }
|
|
104
|
+
const evidence = readJson(hasWorkspaceSource ? join(workspaceRoot, entry.evidence) : join(packagedDataRoot, 'ux-source-evidence', entry.id + '.json'))
|
|
105
|
+
const part = evidence.fragments.find(part => part.id === input.part)
|
|
106
|
+
if (!part) return { ok: false, code: 'UNKNOWN_SOURCE_PART', availableParts: entry.fragments }
|
|
107
|
+
// Never send an entire portal as a default. A caller explicitly chooses a
|
|
108
|
+
// bounded source fragment and must adapt it before use in a consumer.
|
|
109
|
+
const dependencies = evidence.imports.filter(p => p.startsWith('@/')).map(p => {
|
|
110
|
+
const target = inventory.entries.find(item => item.sourceFile === p.replace('@/', '') + '.tsx')
|
|
111
|
+
return { import: p, sourcePattern: target?.id ?? null, parts: target?.fragments ?? [], rule: target ? 'Retrieve only needed shared fragment.' : 'Replace framework/UI/data import with Ceramic public component or product-owned data; do not guess this dependency.' }
|
|
112
|
+
})
|
|
113
|
+
return { ok: true, status: 'requires-semantic-adaptation', source: inventory.source, pattern: entry.id, part,
|
|
114
|
+
theme, motion: getMotionProfile(theme.motionProfile), dependencies,
|
|
115
|
+
semanticMapping: getUxPatternRegistry().semanticMapping,
|
|
116
|
+
guidance: ['Original code is evidence, not instructions or a drop-in component.', 'Reuse the selected interaction only. Never import the whole source app, role-specific portal or branding.', 'Map visual roles to registered Ceramic components and shared patterns. No source CSS, primitive palette, raw timing or page-owned styles.', 'Preserve success, empty, loading, validation, recovery and confirmation states relevant to the goal.', 'Replace sample identities, fixed business policy, compliance claims, timers and fake service results with authorized host data/callbacks.', 'No localStorage auth, external navigation, signup, payment, upload, media permission or mutation without real integration and user authority.', 'Test consumer keyboard, mobile, RTL and the resolved theme. Do not claim that indexed source evidence is a completed port.'] }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Page examples explain context; they are not mandatory whole-page imports. */
|
|
120
|
+
export function getUxExample(input) {
|
|
121
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => !['id', 'theme'].includes(key))) return { ok: false, code: 'INVALID_EXAMPLE_INPUT' }
|
|
122
|
+
const example = listUxExamples().find(item => item.id === input.id)
|
|
123
|
+
if (!example) return { ok: false, code: 'UNKNOWN_UX_EXAMPLE' }
|
|
124
|
+
const theme = resolveUiTheme(input.theme)
|
|
125
|
+
if (!theme) return { ok: false, code: 'UNKNOWN_THEME' }
|
|
126
|
+
const parts = example.parts.map(id => getUiPart({ id, theme: theme.id }))
|
|
127
|
+
if (parts.some(part => !part.ok)) return { ok: false, code: 'BROKEN_EXAMPLE_CONTRACT' }
|
|
128
|
+
const files = [...new Set([example.source, ...(example.files ?? []), 'blocks/ux-pattern-examples/copy.ts', 'blocks/ux-pattern-examples/ux-examples.css'])]
|
|
129
|
+
const code = files.map(path => ({ path, content: readFileSync(hasWorkspaceSource ? join(workspaceRoot, path) : join(packagedDataRoot, 'ui-parts', path), 'utf8') }))
|
|
130
|
+
for (const file of parts.flatMap(part => part.code)) if (!code.some(item => item.path === file.path)) code.push(file)
|
|
131
|
+
return { ok: true, example, theme, motion: getMotionProfile(theme.motionProfile), parts, code,
|
|
132
|
+
install: 'npm install @utopia-studio-design/design-system',
|
|
133
|
+
availability: 'Example bundles and MCP tools are available in Ceramic CLI 0.8.0 and later.',
|
|
134
|
+
guidance: 'Inspect the contextual page example, then reuse only the parts that fit the user goal. Never require the user to browse or select examples first. Keep shared semantic CSS and the resolved user/room theme; Cloudblur is the fallback. Replace fixture data with authorized product data and callbacks. Demo signup, consent, completion and review do not create accounts, save, approve or deploy anything.' }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Candidate discovery, not a command to add every matching part. The coding
|
|
138
|
+
* agent decides fit in context; the user never has to choose these parts. */
|
|
139
|
+
export function findUiParts(goal) {
|
|
140
|
+
if (typeof goal !== 'string' || !goal.trim() || goal.length > 2000) return { ok: false, code: 'INVALID_GOAL' }
|
|
141
|
+
const text = goal.toLowerCase()
|
|
142
|
+
const candidates = listUiParts().map(part => {
|
|
143
|
+
const matches = part.keywords.filter(keyword => text.includes(keyword.toLowerCase()))
|
|
144
|
+
return { ...part, score: matches.length, matchedIntents: matches }
|
|
145
|
+
}).filter(part => part.score > 0).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
|
146
|
+
const sourceCandidates = findUxSourcePatterns(goal).candidates.slice(0, 8)
|
|
147
|
+
return { ok: true, candidates, sourceCandidates, guidance: 'The AI must check useWhen/avoidWhen against the goal and use only needed parts. Never ask the user to pick parts. Prefer ready Ceramic parts. sourceCandidates expose additional original interactions requiring semantic adaptation via get_ux_source_part, not whole-page import. If nothing fits, use ordinary components; never force a pattern.' }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function resolveUiTheme(theme) {
|
|
151
|
+
const room = getRoomContext()
|
|
152
|
+
const id = theme ?? room?.themeId ?? room?.theme?.id ?? 'utopia-cloudblur'
|
|
153
|
+
// An invalid explicit/custom theme is not permission to silently rebrand.
|
|
154
|
+
return typeof id === 'string' && id.trim() ? getTheme(id) : null
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function getUiPart(input) {
|
|
158
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => !['id', 'theme'].includes(key))) return { ok: false, code: 'INVALID_PART_INPUT' }
|
|
159
|
+
const part = listUiParts().find(part => part.id === input.id)
|
|
160
|
+
if (!part) return { ok: false, code: 'UNKNOWN_UI_PART' }
|
|
161
|
+
const theme = resolveUiTheme(input.theme)
|
|
162
|
+
if (!theme) return { ok: false, code: 'UNKNOWN_THEME' }
|
|
163
|
+
const files = [...new Set([part.source, part.styles, ...(part.files ?? []), ...(part.id === 'choice-cards' || part.id === 'list-filters' || part.id === 'review-list' ? ['blocks/semantic-pattern-parts/types.ts'] : [])])]
|
|
164
|
+
const code = files.map(path => ({ path, content: readFileSync(hasWorkspaceSource ? join(workspaceRoot, path) : join(packagedDataRoot, 'ui-parts', path), 'utf8') }))
|
|
165
|
+
return { ok: true, part, theme, motion: getMotionProfile(theme.motionProfile), components: part.components.map(getComponent), code, guidance: 'Copy only this reusable export and its dependencies. All labels, data and callbacks are product-owned. Do not copy a screen shell or sample identities. CSS belongs to the shared composition, not the page. Apply the resolved theme to the common host. Never turn fixture results into real authorization.' }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function prepareUiComposition(input) {
|
|
169
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => !['goal', 'theme'].includes(key))) return { ok: false, code: 'INVALID_COMPOSITION_INPUT' }
|
|
170
|
+
const found = findUiParts(input.goal)
|
|
171
|
+
if (!found.ok) return found
|
|
172
|
+
const theme = resolveUiTheme(input.theme)
|
|
173
|
+
if (!theme) return { ok: false, code: 'UNKNOWN_THEME' }
|
|
174
|
+
const selected = found.candidates.slice(0, 4)
|
|
175
|
+
const parts = selected.map(part => getUiPart({ id: part.id, theme: theme.id }))
|
|
176
|
+
const ids = selected.map(part => part.id)
|
|
177
|
+
const bindings = []
|
|
178
|
+
if (ids.includes('list-filters') && ids.includes('review-list')) bindings.push({ from: 'ListFilters.query/status', via: 'Host-owned state and authorized filtered query', to: 'ReviewList.rows', rule: 'Reset both filter values; render empty/loading/error states from the query result.' })
|
|
179
|
+
if (ids.includes('metric-summary') && ids.includes('review-list')) bindings.push({ from: 'Host-authorized rows and scope', to: 'KPIGrid.metrics', rule: 'Calculate counts for the same filter scope. Never invent totals from unloaded/paginated rows.' })
|
|
180
|
+
if (ids.includes('outcome-notice')) bindings.push({ from: 'Confirmed service result', to: 'OutcomeNotice', rule: 'Show success only after confirmation; retry must call a real recovery callback.' })
|
|
181
|
+
return { ok: true, status: selected.length ? 'composition-proposal' : found.sourceCandidates.length ? 'source-adaptation-candidates' : 'no-pattern-needed', goal: input.goal.trim(), theme: theme.id, parts, sourceCandidates: found.sourceCandidates, bindings, guidance: found.guidance, nextSteps: ['AI checks whether each candidate fits; discard unrelated parts without making the user choose.', 'When a relevant interaction exists only in sourceCandidates, use get_ux_source_part to inspect just that fragment and adapt its semantics; do not call it a ready Ceramic port.', 'Compose the selected exports around host-owned product data and callbacks. No complete portal or page import.', 'Use existing Ceramic components for anything not covered; patterns are optional.', 'Confirm external side effects separately; this read-only proposal does not build, save, approve or deploy.', 'Verify keyboard, responsive layout, loading/empty/error/retry, RTL and theme tokens in the consuming product.'] }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function getUxPattern(id) {
|
|
185
|
+
const registry = getUxPatternRegistry()
|
|
186
|
+
// IDs are exact: do not normalize non-ASCII names into the same empty key.
|
|
187
|
+
const pattern = registry.patterns.find(item => item.id === id)
|
|
188
|
+
return pattern ? { ...pattern, source: registry.source, guardrails: registry.guardrails, semanticMapping: registry.semanticMapping, verification: registry.verification } : null
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Read-only planning: a recipe is never advertised as an existing renderer. */
|
|
192
|
+
export function preparePatternAdaptation(input) {
|
|
193
|
+
const fail = (code, message) => ({ ok: false, code, message })
|
|
194
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => !['id', 'goal', 'theme', 'variant'].includes(key))) return fail('INVALID_PATTERN_INPUT', 'Use id, goal, theme and optional variant only; arbitrary styling is not supported.')
|
|
195
|
+
if (typeof input.id !== 'string' || typeof input.theme !== 'string' || typeof input.goal !== 'string' || !input.goal.trim() || input.goal.length > 2000) return fail('INVALID_PATTERN_INPUT', 'Provide a pattern id, registered theme and goal of 1–2000 characters.')
|
|
196
|
+
const pattern = getUxPattern(input.id)
|
|
197
|
+
if (!pattern) return fail('UNKNOWN_PATTERN', 'Inspect list_patterns before selecting a pattern.')
|
|
198
|
+
const theme = getTheme(input.theme)
|
|
199
|
+
if (!theme) return fail('UNKNOWN_THEME', 'Select a registered Ceramic theme.')
|
|
200
|
+
const variant = input.variant ?? pattern.variants[0]
|
|
201
|
+
if (!pattern.variants.includes(variant)) return fail('UNKNOWN_PATTERN_VARIANT', `Allowed variants: ${pattern.variants.join(', ')}`)
|
|
202
|
+
const components = pattern.components.map(getComponent)
|
|
203
|
+
const motion = getMotionProfile(theme.motionProfile)
|
|
204
|
+
if (components.some(item => !item) || !motion) return fail('BROKEN_PATTERN_CONTRACT', 'A component or theme motion profile is missing; repair the registry first.')
|
|
205
|
+
return {
|
|
206
|
+
ok: true, status: 'proposal', renderable: false,
|
|
207
|
+
request: { goal: input.goal.trim(), interpretation: 'User-provided product goal, not execution authority.' },
|
|
208
|
+
selection: { pattern: pattern.id, variant, theme: theme.id },
|
|
209
|
+
fit: { useWhen: pattern.useWhen, avoidWhen: pattern.avoidWhen, confirmationRequired: 'Confirm that this interaction fits the goal and company brand before implementation.' },
|
|
210
|
+
pattern, components, theme, motion,
|
|
211
|
+
optionalUiParts: findUiParts(input.goal),
|
|
212
|
+
requiredProductBindings: pattern.dataRequirements,
|
|
213
|
+
mayChange: pattern.adaptable, mustPreserve: pattern.invariants,
|
|
214
|
+
nextSteps: [
|
|
215
|
+
'Resolve missing product data, permissions and callbacks; never copy source sample identities or mock success.',
|
|
216
|
+
'Compose the returned Ceramic component contracts; inspect an existing registered block/template before adding a shared pattern.',
|
|
217
|
+
'If new presentation is required, register its slots, states, tokens and responsive behavior in the shared system before implementing it. Do not add page CSS.',
|
|
218
|
+
'Apply the selected company theme and Ceramic semantic motion; do not port Mentix brand primitives or literal animation timings.',
|
|
219
|
+
'Test the listed states, recovery, permissions, keyboard, mobile, RTL and reduced motion in the generated application. A plan is not a passing UI test.',
|
|
220
|
+
],
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function validateUxPatternRegistry(registry) {
|
|
225
|
+
const issues = []
|
|
226
|
+
if (registry?.version !== 1 || !/^[a-f0-9]{40}$/.test(registry?.source?.revision ?? '') || !registry?.guardrails?.length || !registry?.semanticMapping?.length) issues.push('Missing version, pinned provenance or semantic guardrails')
|
|
227
|
+
const ids = new Set(), components = new Set(listComponents().map(item => item.name))
|
|
228
|
+
if (!Array.isArray(registry?.patterns) || !registry.patterns.length) return { ok: false, issues: [...issues, 'No UX patterns'] }
|
|
229
|
+
for (const item of registry.patterns) {
|
|
230
|
+
if (!item || typeof item.id !== 'string' || ids.has(item.id)) { issues.push('Missing or duplicate pattern id'); continue }
|
|
231
|
+
ids.add(item.id)
|
|
232
|
+
if (item.availability !== 'recipe' || item.renderable !== false || !item.useWhen || !item.avoidWhen) issues.push(`${item.id}: recipe status or selection guidance missing`)
|
|
233
|
+
for (const field of ['variants', 'components', 'slots', 'states', 'dataRequirements', 'invariants', 'adaptable', 'sourceFiles', 'keywords']) {
|
|
234
|
+
if (!Array.isArray(item[field]) || !item[field].length || item[field].some(value => typeof value !== 'string' || !value)) issues.push(`${item.id}: ${field} incomplete`)
|
|
235
|
+
}
|
|
236
|
+
if (Array.isArray(item.components) && item.components.some(name => !components.has(name))) issues.push(`${item.id}: unknown Ceramic component`)
|
|
237
|
+
}
|
|
238
|
+
return { ok: issues.length === 0, issues }
|
|
239
|
+
}
|
|
240
|
+
|
|
61
241
|
export function listBlocks() {
|
|
62
242
|
return readJson(join(paths().manifests, 'blocks.json')).blocks
|
|
63
243
|
}
|
|
@@ -168,6 +348,41 @@ export function prepareLogoMakerRun({ skillId, brandName, brief, referenceIds =
|
|
|
168
348
|
}
|
|
169
349
|
}
|
|
170
350
|
|
|
351
|
+
export function getBrandbookSkill() {
|
|
352
|
+
return readJson(join(paths().manifests, 'brandbook-skill.json'))
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export function prepareBrandbookRun({ brandName, brief }, root = process.cwd()) {
|
|
356
|
+
const room = getRoomContext(root)
|
|
357
|
+
const resolvedBrandName = String(brandName || room?.brandName || '').trim()
|
|
358
|
+
const resolvedBrief = String(brief || '').trim()
|
|
359
|
+
if (!resolvedBrandName) return { ok: false, code: 'BRAND_NAME_REQUIRED', message: 'Provide brandName or connect a Ceramic Brand Room.' }
|
|
360
|
+
if (resolvedBrief.length < 12) return { ok: false, code: 'BRIEF_TOO_SHORT', message: 'Provide at least 12 characters of brand intent.' }
|
|
361
|
+
const skill = getBrandbookSkill()
|
|
362
|
+
const prompt = [
|
|
363
|
+
`Create a Ceramic brand-book direction session for ${resolvedBrandName}.`, resolvedBrief, '',
|
|
364
|
+
'Follow the Ceramic Brandbook skill contract:',
|
|
365
|
+
'- Read available Brand Room context and supplied assets; a logo is optional.',
|
|
366
|
+
'- Present exactly three materially different directions labeled A, B, and C.',
|
|
367
|
+
'- Compare typography, semantic color roles, imagery, motion, Marketing expression, Solution expression, fit, and tradeoff.',
|
|
368
|
+
'- Refine the selected route with the user before writing the final BRAND.md.',
|
|
369
|
+
'- Produce exactly one editorial source, BRAND.md, with complete English and Arabic parity unless the user changes the language scope.',
|
|
370
|
+
'- Use the active chat image generator by default; keep provider choice outside the brand contract and record actual provenance.',
|
|
371
|
+
'- Choose one to three brand-fit Solution applications from phone, desktop, and environment.',
|
|
372
|
+
'- Generate a dedicated scene for every photographic desktop or environment application with the active image skill; do not reuse general brand photography.',
|
|
373
|
+
'- Put mockups directly on the semantic brand canvas without generic backing panels or caption overlays.',
|
|
374
|
+
].join('\n')
|
|
375
|
+
return { ok: true, toolId: skill.id, phase: 'direction-proposals', room,
|
|
376
|
+
input: { brandName: resolvedBrandName, brief: resolvedBrief }, prompt,
|
|
377
|
+
workflow: skill.workflow, rules: skill.rules,
|
|
378
|
+
source: {
|
|
379
|
+
provider: 'github', repository: skill.repository, branch: skill.branch, revision: skill.revision,
|
|
380
|
+
skillPath: skill.skillPath, sourceDocument: skill.sourceDocument,
|
|
381
|
+
instruction: 'Resolve the pinned GitHub revision and read SKILL.md before presenting directions.',
|
|
382
|
+
},
|
|
383
|
+
nextAction: 'Present A, B, and C for user refinement. Do not build or save the final brand book yet.' }
|
|
384
|
+
}
|
|
385
|
+
|
|
171
386
|
export function listDocs() {
|
|
172
387
|
return readdirSync(paths().docs)
|
|
173
388
|
.filter((name) => name.endsWith('.md'))
|
|
@@ -193,6 +408,10 @@ export function search(query) {
|
|
|
193
408
|
const terms = String(query).toLowerCase().split(/\s+/).filter(Boolean)
|
|
194
409
|
const score = (text) => terms.reduce((total, term) => total + (text.toLowerCase().includes(term) ? 1 : 0), 0)
|
|
195
410
|
const results = [
|
|
411
|
+
...listUiParts().map((item) => ({ kind: 'ui-part', id: item.id, title: item.title.en, summary: [Object.values(item.title).join(' '), item.useWhen, ...item.keywords].join(' '), item })),
|
|
412
|
+
...listUxExamples().map(item => ({ kind: 'ux-example', id: item.id, title: item.title.en, summary: [...Object.values(item.title), ...Object.values(item.description), ...item.parts].join(' '), item })),
|
|
413
|
+
...listUxSourcePatterns().map(item => ({ kind: 'ux-source-pattern', id: item.id, title: item.title.en, summary: [item.category, ...Object.values(item.title), ...item.fragments.map(f => f.id)].join(' '), item })),
|
|
414
|
+
...listUxPatterns().map((item) => ({ kind: 'ux-pattern', id: item.id, title: item.title, summary: [item.useWhen, ...item.keywords].join(' '), item })),
|
|
196
415
|
...listComponents().map((item) => ({ kind: 'component', id: item.name, title: item.name, summary: [...item.useWhen, item.category].join(' '), item })),
|
|
197
416
|
...listBlockDefinitions().map((item) => ({ kind: 'block-definition', id: item.id, title: item.title, summary: `${item.category} ${item.description} ${item.availability}`, item })),
|
|
198
417
|
...listBlocks().map((item) => ({ kind: 'block', id: item.id, title: item.title, summary: `${item.category} ${item.purpose}`, item })),
|
|
@@ -200,10 +419,11 @@ export function search(query) {
|
|
|
200
419
|
...listThemes().map((item) => ({ kind: 'theme', id: item.id, title: item.name, summary: item.role, item })),
|
|
201
420
|
...listMotionProfiles().map((item) => ({ kind: 'motion-profile', id: item.id, title: item.label, summary: `${item.description} ${item.rules.join(' ')}`, item })),
|
|
202
421
|
...listLogoSkills().map((item) => ({ kind: 'logo-skill', id: item.id, title: item.name, summary: `${item.tagline} ${item.description} ${item.guidance}`, item })),
|
|
422
|
+
{ kind: 'brandbook-skill', id: getBrandbookSkill().id, title: getBrandbookSkill().name, summary: 'Brand identity BRAND.md three directions A B C marketing solution mockups image generation Arabic', item: getBrandbookSkill() },
|
|
203
423
|
...listDocs().map((topic) => ({ kind: 'docs', id: topic, title: topic, summary: getDoc(topic)?.content.slice(0, 500) ?? '', item: { topic } })),
|
|
204
424
|
]
|
|
205
425
|
return results
|
|
206
|
-
.map((result) => ({ ...result, score: score(`${result.title} ${result.summary}`) }))
|
|
426
|
+
.map((result) => ({ ...result, score: score(`${result.id} ${result.title} ${result.summary}`) }))
|
|
207
427
|
.filter((result) => result.score > 0)
|
|
208
428
|
.sort((a, b) => b.score - a.score || a.title.localeCompare(b.title))
|
|
209
429
|
.map(({ summary: _summary, ...result }) => result)
|
|
@@ -223,6 +443,7 @@ export function capabilityManifest() {
|
|
|
223
443
|
{ name: 'init', args: ['[directory]'], flags: ['--yes', '--theme <id>'], responseType: 'init-result' },
|
|
224
444
|
{ name: 'room', args: ['connect'], flags: ['--id <id>', '--slug <slug>', '--name <room>', '--brand <brand>', '--source <mode>', '--client <client>', '--directory <path>'], responseType: 'room-connect-result' },
|
|
225
445
|
{ name: 'search', args: ['<query>'], flags: [], responseType: 'search-results' },
|
|
446
|
+
{ name: 'pattern', args: ['<id>|--list'], flags: ['--goal <text>', '--theme <id>', '--variant <id>'], responseType: 'ux-pattern|ux-pattern-list|pattern-adaptation-plan' },
|
|
226
447
|
{ name: 'component', args: ['<name>|--list'], flags: [], responseType: 'component|component-list' },
|
|
227
448
|
{ name: 'registry', args: ['<block-id>|--list'], flags: ['--renderable'], responseType: 'block-definition|block-definition-list' },
|
|
228
449
|
{ name: 'layout', args: ['validate <file>', 'serialize <file>'], flags: [], responseType: 'layout-validation-result|layout-serialization-result' },
|
|
@@ -238,14 +459,14 @@ export function capabilityManifest() {
|
|
|
238
459
|
],
|
|
239
460
|
mcp: {
|
|
240
461
|
...mcpLaunch,
|
|
241
|
-
tools: ['get_room_context', 'search', 'list_components', 'get_component', 'list_block_definitions', 'get_block_definition', 'validate_layout', 'validate_agent_layout_mutation', 'list_blocks', 'get_block', 'list_templates', 'get_template', 'list_themes', 'get_theme', 'list_motion_profiles', 'get_motion_profile', 'list_logo_skills', 'get_logo_skill', 'prepare_logo_maker_run', 'list_docs', 'get_docs', 'doctor'],
|
|
462
|
+
tools: ['get_room_context', 'search', 'list_ux_source_patterns', 'find_ux_source_patterns', 'get_ux_source_part', 'list_ux_examples', 'get_ux_example', 'find_ui_parts', 'get_ui_part', 'prepare_ui_composition', 'list_patterns', 'get_pattern', 'prepare_pattern_adaptation', 'list_components', 'get_component', 'list_block_definitions', 'get_block_definition', 'validate_layout', 'validate_agent_layout_mutation', 'list_blocks', 'get_block', 'list_templates', 'get_template', 'list_themes', 'get_theme', 'list_motion_profiles', 'get_motion_profile', 'list_logo_skills', 'get_logo_skill', 'prepare_logo_maker_run', 'get_brandbook_skill', 'prepare_brandbook_run', 'list_docs', 'get_docs', 'doctor'],
|
|
242
463
|
},
|
|
243
464
|
}
|
|
244
465
|
}
|
|
245
466
|
|
|
246
467
|
export function repositoryDoctor() {
|
|
247
468
|
const required = [
|
|
248
|
-
'catalog.json', 'components.json', 'blocks.json', 'composition-registry.json', 'templates.json', 'themes.json', 'motion-profiles.json', 'logo-skills.json',
|
|
469
|
+
'catalog.json', 'components.json', 'blocks.json', 'composition-registry.json', 'templates.json', 'themes.json', 'motion-profiles.json', 'logo-skills.json', 'ux-patterns.json', 'brandbook-skill.json',
|
|
249
470
|
].map((name) => join(paths().manifests, name))
|
|
250
471
|
const docs = ['quick-start-ai', 'arabic-friendly', 'theme-authoring', 'composition-registry'].map((name) => join(paths().docs, `${name}.md`))
|
|
251
472
|
const missing = [...required, ...docs].filter((path) => !existsSync(path))
|
|
@@ -270,8 +491,10 @@ export function repositoryDoctor() {
|
|
|
270
491
|
&& definition.agentPermissions
|
|
271
492
|
) === true
|
|
272
493
|
if (!compositionContract) contractIssues.push('composition-registry.json: incomplete block or layout contract')
|
|
494
|
+
const uxPatterns = missing.includes(join(paths().manifests, 'ux-patterns.json')) ? { ok: false, issues: ['Missing UX pattern registry'] } : validateUxPatternRegistry(getUxPatternRegistry())
|
|
495
|
+
contractIssues.push(...uxPatterns.issues)
|
|
273
496
|
return {
|
|
274
|
-
ok: missing.length === 0 && motionContract && compositionContract,
|
|
497
|
+
ok: missing.length === 0 && motionContract && compositionContract && uxPatterns.ok,
|
|
275
498
|
checks: {
|
|
276
499
|
manifests: required.every(existsSync),
|
|
277
500
|
docs: docs.every(existsSync),
|
|
@@ -279,6 +502,7 @@ export function repositoryDoctor() {
|
|
|
279
502
|
blocks: listBlocks().length,
|
|
280
503
|
blockDefinitions: compositionRegistry?.definitions?.length ?? 0,
|
|
281
504
|
compositionContract,
|
|
505
|
+
uxPatternContract: uxPatterns.ok,
|
|
282
506
|
layoutSchemaVersion: compositionRegistry?.layoutSchemaVersion ?? null,
|
|
283
507
|
templates: listTemplates().length,
|
|
284
508
|
themes: listThemes().length,
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utopia-studio-design/design-system-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "AI-readable CLI and MCP server for Ceramic Design System",
|
|
5
5
|
"ceramic": {
|
|
6
|
-
"designSystem": "^0.
|
|
6
|
+
"designSystem": "^0.11.0"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
9
|
"license": "MIT",
|