@astryxdesign/cli 0.3.0-canary.d1b7d82 → 0.3.0-canary.dccdabe

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 (109) hide show
  1. package/README.md +8 -8
  2. package/api/blog/blog.doc.mjs +2 -2
  3. package/api/build/build.doc.mjs +1 -1
  4. package/api/component/component.doc.mjs +1 -1
  5. package/api/discover/discover.doc.mjs +2 -2
  6. package/api/docs/docs.doc.mjs +2 -2
  7. package/api/doctor/doctor.doc.mjs +3 -3
  8. package/api/hook/hook.doc.mjs +1 -1
  9. package/api/init/init.doc.mjs +1 -1
  10. package/api/integration/summarizeIssues.doc.mjs +1 -1
  11. package/api/integration/validateIntegration.doc.mjs +1 -1
  12. package/api/json/assertResponse.doc.mjs +2 -2
  13. package/api/json/isError.doc.mjs +1 -1
  14. package/api/json/parseResponse.doc.mjs +2 -2
  15. package/api/layout/layoutCheck.doc.mjs +1 -1
  16. package/api/layout/layoutExpand.doc.mjs +1 -1
  17. package/api/layout/layoutGrammar.doc.mjs +1 -1
  18. package/api/swizzle/swizzle.doc.mjs +1 -1
  19. package/api/template/template.test.mjs +12 -13
  20. package/api/theme/listThemes.doc.mjs +2 -2
  21. package/api/theme/themeAdd.doc.mjs +1 -1
  22. package/api/theme/themeBuild.doc.mjs +3 -3
  23. package/api/theme/themeList.doc.mjs +2 -2
  24. package/api/upgrade/upgrade.doc.mjs +1 -1
  25. package/assets/codemods/transforms/v0.3.0/__tests__/migrate-grid-minchildwidth-to-columns.test.mjs +82 -0
  26. package/assets/codemods/transforms/v0.3.0/migrate-grid-minchildwidth-to-columns.mjs +38 -2
  27. package/assets/docs/browser-support.doc.mjs +6 -6
  28. package/assets/docs/icons.doc.mjs +3 -3
  29. package/assets/docs/illustrations.doc.mjs +2 -2
  30. package/assets/docs/internationalization.doc.mjs +1 -1
  31. package/assets/docs/layout.doc.dense.mjs +1 -1
  32. package/assets/docs/migration.doc.mjs +1 -1
  33. package/assets/docs/principles.doc.mjs +1 -1
  34. package/assets/docs/styling-libraries.doc.mjs +1 -1
  35. package/assets/docs/styling.doc.mjs +4 -4
  36. package/assets/docs/theme.doc.mjs +3 -3
  37. package/assets/docs/typography.doc.mjs +2 -2
  38. package/assets/templates/blocks/components/AspectRatio/AspectRatioCircleImage.tsx +1 -1
  39. package/assets/templates/blocks/components/AspectRatio/AspectRatioImageGallery.tsx +1 -1
  40. package/assets/templates/blocks/components/AspectRatio/AspectRatioShowcase.tsx +3 -3
  41. package/assets/templates/blocks/components/AspectRatio/AspectRatioSquareImage.tsx +1 -1
  42. package/assets/templates/blocks/components/AspectRatio/AspectRatioWidescreen.tsx +1 -1
  43. package/assets/templates/blocks/components/Avatar/AvatarFallbackChain.tsx +6 -6
  44. package/assets/templates/blocks/components/Avatar/AvatarGroup.tsx +5 -5
  45. package/assets/templates/blocks/components/Avatar/AvatarInteractive.tsx +2 -2
  46. package/assets/templates/blocks/components/Avatar/AvatarShowcase.tsx +4 -4
  47. package/assets/templates/blocks/components/Avatar/AvatarTooltip.tsx +4 -4
  48. package/assets/templates/blocks/components/Avatar/AvatarUserCard.tsx +3 -3
  49. package/assets/templates/blocks/components/Avatar/AvatarWithImage.tsx +4 -4
  50. package/assets/templates/blocks/components/Avatar/AvatarWithStatus.tsx +3 -3
  51. package/assets/templates/blocks/components/ChatComposerDrawer/ChatComposerDrawerAttachments.tsx +5 -5
  52. package/assets/templates/blocks/components/Lightbox/LightboxGallery.tsx +4 -4
  53. package/assets/templates/blocks/components/Lightbox/LightboxShowcase.tsx +1 -1
  54. package/assets/templates/blocks/components/Lightbox/LightboxVideo.tsx +1 -1
  55. package/assets/templates/blocks/components/Lightbox/LightboxZoom.tsx +2 -2
  56. package/assets/templates/blocks/components/MediaTheme/MediaThemeImageOverlay.tsx +1 -1
  57. package/assets/templates/blocks/components/MediaTheme/MediaThemeLightScrim.tsx +1 -1
  58. package/assets/templates/blocks/components/MediaTheme/MediaThemeShowcase.tsx +1 -1
  59. package/assets/templates/blocks/components/Overlay/OverlayBottomStrip.tsx +1 -1
  60. package/assets/templates/blocks/components/Overlay/OverlayHoverReveal.tsx +1 -1
  61. package/assets/templates/blocks/components/Overlay/OverlayShowcase.tsx +1 -1
  62. package/assets/templates/blocks/components/TopNav/TopNavMegaMenu.tsx +1 -1
  63. package/assets/templates/pages/centered-hero/page.tsx +1 -1
  64. package/assets/templates/pages/classic-gallery/page.tsx +10 -10
  65. package/assets/templates/pages/detail-page/page.tsx +5 -5
  66. package/assets/templates/pages/form-two-column/page.tsx +1 -1
  67. package/assets/templates/pages/gallery-hero/page.tsx +3 -3
  68. package/assets/templates/pages/library/page.tsx +30 -30
  69. package/assets/templates/pages/login/page.tsx +1 -2
  70. package/assets/templates/pages/login-card/page.tsx +1 -2
  71. package/assets/templates/pages/login-split/page.tsx +5 -6
  72. package/assets/templates/pages/login-sso/page.tsx +2 -3
  73. package/assets/templates/pages/mixed-gallery/page.tsx +5 -5
  74. package/assets/templates/pages/payment-form/page.tsx +3 -3
  75. package/assets/templates/pages/product-detail/page.tsx +7 -7
  76. package/assets/templates/pages/product-gallery/page.tsx +6 -6
  77. package/assets/templates/pages/shell-top-nav/page.tsx +2 -2
  78. package/assets/templates/pages/side-gallery/page.tsx +9 -9
  79. package/assets/templates/pages/table-page-chart/page.tsx +6 -6
  80. package/assets/templates/pages/theme-showcase/page.tsx +6 -6
  81. package/authoring/codemod/codemod.doc.mjs +2 -2
  82. package/authoring/config/config.doc.mjs +1 -1
  83. package/authoring/doctypes/base/type.ts +6 -0
  84. package/authoring/doctypes/command/command.doc.mjs +2 -2
  85. package/authoring/doctypes/component/component.doc.mjs +7 -7
  86. package/authoring/doctypes/enum/enum.doc.mjs +2 -2
  87. package/authoring/doctypes/function/function.doc.mjs +3 -3
  88. package/authoring/doctypes/hook/hook.doc.mjs +3 -3
  89. package/authoring/doctypes/reference/reference.doc.mjs +3 -3
  90. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  91. package/authoring/doctypes/template/template.doc.mjs +3 -3
  92. package/clients/cli/commands/build.doc.mjs +1 -1
  93. package/clients/cli/commands/component.doc.mjs +1 -1
  94. package/clients/cli/commands/doctor.doc.mjs +2 -2
  95. package/clients/cli/commands/layout-expand.doc.mjs +1 -1
  96. package/clients/cli/commands/layout.doc.mjs +1 -1
  97. package/clients/cli/commands/manifest.doc.mjs +3 -3
  98. package/clients/cli/commands/search.doc.mjs +1 -1
  99. package/clients/cli/commands/theme-add.doc.mjs +1 -1
  100. package/clients/cli/commands/theme-build.doc.mjs +1 -1
  101. package/clients/cli/commands/theme-list.doc.mjs +1 -1
  102. package/clients/cli/commands/theme.doc.mjs +2 -2
  103. package/clients/cli/commands/validate-integration.doc.mjs +2 -2
  104. package/foundation/agent-docs/agent-docs.mjs +3 -4
  105. package/foundation/agent-docs/agent-docs.test.mjs +8 -0
  106. package/foundation/discovery/template-adapter.mjs +8 -4
  107. package/foundation/response/response-types.doc.mjs +7 -7
  108. package/foundation/response/response.doc.mjs +5 -5
  109. package/package.json +9 -9
