mdx-artifacts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +234 -0
  3. package/README.zh-CN.md +129 -0
  4. package/agents/AGENTS.snippet.md +13 -0
  5. package/artifact-docs/examples/commentable-feedback.mdx +126 -0
  6. package/artifact-docs/examples/decision-matrix.mdx +80 -0
  7. package/artifact-docs/examples/layout-composition.mdx +216 -0
  8. package/artifact-docs/examples/streamlit-style-mixed.mdx +183 -0
  9. package/dist/lib/cli/artifact-state.d.ts +27 -0
  10. package/dist/lib/cli/artifact-state.js +115 -0
  11. package/dist/lib/cli/build.d.ts +1 -0
  12. package/dist/lib/cli/build.js +25 -0
  13. package/dist/lib/cli/components.d.ts +3 -0
  14. package/dist/lib/cli/components.js +58 -0
  15. package/dist/lib/cli/config.d.ts +2 -0
  16. package/dist/lib/cli/config.js +36 -0
  17. package/dist/lib/cli/dev.d.ts +1 -0
  18. package/dist/lib/cli/dev.js +24 -0
  19. package/dist/lib/cli/index.d.ts +2 -0
  20. package/dist/lib/cli/index.js +69 -0
  21. package/dist/lib/cli/review.d.ts +33 -0
  22. package/dist/lib/cli/review.js +390 -0
  23. package/dist/lib/cli/scaffold.d.ts +1 -0
  24. package/dist/lib/cli/scaffold.js +56 -0
  25. package/dist/lib/cli/types.d.ts +7 -0
  26. package/dist/lib/cli/types.js +1 -0
  27. package/dist/lib/cli/validate.d.ts +6 -0
  28. package/dist/lib/cli/validate.js +79 -0
  29. package/dist/lib/cli/vite-artifact.d.ts +13 -0
  30. package/dist/lib/cli/vite-artifact.js +213 -0
  31. package/dist/lib/react/components/AnnotatedCode.d.ts +19 -0
  32. package/dist/lib/react/components/AnnotatedCode.js +30 -0
  33. package/dist/lib/react/components/ArtifactState.d.ts +68 -0
  34. package/dist/lib/react/components/ArtifactState.js +286 -0
  35. package/dist/lib/react/components/Callout.d.ts +9 -0
  36. package/dist/lib/react/components/Callout.js +21 -0
  37. package/dist/lib/react/components/CodeBlock.d.ts +10 -0
  38. package/dist/lib/react/components/CodeBlock.js +28 -0
  39. package/dist/lib/react/components/Comments.d.ts +53 -0
  40. package/dist/lib/react/components/Comments.js +613 -0
  41. package/dist/lib/react/components/ComparisonSet.d.ts +24 -0
  42. package/dist/lib/react/components/ComparisonSet.js +30 -0
  43. package/dist/lib/react/components/DecisionMatrix.d.ts +16 -0
  44. package/dist/lib/react/components/DecisionMatrix.js +27 -0
  45. package/dist/lib/react/components/DiffBlock.d.ts +15 -0
  46. package/dist/lib/react/components/DiffBlock.js +24 -0
  47. package/dist/lib/react/components/ExportPanel.d.ts +7 -0
  48. package/dist/lib/react/components/ExportPanel.js +84 -0
  49. package/dist/lib/react/components/InlineText.d.ts +9 -0
  50. package/dist/lib/react/components/InlineText.js +18 -0
  51. package/dist/lib/react/components/Layout.d.ts +44 -0
  52. package/dist/lib/react/components/Layout.js +28 -0
  53. package/dist/lib/react/components/MarkdownBody.d.ts +7 -0
  54. package/dist/lib/react/components/MarkdownBody.js +36 -0
  55. package/dist/lib/react/components/OptionGrid.d.ts +13 -0
  56. package/dist/lib/react/components/OptionGrid.js +21 -0
  57. package/dist/lib/react/components/Section.d.ts +7 -0
  58. package/dist/lib/react/components/Section.js +41 -0
  59. package/dist/lib/react/components/SeverityBadge.d.ts +7 -0
  60. package/dist/lib/react/components/SeverityBadge.js +14 -0
  61. package/dist/lib/react/index.d.ts +33 -0
  62. package/dist/lib/react/index.js +16 -0
  63. package/dist/lib/react/registry.d.ts +24 -0
  64. package/dist/lib/react/registry.js +1002 -0
  65. package/dist/lib/react/styles.css +1417 -0
  66. package/docs/component-protocol.md +273 -0
  67. package/docs/component-taxonomy.md +273 -0
  68. package/docs/design.md +239 -0
  69. package/docs/design.zh-CN.md +217 -0
  70. package/docs/naming.md +123 -0
  71. package/docs/testing.md +138 -0
  72. package/package.json +90 -0
