@tamagui/cli 3.0.0-beta.1093.1 → 3.0.0-beta.1097.1

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.
@@ -25,7 +25,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
  }) : target, mod));
26
26
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
27
  var generate_prompt_exports = {};
28
- __export(generate_prompt_exports, { generatePrompt: () => generatePrompt });
28
+ __export(generate_prompt_exports, {
29
+ generateMarkdown: () => generateMarkdown,
30
+ generatePrompt: () => generatePrompt
31
+ });
29
32
  module.exports = __toCommonJS(generate_prompt_exports);
30
33
  var import_node_path = require("node:path");
31
34
  var FS = __toESM(require("fs-extra"));
@@ -36,20 +39,24 @@ async function generatePrompt(options) {
36
39
  await loadTamagui({
37
40
  ...options.tamaguiOptions,
38
41
  platform: "web"
39
- });
42
+ }, true);
40
43
  const configPath = (0, import_node_path.join)(paths.dotDir, "tamagui.config.json");
41
44
  if (!FS.existsSync(configPath)) {
42
45
  throw new Error(`Config file not found at ${configPath}. Please run 'tamagui generate' first.`);
43
46
  }
44
47
  const config = await FS.readJSON(configPath);
45
- const markdown = generateMarkdown(config);
48
+ const configSetting = config.tamaguiConfig?.settings?.styleValueSyntax;
49
+ const explicitChoice = options.styleValueSyntax || process.env.TAMAGUI_STYLE_VALUE_SYNTAX || configSetting;
50
+ const { resolveStyleValueSyntax } = require("./setup-prompt.cjs");
51
+ const resolvedSyntax = await resolveStyleValueSyntax(explicitChoice);
52
+ const markdown = generateMarkdown(config, { styleValueSyntax: resolvedSyntax });
46
53
  const outputPath = output || (0, import_node_path.join)(process.cwd(), "tamagui-prompt.md");
47
54
  await FS.writeFile(outputPath, markdown, "utf-8");
48
55
  console.info(`
49
56
  \u2713 Generated prompt file at ${outputPath}
50
57
  `);
51
58
  }