@@ -158,7 +158,7 @@ export default function ShellTopNav() {
158
158
  <MegaFeatured
159
159
  title="The Autumn Edit"
160
160
  description="Layering staples in warm, earthy tones."
161
- image="https://lookaside.facebook.com/assets/astryx/texture-beige-horizontal-1.png"
161
+ image="/template-assets/texture-beige-horizontal-1.png"
162
162
  imageAlt="Autumn collection lookbook"
163
163
  linkLabel="Shop the edit"
164
164
  linkHref="#autumn-edit"
@@ -172,7 +172,7 @@ export default function ShellTopNav() {
172
172
  <MegaFeatured
173
173
  title="Meet Studio Mara"
174
174
  description="Modern tailoring, made to last."
175
- image="https://lookaside.facebook.com/assets/astryx/texture-beige-horizontal-2.png"
175
+ image="/template-assets/texture-beige-horizontal-2.png"
176
176
  imageAlt="Studio Mara lookbook"
177
177
  linkLabel="Discover the label"
178
178
  linkHref="#studio-mara"
@@ -25,39 +25,39 @@ const imageClip = {
25
25
 
26
26
  const IMAGES = [
27
27
  {
28
- src: 'https://lookaside.facebook.com/assets/astryx/colorful-lifestyle-vertical-3.png',
28
+ src: '/template-assets/colorful-lifestyle-vertical-3.png',
29
29
  alt: 'Colorful lifestyle scene',
30
30
  },
31
31
  {
32
- src: 'https://lookaside.facebook.com/assets/astryx/colorful-lifestyle-horizontal-1.png',
32
+ src: '/template-assets/colorful-lifestyle-horizontal-1.png',
33
33
  alt: 'Colorful lifestyle horizontal',
34
34
  },
35
35
  {
36
- src: 'https://lookaside.facebook.com/assets/astryx/colorful-lifestyle-vertical-1.png',
36
+ src: '/template-assets/colorful-lifestyle-vertical-1.png',
37
37
  alt: 'Colorful lifestyle vertical',
38
38
  },
39
39
  {
40
- src: 'https://lookaside.facebook.com/assets/astryx/colorful-home-vertical-2.png',
40
+ src: '/template-assets/colorful-home-vertical-2.png',
41
41
  alt: 'Colorful home interior',
42
42
  },
43
43
  {
44
- src: 'https://lookaside.facebook.com/assets/astryx/colorful-home-vertical-3.png',
44
+ src: '/template-assets/colorful-home-vertical-3.png',
45
45
  alt: 'Colorful home scene',
46
46
  },
47
47
  {
48
- src: 'https://lookaside.facebook.com/assets/astryx/colorful-home-vertical-1.png',
48
+ src: '/template-assets/colorful-home-vertical-1.png',
49
49
  alt: 'Colorful home vertical',
50
50
  },
51
51
  {
52
- src: 'https://lookaside.facebook.com/assets/astryx/colorful-lifestyle-horizontal-2.png',
52
+ src: '/template-assets/colorful-lifestyle-horizontal-2.png',
53
53
  alt: 'Colorful lifestyle wide',
54
54
  },
55
55
  {
56
- src: 'https://lookaside.facebook.com/assets/astryx/colorful-lifestyle-vertical-2.png',
56
+ src: '/template-assets/colorful-lifestyle-vertical-2.png',
57
57
  alt: 'Colorful lifestyle detail',
58
58
  },
59
59
  {
60
- src: 'https://lookaside.facebook.com/assets/astryx/colorful-lifestyle-vertical-4.png',
60
+ src: '/template-assets/colorful-lifestyle-vertical-4.png',
61
61
  alt: 'Colorful lifestyle portrait',
62
62
  },
63
63
  ];
@@ -46,37 +46,37 @@ const PRODUCTS = [
46
46
  {
47
47
  name: 'Ceremonial Matcha Latte',
48
48
  category: 'Matcha' as ProductCategory,
49
- image: 'https://lookaside.facebook.com/assets/astryx/matcha-product-1.png',
49
+ image: '/template-assets/matcha-product-1.png',
50
50
  price: 6,
51
51
  },
52
52
  {
53
53
  name: 'Oat Milk Cappuccino',
54
54
  category: 'Coffee' as ProductCategory,
55
- image: 'https://lookaside.facebook.com/assets/astryx/matcha-product-2.png',
55
+ image: '/template-assets/matcha-product-2.png',
56
56
  price: 5,
57
57
  },
58
58
  {
59
59
  name: 'Jasmine Green Tea',
60
60
  category: 'Tea' as ProductCategory,
61
- image: 'https://lookaside.facebook.com/assets/astryx/matcha-product-3.png',
61
+ image: '/template-assets/matcha-product-3.png',
62
62
  price: 4,
63
63
  },
64
64
  {
65
65
  name: 'Mango Matcha Smoothie',
66
66
  category: 'Smoothie' as ProductCategory,
67
- image: 'https://lookaside.facebook.com/assets/astryx/matcha-product-4.png',
67
+ image: '/template-assets/matcha-product-4.png',
68
68
  price: 8,
69
69
  },
70
70
  {
71
71
  name: 'Hojicha Latte',
72
72
  category: 'Specialty' as ProductCategory,
73
- image: 'https://lookaside.facebook.com/assets/astryx/matcha-product-5.png',
73
+ image: '/template-assets/matcha-product-5.png',
74
74
  price: 7,
75
75
  },
76
76
  {
77
77
  name: 'Iced Yuzu Matcha',
78
78
  category: 'Matcha' as ProductCategory,
79
- image: 'https://lookaside.facebook.com/assets/astryx/matcha-product-6.png',
79
+ image: '/template-assets/matcha-product-6.png',
80
80
  price: 7,
81
81
  },
82
82
  ];
@@ -283,13 +283,13 @@ const DEFAULT_PRODUCTS: ProductSpec[] = [
283
283
  // Neutral product photos, served from the shared astryx asset CDN so the
284
284
  // scaffolded template renders real imagery without needing local public assets.
285
285
  const DEFAULT_IMAGES: Record<string, string> = {
286
- watch: 'https://lookaside.facebook.com/assets/astryx/Neutral-Watch.png',
286
+ watch: '/template-assets/Neutral-Watch.png',
287
287
  headphones:
288
- 'https://lookaside.facebook.com/assets/astryx/Neutral-Headphones.png',
289
- backpack: 'https://lookaside.facebook.com/assets/astryx/Neutral-Backpack.png',
290
- wallet: 'https://lookaside.facebook.com/assets/astryx/Neutral-Wallet.png',
291
- tumbler: 'https://lookaside.facebook.com/assets/astryx/Neutral-Tumbler.png',
292
- throw_: 'https://lookaside.facebook.com/assets/astryx/Neutral-Blanket.png',
288
+ '/template-assets/Neutral-Headphones.png',
289
+ backpack: '/template-assets/Neutral-Backpack.png',
290
+ wallet: '/template-assets/Neutral-Wallet.png',
291
+ tumbler: '/template-assets/Neutral-Tumbler.png',
292
+ throw_: '/template-assets/Neutral-Blanket.png',
293
293
  };
294
294
 
295
295
  export interface ThemeShowcaseProps {
@@ -42,7 +42,7 @@ export const doc = {
42
42
  name: 'fileExtensions',
43
43
  type: 'string[]',
44
44
  description:
45
- 'File extensions this codemod applies to. Code codemods only — a ' +
45
+ 'File extensions this codemod applies to. Code codemods only; a ' +
46
46
  'config codemod always targets astryx.config.*.',
47
47
  example: "['.tsx', '.ts']",
48
48
  },
@@ -140,7 +140,7 @@ export const doc = {
140
140
  type: 'prose',
141
141
  text:
142
142
  "The config-codemod variant (type: 'config') carries the same fields " +
143
- 'as a code codemod except fileExtensions — it always targets the ' +
143
+ 'as a code codemod except fileExtensions: it always targets the ' +
144
144
  'astryx.config.* file rather than a set of source files.',
145
145
  },
146
146
  {
@@ -46,7 +46,7 @@ export const doc = {
46
46
  {
47
47
  name: 'experimental',
48
48
  type: '{ xle?: { components?: Record<string, XleComponent> } }',
49
- description: 'Unstable features — may change without a breaking bump.',
49
+ description: 'Unstable features; may change without a breaking bump.',
50
50
  fields: [
51
51
  {
52
52
  name: 'experimental.xle.components',
@@ -270,6 +270,12 @@ export interface ComponentThemingDerivedVar {
270
270
  /** Named expansion strategy instead of specific vars.
271
271
  * `'container'` — expands padding to 7 container layout tokens. */
272
272
  expand?: 'container';
273
+ /** Emit only the internal `vars`, dropping the source property from the
274
+ * generated rule. Use when the class-carrying element must not receive the
275
+ * standard property itself — the value reaches a child through the var
276
+ * (e.g. TextArea's flush wrapper drives the inner textarea's inline
277
+ * padding). Without this, the property is emitted alongside the var. */
278
+ replaces?: boolean;
273
279
  }
274
280
 
275
281
  /**
@@ -15,7 +15,7 @@ export const doc = {
15
15
  displayName: 'CommandDoc',
16
16
  namespace: 'authoring',
17
17
  description:
18
- 'The doc-type for a CLI command — the terminal binding of an operation. A command ' +
18
+ 'The doc-type for a CLI command: the terminal binding of an operation. A command ' +
19
19
  'is not its own behavior; it is a FunctionDoc exposed on the CLI, referenced via `fn`, ' +
20
20
  'carrying only CLI-surface facts (args, flags, subcommands, examples, exit codes). ' +
21
21
  'A `defineCommand` converter turns it into Commander config + `--help`.',
@@ -231,7 +231,7 @@ export const doc = {
231
231
  notes: [
232
232
  {
233
233
  type: 'prose',
234
- text: 'A command carries only CLI-surface facts. Behavior, parameters, returns, and thrown errors live in the FunctionDoc it points at via `fn` — the function does not know it has a CLI.',
234
+ text: 'A command carries only CLI-surface facts. Behavior, parameters, returns, and thrown errors live in the FunctionDoc it points at via `fn`; the function does not know it has a CLI.',
235
235
  },
236
236
  {
237
237
  type: 'prose',
@@ -50,7 +50,7 @@ export const doc = {
50
50
  name: 'hiddenComponents',
51
51
  type: 'string[]',
52
52
  description:
53
- 'Sub-component names to hide from human-facing UI (CLI listings, docs catalogs). They stay public and importable — agents and tooling can still discover them via source.',
53
+ 'Sub-component names to hide from human-facing UI (CLI listings, docs catalogs). They stay public and importable; agents and tooling can still discover them via source.',
54
54
  },
55
55
  {
56
56
  name: 'hidden',
@@ -104,7 +104,7 @@ export const doc = {
104
104
  name: 'theming.derived',
105
105
  type: 'ComponentThemingDerivedVar[]',
106
106
  description:
107
- 'Maps standard CSS properties to internal vars for theme-pipeline expansion. Ordered by priority — earlier entries emit first.',
107
+ 'Maps standard CSS properties to internal vars for theme-pipeline expansion. Ordered by priority: earlier entries emit first.',
108
108
  },
109
109
  ],
110
110
  },
@@ -170,7 +170,7 @@ export const doc = {
170
170
  name: 'description',
171
171
  type: 'string',
172
172
  description:
173
- "SubComponentDoc variant (required there): one-sentence description of the sub-component's role within the parent composition. Single/Multi docs have no top-level description — they derive their summary from `usage`.",
173
+ "SubComponentDoc variant (required there): one-sentence description of the sub-component's role within the parent composition. Single/Multi docs have no top-level description; they derive their summary from `usage`.",
174
174
  },
175
175
  ],
176
176
  examples: [
@@ -187,7 +187,7 @@ export const docs = {
187
187
  'A Switch toggles a single setting on or off. Use it for instant, binary preferences that apply immediately without a submit step.',
188
188
  bestPractices: [
189
189
  {guidance: true, description: 'Apply the change immediately when toggled.'},
190
- {guidance: false, description: 'Use a Switch for actions that need confirmation — prefer a Checkbox in a form.'},
190
+ {guidance: false, description: 'Use a Switch for actions that need confirmation; prefer a Checkbox in a form.'},
191
191
  ],
192
192
  },
193
193
  props: [
@@ -226,9 +226,9 @@ export const docs = {
226
226
  type: 'list',
227
227
  style: 'unordered',
228
228
  items: [
229
- 'SingleComponentDoc — one primary component; put props directly on the doc via `props`. Use for Switch, Badge, Spinner, TextInput.',
230
- 'MultiComponentDoc — a directory exporting several components/hooks; list them in `components` (inline ComponentEntry or name-only ComponentRef). Use for Table, Dialog, TabList.',
231
- 'SubComponentDoc — a single sub-component in its own {Name}.doc.mjs inside the parent directory; set `subComponentOf` to the parent name. It inherits family fields and may omit `usage`.',
229
+ 'SingleComponentDoc: one primary component; put props directly on the doc via `props`. Use for Switch, Badge, Spinner, TextInput.',
230
+ 'MultiComponentDoc: a directory exporting several components/hooks; list them in `components` (inline ComponentEntry or name-only ComponentRef). Use for Table, Dialog, TabList.',
231
+ 'SubComponentDoc: a single sub-component in its own {Name}.doc.mjs inside the parent directory; set `subComponentOf` to the parent name. It inherits family fields and may omit `usage`.',
232
232
  ],
233
233
  },
234
234
  {
@@ -14,7 +14,7 @@ export const doc = {
14
14
  displayName: 'EnumDoc',
15
15
  namespace: 'authoring',
16
16
  description:
17
- 'The doc-type for a closed vocabulary — a fixed set of literal values such as ' +
17
+ 'The doc-type for a closed vocabulary: a fixed set of literal values such as ' +
18
18
  'error codes or response-type discriminants. Colocated as a `.doc.mjs` next to ' +
19
19
  'the source of truth it documents.',
20
20
  appliesTo: '<enum>.doc.mjs',
@@ -59,7 +59,7 @@ export const doc = {
59
59
  {
60
60
  name: 'members',
61
61
  type: 'EnumMemberDoc[]',
62
- description: 'The enumerated members — one entry per literal value.',
62
+ description: 'The enumerated members: one entry per literal value.',
63
63
  required: true,
64
64
  fields: [
65
65
  {
@@ -41,7 +41,7 @@ export const doc = {
41
41
  name: 'kind',
42
42
  type: "'hook' | 'api'",
43
43
  description:
44
- 'Which flavor — drives docsite sectioning; inferred from importPath if omitted.',
44
+ 'Which flavor; drives docsite sectioning; inferred from importPath if omitted.',
45
45
  },
46
46
  {
47
47
  name: 'summary',
@@ -127,7 +127,7 @@ export const doc = {
127
127
  {
128
128
  name: 'returns[].name',
129
129
  type: 'string',
130
- description: 'Field name (hooks) — omit for API envelope entries.',
130
+ description: 'Field name (hooks); omit for API envelope entries.',
131
131
  },
132
132
  {
133
133
  name: 'returns[].type',
@@ -191,7 +191,7 @@ export const doc = {
191
191
  name: 'usage',
192
192
  type: 'UsageDoc',
193
193
  description:
194
- 'Usage documentation (hooks) — description, best practices, anatomy. Same shape as HookDoc.usage.',
194
+ 'Usage documentation (hooks): description, best practices, anatomy. Same shape as HookDoc.usage.',
195
195
  },
196
196
  {
197
197
  name: 'command',
@@ -43,7 +43,7 @@ export const doc = {
43
43
  name: 'group',
44
44
  type: 'string',
45
45
  description:
46
- 'Optional group for sidebar/docs organization — same as ComponentDoc.group.',
46
+ 'Optional group for sidebar/docs organization; same as ComponentDoc.group.',
47
47
  },
48
48
  {
49
49
  name: 'keywords',
@@ -117,7 +117,7 @@ export const doc = {
117
117
  {
118
118
  name: 'usage',
119
119
  type: 'UsageDoc',
120
- description: 'Usage documentation — description and best practices.',
120
+ description: 'Usage documentation: description and best practices.',
121
121
  required: true,
122
122
  fields: [
123
123
  {
@@ -190,7 +190,7 @@ export const docs = {
190
190
  notes: [
191
191
  {
192
192
  type: 'prose',
193
- text: "A hook's discriminant is `type: 'function'` — HookDoc and FunctionDoc share the generalized function kind. HookDoc is the hook-flavored view: named `returns` fields and a required `usage` block.",
193
+ text: "A hook's discriminant is `type: 'function'`: HookDoc and FunctionDoc share the generalized function kind. HookDoc is the hook-flavored view: named `returns` fields and a required `usage` block.",
194
194
  },
195
195
  {
196
196
  type: 'prose',
@@ -14,7 +14,7 @@ export const doc = {
14
14
  displayName: 'ReferenceDoc',
15
15
  namespace: 'authoring',
16
16
  description:
17
- 'The doc-type for a reference/topic doc — tokens, principles, theming, patterns, ' +
17
+ 'The doc-type for a reference/topic doc: tokens, principles, theming, patterns, ' +
18
18
  'accessibility, migration guides. Unlike ComponentDoc it is not tied to a component: ' +
19
19
  'drop a `.doc.mjs` in the docs directory and it shows up in `astryx docs`. Content is ' +
20
20
  'built from ordered sections of mixed content blocks.',
@@ -120,7 +120,7 @@ export const docs = {
120
120
  notes: [
121
121
  {
122
122
  type: 'prose',
123
- text: 'Each `sections[].content` is an ordered array of ReferenceContentBlock — a discriminated union. New block types can be added without breaking existing docs. The same union is reused by the `notes` field on SchemaDoc and CommandDoc.',
123
+ text: 'Each `sections[].content` is an ordered array of ReferenceContentBlock, a discriminated union. New block types can be added without breaking existing docs. The same union is reused by the `notes` field on SchemaDoc and CommandDoc.',
124
124
  },
125
125
  {
126
126
  type: 'code',
@@ -140,7 +140,7 @@ export const docs = {
140
140
  },
141
141
  {
142
142
  type: 'prose',
143
- text: "A section may set `previewType` to render a visual preview column for token tables — one of 'swatch' | 'shadow-box' | 'radius-box' | 'spacing-bar' | 'size-bar' | 'border-line' | 'duration-bar' | 'easing-curve' | 'font-sample'.",
143
+ text: "A section may set `previewType` to render a visual preview column for token tables: one of 'swatch' | 'shadow-box' | 'radius-box' | 'spacing-bar' | 'size-bar' | 'border-line' | 'duration-bar' | 'easing-curve' | 'font-sample'.",
144
144
  },
145
145
  ],
146
146
  };
@@ -190,7 +190,7 @@ export const doc = {
190
190
  type: 'list',
191
191
  style: 'dont',
192
192
  items: [
193
- 'Set `required: false` for optional fields — omit `required` entirely instead.',
193
+ 'Set `required: false` for optional fields; omit `required` entirely instead.',
194
194
  ],
195
195
  },
196
196
  ],
@@ -150,13 +150,13 @@ export const doc = {
150
150
  type: 'list',
151
151
  style: 'unordered',
152
152
  items: [
153
- "PageTemplateDoc (type: 'page') — a full page template; `name` doubles as its display value.",
154
- "BlockTemplateDoc (type: 'block') — an example of a component; requires `exampleFor` and `aspectRatio`.",
153
+ "PageTemplateDoc (type: 'page'): a full page template; `name` doubles as its display value.",
154
+ "BlockTemplateDoc (type: 'block'): an example of a component; requires `exampleFor` and `aspectRatio`.",
155
155
  ],
156
156
  },
157
157
  {
158
158
  type: 'prose',
159
- text: "`category` uses the shared TemplateCategory taxonomy: 'Group - Variant' strings (e.g. 'Table - Bulk Actions'). Not every value maps to an existing template — unused values are reserved so authors get autocomplete for the full taxonomy.",
159
+ text: "`category` uses the shared TemplateCategory taxonomy: 'Group - Variant' strings (e.g. 'Table - Bulk Actions'). Not every value maps to an existing template; unused values are reserved so authors get autocomplete for the full taxonomy.",
160
160
  },
161
161
  ],
162
162
  };
@@ -17,7 +17,7 @@ export const doc = {
17
17
  'Build a page: composition kit for an idea, or the workflow playbook (no args)',
18
18
  description:
19
19
  'The assemble-a-page entry point. With no query it returns the how-to-build-a-page ' +
20
- 'playbook; with a query it groups the unified search hits into a composition kit — ' +
20
+ 'playbook; with a query it groups the unified search hits into a composition kit: ' +
21
21
  'the closest templates, drop-in blocks, and idea-specific components and hooks.',
22
22
  fn: 'build',
23
23
  args: [{name: 'query', param: 'query', required: false}],
@@ -69,7 +69,7 @@ export const doc = {
69
69
  {code: 0, when: 'success'},
70
70
  {
71
71
  code: 1,
72
- when: 'unknown component, category, or package — or @astryxdesign/core cannot be resolved',
72
+ when: 'unknown component, category, or package, or @astryxdesign/core cannot be resolved',
73
73
  },
74
74
  ],
75
75
  related: ['search', 'hook', 'docs', 'template', 'swizzle'],
@@ -15,8 +15,8 @@ export const doc = {
15
15
  namespace: 'cli',
16
16
  summary: 'Diagnose your XDS setup and report problems with fixes',
17
17
  description:
18
- 'Runs read-only diagnostics — Node version, @astryxdesign/core install and version ' +
19
- 'alignment, installed themes, config validity, agent docs, and the package manager — ' +
18
+ 'Runs read-only diagnostics: Node version, @astryxdesign/core install and version ' +
19
+ 'alignment, installed themes, config validity, agent docs, and the package manager, ' +
20
20
  'and reports pass/warn/fail with an actionable fix for each problem. Safe as a CI gate.',
21
21
  fn: 'doctor',
22
22
  examples: [
@@ -17,7 +17,7 @@ export const doc = {
17
17
  summary: 'Expand a layout expression into validated XDS TSX',
18
18
  description:
19
19
  'Parses and validates a compressed XLE/XLO expression, then expands it into ' +
20
- 'ready-to-use XDS TSX — routing children into slots, scaffolding typed useState for ' +
20
+ 'ready-to-use XDS TSX, routing children into slots, scaffolding typed useState for ' +
21
21
  'interactive controls, and splicing referenced blocks. Writes to a path, or returns the code.',
22
22
  fn: 'layoutExpand',
23
23
  args: [
@@ -16,7 +16,7 @@ export const doc = {
16
16
  summary: 'Generate XDS layouts from compressed expressions (XLE/XLO)',
17
17
  description:
18
18
  'The layout command group. Running astryx layout with no subcommand prints the ' +
19
- 'subcommand list; the work happens in the subcommands — expand an expression into ' +
19
+ 'subcommand list; the work happens in the subcommands: expand an expression into ' +
20
20
  'TSX (expand), validate one (check), or print the grammar cheatsheet (grammar).',
21
21
  subcommands: ['expand', 'check', 'grammar'],
22
22
  examples: [
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * @file CommandDoc for `astryx manifest`. A CLI-only capability manifest with no
5
5
  * wrapped API function (so no `fn`); it emits the full command surface — args,
6
- * flags, subcommands, and JSON response types — as a single typed envelope.
6
+ * flags, subcommands, and JSON response types, as a single typed envelope.
7
7
  * @position packages/cli/clients/cli/commands — command documentation
8
8
  */
9
9
 
@@ -15,8 +15,8 @@ export const doc = {
15
15
  namespace: 'cli',
16
16
  summary: 'Print the full CLI capability manifest (use with --json).',
17
17
  description:
18
- 'Emits the complete CLI surface — every command with its arguments, options, ' +
19
- 'subcommands, and JSON response types — as a single typed envelope for agents and ' +
18
+ 'Emits the complete CLI surface: every command with its arguments, options, ' +
19
+ 'subcommands, and JSON response types, as a single typed envelope for agents and ' +
20
20
  'tooling to introspect. Intended to be run with --json.',
21
21
  examples: [
22
22
  {label: 'Full manifest', cli: 'astryx manifest --json'},
@@ -15,7 +15,7 @@ export const doc = {
15
15
  namespace: 'cli',
16
16
  summary: 'Search components, hooks, docs, and templates in one ranked list',
17
17
  description:
18
- 'Terminal front-end to search() — prints one ranked, greppable list across ' +
18
+ 'Terminal front-end to search(): prints one ranked, greppable list across ' +
19
19
  'every content domain, each row carrying a follow-up command to act on it.',
20
20
  fn: 'search',
21
21
  args: [{name: 'query', param: 'query', required: true}],
@@ -16,7 +16,7 @@ export const doc = {
16
16
  namespace: 'cli',
17
17
  summary: 'Scaffold a theme into your project as editable source',
18
18
  description:
19
- "Copies a bundled theme's source into your project so you own it — no theme package " +
19
+ "Copies a bundled theme's source into your project so you own it, no theme package " +
20
20
  'needed. Writes are staged then renamed, rolling back on failure. Running it with no ' +
21
21
  'slug, or with --list, lists the bundled themes instead.',
22
22
  fn: 'themeAdd',
@@ -17,7 +17,7 @@ export const doc = {
17
17
  summary: 'Compile a defineTheme file to CSS + JS',
18
18
  description:
19
19
  'Compiles a file that calls defineTheme() into a scoped CSS file, a JS module, and ' +
20
- 'type declarations — the exact CSS the <Theme> runtime emits. With --check it writes ' +
20
+ 'type declarations: the exact CSS the <Theme> runtime emits. With --check it writes ' +
21
21
  'nothing and instead reports whether the committed outputs have drifted from source.',
22
22
  fn: 'themeBuild',
23
23
  args: [{name: 'file', param: 'file', required: true}],
@@ -16,7 +16,7 @@ export const doc = {
16
16
  namespace: 'cli',
17
17
  summary: 'List themes available to add',
18
18
  description:
19
- 'Lists the themes bundled with this CLI build — the ones theme add can scaffold — ' +
19
+ 'Lists the themes bundled with this CLI build (the ones theme add can scaffold), ' +
20
20
  'each with its slug, display name, description, and maintained flag.',
21
21
  fn: 'themeList',
22
22
  examples: [{label: 'List bundled themes', cli: 'astryx theme list --json'}],
@@ -13,10 +13,10 @@ export const doc = {
13
13
  name: 'theme',
14
14
  displayName: 'astryx theme',
15
15
  namespace: 'cli',
16
- summary: 'Theme tools — build, export, and manage themes',
16
+ summary: 'Theme tools: build, export, and manage themes',
17
17
  description:
18
18
  'The theme command group. Running astryx theme with no subcommand prints the ' +
19
- 'subcommand list; the work happens in the subcommands — compile a theme (build), ' +
19
+ 'subcommand list; the work happens in the subcommands: compile a theme (build), ' +
20
20
  'scaffold one into your project (add), or list the bundled themes (list).',
21
21
  subcommands: ['build', 'add', 'list'],
22
22
  examples: [
@@ -16,8 +16,8 @@ export const doc = {
16
16
  namespace: 'cli',
17
17
  summary: 'Validate an Astryx integration package (manifest + contributions)',
18
18
  description:
19
- 'Validates ONE integration at a time — the local package rooted at cwd, or an ' +
20
- 'installed package resolved by name — schema-checking its manifest, verifying each ' +
19
+ 'Validates ONE integration at a time: the local package rooted at cwd, or an ' +
20
+ 'installed package resolved by name, schema-checking its manifest, verifying each ' +
21
21
  'declared contribution root, and reporting every finding. Safe as a CI gate.',
22
22
  fn: 'validateIntegration',
23
23
  args: [{name: 'package', param: 'pkg', required: false}],
@@ -341,10 +341,9 @@ export function generateCompressedIndex(version, {coreDir, invocation = getCliIn
341
341
 
342
342
  // Rules — the top error-preventers.
343
343
  lines.push('RULES:');
344
- lines.push('- No <div> — components do all layout/spacing. Full page → AppShell; sidebar nav → SideNav.');
345
- lines.push('- Frame first: pick the shell (AppShell / Layout+LayoutPanel) and budget regions in px BEFORE writing content (`astryx docs layout`).');
346
- lines.push('- Dense data = rows (Table, List/Item) edge-to-edge — never Card-wrapped list items. Card = dashboard widgets, galleries, settings groups only.');
347
- lines.push('- Status → StatusDot/Token; Badge only for counts and enumerated states, never decoration.');
344
+ lines.push('- No <div> — components do all layout/spacing, page frame included.');
345
+ lines.push('- Frame first: read `astryx docs layout` before writing any page or screen — page frame, region widths, breakpoint behavior.');
346
+ lines.push('- Dense data = rows (Table, List/Item), never Card-wrapped list items; Card is for standalone widgets. Status = StatusDot/Token; Badge = counts only.');
348
347
  // Styling guidance tailored to the project's configured system — never
349
348
  // recommend a path that isn't compiled here (xstyle needs the StyleX compiler;
350
349
  // utilities need Tailwind). Tokens are always the source of truth.
@@ -46,6 +46,14 @@ describe('generateCompressedIndex', () => {
46
46
  expect(result).toMatch(/never override --color-/);
47
47
  });
48
48
 
49
+ it('sends frame choice to the layout doc rather than naming a shell', () => {
50
+ const result = generateCompressedIndex('1.0.0');
51
+ const frameRule = result.split('\n').find(l => l.includes('Frame first'));
52
+ expect(frameRule).toContain('astryx docs layout');
53
+ expect(result).not.toMatch(/AppShell/);
54
+ expect(frameRule).not.toMatch(/https?:/);
55
+ });
56
+
49
57
  it('includes the post-generation self-check rule', () => {
50
58
  const result = generateCompressedIndex('1.0.0');
51
59
  expect(result).toContain('SELF-CHECK before you finish');
@@ -153,14 +153,18 @@ const PLACEHOLDER_IMAGE =
153
153
  'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20400%20300%22%20preserveAspectRatio%3D%22xMidYMid%20slice%22%3E%3Crect%20width%3D%22400%22%20height%3D%22300%22%20fill%3D%22%23f5f6f8%22%2F%3E%3Cg%20transform%3D%22translate%28200%20150%29%22%20fill%3D%22none%22%20stroke%3D%22%23c2cad6%22%20stroke-width%3D%225%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%22-44%22%20y%3D%22-44%22%20width%3D%2288%22%20height%3D%2288%22%20rx%3D%2216%22%2F%3E%3Ccircle%20cx%3D%2218%22%20cy%3D%22-18%22%20r%3D%222.5%22%20fill%3D%22%23c2cad6%22%20stroke%3D%22none%22%2F%3E%3Cpath%20d%3D%22M-34%2030%20L-8%200%20L10%2018%20L20%208%20L34%2024%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E';
154
154
 
155
155
  /**
156
- * Demo-image sources to strip from scaffolded projects — Meta's lookaside CDN
157
- * (a Meta-only network dependency). Genuine third-party URLs (e.g. brand logos
158
- * from paypalobjects.com) are intentionally left untouched.
156
+ * Demo-image sources to strip from scaffolded projects. Template demo imagery
157
+ * is self-hosted under the docsite's `/template-assets/*` dir (committed there,
158
+ * mirrored into the sandbox preview by scripts/sync-templates.js). Those paths
159
+ * only resolve inside the Astryx docsite/sandbox, so on scaffold they're
160
+ * replaced with a self-contained placeholder — a scaffolded project has no
161
+ * `/template-assets/` dir and would otherwise 404. Genuine third-party URLs
162
+ * (e.g. brand logos from paypalobjects.com) are intentionally left untouched.
159
163
  *
160
164
  * @type {RegExp[]}
161
165
  */
162
166
  const DEMO_IMAGE_PATTERNS = [
163
- /https?:\/\/(?:[\w-]+\.)*lookaside\.facebook\.com\/[^\s'"`)]+/g,
167
+ /\/template-assets\/[\w-]+\.\w+/g,
164
168
  ];
165
169
 
166
170
  /**