@@ -0,0 +1,216 @@
1
+ import {
2
+ Columns,
3
+ ComparisonSet,
4
+ DecisionMatrix,
5
+ ExportPanel,
6
+ Frame,
7
+ Grid,
8
+ InlineText,
9
+ MarkdownBody,
10
+ SplitPane,
11
+ Stack,
12
+ } from "mdx-artifacts/react";
13
+
14
+ # Layout Composition Example
15
+
16
+ <Stack gap="lg">
17
+ <Frame surface="plain" padding="lg">
18
+ <Stack gap="sm">
19
+ <InlineText
20
+ as="h2"
21
+ variant="title"
22
+ text="A composed artifact using **layout primitives** and text components"
23
+ />
24
+ <MarkdownBody
25
+ body={`This example shows how \`Stack\`, \`Columns\`, \`Grid\`, \`SplitPane\`, and \`Frame\` can arrange existing semantic components without replacing them.
26
+
27
+ The layout components control placement and surface. Components such as \`DecisionMatrix\`, \`ComparisonSet\`, and \`ExportPanel\` still own the artifact meaning.`}
28
+ />
29
+
30
+ </Stack>
31
+
32
+ </Frame>
33
+
34
+ <SplitPane ratio="3:1" gap="lg">
35
+ <DecisionMatrix
36
+ id="decision.layout-authoring"
37
+ question="Should layout primitives become the default authoring path?"
38
+ options={[
39
+ {
40
+ id: "semantic-first",
41
+ name: "Semantic-first authoring",
42
+ summary: "Agents choose task-oriented components first, then use layout only when composition needs it.",
43
+ pros: ["Stable protocol", "Less ad hoc JSX", "Better validation path"],
44
+ cons: ["Less free-form than raw HTML"],
45
+ confidence: "high",
46
+ verdict: "Recommended"
47
+ },
48
+ {
49
+ id: "layout-first",
50
+ name: "Layout-first authoring",
51
+ summary: "Agents assemble artifacts from generic layout blocks and arbitrary children.",
52
+ pros: ["Maximum flexibility"],
53
+ cons: ["Easy to recreate raw HTML", "Harder to query through CLI", "Harder to keep consistent"],
54
+ confidence: "low",
55
+ verdict: "Keep as an advanced escape hatch"
56
+ }
57
+ ]}
58
+ />
59
+
60
+ <Frame surface="subtle">
61
+ <Stack gap="sm">
62
+ <InlineText as="h3" variant="subtitle" text="Reading note" />
63
+ <MarkdownBody
64
+ variant="compact"
65
+ body={`Use layout primitives when the artifact needs a custom reading shape:
66
+
67
+ - main content plus sidebar
68
+ - equal option previews
69
+ - framed snippets or mockups
70
+
71
+ Do not use them to rebuild existing semantic components.`}
72
+ />
73
+
74
+ </Stack>
75
+ </Frame>
76
+
77
+ </SplitPane>
78
+
79
+ <Columns ratio="1:1:1" gap="md">
80
+ <Frame surface="outlined">
81
+ <InlineText as="h3" variant="subtitle" text="InlineText" />
82
+ <MarkdownBody
83
+ variant="compact"
84
+ body={`Short labels and headings can use **inline Markdown**, including *emphasis*, ~~removed text~~, and \`inline code\`.`}
85
+ />
86
+ </Frame>
87
+
88
+ <Frame surface="outlined">
89
+ <InlineText as="h3" variant="subtitle" text="MarkdownBody" />
90
+ <MarkdownBody
91
+ variant="compact"
92
+ body={`Body copy supports controlled Markdown:
93
+
94
+ 1. paragraphs
95
+ 2. ordered lists
96
+ 3. unordered lists
97
+ 4. blockquotes`}
98
+ />
99
+
100
+ </Frame>
101
+
102
+ <Frame surface="outlined">
103
+
104
+ <InlineText as="h3" variant="subtitle" text="Frame" />
105
+
106
+ <MarkdownBody
107
+
108
+ variant="compact"
109
+ body={`Frame provides surface treatment:
110
+
111
+ - \`none\`
112
+ - \`plain\`
113
+ - \`subtle\`
114
+ - \`outlined\``}
115
+ />
116
+
117
+ </Frame>
118
+
119
+ </Columns>
120
+
121
+ <ComparisonSet id="comparison.content-shapes" title="Compare content shapes" columns={3}>
122
+
123
+ <ComparisonSet.Item id="markdown-body" title="Markdown body" value="markdown-body">
124
+
125
+ <MarkdownBody
126
+
127
+ variant="compact"
128
+ body={`# This **item** is ~~plain Markdown content~~.
129
+
130
+ #### **item** is ~~plain Markdown content~~.
131
+
132
+ - Good for notes
133
+ - Easy for agents to write
134
+ - Easy to validate
135
+
136
+ 1. Ordered item
137
+ 2. Nested content check
138
+
139
+ `}
140
+ />
141
+
142
+ </ComparisonSet.Item>
143
+
144
+ <ComparisonSet.Item id="framed-snippet" title="Framed snippet" value="framed-snippet">
145
+ <Frame surface="subtle">
146
+ <MarkdownBody
147
+ variant="compact"
148
+ body={`This slot can hold a future \`CodeBlock\`, \`MermaidBlock\`, image renderer, or project-local component.`}
149
+ />
150
+ </Frame>
151
+ </ComparisonSet.Item>
152
+
153
+ <ComparisonSet.Item id="custom-composition" title="Custom composition" value="custom-composition">
154
+ <Stack gap="sm">
155
+ <InlineText text="Nested layout is allowed when the item needs it." variant="label" />
156
+ <MarkdownBody body="The comparison item owns the title. Its body remains open to child components." variant="compact" />
157
+ </Stack>
158
+
159
+ </ComparisonSet.Item>
160
+
161
+ </ComparisonSet>
162
+
163
+ <Grid columns={2} gap="md">
164
+
165
+ <Frame surface="outlined">
166
+
167
+ <InlineText as="h3" variant="subtitle" text="Grid is only layout" />
168
+
169
+ <MarkdownBody
170
+
171
+ body={`This card is built manually from \`Frame\`, \`InlineText\`, and \`MarkdownBody\`.
172
+
173
+ Use this when there is no reusable artifact meaning beyond placement.`}
174
+ />
175
+
176
+ </Frame>
177
+
178
+ <Frame surface="plain" padding="lg">
179
+ <Stack gap="md">
180
+ <InlineText as="h3" variant="subtitle" text="Surface check" />
181
+ <MarkdownBody
182
+ body={`This frame uses \`surface=\"plain\"\`. In light mode it should read as a white surface on a very light neutral page background.
183
+
184
+ In dark mode it should read as a quiet elevated surface, not as a blue-tinted panel.`}
185
+ />
186
+
187
+ </Stack>
188
+ </Frame>
189
+
190
+ </Grid>
191
+
192
+ <ExportPanel
193
+ title="Export layout review"
194
+ formats={["markdown", "json"]}
195
+ value={{
196
+ recommendation: "Keep layout primitives as advanced composition tools and use ComparisonSet for comparable mixed-content candidates.",
197
+ componentsShown: [
198
+ "Stack",
199
+ "Columns",
200
+ "Grid",
201
+ "SplitPane",
202
+ "Frame",
203
+ "ComparisonSet",
204
+ "InlineText",
205
+ "MarkdownBody"
206
+ ],
207
+ reviewChecklist: [
208
+ "Light mode surface contrast is readable.",
209
+ "Dark mode surface contrast is readable.",
210
+ "Semantic components remain the main authoring path.",
211
+ "ComparisonSet items can hold mixed child components.",
212
+ "Text components render controlled Markdown correctly."
213
+ ]
214
+ }}
215
+ />
216
+ </Stack>
@@ -0,0 +1,183 @@
1
+ import {
2
+ Callout,
3
+ CodeBlock,
4
+ ComparisonSet,
5
+ DecisionMatrix,
6
+ ExportPanel,
7
+ Frame,
8
+ MarkdownBody,
9
+ OptionGrid,
10
+ } from "mdx-artifacts/react";
11
+
12
+ # Streamlit-Style Linear Artifact
13
+
14
+ This example tests the simplest authoring path: write native MDX prose, place a component, continue writing prose, then place the next component.
15
+
16
+ The goal is not to create a complex layout. The goal is to check whether the artifact already feels natural when the author writes in a linear flow.
17
+
18
+ ## Context
19
+
20
+ Streamlit works well because authors can write a small amount of text, drop in a widget or chart, then keep going. MDX already gives this project a similar baseline:
21
+
22
+ - markdown headings define the reading structure
23
+ - paragraphs carry narrative context
24
+ - components own reusable workflow UI
25
+ - the artifact shell controls page width and vertical rhythm
26
+
27
+ <Callout
28
+ id="callout.linear-check"
29
+ tone="info"
30
+ title="What this example is checking"
31
+ body="The artifact should read as one continuous document, not as disconnected component demos."
32
+ />
33
+
34
+ ## Decision point
35
+
36
+ The first question is whether the core authoring experience should stay close to native MDX or move toward more explicit text components.
37
+
38
+ <DecisionMatrix
39
+ id="decision.native-mdx"
40
+ question="Should top-level prose use native MDX by default?"
41
+ options={[
42
+ {
43
+ id: "native-mdx",
44
+ name: "Native MDX prose",
45
+ summary:
46
+ "Use regular headings, paragraphs, lists, links, and blockquotes between workflow components.",
47
+ pros: [
48
+ "Shortest authoring path",
49
+ "Agent-friendly",
50
+ "No extra text API to learn",
51
+ ],
52
+ cons: [
53
+ "Needs strong default prose styling",
54
+ "Harder to constrain every edge case",
55
+ ],
56
+ confidence: "high",
57
+ verdict: "Recommended baseline",
58
+ },
59
+ {
60
+ id: "text-components",
61
+ name: "Text components everywhere",
62
+ summary:
63
+ "Require components such as InlineText and MarkdownBody for most visible copy.",
64
+ pros: ["More controlled rendering", "Clear metadata boundary"],
65
+ cons: ["Verbose for simple documents", "Less natural than MDX"],
66
+ confidence: "medium",
67
+ verdict: "Use inside reusable components",
68
+ },
69
+ {
70
+ id: "raw-html",
71
+ name: "Raw HTML escape hatch",
72
+ summary:
73
+ "Let authors write arbitrary HTML when layout or text rendering gets specific.",
74
+ pros: ["Maximum flexibility"],
75
+ cons: ["Style drift", "Weak validation", "Harder for agents to reuse"],
76
+ confidence: "low",
77
+ verdict: "Avoid unless necessary",
78
+ },
79
+ ]}
80
+ />
81
+
82
+ After the decision block, the document should continue naturally. This paragraph is intentionally plain MDX so the spacing between prose and the component above is easy to judge.
83
+
84
+ > A good default artifact should let the author think in reading order first, then reach for layout primitives only when the content needs a custom arrangement.
85
+
86
+ ## Component menu
87
+
88
+ The next block lists the current component roles. It should feel like a continuation of the same document, not a separate app screen.
89
+
90
+ <OptionGrid
91
+ id="option.linear-building-blocks"
92
+ title="Linear authoring building blocks"
93
+ options={[
94
+ {
95
+ id: "native-mdx",
96
+ name: "Native MDX",
97
+ intent: "Own top-level narrative flow.",
98
+ tradeoffs: [
99
+ "Best for headings and explanatory paragraphs",
100
+ "Needs polished document-level CSS",
101
+ ],
102
+ },
103
+ {
104
+ id: "markdown-body",
105
+ name: "MarkdownBody",
106
+ intent: "Own controlled body copy inside a component slot.",
107
+ tradeoffs: [
108
+ "Good for reusable components",
109
+ "Less convenient for top-level prose",
110
+ ],
111
+ },
112
+ {
113
+ id: "workflow-components",
114
+ name: "Workflow components",
115
+ intent:
116
+ "Own structured decisions, comparisons, code notes, and export surfaces.",
117
+ tradeoffs: [
118
+ "Agent-queryable through the registry",
119
+ "Should stay higher-level than raw cards",
120
+ ],
121
+ },
122
+ ]}
123
+ />
124
+
125
+ ## Mixed content
126
+
127
+ Here is a small comparison set with component-local Markdown inside each item. This shows the boundary between native MDX prose and reusable component content.
128
+
129
+ <ComparisonSet id="comparison.text-forms" title="Where each text form belongs" columns={3}>
130
+ <ComparisonSet.Item id="top-level-mdx" title="Top-level MDX" value="top-level-mdx">
131
+ <Frame surface="subtle">
132
+ <MarkdownBody
133
+ variant="compact"
134
+ body="Use this for the document's main reading order: headings, paragraphs, lists, and short notes between components."
135
+ />
136
+ </Frame>
137
+ </ComparisonSet.Item>
138
+
139
+ <ComparisonSet.Item id="markdown-body" title="MarkdownBody" value="markdown-body">
140
+ <Frame surface="subtle">
141
+ <MarkdownBody
142
+ variant="compact"
143
+ body="Use this when a component needs a controlled body field with safe Markdown rendering."
144
+ />
145
+ </Frame>
146
+ </ComparisonSet.Item>
147
+
148
+ <ComparisonSet.Item id="code-block" title="CodeBlock" value="code-block">
149
+ <CodeBlock
150
+ id="code.linear-example"
151
+ filename="artifact-docs/examples/streamlit-style-mixed.mdx"
152
+ language="mdx"
153
+ code={`# Title
154
+
155
+ Plain MDX prose.
156
+
157
+ <DecisionMatrix ... />
158
+
159
+ More prose.`}
160
+ />
161
+
162
+ </ComparisonSet.Item>
163
+ </ComparisonSet>
164
+
165
+ ## Export
166
+
167
+ The export block closes the artifact with a structured summary. In a real artifact, this is where the reader or agent can copy the decision back into the workflow.
168
+
169
+ <ExportPanel
170
+ title="Export linear authoring verdict"
171
+ formats={["markdown", "json"]}
172
+ value={{
173
+ pattern: "Native MDX prose plus ordered workflow components",
174
+ recommendation:
175
+ "Keep native MDX as the top-level write path and use components for reusable structured UI.",
176
+ checks: [
177
+ "Top-level headings and paragraphs remain readable.",
178
+ "Components appear in the same order they are written.",
179
+ "Document spacing makes the artifact feel continuous.",
180
+ "MarkdownBody stays useful inside component slots.",
181
+ ],
182
+ }}
183
+ />
@@ -0,0 +1,27 @@
1
+ export type ArtifactRoute = {
2
+ routePath: string;
3
+ slug: string;
4
+ sourcePath: string;
5
+ sourceRelativePath: string;
6
+ statePath: string;
7
+ stateRelativePath: string;
8
+ };
9
+ export type ArtifactState = {
10
+ version: number;
11
+ source: string;
12
+ threads: unknown[];
13
+ interactions: Record<string, unknown>;
14
+ [key: string]: unknown;
15
+ };
16
+ export declare function createArtifactRoute(projectRoot: string, mdxPath: string, docsDir: string): ArtifactRoute;
17
+ export declare function createArtifactMeta(projectRoot: string, artifact: ArtifactRoute): Promise<{
18
+ routePath: string;
19
+ slug: string;
20
+ sourcePath: string;
21
+ statePath: string;
22
+ stateExists: boolean;
23
+ writable: boolean;
24
+ }>;
25
+ export declare function readArtifactState(artifact: ArtifactRoute): Promise<ArtifactState>;
26
+ export declare function writeArtifactState(projectRoot: string, artifact: ArtifactRoute, value: unknown): Promise<ArtifactState>;
27
+ export declare function createDefaultArtifactState(artifact: ArtifactRoute): ArtifactState;
@@ -0,0 +1,115 @@
1
+ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export function createArtifactRoute(projectRoot, mdxPath, docsDir) {
4
+ const sourcePath = path.resolve(mdxPath);
5
+ const statePath = sourcePath.replace(/\.mdx$/i, ".state.json");
6
+ const slug = createArtifactSlug(projectRoot, sourcePath, docsDir);
7
+ return {
8
+ routePath: `/artifacts/${slug}`,
9
+ slug,
10
+ sourcePath,
11
+ sourceRelativePath: toProjectRelativePath(projectRoot, sourcePath),
12
+ statePath,
13
+ stateRelativePath: toProjectRelativePath(projectRoot, statePath)
14
+ };
15
+ }
16
+ export async function createArtifactMeta(projectRoot, artifact) {
17
+ return {
18
+ routePath: artifact.routePath,
19
+ slug: artifact.slug,
20
+ sourcePath: artifact.sourceRelativePath,
21
+ statePath: artifact.stateRelativePath,
22
+ stateExists: await fileExists(artifact.statePath),
23
+ writable: isSafeStatePath(projectRoot, artifact)
24
+ };
25
+ }
26
+ export async function readArtifactState(artifact) {
27
+ try {
28
+ const source = await readFile(artifact.statePath, "utf8");
29
+ return normalizeArtifactState(JSON.parse(source), artifact);
30
+ }
31
+ catch (error) {
32
+ if (error.code === "ENOENT") {
33
+ return createDefaultArtifactState(artifact);
34
+ }
35
+ throw error;
36
+ }
37
+ }
38
+ export async function writeArtifactState(projectRoot, artifact, value) {
39
+ if (!isSafeStatePath(projectRoot, artifact)) {
40
+ throw new Error("State file must be next to its MDX source inside the project workspace.");
41
+ }
42
+ const state = normalizeArtifactState(value, artifact);
43
+ await mkdir(path.dirname(artifact.statePath), { recursive: true });
44
+ await writeFile(artifact.statePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
45
+ return state;
46
+ }
47
+ export function createDefaultArtifactState(artifact) {
48
+ return {
49
+ version: 1,
50
+ source: artifact.sourceRelativePath,
51
+ threads: [],
52
+ interactions: {}
53
+ };
54
+ }
55
+ function normalizeArtifactState(value, artifact) {
56
+ if (!isRecord(value)) {
57
+ throw new Error("Artifact state must be a JSON object.");
58
+ }
59
+ return {
60
+ ...value,
61
+ version: typeof value.version === "number" ? value.version : 1,
62
+ source: artifact.sourceRelativePath,
63
+ threads: Array.isArray(value.threads) ? value.threads : [],
64
+ interactions: isRecord(value.interactions) ? value.interactions : {}
65
+ };
66
+ }
67
+ function createArtifactSlug(projectRoot, mdxPath, docsDir) {
68
+ const docsRoot = path.resolve(projectRoot, docsDir);
69
+ const relativeToDocs = path.relative(docsRoot, mdxPath);
70
+ const pathForSlug = relativeToDocs.startsWith("..") || path.isAbsolute(relativeToDocs)
71
+ ? path.basename(mdxPath, path.extname(mdxPath))
72
+ : relativeToDocs.replace(/\.mdx$/i, "");
73
+ return pathForSlug
74
+ .split(path.sep)
75
+ .map((segment) => slugify(segment))
76
+ .filter(Boolean)
77
+ .join("/");
78
+ }
79
+ function slugify(value) {
80
+ const slug = value
81
+ .toLowerCase()
82
+ .replace(/[`*_~[\]()]/g, "")
83
+ .replace(/[^a-z0-9]+/g, "-")
84
+ .replace(/^-+|-+$/g, "")
85
+ .slice(0, 64);
86
+ return slug || "artifact";
87
+ }
88
+ function isSafeStatePath(projectRoot, artifact) {
89
+ return (isInsidePath(projectRoot, artifact.sourcePath) &&
90
+ isInsidePath(projectRoot, artifact.statePath) &&
91
+ isSiblingStatePath(artifact));
92
+ }
93
+ function isInsidePath(root, target) {
94
+ const relative = path.relative(path.resolve(root), path.resolve(target));
95
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
96
+ }
97
+ function isSiblingStatePath(artifact) {
98
+ const expected = artifact.sourcePath.replace(/\.mdx$/i, ".state.json");
99
+ return path.resolve(expected) === path.resolve(artifact.statePath);
100
+ }
101
+ function toProjectRelativePath(projectRoot, target) {
102
+ return path.relative(projectRoot, target).replaceAll(path.sep, "/");
103
+ }
104
+ async function fileExists(filePath) {
105
+ try {
106
+ await access(filePath);
107
+ return true;
108
+ }
109
+ catch {
110
+ return false;
111
+ }
112
+ }
113
+ function isRecord(value) {
114
+ return typeof value === "object" && value !== null && !Array.isArray(value);
115
+ }
@@ -0,0 +1 @@
1
+ export declare function buildCommand(projectRoot: string, input: string): Promise<void>;
@@ -0,0 +1,25 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { loadConfig } from "./config.js";
4
+ import { buildArtifact, createArtifactProject } from "./vite-artifact.js";
5
+ export async function buildCommand(projectRoot, input) {
6
+ const config = await loadConfig(projectRoot);
7
+ const mdxPath = path.resolve(projectRoot, input);
8
+ const project = await createArtifactProject(projectRoot, mdxPath, config);
9
+ try {
10
+ const html = await buildArtifact(project);
11
+ const outputPath = outputFileFor(projectRoot, mdxPath, config.docsDir, config.outDir);
12
+ await mkdir(path.dirname(outputPath), { recursive: true });
13
+ await writeFile(outputPath, html);
14
+ console.log(`built ${path.relative(projectRoot, outputPath)}`);
15
+ }
16
+ finally {
17
+ await project.cleanup();
18
+ }
19
+ }
20
+ function outputFileFor(projectRoot, mdxPath, docsDir, outDir) {
21
+ const docsRoot = path.resolve(projectRoot, docsDir);
22
+ const relative = path.relative(docsRoot, mdxPath);
23
+ const safeRelative = relative.startsWith("..") ? path.basename(mdxPath) : relative;
24
+ return path.resolve(projectRoot, outDir, safeRelative.replace(/\.mdx$/, ".html"));
25
+ }
@@ -0,0 +1,3 @@
1
+ export declare function componentsCommand(input?: string, options?: {
2
+ json?: boolean;
3
+ }): void;
@@ -0,0 +1,58 @@
1
+ import { componentRegistry, findComponentMeta } from "../react/registry.js";
2
+ export function componentsCommand(input, options = {}) {
3
+ if (input) {
4
+ const component = findComponentMeta(input);
5
+ if (!component) {
6
+ throw new Error(`Unknown component: ${input}`);
7
+ }
8
+ if (options.json) {
9
+ console.log(JSON.stringify(component, null, 2));
10
+ return;
11
+ }
12
+ printComponent(component);
13
+ return;
14
+ }
15
+ if (options.json) {
16
+ console.log(JSON.stringify(componentRegistry, null, 2));
17
+ return;
18
+ }
19
+ console.log("Available artifact components:\n");
20
+ for (const component of componentRegistry) {
21
+ console.log(`- ${component.name}: ${component.description}`);
22
+ }
23
+ console.log("\nUse `artifact-kit components <ComponentName>` to inspect props and examples.");
24
+ console.log("Use `artifact-kit components --json` for machine-readable metadata.");
25
+ }
26
+ function printComponent(component) {
27
+ console.log(`${component.name}\n`);
28
+ console.log(component.description);
29
+ if (component.category || component.stability) {
30
+ console.log(`\nMetadata: ${[component.category ? `category=${component.category}` : undefined, component.stability ? `stability=${component.stability}` : undefined].filter(Boolean).join(", ")}`);
31
+ }
32
+ console.log("\nUse when:");
33
+ for (const item of component.useWhen) {
34
+ console.log(`- ${item}`);
35
+ }
36
+ console.log("\nProps:");
37
+ for (const prop of component.props) {
38
+ printProp(prop);
39
+ }
40
+ if (component.types?.length) {
41
+ console.log("\nNested types:");
42
+ for (const type of component.types) {
43
+ console.log(`\n${type.name}${type.description ? `: ${type.description}` : ""}`);
44
+ for (const field of type.fields) {
45
+ printProp(field);
46
+ }
47
+ }
48
+ }
49
+ console.log("\nExample:");
50
+ console.log(component.example);
51
+ }
52
+ function printProp(prop) {
53
+ console.log(`- ${prop.name}${prop.required ? " (required)" : ""}: ${prop.type}`);
54
+ if (prop.contentType) {
55
+ console.log(` content type: ${prop.contentType}`);
56
+ }
57
+ console.log(` ${prop.description}`);
58
+ }
@@ -0,0 +1,2 @@
1
+ import type { ArtifactKitConfig } from "./types";
2
+ export declare function loadConfig(projectRoot: string): Promise<Required<ArtifactKitConfig>>;
@@ -0,0 +1,36 @@
1
+ import { access } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ const defaultConfig = {
5
+ docsDir: "artifact-docs",
6
+ includeDefaultStyles: true,
7
+ outDir: "dist/artifacts",
8
+ port: 4321,
9
+ styles: []
10
+ };
11
+ export async function loadConfig(projectRoot) {
12
+ const configPath = await findConfigPath(projectRoot);
13
+ if (!configPath) {
14
+ return defaultConfig;
15
+ }
16
+ try {
17
+ const imported = (await import(pathToFileURL(configPath).href));
18
+ return { ...defaultConfig, ...(imported.default ?? {}) };
19
+ }
20
+ catch (error) {
21
+ throw new Error(`Failed to read ${path.basename(configPath)}: ${String(error)}`);
22
+ }
23
+ }
24
+ async function findConfigPath(projectRoot) {
25
+ for (const filename of ["artifact-kit.config.mjs", "artifact-kit.config.js", "artifact-kit.config.ts"]) {
26
+ const configPath = path.join(projectRoot, filename);
27
+ try {
28
+ await access(configPath);
29
+ return configPath;
30
+ }
31
+ catch {
32
+ // Try the next supported config filename.
33
+ }
34
+ }
35
+ return undefined;
36
+ }
@@ -0,0 +1 @@
1
+ export declare function devCommand(projectRoot: string, input: string): Promise<void>;