52
- function generateMarkdown(config) {
59
+ function generateMarkdown(config, options) {
53
60
  const sections = [];
54
61
  sections.push("# Tamagui Configuration\n\n");
55
62
  sections.push("This document provides an overview of the Tamagui configuration for this project.\n\n");
@@ -60,196 +67,168 @@ function generateMarkdown(config) {
60
67
  }
61
68
  const getPropName = (fullProp) => {
62
69
  const settings2 = config.tamaguiConfig?.settings || {};
63
- if (settings2.onlyAllowShorthands && reverseShorthands[fullProp]) {
64
- return reverseShorthands[fullProp];
70
+ if (settings2.onlyAllowShorthands) {
71
+ if (reverseShorthands[fullProp]) return reverseShorthands[fullProp];
72
+ if (fullProp === "backgroundColor" && reverseShorthands["background"]) {
73
+ return reverseShorthands["background"];
74
+ }
65
75
  }
66
76
  return fullProp;
67
77
  };
68
78
  const settings = config.tamaguiConfig?.settings || {};
69
- if (Object.keys(settings).length > 0) {
70
- sections.push("## Configuration Settings\n\n");
71
- sections.push("**IMPORTANT:** These settings affect how you write Tamagui code in this project.\n\n");
72
- if (settings.defaultFont) {
73
- sections.push(`### Default Font: \`${settings.defaultFont}\`
79
+ const syntaxChoice = options?.styleValueSyntax || settings.styleValueSyntax || "both";
80
+ sections.push("## Configuration Settings\n\n");
81
+ sections.push("**IMPORTANT:** These settings affect how you write Tamagui code in this project.\n\n");
82
+ if (settings.defaultFont) {
83
+ sections.push(`### Default Font: \`${settings.defaultFont}\`
74
84
 
75
85
  `);
76
- sections.push(`All text components will use the "${settings.defaultFont}" font family by default.
86
+ sections.push(`All text components will use the "${settings.defaultFont}" font family by default.
77
87
 
78
88
  `);
79
- }
80
- if (settings.onlyAllowShorthands !== void 0) {
81
- sections.push(`### Only Allow Shorthands: \`${settings.onlyAllowShorthands}\`
89
+ }
90
+ if (settings.onlyAllowShorthands !== void 0) {
91
+ sections.push(`### Only Allow Shorthands: \`${settings.onlyAllowShorthands}\`
82
92
 
83
93
  `);
84
- if (settings.onlyAllowShorthands) {
85
- sections.push("**You MUST use shorthand properties in this project.**\n\n");
86
- sections.push("Full property names are not allowed. For example:\n");
87
- sections.push("- ✅ `<View w=\"10\" />` (correct)\n");
88
- sections.push("- ❌ `<View width=\"10\" />` (will error)\n\n");
89
- sections.push("See the Shorthand Properties section below for all available shorthands.\n\n");
90
- } else {
91
- sections.push("You can use either shorthand or full property names.\n\n");
92
- }
94
+ if (settings.onlyAllowShorthands) {
95
+ sections.push("**You MUST use shorthand properties in this project.**\n\n");
96
+ sections.push("Full property names are not allowed. For example:\n");
97
+ sections.push("- ✅ `<View w=\"10\" />` (correct)\n");
98
+ sections.push("- ❌ `<View width=\"10\" />` (will error)\n\n");
99
+ sections.push("See the Shorthand Properties section below for all available shorthands.\n\n");
100
+ } else {
101
+ sections.push("You can use either shorthand or full property names.\n\n");
93
102
  }
94
- if (settings.addThemeClassName !== void 0) {
95
- sections.push(`### Theme Class Name: \`${settings.addThemeClassName}\`
103
+ }
104
+ if (syntaxChoice === "string") {
105
+ sections.push("### Style value syntax: `string`\n\n");
106
+ sections.push("Only the string form is allowed in this project (e.g. `bg=\"red hover:blue\"`).\n\n");
107
+ } else if (syntaxChoice === "object") {
108
+ sections.push("### Style value syntax: `object`\n\n");
109
+ sections.push("Only the object form is allowed in this project (e.g. `bg={{ default: 'red', hover: 'blue' }}`).\n\n");
110
+ } else {
111
+ sections.push("### Style value syntax\n\n");
112
+ sections.push("Both string and object style value syntax are allowed.\n\n");
113
+ }
114
+ if (settings.addThemeClassName !== void 0) {
115
+ sections.push(`### Theme Class Name: \`${settings.addThemeClassName}\`
96
116
 
97
117
  `);
98
- if (settings.addThemeClassName === "html") {
99
- sections.push("Theme classes are applied to the root HTML element.\n\n");
100
- }
118
+ if (settings.addThemeClassName === "html") {
119
+ sections.push("Theme classes are applied to the root HTML element.\n\n");
101
120
  }
102
- const platform = settings.platform || settings.defaultProps?.platform;
103
- if (platform) {
104
- sections.push(`### Platform Mode: \`${platform}\`
121
+ }
122
+ if (settings.allowedStyleValues) {
123
+ sections.push("### Allowed Style Values\n\n");
124
+ sections.push(`Type validation: \`${JSON.stringify(settings.allowedStyleValues)}\`.
105
125
 
106
126
  `);
107
- if (platform === "web") {
108
- sections.push("This project is configured for **web only**.\n\n");
109
- } else if (platform === "native") {
110
- sections.push("This project is configured for **React Native only**.\n\n");
111
- }
112
- }
113
- const configString = JSON.stringify(config.tamaguiConfig);
114
- if (configString.includes("semi-strict-web")) {
115
- sections.push("### Mode: `semi-strict-web`\n\n");
116
- sections.push("This configuration uses semi-strict-web mode, which:\n");
117
- sections.push("- Optimizes for web performance\n");
118
- sections.push("- May have limited React Native API support\n");
119
- sections.push("- Focuses on web-first development\n\n");
120
- }
127
+ sections.push("Single-token values are type-checked. Run `tamagui check --strict` to also validate conditional payloads.\n\n");
121
128
  }
122
- const componentsSection = [];
123
- const allComponents = [];
124
- for (const componentModule of config.components) {
125
- const componentNames = Object.keys(componentModule.nameToInfo);
126
- allComponents.push(...componentNames);
127
- }
128
- const componentGroups = /* @__PURE__ */ new Map();
129
- const processed = /* @__PURE__ */ new Set();
130
- const sortedComponents = [...allComponents].sort((a, b) => a.length - b.length);
131
- for (const name of sortedComponents) {
132
- if (processed.has(name)) continue;
133
- const children = allComponents.filter((other) => other !== name && other.startsWith(name) && other[name.length]?.match(/[A-Z]/));
134
- if (children.length > 0) {
135
- componentGroups.set(name, new Set(children));
136
- processed.add(name);
137
- children.forEach((child) => processed.add(child));
138
- }
139
- }
140
- const standaloneComponents = allComponents.filter((name) => !processed.has(name));
141
- componentsSection.push("## Components\n\n");
142
- componentsSection.push("The following components are available:\n\n");
143
- const allBaseComponents = [...standaloneComponents, ...Array.from(componentGroups.keys())].sort();
144
- for (const name of allBaseComponents) {
145
- componentsSection.push(`- ${name}
129
+ sections.push("## Flat Value Grammar\n\n");
130
+ sections.push("Conditional style values follow this grammar:\n\n");
131
+ sections.push("```txt\n");
132
+ sections.push("value := base? clause*\n");
133
+ sections.push("clause := modifier(:modifier)*:payload\n");
134
+ sections.push("```\n\n");
135
+ sections.push("- No `$` sigils: bare token names (`bg=\"background\"`, not `bg=\"$background\"`).\n");
136
+ sections.push("- Kebab-case theme names: `background-hover`, `border-color`, `shadow-color`.\n");
137
+ sections.push("- Numbers are px: `p={4}` is 4px, while `p=\"4\"` is space token 4.\n");
138
+ sections.push("- Specificity precedence: platform (`ios:` > `native:` > bare) > condition count > category (media < container < theme < group < state).\n\n");
139
+ const bgProp = getPropName("background");
140
+ const pProp = getPropName("padding");
141
+ if (syntaxChoice === "string") {
142
+ sections.push("**String form:**\n\n");
143
+ sections.push("```tsx\n");
144
+ sections.push(`<View ${bgProp}="background hover:background-hover dark:blue-500" ${pProp}="4 sm:6 max-sm:2" />
146
145
  `);
147
- if (componentGroups.has(name)) {
148
- const children = Array.from(componentGroups.get(name)).sort();
149
- for (const child of children) {
150
- const suffix = child.slice(name.length);
151
- componentsSection.push(` - ${name}.${suffix}
146
+ sections.push("```\n\n");
147
+ } else if (syntaxChoice === "object") {
148
+ sections.push("**Object form:**\n\n");
149
+ sections.push("```tsx\n");
150
+ sections.push(`<View ${bgProp}={{ default: 'background', hover: 'background-hover', dark: 'blue-500' }} ${pProp}={{ default: '4', sm: '6', 'max-sm': '2' }} />
152
151
  `);
153
- }
154
- }
152
+ sections.push("```\n\n");
153
+ } else {
154
+ sections.push("Both forms are allowed:\n\n");
155
+ sections.push("```tsx\n");
156
+ sections.push("// string form\n");
157
+ sections.push(`<View ${bgProp}="background hover:background-hover dark:blue-500" ${pProp}="4 sm:6 max-sm:2" />
158
+
159
+ `);
160
+ sections.push("// object form\n");
161
+ sections.push(`<View ${bgProp}={{ default: 'background', hover: 'background-hover', dark: 'blue-500' }} ${pProp}={{ default: '4', sm: '6', 'max-sm': '2' }} />
162
+ `);
163
+ sections.push("```\n\n");
155
164
  }
156
- componentsSection.push("\n");
157
165
  sections.push("## Shorthand Properties\n\n");
158
166
  sections.push("These shorthand properties are available for styling:\n\n");
159
167
  const shorthandEntries = Object.entries(shorthands).sort(([a], [b]) => a.localeCompare(b));
160
168
  sections.push(shorthandEntries.map(([short, full]) => `- \`${short}\` \u2192 \`${full}\``).join("\n"));
161
169
  sections.push("\n\n");
170
+ const sizes = config.tamaguiConfig?.sizes;
171
+ if (sizes && typeof sizes === "object") {
172
+ sections.push("## Named Control Sizes\n\n");
173
+ sections.push(`Control components (Button, Input, etc.) use the configured names below. Default is \`${sizes.default ?? "md"}\`.
174
+
175
+ `);
176
+ sections.push("| Size | Configuration |\n");
177
+ sections.push("|---|---|\n");
178
+ for (const [sizeKey, val] of Object.entries(sizes)) {
179
+ if (sizeKey === "default") continue;
180
+ const valStr = typeof val === "object" && val !== null ? JSON.stringify(val) : String(val);
181
+ const isDefault = sizes.default === sizeKey ? " (default)" : "";
182
+ sections.push(`| \`${sizeKey}\`${isDefault} | ${valStr} |
183
+ `);
184
+ }
185
+ sections.push("\n");
186
+ }
162
187
  sections.push("## Themes\n\n");
163
188
  const themes = config.tamaguiConfig?.themes || {};
164
189
  const themeNames = Object.keys(themes).sort();
165
- const hierarchy = {
166
- level1: /* @__PURE__ */ new Set(),
167
- level2: /* @__PURE__ */ new Set(),
168
- level3: /* @__PURE__ */ new Set(),
169
- components: /* @__PURE__ */ new Set()
170
- };
171
- for (const themeName of themeNames) {
172
- const parts = themeName.split("_");
173
- if (parts[0] === "light" || parts[0] === "dark") {
174
- hierarchy.level1.add(parts[0]);
175
- if (parts.length > 1 && parts[1] && !parts[1].startsWith("alt") && parts[1] !== "active") {
176
- if (parts[1][0] === parts[1][0].toLowerCase()) {
177
- hierarchy.level2.add(parts[1]);
178
- }
179
- }
180
- for (const part of parts) {
181
- if (part.startsWith("alt") || part === "active") {
182
- hierarchy.level3.add(part);
183
- }
184
- }
185
- for (const part of parts) {
186
- if (part[0] && part[0] === part[0].toUpperCase() && part[0] !== part[0].toLowerCase()) {
187
- hierarchy.components.add(part);
188
- }
189
- }
190
- } else {
191
- if (parts.length === 1) {
192
- hierarchy.level1.add(themeName);
193
- }
194
- }
195
- }
196
- sections.push("Themes are organized hierarchically and can be combined:\n\n");
197
- if (hierarchy.level1.size > 0) {
198
- sections.push("**Level 1 (Base):**\n\n");
199
- sections.push(Array.from(hierarchy.level1).sort().map((name) => `- ${name}`).join("\n"));
200
- sections.push("\n\n");
201
- }
202
- if (hierarchy.level2.size > 0) {
203
- sections.push("**Level 2 (Color Schemes):**\n\n");
204
- sections.push(Array.from(hierarchy.level2).sort().map((name) => `- ${name}`).join("\n"));
205
- sections.push("\n\n");
206
- }
207
- if (hierarchy.level3.size > 0) {
208
- sections.push("**Level 3 (Variants):**\n\n");
209
- sections.push(Array.from(hierarchy.level3).sort().map((name) => `- ${name}`).join("\n"));
210
- sections.push("\n\n");
211
- }
212
- if (hierarchy.components.size > 0) {
213
- sections.push("**Component Themes:**\n\n");
214
- sections.push(Array.from(hierarchy.components).sort().map((name) => `- ${name}`).join("\n"));
215
- sections.push("\n\n");
190
+ sections.push(themeNames.map((name) => `- \`${name}\``).join("\n"));
191
+ sections.push("\n\nTheme names above are exact configured names. Nested themes resolve relative to their parent. Use an explicit theme boundary in a component skin.\n\n");
192
+ if (themeNames.length) {
193
+ sections.push("### Theme Usage\n\n");
194
+ sections.push(`\`\`\`tsx
195
+ <Theme name=${JSON.stringify(themeNames[0])}>
196
+ <Button>Uses this theme</Button>
197
+ </Theme>
198
+ \`\`\`
199
+
200
+ `);
216
201
  }
217
- sections.push("### Theme Usage\n\n");
218
- sections.push("Themes are combined hierarchically. For example, `light_blue_alt1_Button` combines:\n");
219
- sections.push("- Base: `light`\n");
220
- sections.push("- Color: `blue`\n");
221
- sections.push("- Variant: `alt1`\n");
222
- sections.push("- Component: `Button`\n\n");
223
- sections.push("**Basic usage:**\n\n");
224
- sections.push("```tsx\n");
225
- sections.push("// Apply a theme to components\n");
226
- sections.push("export default () => (\n");
227
- sections.push(" <Theme name=\"dark\">\n");
228
- sections.push(" <Button>I'm a dark button</Button>\n");
229
- sections.push(" </Theme>\n");
230
- sections.push(")\n\n");
231
- sections.push("// Themes nest and combine automatically\n");
232
- sections.push("export default () => (\n");
233
- sections.push(" <Theme name=\"dark\">\n");
234
- sections.push(" <Theme name=\"blue\">\n");
235
- sections.push(" <Button>Uses dark_blue theme</Button>\n");
236
- sections.push(" </Theme>\n");
237
- sections.push(" </Theme>\n");
238
- sections.push(")\n");
239
- sections.push("```\n\n");
240
202
  sections.push("**Accessing theme values:**\n\n");
241
203
  sections.push("Components access theme values by their bare names:\n\n");
242
- sections.push("```tsx\n");
243
- sections.push(`<View ${getPropName("backgroundColor")}="background" ${getPropName("color")}="color" />
204
+ const colorProp = getPropName("color");
205
+ if (syntaxChoice === "string") {
206
+ sections.push("```tsx\n");
207
+ sections.push(`<View ${bgProp}="background hover:background-hover" ${colorProp}="color" />
244
208
  `);
245
- sections.push("```\n\n");
209
+ sections.push("```\n\n");
210
+ } else if (syntaxChoice === "object") {
211
+ sections.push("```tsx\n");
212
+ sections.push(`<View ${bgProp}={{ default: 'background', hover: 'background-hover' }} ${colorProp}={{ default: 'color' }} />
213
+ `);
214
+ sections.push("```\n\n");
215
+ } else {
216
+ sections.push("```tsx\n");
217
+ sections.push("// string form\n");
218
+ sections.push(`<View ${bgProp}="background hover:background-hover" ${colorProp}="color" />
219
+
220
+ `);
221
+ sections.push("// object form\n");
222
+ sections.push(`<View ${bgProp}={{ default: 'background', hover: 'background-hover' }} ${colorProp}={{ default: 'color' }} />
223
+ `);
224
+ sections.push("```\n\n");
225
+ }
246
226
  sections.push("**Special props:**\n\n");
247
227
  sections.push("- `theme=\"inverse\"`: Uses the opposite light or dark sub-theme\n");
248
- sections.push("- `reset`: Reverts to grandparent theme\n\n");
249
228
  sections.push("## Tokens\n\n");
250
229
  sections.push("Tokens are design system values referenced by their bare names.\n\n");
251
230
  const tokens = config.tamaguiConfig?.tokens || {};
252
- if (tokens.space) {
231
+ if (tokens.space && Object.keys(tokens.space).length > 0) {
253
232
  sections.push("### Space Tokens\n\n");
254
233
  const spaceTokens = Object.entries(tokens.space).sort(([a], [b]) => {
255
234
  const numA = parseFloat(a);
@@ -262,7 +241,7 @@ function generateMarkdown(config) {
262
241
  sections.push(spaceTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
263
242
  sections.push("\n\n");
264
243
  }
265
- if (tokens.size) {
244
+ if (tokens.size && Object.keys(tokens.size).length > 0) {
266
245
  sections.push("### Size Tokens\n\n");
267
246
  const sizeTokens = Object.entries(tokens.size).sort(([a], [b]) => {
268
247
  const numA = parseFloat(a);
@@ -275,7 +254,7 @@ function generateMarkdown(config) {
275
254
  sections.push(sizeTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
276
255
  sections.push("\n\n");
277
256
  }
278
- if (tokens.radius) {
257
+ if (tokens.radius && Object.keys(tokens.radius).length > 0) {
279
258
  sections.push("### Radius Tokens\n\n");
280
259
  const radiusTokens = Object.entries(tokens.radius).sort(([a], [b]) => {
281
260
  const numA = parseFloat(a);
@@ -288,7 +267,7 @@ function generateMarkdown(config) {
288
267
  sections.push(radiusTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
289
268
  sections.push("\n\n");
290
269
  }
291
- if (tokens.zIndex) {
270
+ if (tokens.zIndex && Object.keys(tokens.zIndex).length > 0) {
292
271
  sections.push("### Z-Index Tokens\n\n");
293
272
  const zIndexTokens = Object.entries(tokens.zIndex).sort(([a], [b]) => {
294
273
  const numA = parseFloat(a);
@@ -301,60 +280,150 @@ function generateMarkdown(config) {
301
280
  sections.push(zIndexTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
302
281
  sections.push("\n\n");
303
282
  }
304
- if (tokens.color) {
283
+ let sampleColorBg = "blue-500";
284
+ let sampleColorText = "color";
285
+ if (tokens.color && Object.keys(tokens.color).length > 0) {
305
286
  sections.push("### Color Tokens\n\n");
306
- const colorTokens = Object.entries(tokens.color).sort(([a], [b]) => a.localeCompare(b));
307
- sections.push(colorTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
308
- sections.push("\n\n");
287
+ const colorKeys = Object.keys(tokens.color).sort();
288
+ const paletteMap = /* @__PURE__ */ new Map();
289
+ const standaloneColors = [];
290
+ for (const key of colorKeys) {
291
+ const match = key.match(/^([a-z]+)-(\d+)$/);
292
+ if (match) {
293
+ const [, palette, step] = match;
294
+ if (!paletteMap.has(palette)) {
295
+ paletteMap.set(palette, /* @__PURE__ */ new Set());
296
+ }
297
+ paletteMap.get(palette).add(step);
298
+ } else {
299
+ standaloneColors.push([key, tokens.color[key]]);
300
+ }
301
+ }
302
+ if (paletteMap.size >= 4) {
303
+ const palettes = Array.from(paletteMap.keys()).sort().join(", ");
304
+ sections.push(`**Tailwind Palettes (\`<name>-<50..950>\`):** ${palettes}
305
+
306
+ `);
307
+ if (standaloneColors.length > 0) {
308
+ sections.push("**Named Colors:**\n\n");
309
+ sections.push(standaloneColors.map(([k, v]) => `- \`${k}\`: ${formatTokenValue(v)}`).join("\n"));
310
+ sections.push("\n\n");
311
+ }
312
+ sampleColorBg = colorKeys.find((k) => k.startsWith("blue-")) || colorKeys[0];
313
+ sampleColorText = colorKeys.find((k) => k.startsWith("gray-") || k.startsWith("zinc-")) || colorKeys[1] || "color";
314
+ } else {
315
+ sections.push(colorKeys.map((key) => `- \`${key}\`: ${formatTokenValue(tokens.color[key])}`).join("\n"));
316
+ sections.push("\n\n");
317
+ sampleColorBg = colorKeys[0] || "background";
318
+ sampleColorText = colorKeys[1] || "color";
319
+ }
320
+ } else {
321
+ sampleColorBg = "background";
322
+ sampleColorText = "color";
309
323
  }
310
324
  sections.push("### Token Usage\n\n");
311
325
  sections.push("Tokens can be used in component props by their bare names:\n\n");
312
- sections.push("```tsx\n");
313
- sections.push("// Space tokens - for margin, padding, gap\n");
314
- sections.push(`<View ${getPropName("padding")}="4" ${getPropName("gap")}="2" ${getPropName("margin")}="3" />
326
+ const paddingProp = getPropName("padding");
327
+ const gapProp = getPropName("gap");
328
+ const marginProp = getPropName("margin");
329
+ const widthProp = getPropName("width");
330
+ const heightProp = getPropName("height");
331
+ const radiusProp = getPropName("borderRadius");
332
+ const sampleRadius = tokens.radius?.md ? "md" : Object.keys(tokens.radius || {})[0] || "0px";
333
+ if (syntaxChoice === "string") {
334
+ sections.push("```tsx\n");
335
+ sections.push(`// Space tokens - for margin, padding, gap
336
+ <View ${paddingProp}="4 sm:6" ${gapProp}="2" ${marginProp}="3" />
315
337
 
316
338
  `);
317
- sections.push("// Size tokens - for width, height, dimensions\n");
318
- sections.push(`<View ${getPropName("width")}="10" ${getPropName("height")}="6" />
339
+ sections.push(`// Size tokens - for width, height, dimensions
340
+ <View ${widthProp}="10 sm:12" ${heightProp}="6" />
319
341
 
320
342
  `);
321
- sections.push("// Color tokens - for colors and backgrounds\n");
322
- sections.push(`<View ${getPropName("backgroundColor")}="blue5" ${getPropName("color")}="gray12" />
343
+ sections.push(`// Color tokens - for colors and backgrounds
344
+ <View ${bgProp}="${sampleColorBg} hover:${sampleColorBg}" ${colorProp}="${sampleColorText}" />
323
345
 
324
346
  `);
325
- sections.push("// Radius tokens - for border-radius\n");
326
- sections.push(`<View ${getPropName("borderRadius")}="4" />
347
+ sections.push(`// Radius tokens - for border-radius
348
+ <View ${radiusProp}="${sampleRadius}" />
327
349
  `);
328
- sections.push("```\n\n");
350
+ sections.push("```\n\n");
351
+ } else if (syntaxChoice === "object") {
352
+ sections.push("```tsx\n");
353
+ sections.push(`// Space tokens - for margin, padding, gap
354
+ <View ${paddingProp}={{ default: '4', sm: '6' }} ${gapProp}="2" ${marginProp}="3" />
355
+
356
+ `);
357
+ sections.push(`// Size tokens - for width, height, dimensions
358
+ <View ${widthProp}={{ default: '10', sm: '12' }} ${heightProp}="6" />
359
+
360
+ `);
361
+ sections.push(`// Color tokens - for colors and backgrounds
362
+ <View ${bgProp}={{ default: '${sampleColorBg}', hover: '${sampleColorBg}' }} ${colorProp}={{ default: '${sampleColorText}' }} />
363
+
364
+ `);
365
+ sections.push(`// Radius tokens - for border-radius
366
+ <View ${radiusProp}="${sampleRadius}" />
367
+ `);
368
+ sections.push("```\n\n");
369
+ } else {
370
+ sections.push("```tsx\n");
371
+ sections.push("// String form\n");
372
+ sections.push(`<View ${paddingProp}="4 sm:6" ${widthProp}="10 sm:12" ${bgProp}="${sampleColorBg}" />
373
+
374
+ `);
375
+ sections.push("// Object form\n");
376
+ sections.push(`<View ${paddingProp}={{ default: '4', sm: '6' }} ${widthProp}={{ default: '10', sm: '12' }} ${bgProp}={{ default: '${sampleColorBg}' }} />
377
+
378
+ `);
379
+ sections.push(`// Space and radius tokens
380
+ <View ${gapProp}="2" ${marginProp}="3" ${heightProp}="6" ${radiusProp}="${sampleRadius}" />
381
+ `);
382
+ sections.push("```\n\n");
383
+ }
329
384
  if (config.tamaguiConfig?.media) {
330
385
  sections.push("## Media Queries\n\n");
331
386
  sections.push("Available responsive breakpoints:\n\n");
332
387
  const media = config.tamaguiConfig.media;
333
388
  const mediaEntries = Object.entries(media).sort(([a], [b]) => a.localeCompare(b));
334
389
  for (const [name, query] of mediaEntries) {
335
- sections.push(`- **${name}**: ${JSON.stringify(query)}
390
+ sections.push(`- **${name}**: ${formatMediaQuery(query)}
336
391
  `);
337
392
  }
338
393
  sections.push("\n");
339
394
  sections.push("### Media Query Usage\n\n");
340
395
  sections.push("Media queries can be used as style props or with the `useMedia` hook:\n\n");
341
- sections.push("```tsx\n");
342
- sections.push("// As a clause in the same style value\n");
343
- const firstMediaName = mediaEntries[0]?.[0];
344
- if (firstMediaName) {
345
- sections.push(`<View ${getPropName("width")}="100% ${firstMediaName}:50%" />
396
+ const representative = mediaEntries.find(([n]) => n === "sm" || n === "md")?.[0] || mediaEntries.find(([n]) => !n.includes("-"))?.[0] || mediaEntries[0]?.[0];
397
+ if (representative) {
398
+ const isIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(representative);
399
+ const mediaAccess = isIdentifier ? `media.${representative}` : `media['${representative}']`;
400
+ sections.push("```tsx\n");
401
+ if (syntaxChoice === "string") {
402
+ sections.push("// As a clause in the same style value\n");
403
+ sections.push(`<View ${widthProp}="100% ${representative}:50%" />
346
404
 
347
405
  `);
348
- }
349
- sections.push("// Using the useMedia hook\n");
350
- sections.push("const media = useMedia()\n");
351
- if (firstMediaName) {
352
- sections.push(`if (media.${firstMediaName}) {
406
+ } else if (syntaxChoice === "object") {
407
+ sections.push("// Using the object form\n");
408
+ sections.push(`<View ${widthProp}={{ default: '100%', '${representative}': '50%' }} />
409
+
410
+ `);
411
+ } else {
412
+ sections.push("// String form and object form\n");
413
+ sections.push(`<View ${widthProp}="100% ${representative}:50%" />
414
+ `);
415
+ sections.push(`<View ${widthProp}={{ default: '100%', '${representative}': '50%' }} />
416
+
417
+ `);
418
+ }
419
+ sections.push("// Using the useMedia hook\n");
420
+ sections.push("const media = useMedia()\n");
421
+ sections.push(`if (${mediaAccess}) {
353
422
  `);
354
423
  sections.push(" // Render for this breakpoint\n");
355
424
  sections.push("}\n");
425
+ sections.push("```\n\n");
356
426
  }
357
- sections.push("```\n\n");
358
427
  }
359
428
  if (config.tamaguiConfig?.fonts) {
360
429
  sections.push("## Fonts\n\n");
@@ -374,6 +443,48 @@ function generateMarkdown(config) {
374
443
  sections.push("\n\n");
375
444
  }
376
445
  }
446
+ const componentsSection = [];
447
+ const componentSet = /* @__PURE__ */ new Set();
448
+ let barrelExported = false;
449
+ const candidateModules = ["tamagui", ...(config.components || []).map((c) => c.moduleName)];
450
+ for (const modName of candidateModules) {
451
+ if (!modName) continue;
452
+ try {
453
+ const mod = require(modName);
454
+ for (const [key, val] of Object.entries(mod)) {
455
+ if (/^[A-Z]/.test(key) && (typeof val === "function" || typeof val === "object" && val !== null)) {
456
+ if (!key.endsWith("Context") && !key.endsWith("Provider") && key !== "Fragment") {
457
+ componentSet.add(key);
458
+ barrelExported = true;
459
+ }
460
+ }
461
+ }
462
+ if (barrelExported) break;
463
+ } catch {}
464
+ }
465
+ if (!barrelExported && config.components) {
466
+ for (const componentModule of config.components) {
467
+ for (const name of Object.keys(componentModule.nameToInfo || {})) {
468
+ componentSet.add(name);
469
+ }
470
+ }
471
+ }
472
+ const allComponents = Array.from(componentSet).filter((name) => {
473
+ if (name.endsWith("Frame")) {
474
+ const base = name.replace(/Frame$/, "");
475
+ if (componentSet.has(base) || name.startsWith("Popper") || name.startsWith("DialogPortal") || name.startsWith("SelectScrollButton")) {
476
+ return false;
477
+ }
478
+ }
479
+ return true;
480
+ });
481
+ componentsSection.push("## Components\n\n");
482
+ componentsSection.push("Available named exports (import these names directly):\n\n");
483
+ for (const name of allComponents.sort()) {
484
+ componentsSection.push(`- ${name}
485
+ `);
486
+ }
487
+ componentsSection.push("\n");
377
488
  sections.push(...componentsSection);
378
489
  return sections.join("");
379
490
  }
@@ -383,3 +494,34 @@ function formatTokenValue(value) {
383
494
  }
384
495
  return String(value);
385
496
  }
497
+ function formatMediaQuery(query) {
498
+ if (typeof query !== "object" || query === null) {
499
+ return String(query);
500
+ }
501
+ const parts = [];
502
+ if (query.minWidth !== void 0) {
503
+ parts.push(`min-width: ${query.minWidth}px (screens >= ${query.minWidth}px wide)`);
504
+ }
505
+ if (query.maxWidth !== void 0) {
506
+ parts.push(`max-width: ${query.maxWidth}px (screens <= ${query.maxWidth}px wide)`);
507
+ }
508
+ if (query.minHeight !== void 0) {
509
+ parts.push(`min-height: ${query.minHeight}px (screens >= ${query.minHeight}px tall)`);
510
+ }
511
+ if (query.maxHeight !== void 0) {
512
+ parts.push(`max-height: ${query.maxHeight}px (screens <= ${query.maxHeight}px tall)`);
513
+ }
514
+ if (query.hover !== void 0) {
515
+ parts.push(`pointer: hover (${query.hover})`);
516
+ }
517
+ if (query.pointer !== void 0) {
518
+ parts.push(`pointer: ${query.pointer}`);
519
+ }
520
+ if (query.prefersReducedMotion !== void 0) {
521
+ parts.push(`prefers-reduced-motion: ${query.prefersReducedMotion}`);
522
+ }
523
+ if (parts.length === 0) {
524
+ return JSON.stringify(query);
525
+ }
526
+ return parts.join(", ");
527
+ }