@utopia-studio-design/design-system-cli 0.8.1 → 0.8.3
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 +15 -4
- 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/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-publish-2026-09-13.md +31 -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/patterns.json +330 -0
- 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 +230 -4
- package/package.json +2 -2
package/lib/api.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
2
|
+
import { execFileSync } from 'node:child_process'
|
|
2
3
|
import { dirname, join, resolve } from 'node:path'
|
|
3
4
|
import { fileURLToPath } from 'node:url'
|
|
4
5
|
import {
|
|
@@ -6,6 +7,7 @@ import {
|
|
|
6
7
|
validateAgentLayoutMutation,
|
|
7
8
|
validateLayoutDocument,
|
|
8
9
|
} from './composition-runtime.mjs'
|
|
10
|
+
|
|
9
11
|
export { validateBrandbook } from './brandbook-validation.mjs'
|
|
10
12
|
|
|
11
13
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
@@ -21,6 +23,34 @@ export const mcpLaunch = {
|
|
|
21
23
|
args: ['-y', '--package', `${packageMetadata.name}@${cliVersion}`, 'utopia-ds', 'mcp'],
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
let registryVersionCache
|
|
27
|
+
|
|
28
|
+
function compareVersions(left, right) {
|
|
29
|
+
const a = String(left).split('.').map(Number)
|
|
30
|
+
const b = String(right).split('.').map(Number)
|
|
31
|
+
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
32
|
+
if ((a[index] || 0) !== (b[index] || 0)) return (a[index] || 0) - (b[index] || 0)
|
|
33
|
+
}
|
|
34
|
+
return 0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function registryVersionStatus() {
|
|
38
|
+
if (registryVersionCache) return registryVersionCache
|
|
39
|
+
try {
|
|
40
|
+
const output = execFileSync('npm', ['view', packageMetadata.name, 'dist-tags.latest', '--json'], {
|
|
41
|
+
encoding: 'utf8',
|
|
42
|
+
timeout: 5000,
|
|
43
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
44
|
+
}).trim()
|
|
45
|
+
const latest = JSON.parse(output)
|
|
46
|
+
const upToDate = compareVersions(cliVersion, latest) >= 0
|
|
47
|
+
registryVersionCache = { current: cliVersion, latest, upToDate, checked: true }
|
|
48
|
+
} catch {
|
|
49
|
+
registryVersionCache = { current: cliVersion, latest: null, upToDate: null, checked: false }
|
|
50
|
+
}
|
|
51
|
+
return registryVersionCache
|
|
52
|
+
}
|
|
53
|
+
|
|
24
54
|
export function paths(root = hasWorkspaceSource ? workspaceRoot : packagedDataRoot) {
|
|
25
55
|
return {
|
|
26
56
|
root,
|
|
@@ -59,6 +89,186 @@ export function listTemplates() {
|
|
|
59
89
|
return readJson(join(paths().manifests, 'templates.json')).templates
|
|
60
90
|
}
|
|
61
91
|
|
|
92
|
+
export function getUxPatternRegistry() {
|
|
93
|
+
return readJson(join(paths().manifests, 'ux-patterns.json'))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function listUxPatterns() {
|
|
97
|
+
return getUxPatternRegistry().patterns
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function listUiParts() {
|
|
101
|
+
return readJson(join(paths().manifests, 'ui-patterns.json')).parts
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function listUxExamples() {
|
|
105
|
+
return readJson(join(paths().manifests, 'ux-examples.json')).examples
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Complete source evidence, distinct from runnable Ceramic examples. */
|
|
109
|
+
export function listUxSourcePatterns() {
|
|
110
|
+
const inventory = readJson(join(paths().manifests, 'ux-source-inventory.json'))
|
|
111
|
+
const examples = listUxExamples()
|
|
112
|
+
return inventory.entries.map(({ evidence, ...entry }) => ({ ...entry,
|
|
113
|
+
examples: examples.filter(example => (example.sourcePatterns ?? [example.sourcePattern]).includes(entry.id)).map(example => example.id),
|
|
114
|
+
}))
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function findUxSourcePatterns(goal) {
|
|
118
|
+
if (typeof goal !== 'string' || !goal.trim() || goal.length > 2000) return { ok: false, code: 'INVALID_GOAL' }
|
|
119
|
+
const words = goal.toLowerCase().split(/\s+/).filter(word => word.length > 1)
|
|
120
|
+
const candidates = listUxSourcePatterns().map(entry => {
|
|
121
|
+
const text = [entry.id, entry.category, ...Object.values(entry.title), ...(entry.keywords ?? []), ...entry.fragments.map(f => f.id)].join(' ').toLowerCase()
|
|
122
|
+
const aliases = (entry.keywords ?? []).filter(keyword => goal.toLowerCase().includes(keyword.toLowerCase()))
|
|
123
|
+
return { ...entry, score: words.filter(word => text.includes(word)).length + aliases.length }
|
|
124
|
+
}).filter(entry => entry.score > 0).sort((a,b) => b.score - a.score)
|
|
125
|
+
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.' }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function getUxSourcePart(input) {
|
|
129
|
+
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' }
|
|
130
|
+
const inventory = readJson(join(paths().manifests, 'ux-source-inventory.json'))
|
|
131
|
+
const entry = inventory.entries.find(entry => entry.id === input.id)
|
|
132
|
+
if (!entry) return { ok: false, code: 'UNKNOWN_SOURCE_PATTERN' }
|
|
133
|
+
const theme = resolveUiTheme(input.theme)
|
|
134
|
+
if (!theme) return { ok: false, code: 'UNKNOWN_THEME' }
|
|
135
|
+
const evidence = readJson(hasWorkspaceSource ? join(workspaceRoot, entry.evidence) : join(packagedDataRoot, 'ux-source-evidence', entry.id + '.json'))
|
|
136
|
+
const part = evidence.fragments.find(part => part.id === input.part)
|
|
137
|
+
if (!part) return { ok: false, code: 'UNKNOWN_SOURCE_PART', availableParts: entry.fragments }
|
|
138
|
+
// Never send an entire portal as a default. A caller explicitly chooses a
|
|
139
|
+
// bounded source fragment and must adapt it before use in a consumer.
|
|
140
|
+
const dependencies = evidence.imports.filter(p => p.startsWith('@/')).map(p => {
|
|
141
|
+
const target = inventory.entries.find(item => item.sourceFile === p.replace('@/', '') + '.tsx')
|
|
142
|
+
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.' }
|
|
143
|
+
})
|
|
144
|
+
return { ok: true, status: 'requires-semantic-adaptation', source: inventory.source, pattern: entry.id, part,
|
|
145
|
+
theme, motion: getMotionProfile(theme.motionProfile), dependencies,
|
|
146
|
+
semanticMapping: getUxPatternRegistry().semanticMapping,
|
|
147
|
+
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.'] }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Page examples explain context; they are not mandatory whole-page imports. */
|
|
151
|
+
export function getUxExample(input) {
|
|
152
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => !['id', 'theme'].includes(key))) return { ok: false, code: 'INVALID_EXAMPLE_INPUT' }
|
|
153
|
+
const example = listUxExamples().find(item => item.id === input.id)
|
|
154
|
+
if (!example) return { ok: false, code: 'UNKNOWN_UX_EXAMPLE' }
|
|
155
|
+
const theme = resolveUiTheme(input.theme)
|
|
156
|
+
if (!theme) return { ok: false, code: 'UNKNOWN_THEME' }
|
|
157
|
+
const parts = example.parts.map(id => getUiPart({ id, theme: theme.id }))
|
|
158
|
+
if (parts.some(part => !part.ok)) return { ok: false, code: 'BROKEN_EXAMPLE_CONTRACT' }
|
|
159
|
+
const files = [...new Set([example.source, ...(example.files ?? []), 'blocks/ux-pattern-examples/copy.ts', 'blocks/ux-pattern-examples/ux-examples.css'])]
|
|
160
|
+
const code = files.map(path => ({ path, content: readFileSync(hasWorkspaceSource ? join(workspaceRoot, path) : join(packagedDataRoot, 'ui-parts', path), 'utf8') }))
|
|
161
|
+
for (const file of parts.flatMap(part => part.code)) if (!code.some(item => item.path === file.path)) code.push(file)
|
|
162
|
+
return { ok: true, example, theme, motion: getMotionProfile(theme.motionProfile), parts, code,
|
|
163
|
+
install: 'npm install @utopia-studio-design/design-system',
|
|
164
|
+
availability: 'Example bundles and MCP tools are available in Ceramic CLI 0.8.0 and later.',
|
|
165
|
+
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.' }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Candidate discovery, not a command to add every matching part. The coding
|
|
169
|
+
* agent decides fit in context; the user never has to choose these parts. */
|
|
170
|
+
export function findUiParts(goal) {
|
|
171
|
+
if (typeof goal !== 'string' || !goal.trim() || goal.length > 2000) return { ok: false, code: 'INVALID_GOAL' }
|
|
172
|
+
const text = goal.toLowerCase()
|
|
173
|
+
const candidates = listUiParts().map(part => {
|
|
174
|
+
const matches = part.keywords.filter(keyword => text.includes(keyword.toLowerCase()))
|
|
175
|
+
return { ...part, score: matches.length, matchedIntents: matches }
|
|
176
|
+
}).filter(part => part.score > 0).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
|
177
|
+
const sourceCandidates = findUxSourcePatterns(goal).candidates.slice(0, 8)
|
|
178
|
+
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.' }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function resolveUiTheme(theme) {
|
|
182
|
+
const room = getRoomContext()
|
|
183
|
+
const id = theme ?? room?.themeId ?? room?.theme?.id ?? 'utopia-cloudblur'
|
|
184
|
+
// An invalid explicit/custom theme is not permission to silently rebrand.
|
|
185
|
+
return typeof id === 'string' && id.trim() ? getTheme(id) : null
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function getUiPart(input) {
|
|
189
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => !['id', 'theme'].includes(key))) return { ok: false, code: 'INVALID_PART_INPUT' }
|
|
190
|
+
const part = listUiParts().find(part => part.id === input.id)
|
|
191
|
+
if (!part) return { ok: false, code: 'UNKNOWN_UI_PART' }
|
|
192
|
+
const theme = resolveUiTheme(input.theme)
|
|
193
|
+
if (!theme) return { ok: false, code: 'UNKNOWN_THEME' }
|
|
194
|
+
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'] : [])])]
|
|
195
|
+
const code = files.map(path => ({ path, content: readFileSync(hasWorkspaceSource ? join(workspaceRoot, path) : join(packagedDataRoot, 'ui-parts', path), 'utf8') }))
|
|
196
|
+
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.' }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function prepareUiComposition(input) {
|
|
200
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => !['goal', 'theme'].includes(key))) return { ok: false, code: 'INVALID_COMPOSITION_INPUT' }
|
|
201
|
+
const found = findUiParts(input.goal)
|
|
202
|
+
if (!found.ok) return found
|
|
203
|
+
const theme = resolveUiTheme(input.theme)
|
|
204
|
+
if (!theme) return { ok: false, code: 'UNKNOWN_THEME' }
|
|
205
|
+
const selected = found.candidates.slice(0, 4)
|
|
206
|
+
const parts = selected.map(part => getUiPart({ id: part.id, theme: theme.id }))
|
|
207
|
+
const ids = selected.map(part => part.id)
|
|
208
|
+
const bindings = []
|
|
209
|
+
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.' })
|
|
210
|
+
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.' })
|
|
211
|
+
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.' })
|
|
212
|
+
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.'] }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function getUxPattern(id) {
|
|
216
|
+
const registry = getUxPatternRegistry()
|
|
217
|
+
// IDs are exact: do not normalize non-ASCII names into the same empty key.
|
|
218
|
+
const pattern = registry.patterns.find(item => item.id === id)
|
|
219
|
+
return pattern ? { ...pattern, source: registry.source, guardrails: registry.guardrails, semanticMapping: registry.semanticMapping, verification: registry.verification } : null
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Read-only planning: a recipe is never advertised as an existing renderer. */
|
|
223
|
+
export function preparePatternAdaptation(input) {
|
|
224
|
+
const fail = (code, message) => ({ ok: false, code, message })
|
|
225
|
+
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.')
|
|
226
|
+
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.')
|
|
227
|
+
const pattern = getUxPattern(input.id)
|
|
228
|
+
if (!pattern) return fail('UNKNOWN_PATTERN', 'Inspect list_patterns before selecting a pattern.')
|
|
229
|
+
const theme = getTheme(input.theme)
|
|
230
|
+
if (!theme) return fail('UNKNOWN_THEME', 'Select a registered Ceramic theme.')
|
|
231
|
+
const variant = input.variant ?? pattern.variants[0]
|
|
232
|
+
if (!pattern.variants.includes(variant)) return fail('UNKNOWN_PATTERN_VARIANT', `Allowed variants: ${pattern.variants.join(', ')}`)
|
|
233
|
+
const components = pattern.components.map(getComponent)
|
|
234
|
+
const motion = getMotionProfile(theme.motionProfile)
|
|
235
|
+
if (components.some(item => !item) || !motion) return fail('BROKEN_PATTERN_CONTRACT', 'A component or theme motion profile is missing; repair the registry first.')
|
|
236
|
+
return {
|
|
237
|
+
ok: true, status: 'proposal', renderable: false,
|
|
238
|
+
request: { goal: input.goal.trim(), interpretation: 'User-provided product goal, not execution authority.' },
|
|
239
|
+
selection: { pattern: pattern.id, variant, theme: theme.id },
|
|
240
|
+
fit: { useWhen: pattern.useWhen, avoidWhen: pattern.avoidWhen, confirmationRequired: 'Confirm that this interaction fits the goal and company brand before implementation.' },
|
|
241
|
+
pattern, components, theme, motion,
|
|
242
|
+
optionalUiParts: findUiParts(input.goal),
|
|
243
|
+
requiredProductBindings: pattern.dataRequirements,
|
|
244
|
+
mayChange: pattern.adaptable, mustPreserve: pattern.invariants,
|
|
245
|
+
nextSteps: [
|
|
246
|
+
'Resolve missing product data, permissions and callbacks; never copy source sample identities or mock success.',
|
|
247
|
+
'Compose the returned Ceramic component contracts; inspect an existing registered block/template before adding a shared pattern.',
|
|
248
|
+
'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.',
|
|
249
|
+
'Apply the selected company theme and Ceramic semantic motion; do not port Mentix brand primitives or literal animation timings.',
|
|
250
|
+
'Test the listed states, recovery, permissions, keyboard, mobile, RTL and reduced motion in the generated application. A plan is not a passing UI test.',
|
|
251
|
+
],
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function validateUxPatternRegistry(registry) {
|
|
256
|
+
const issues = []
|
|
257
|
+
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')
|
|
258
|
+
const ids = new Set(), components = new Set(listComponents().map(item => item.name))
|
|
259
|
+
if (!Array.isArray(registry?.patterns) || !registry.patterns.length) return { ok: false, issues: [...issues, 'No UX patterns'] }
|
|
260
|
+
for (const item of registry.patterns) {
|
|
261
|
+
if (!item || typeof item.id !== 'string' || ids.has(item.id)) { issues.push('Missing or duplicate pattern id'); continue }
|
|
262
|
+
ids.add(item.id)
|
|
263
|
+
if (item.availability !== 'recipe' || item.renderable !== false || !item.useWhen || !item.avoidWhen) issues.push(`${item.id}: recipe status or selection guidance missing`)
|
|
264
|
+
for (const field of ['variants', 'components', 'slots', 'states', 'dataRequirements', 'invariants', 'adaptable', 'sourceFiles', 'keywords']) {
|
|
265
|
+
if (!Array.isArray(item[field]) || !item[field].length || item[field].some(value => typeof value !== 'string' || !value)) issues.push(`${item.id}: ${field} incomplete`)
|
|
266
|
+
}
|
|
267
|
+
if (Array.isArray(item.components) && item.components.some(name => !components.has(name))) issues.push(`${item.id}: unknown Ceramic component`)
|
|
268
|
+
}
|
|
269
|
+
return { ok: issues.length === 0, issues }
|
|
270
|
+
}
|
|
271
|
+
|
|
62
272
|
export function listBlocks() {
|
|
63
273
|
return readJson(join(paths().manifests, 'blocks.json')).blocks
|
|
64
274
|
}
|
|
@@ -229,6 +439,10 @@ export function search(query) {
|
|
|
229
439
|
const terms = String(query).toLowerCase().split(/\s+/).filter(Boolean)
|
|
230
440
|
const score = (text) => terms.reduce((total, term) => total + (text.toLowerCase().includes(term) ? 1 : 0), 0)
|
|
231
441
|
const results = [
|
|
442
|
+
...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 })),
|
|
443
|
+
...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 })),
|
|
444
|
+
...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 })),
|
|
445
|
+
...listUxPatterns().map((item) => ({ kind: 'ux-pattern', id: item.id, title: item.title, summary: [item.useWhen, ...item.keywords].join(' '), item })),
|
|
232
446
|
...listComponents().map((item) => ({ kind: 'component', id: item.name, title: item.name, summary: [...item.useWhen, item.category].join(' '), item })),
|
|
233
447
|
...listBlockDefinitions().map((item) => ({ kind: 'block-definition', id: item.id, title: item.title, summary: `${item.category} ${item.description} ${item.availability}`, item })),
|
|
234
448
|
...listBlocks().map((item) => ({ kind: 'block', id: item.id, title: item.title, summary: `${item.category} ${item.purpose}`, item })),
|
|
@@ -240,7 +454,7 @@ export function search(query) {
|
|
|
240
454
|
...listDocs().map((topic) => ({ kind: 'docs', id: topic, title: topic, summary: getDoc(topic)?.content.slice(0, 500) ?? '', item: { topic } })),
|
|
241
455
|
]
|
|
242
456
|
return results
|
|
243
|
-
.map((result) => ({ ...result, score: score(`${result.title} ${result.summary}`) }))
|
|
457
|
+
.map((result) => ({ ...result, score: score(`${result.id} ${result.title} ${result.summary}`) }))
|
|
244
458
|
.filter((result) => result.score > 0)
|
|
245
459
|
.sort((a, b) => b.score - a.score || a.title.localeCompare(b.title))
|
|
246
460
|
.map(({ summary: _summary, ...result }) => result)
|
|
@@ -260,6 +474,7 @@ export function capabilityManifest() {
|
|
|
260
474
|
{ name: 'init', args: ['[directory]'], flags: ['--yes', '--theme <id>'], responseType: 'init-result' },
|
|
261
475
|
{ name: 'room', args: ['connect'], flags: ['--id <id>', '--slug <slug>', '--name <room>', '--brand <brand>', '--source <mode>', '--client <client>', '--directory <path>'], responseType: 'room-connect-result' },
|
|
262
476
|
{ name: 'search', args: ['<query>'], flags: [], responseType: 'search-results' },
|
|
477
|
+
{ name: 'pattern', args: ['<id>|--list'], flags: ['--goal <text>', '--theme <id>', '--variant <id>'], responseType: 'ux-pattern|ux-pattern-list|pattern-adaptation-plan' },
|
|
263
478
|
{ name: 'component', args: ['<name>|--list'], flags: [], responseType: 'component|component-list' },
|
|
264
479
|
{ name: 'registry', args: ['<block-id>|--list'], flags: ['--renderable'], responseType: 'block-definition|block-definition-list' },
|
|
265
480
|
{ name: 'layout', args: ['validate <file>', 'serialize <file>'], flags: [], responseType: 'layout-validation-result|layout-serialization-result' },
|
|
@@ -275,14 +490,15 @@ export function capabilityManifest() {
|
|
|
275
490
|
],
|
|
276
491
|
mcp: {
|
|
277
492
|
...mcpLaunch,
|
|
278
|
-
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', 'get_brandbook_skill', 'prepare_brandbook_run', 'validate_brandbook', 'list_docs', 'get_docs', 'doctor'],
|
|
493
|
+
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', 'validate_brandbook', 'list_docs', 'get_docs', 'doctor'],
|
|
279
494
|
},
|
|
280
495
|
}
|
|
281
496
|
}
|
|
282
497
|
|
|
283
498
|
export function repositoryDoctor() {
|
|
499
|
+
const registry = registryVersionStatus()
|
|
284
500
|
const required = [
|
|
285
|
-
'catalog.json', 'components.json', 'blocks.json', 'composition-registry.json', 'templates.json', 'themes.json', 'motion-profiles.json', 'logo-skills.json', 'brandbook-skill.json',
|
|
501
|
+
'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',
|
|
286
502
|
].map((name) => join(paths().manifests, name))
|
|
287
503
|
const docs = ['quick-start-ai', 'arabic-friendly', 'theme-authoring', 'composition-registry'].map((name) => join(paths().docs, `${name}.md`))
|
|
288
504
|
const missing = [...required, ...docs].filter((path) => !existsSync(path))
|
|
@@ -307,8 +523,10 @@ export function repositoryDoctor() {
|
|
|
307
523
|
&& definition.agentPermissions
|
|
308
524
|
) === true
|
|
309
525
|
if (!compositionContract) contractIssues.push('composition-registry.json: incomplete block or layout contract')
|
|
526
|
+
const uxPatterns = missing.includes(join(paths().manifests, 'ux-patterns.json')) ? { ok: false, issues: ['Missing UX pattern registry'] } : validateUxPatternRegistry(getUxPatternRegistry())
|
|
527
|
+
contractIssues.push(...uxPatterns.issues)
|
|
310
528
|
return {
|
|
311
|
-
ok: missing.length === 0 && motionContract && compositionContract,
|
|
529
|
+
ok: missing.length === 0 && motionContract && compositionContract && uxPatterns.ok,
|
|
312
530
|
checks: {
|
|
313
531
|
manifests: required.every(existsSync),
|
|
314
532
|
docs: docs.every(existsSync),
|
|
@@ -316,6 +534,7 @@ export function repositoryDoctor() {
|
|
|
316
534
|
blocks: listBlocks().length,
|
|
317
535
|
blockDefinitions: compositionRegistry?.definitions?.length ?? 0,
|
|
318
536
|
compositionContract,
|
|
537
|
+
uxPatternContract: uxPatterns.ok,
|
|
319
538
|
layoutSchemaVersion: compositionRegistry?.layoutSchemaVersion ?? null,
|
|
320
539
|
templates: listTemplates().length,
|
|
321
540
|
themes: listThemes().length,
|
|
@@ -326,7 +545,14 @@ export function repositoryDoctor() {
|
|
|
326
545
|
&& mcpLaunch.command === 'npx'
|
|
327
546
|
&& mcpLaunch.args.includes('utopia-ds')
|
|
328
547
|
&& mcpLaunch.args.at(-1) === 'mcp',
|
|
548
|
+
cliVersion: registry.current,
|
|
549
|
+
registryLatest: registry.latest,
|
|
550
|
+
cliUpToDate: registry.upToDate,
|
|
329
551
|
},
|
|
330
552
|
missing: [...missing, ...contractIssues],
|
|
553
|
+
warnings: [
|
|
554
|
+
...(!registry.checked ? [{ code: 'REGISTRY_CHECK_UNAVAILABLE', message: 'Could not read the npm registry latest tag; verify the pinned MCP package version manually.' }] : []),
|
|
555
|
+
...(registry.checked && !registry.upToDate ? [{ code: 'CLI_OUTDATED', message: `Ceramic CLI ${registry.current} is older than npm latest ${registry.latest}. Update the project MCP package pin and restart the client.` }] : []),
|
|
556
|
+
],
|
|
331
557
|
}
|
|
332
558
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utopia-studio-design/design-system-cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
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",
|