mdx-artifacts 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +211 -59
- package/README.zh-CN.md +299 -42
- package/agents/AGENTS.snippet.md +21 -10
- package/artifact-docs/examples/commentable-feedback.mdx +76 -70
- package/artifact-docs/examples/decision-matrix.mdx +122 -50
- package/artifact-docs/examples/layout-composition.mdx +106 -128
- package/artifact-docs/examples/streamlit-style-mixed.mdx +100 -85
- package/dist/lib/cli/{build.js → commands/build.js} +2 -2
- package/dist/lib/cli/{components.js → commands/components.js} +19 -3
- package/dist/lib/cli/{dev.js → commands/dev.js} +2 -2
- package/dist/lib/cli/commands/interactions.d.ts +2 -0
- package/dist/lib/cli/commands/interactions.js +280 -0
- package/dist/lib/cli/commands/review.d.ts +1 -0
- package/dist/lib/cli/commands/review.js +171 -0
- package/dist/lib/cli/commands/scaffold.d.ts +16 -0
- package/dist/lib/cli/commands/scaffold.js +440 -0
- package/dist/lib/cli/commands/validate.d.ts +18 -0
- package/dist/lib/cli/commands/validate.js +311 -0
- package/dist/lib/cli/config/config.d.ts +2 -0
- package/dist/lib/cli/{config.js → config/config.js} +3 -2
- package/dist/lib/cli/{types.d.ts → config/types.d.ts} +2 -1
- package/dist/lib/cli/{vite-artifact.d.ts → dev-server/vite-artifact.d.ts} +3 -3
- package/dist/lib/cli/{vite-artifact.js → dev-server/vite-artifact.js} +170 -10
- package/dist/lib/cli/diagnostics/diagnostics.d.ts +11 -0
- package/dist/lib/cli/diagnostics/diagnostics.js +1 -0
- package/dist/lib/cli/index.js +39 -18
- package/dist/lib/cli/mdx/sortable-list.d.ts +14 -0
- package/dist/lib/cli/mdx/sortable-list.js +520 -0
- package/dist/lib/cli/resources/resource-policy.d.ts +15 -0
- package/dist/lib/cli/resources/resource-policy.js +46 -0
- package/dist/lib/cli/resources/safe-path.d.ts +13 -0
- package/dist/lib/cli/resources/safe-path.js +55 -0
- package/dist/lib/cli/services/interaction-service.d.ts +40 -0
- package/dist/lib/cli/services/interaction-service.js +226 -0
- package/dist/lib/cli/services/review.d.ts +43 -0
- package/dist/lib/cli/{review.js → services/review.js} +34 -172
- package/dist/lib/react/{components → composites/comparison-set}/ComparisonSet.d.ts +1 -1
- package/dist/lib/react/{components → composites/comparison-set}/ComparisonSet.js +3 -3
- package/dist/lib/react/composites/comparison-set/index.d.ts +2 -0
- package/dist/lib/react/composites/comparison-set/index.js +1 -0
- package/dist/lib/react/composites/content-set/ContentItem.d.ts +37 -0
- package/dist/lib/react/composites/content-set/ContentItem.js +49 -0
- package/dist/lib/react/composites/content-set/index.d.ts +2 -0
- package/dist/lib/react/composites/content-set/index.js +1 -0
- package/dist/lib/react/{components → composites/export-panel}/ExportPanel.js +2 -2
- package/dist/lib/react/composites/export-panel/index.d.ts +2 -0
- package/dist/lib/react/composites/export-panel/index.js +1 -0
- package/dist/lib/react/{components → composites/section}/Section.js +1 -1
- package/dist/lib/react/composites/section/index.d.ts +2 -0
- package/dist/lib/react/composites/section/index.js +1 -0
- package/dist/lib/react/index.d.ts +36 -31
- package/dist/lib/react/index.js +18 -15
- package/dist/lib/react/interactions/artifact-state/index.d.ts +1 -0
- package/dist/lib/react/interactions/artifact-state/index.js +1 -0
- package/dist/lib/react/{components → interactions/comments}/Comments.d.ts +2 -2
- package/dist/lib/react/{components → interactions/comments}/Comments.js +3 -3
- package/dist/lib/react/interactions/comments/index.d.ts +1 -0
- package/dist/lib/react/interactions/comments/index.js +1 -0
- package/dist/lib/react/interactions/sortable-list/SortableList.d.ts +29 -0
- package/dist/lib/react/interactions/sortable-list/SortableList.js +282 -0
- package/dist/lib/react/interactions/sortable-list/index.d.ts +1 -0
- package/dist/lib/react/interactions/sortable-list/index.js +1 -0
- package/dist/lib/react/layout/layout-primitives/index.d.ts +2 -0
- package/dist/lib/react/layout/layout-primitives/index.js +1 -0
- package/dist/lib/react/legacy/LegacyContentComponents.d.ts +65 -0
- package/dist/lib/react/legacy/LegacyContentComponents.js +26 -0
- package/dist/lib/react/mdx-components.d.ts +5 -0
- package/dist/lib/react/mdx-components.js +38 -0
- package/dist/lib/react/{components → primitives/annotated-code}/AnnotatedCode.d.ts +1 -1
- package/dist/lib/react/{components → primitives/annotated-code}/AnnotatedCode.js +5 -5
- package/dist/lib/react/primitives/annotated-code/index.d.ts +2 -0
- package/dist/lib/react/primitives/annotated-code/index.js +1 -0
- package/dist/lib/react/primitives/callout/Callout.d.ts +11 -0
- package/dist/lib/react/{components → primitives/callout}/Callout.js +9 -6
- package/dist/lib/react/primitives/callout/index.d.ts +2 -0
- package/dist/lib/react/primitives/callout/index.js +1 -0
- package/dist/lib/react/primitives/code-block/CodeBlock.d.ts +20 -0
- package/dist/lib/react/primitives/code-block/CodeBlock.js +32 -0
- package/dist/lib/react/primitives/code-block/index.d.ts +2 -0
- package/dist/lib/react/primitives/code-block/index.js +1 -0
- package/dist/lib/react/primitives/code-surface/CodeSurface.d.ts +11 -0
- package/dist/lib/react/primitives/code-surface/CodeSurface.js +34 -0
- package/dist/lib/react/primitives/code-surface/index.d.ts +2 -0
- package/dist/lib/react/primitives/code-surface/index.js +1 -0
- package/dist/lib/react/primitives/diff-block/DiffBlock.js +25 -0
- package/dist/lib/react/primitives/diff-block/index.d.ts +2 -0
- package/dist/lib/react/primitives/diff-block/index.js +1 -0
- package/dist/lib/react/{components → primitives/inline-text}/InlineText.d.ts +4 -2
- package/dist/lib/react/primitives/inline-text/InlineText.js +28 -0
- package/dist/lib/react/primitives/inline-text/index.d.ts +2 -0
- package/dist/lib/react/primitives/inline-text/index.js +1 -0
- package/dist/lib/react/primitives/markdown-body/MarkdownBody.d.ts +9 -0
- package/dist/lib/react/primitives/markdown-body/MarkdownBody.js +49 -0
- package/dist/lib/react/primitives/markdown-body/index.d.ts +2 -0
- package/dist/lib/react/primitives/markdown-body/index.js +1 -0
- package/dist/lib/react/primitives/severity-badge/index.d.ts +2 -0
- package/dist/lib/react/primitives/severity-badge/index.js +1 -0
- package/dist/lib/react/registry.d.ts +10 -0
- package/dist/lib/react/registry.js +505 -210
- package/dist/lib/react/styles.css +490 -38
- package/docs/cli-structure.md +141 -0
- package/docs/component-protocol.md +199 -33
- package/docs/component-taxonomy.md +40 -4
- package/docs/design.md +42 -21
- package/docs/design.zh-CN.md +41 -21
- package/docs/naming.md +17 -7
- package/docs/releasing.md +132 -0
- package/docs/testing.md +35 -10
- package/package.json +9 -7
- package/dist/lib/cli/config.d.ts +0 -2
- package/dist/lib/cli/review.d.ts +0 -33
- package/dist/lib/cli/scaffold.d.ts +0 -1
- package/dist/lib/cli/scaffold.js +0 -56
- package/dist/lib/cli/validate.d.ts +0 -6
- package/dist/lib/cli/validate.js +0 -79
- package/dist/lib/react/components/Callout.d.ts +0 -9
- package/dist/lib/react/components/CodeBlock.d.ts +0 -10
- package/dist/lib/react/components/CodeBlock.js +0 -28
- package/dist/lib/react/components/DecisionMatrix.d.ts +0 -16
- package/dist/lib/react/components/DecisionMatrix.js +0 -27
- package/dist/lib/react/components/DiffBlock.js +0 -24
- package/dist/lib/react/components/InlineText.js +0 -18
- package/dist/lib/react/components/MarkdownBody.d.ts +0 -7
- package/dist/lib/react/components/MarkdownBody.js +0 -36
- package/dist/lib/react/components/OptionGrid.d.ts +0 -13
- package/dist/lib/react/components/OptionGrid.js +0 -21
- /package/dist/lib/cli/{build.d.ts → commands/build.d.ts} +0 -0
- /package/dist/lib/cli/{components.d.ts → commands/components.d.ts} +0 -0
- /package/dist/lib/cli/{dev.d.ts → commands/dev.d.ts} +0 -0
- /package/dist/lib/cli/{types.js → config/types.js} +0 -0
- /package/dist/lib/cli/{artifact-state.d.ts → state/artifact-state.d.ts} +0 -0
- /package/dist/lib/cli/{artifact-state.js → state/artifact-state.js} +0 -0
- /package/dist/lib/react/{components → composites/export-panel}/ExportPanel.d.ts +0 -0
- /package/dist/lib/react/{components → composites/section}/Section.d.ts +0 -0
- /package/dist/lib/react/{components → interactions/artifact-state}/ArtifactState.d.ts +0 -0
- /package/dist/lib/react/{components → interactions/artifact-state}/ArtifactState.js +0 -0
- /package/dist/lib/react/{components → layout/layout-primitives}/Layout.d.ts +0 -0
- /package/dist/lib/react/{components → layout/layout-primitives}/Layout.js +0 -0
- /package/dist/lib/react/{components → primitives/diff-block}/DiffBlock.d.ts +0 -0
- /package/dist/lib/react/{components → primitives/severity-badge}/SeverityBadge.d.ts +0 -0
- /package/dist/lib/react/{components → primitives/severity-badge}/SeverityBadge.js +0 -0
|
@@ -2,11 +2,10 @@ import {
|
|
|
2
2
|
Callout,
|
|
3
3
|
CodeBlock,
|
|
4
4
|
ComparisonSet,
|
|
5
|
-
|
|
5
|
+
ContentSet,
|
|
6
6
|
ExportPanel,
|
|
7
7
|
Frame,
|
|
8
8
|
MarkdownBody,
|
|
9
|
-
OptionGrid,
|
|
10
9
|
} from "mdx-artifacts/react";
|
|
11
10
|
|
|
12
11
|
# Streamlit-Style Linear Artifact
|
|
@@ -28,56 +27,60 @@ Streamlit works well because authors can write a small amount of text, drop in a
|
|
|
28
27
|
id="callout.linear-check"
|
|
29
28
|
tone="info"
|
|
30
29
|
title="What this example is checking"
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
>
|
|
31
|
+
The artifact should read as one continuous document, not as disconnected component demos.
|
|
32
|
+
</Callout>
|
|
33
33
|
|
|
34
34
|
## Decision point
|
|
35
35
|
|
|
36
36
|
The first question is whether the core authoring experience should stay close to native MDX or move toward more explicit text components.
|
|
37
37
|
|
|
38
|
-
<
|
|
39
|
-
id="
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
38
|
+
<ContentSet
|
|
39
|
+
id="set.native-mdx"
|
|
40
|
+
title="Should top-level prose use native MDX by default?"
|
|
41
|
+
layout="grid"
|
|
42
|
+
columns={3}
|
|
43
|
+
>
|
|
44
|
+
<ContentSet.Item
|
|
45
|
+
id="native-mdx"
|
|
46
|
+
title="Native MDX prose"
|
|
47
|
+
badge="Recommended"
|
|
48
|
+
tone="positive"
|
|
49
|
+
emphasis="primary"
|
|
50
|
+
summary="Use regular headings, paragraphs, lists, links, and blockquotes between workflow components."
|
|
51
|
+
>
|
|
52
|
+
- Shortest authoring path
|
|
53
|
+
- Agent-friendly
|
|
54
|
+
- No extra text API to learn
|
|
55
|
+
- Needs strong default prose styling
|
|
56
|
+
- Harder to constrain every edge case
|
|
57
|
+
</ContentSet.Item>
|
|
58
|
+
<ContentSet.Item
|
|
59
|
+
id="text-components"
|
|
60
|
+
title="Text components everywhere"
|
|
61
|
+
badge="Component-local"
|
|
62
|
+
tone="info"
|
|
63
|
+
summary="Require components such as `InlineText` and `MarkdownBody` for most visible copy."
|
|
64
|
+
>
|
|
65
|
+
- More controlled rendering
|
|
66
|
+
- Clear metadata boundary
|
|
67
|
+
- Verbose for simple documents
|
|
68
|
+
- Less natural than MDX
|
|
69
|
+
</ContentSet.Item>
|
|
70
|
+
<ContentSet.Item
|
|
71
|
+
id="raw-html"
|
|
72
|
+
title="Raw HTML escape hatch"
|
|
73
|
+
badge="Avoid"
|
|
74
|
+
tone="danger"
|
|
75
|
+
emphasis="subtle"
|
|
76
|
+
summary="Let authors write arbitrary HTML when layout or text rendering gets specific."
|
|
77
|
+
>
|
|
78
|
+
- Maximum flexibility
|
|
79
|
+
- Style drift
|
|
80
|
+
- Weak validation
|
|
81
|
+
- Harder for agents to reuse
|
|
82
|
+
</ContentSet.Item>
|
|
83
|
+
</ContentSet>
|
|
81
84
|
|
|
82
85
|
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
86
|
|
|
@@ -87,40 +90,50 @@ After the decision block, the document should continue naturally. This paragraph
|
|
|
87
90
|
|
|
88
91
|
The next block lists the current component roles. It should feel like a continuation of the same document, not a separate app screen.
|
|
89
92
|
|
|
90
|
-
<
|
|
91
|
-
id="
|
|
93
|
+
<ContentSet
|
|
94
|
+
id="set.linear-building-blocks"
|
|
92
95
|
title="Linear authoring building blocks"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
96
|
+
layout="grid"
|
|
97
|
+
columns={3}
|
|
98
|
+
>
|
|
99
|
+
<ContentSet.Item
|
|
100
|
+
id="native-mdx"
|
|
101
|
+
title="Native MDX"
|
|
102
|
+
badge="Document"
|
|
103
|
+
tone="info"
|
|
104
|
+
emphasis="primary"
|
|
105
|
+
summary="Own top-level narrative flow."
|
|
106
|
+
>
|
|
107
|
+
Use this for the document's main reading order.
|
|
108
|
+
|
|
109
|
+
- Best for headings and explanatory paragraphs
|
|
110
|
+
- Needs polished document-level CSS
|
|
111
|
+
</ContentSet.Item>
|
|
112
|
+
<ContentSet.Item
|
|
113
|
+
id="markdown-body"
|
|
114
|
+
title="MarkdownBody"
|
|
115
|
+
badge="Controlled"
|
|
116
|
+
tone="neutral"
|
|
117
|
+
summary="Own controlled body copy inside a component slot."
|
|
118
|
+
>
|
|
119
|
+
Use this when reusable components need safe Markdown rendering.
|
|
120
|
+
|
|
121
|
+
- Good for reusable components
|
|
122
|
+
- Less convenient for top-level prose
|
|
123
|
+
</ContentSet.Item>
|
|
124
|
+
<ContentSet.Item
|
|
125
|
+
id="workflow-components"
|
|
126
|
+
title="Workflow components"
|
|
127
|
+
badge="Reusable"
|
|
128
|
+
tone="accent"
|
|
129
|
+
summary="Own structured decisions, comparisons, code notes, and export surfaces."
|
|
130
|
+
>
|
|
131
|
+
Use this for reusable artifact meaning rather than layout-only cards.
|
|
132
|
+
|
|
133
|
+
- Agent-queryable through the registry
|
|
134
|
+
- Should stay higher-level than raw cards
|
|
135
|
+
</ContentSet.Item>
|
|
136
|
+
</ContentSet>
|
|
124
137
|
|
|
125
138
|
## Mixed content
|
|
126
139
|
|
|
@@ -131,8 +144,9 @@ Here is a small comparison set with component-local Markdown inside each item. T
|
|
|
131
144
|
<Frame surface="subtle">
|
|
132
145
|
<MarkdownBody
|
|
133
146
|
variant="compact"
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
>
|
|
148
|
+
Use this for the document's main reading order: headings, paragraphs, lists, and short notes between components.
|
|
149
|
+
</MarkdownBody>
|
|
136
150
|
</Frame>
|
|
137
151
|
</ComparisonSet.Item>
|
|
138
152
|
|
|
@@ -140,8 +154,9 @@ Here is a small comparison set with component-local Markdown inside each item. T
|
|
|
140
154
|
<Frame surface="subtle">
|
|
141
155
|
<MarkdownBody
|
|
142
156
|
variant="compact"
|
|
143
|
-
|
|
144
|
-
|
|
157
|
+
>
|
|
158
|
+
Use this when a component needs a controlled body slot with safe Markdown rendering.
|
|
159
|
+
</MarkdownBody>
|
|
145
160
|
</Frame>
|
|
146
161
|
</ComparisonSet.Item>
|
|
147
162
|
|
|
@@ -154,7 +169,7 @@ Here is a small comparison set with component-local Markdown inside each item. T
|
|
|
154
169
|
|
|
155
170
|
Plain MDX prose.
|
|
156
171
|
|
|
157
|
-
<
|
|
172
|
+
<ContentSet ... />
|
|
158
173
|
|
|
159
174
|
More prose.`}
|
|
160
175
|
/>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { loadConfig } from "
|
|
4
|
-
import { buildArtifact, createArtifactProject } from "
|
|
3
|
+
import { loadConfig } from "../config/config.js";
|
|
4
|
+
import { buildArtifact, createArtifactProject } from "../dev-server/vite-artifact.js";
|
|
5
5
|
export async function buildCommand(projectRoot, input) {
|
|
6
6
|
const config = await loadConfig(projectRoot);
|
|
7
7
|
const mdxPath = path.resolve(projectRoot, input);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { componentRegistry, findComponentMeta } from "
|
|
1
|
+
import { componentRegistry, findComponentMeta } from "../../react/registry.js";
|
|
2
2
|
export function componentsCommand(input, options = {}) {
|
|
3
3
|
if (input) {
|
|
4
4
|
const component = findComponentMeta(input);
|
|
@@ -20,15 +20,31 @@ export function componentsCommand(input, options = {}) {
|
|
|
20
20
|
for (const component of componentRegistry) {
|
|
21
21
|
console.log(`- ${component.name}: ${component.description}`);
|
|
22
22
|
}
|
|
23
|
-
console.log("\
|
|
24
|
-
console.log("
|
|
23
|
+
console.log("\nAuthoring rule: prefer MDX children for human-readable content; use props for stable ids, variants, layout controls, export values, and structured data.");
|
|
24
|
+
console.log("\nUse `mdx-artifacts components <ComponentName>` to inspect props, authoring guidance, and examples.");
|
|
25
|
+
console.log("Use `mdx-artifacts components --json` for machine-readable metadata.");
|
|
25
26
|
}
|
|
26
27
|
function printComponent(component) {
|
|
27
28
|
console.log(`${component.name}\n`);
|
|
28
29
|
console.log(component.description);
|
|
30
|
+
if (component.deprecated) {
|
|
31
|
+
console.log(`\nDeprecated: use ${component.deprecated.replacement} for new artifacts.`);
|
|
32
|
+
for (const item of component.deprecated.guidance) {
|
|
33
|
+
console.log(`- ${item}`);
|
|
34
|
+
}
|
|
35
|
+
console.log("\nReplacement example:");
|
|
36
|
+
console.log(component.example);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
29
39
|
if (component.category || component.stability) {
|
|
30
40
|
console.log(`\nMetadata: ${[component.category ? `category=${component.category}` : undefined, component.stability ? `stability=${component.stability}` : undefined].filter(Boolean).join(", ")}`);
|
|
31
41
|
}
|
|
42
|
+
if (component.authoring) {
|
|
43
|
+
console.log(`\nAuthoring: ${component.authoring.kind}`);
|
|
44
|
+
for (const item of component.authoring.guidance) {
|
|
45
|
+
console.log(`- ${item}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
32
48
|
console.log("\nUse when:");
|
|
33
49
|
for (const item of component.useWhen) {
|
|
34
50
|
console.log(`- ${item}`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { loadConfig } from "
|
|
3
|
-
import { createArtifactProject, startDevServer } from "
|
|
2
|
+
import { loadConfig } from "../config/config.js";
|
|
3
|
+
import { createArtifactProject, startDevServer } from "../dev-server/vite-artifact.js";
|
|
4
4
|
export async function devCommand(projectRoot, input) {
|
|
5
5
|
const config = await loadConfig(projectRoot);
|
|
6
6
|
const mdxPath = path.resolve(projectRoot, input);
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { addInteractionItemService, inspectInteractionService, promoteInteractionService, removeInteractionItemService, resetInteractionService, setInteractionOrderService, updateInteractionItemService } from "../services/interaction-service.js";
|
|
2
|
+
export async function interactionsCommand(projectRoot, args) {
|
|
3
|
+
const [subcommand] = args;
|
|
4
|
+
if (subcommand === "inspect") {
|
|
5
|
+
const options = parseInspectArgs(args.slice(1));
|
|
6
|
+
const result = await inspectInteractionService(projectRoot, options.input, options.id);
|
|
7
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatInspectResult(result));
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (subcommand === "set-order") {
|
|
11
|
+
const options = parseSetOrderArgs(args.slice(1));
|
|
12
|
+
const result = await setInteractionOrderService(projectRoot, options.input, options.id, options.orderedIds);
|
|
13
|
+
console.log(formatMutationResult(result));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (subcommand === "reset") {
|
|
17
|
+
const options = parseResetArgs(args.slice(1));
|
|
18
|
+
const result = await resetInteractionService(projectRoot, options.input, options.id);
|
|
19
|
+
console.log(formatMutationResult(result));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (subcommand === "promote") {
|
|
23
|
+
const options = parsePromoteArgs(args.slice(1));
|
|
24
|
+
const result = await promoteInteractionService(projectRoot, options.input, options.id);
|
|
25
|
+
console.log(formatMutationResult(result));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (subcommand === "add-item") {
|
|
29
|
+
const options = parseAddItemArgs(args.slice(1));
|
|
30
|
+
const result = await addInteractionItemService(projectRoot, options.input, options.id, options.item, {
|
|
31
|
+
afterId: options.afterId
|
|
32
|
+
});
|
|
33
|
+
console.log(formatMutationResult(result));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (subcommand === "remove-item") {
|
|
37
|
+
const options = parseRemoveItemArgs(args.slice(1));
|
|
38
|
+
const result = await removeInteractionItemService(projectRoot, options.input, options.id, options.itemId);
|
|
39
|
+
console.log(formatMutationResult(result));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (subcommand === "update-item") {
|
|
43
|
+
const options = parseUpdateItemArgs(args.slice(1));
|
|
44
|
+
const result = await updateInteractionItemService(projectRoot, options.input, options.id, options.itemId, options.patch);
|
|
45
|
+
console.log(formatMutationResult(result));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
throw new Error("interactions requires a subcommand. Use inspect, set-order, reset, promote, add-item, remove-item, or update-item.");
|
|
49
|
+
}
|
|
50
|
+
function parseInspectArgs(args) {
|
|
51
|
+
const positional = args.filter((arg) => arg !== "--json");
|
|
52
|
+
const json = args.includes("--json");
|
|
53
|
+
const [input, id, ...rest] = positional;
|
|
54
|
+
if (!input) {
|
|
55
|
+
throw new Error("interactions inspect requires a .mdx file path.");
|
|
56
|
+
}
|
|
57
|
+
if (!id) {
|
|
58
|
+
throw new Error("interactions inspect requires a component id.");
|
|
59
|
+
}
|
|
60
|
+
if (rest.length > 0) {
|
|
61
|
+
throw new Error(`Unexpected interactions inspect argument: ${rest[0]}`);
|
|
62
|
+
}
|
|
63
|
+
return { input, id, json };
|
|
64
|
+
}
|
|
65
|
+
function parseSetOrderArgs(args) {
|
|
66
|
+
const [input, id, ...rest] = args;
|
|
67
|
+
if (!input) {
|
|
68
|
+
throw new Error("interactions set-order requires a .mdx file path.");
|
|
69
|
+
}
|
|
70
|
+
if (!id) {
|
|
71
|
+
throw new Error("interactions set-order requires a component id.");
|
|
72
|
+
}
|
|
73
|
+
const orderedIds = parseOrderedIds(rest);
|
|
74
|
+
return { input, id, orderedIds };
|
|
75
|
+
}
|
|
76
|
+
function parseResetArgs(args) {
|
|
77
|
+
const [input, id, ...rest] = args;
|
|
78
|
+
if (!input) {
|
|
79
|
+
throw new Error("interactions reset requires a .mdx file path.");
|
|
80
|
+
}
|
|
81
|
+
if (!id) {
|
|
82
|
+
throw new Error("interactions reset requires a component id.");
|
|
83
|
+
}
|
|
84
|
+
if (rest.length > 0) {
|
|
85
|
+
throw new Error(`Unexpected interactions reset argument: ${rest[0]}`);
|
|
86
|
+
}
|
|
87
|
+
return { input, id };
|
|
88
|
+
}
|
|
89
|
+
function parsePromoteArgs(args) {
|
|
90
|
+
const [input, id, ...rest] = args;
|
|
91
|
+
if (!input) {
|
|
92
|
+
throw new Error("interactions promote requires a .mdx file path.");
|
|
93
|
+
}
|
|
94
|
+
if (!id) {
|
|
95
|
+
throw new Error("interactions promote requires a component id.");
|
|
96
|
+
}
|
|
97
|
+
if (rest.length > 0) {
|
|
98
|
+
throw new Error(`Unexpected interactions promote argument: ${rest[0]}`);
|
|
99
|
+
}
|
|
100
|
+
return { input, id };
|
|
101
|
+
}
|
|
102
|
+
function parseAddItemArgs(args) {
|
|
103
|
+
const [input, id, ...rest] = args;
|
|
104
|
+
if (!input) {
|
|
105
|
+
throw new Error("interactions add-item requires a .mdx file path.");
|
|
106
|
+
}
|
|
107
|
+
if (!id) {
|
|
108
|
+
throw new Error("interactions add-item requires a component id.");
|
|
109
|
+
}
|
|
110
|
+
const options = parseOptionMap(rest, ["item-id", "title", "summary", "badge", "tags", "disabled", "after"], []);
|
|
111
|
+
const itemId = options.values.get("item-id");
|
|
112
|
+
const title = options.values.get("title");
|
|
113
|
+
if (!itemId) {
|
|
114
|
+
throw new Error("interactions add-item requires --item-id <id>.");
|
|
115
|
+
}
|
|
116
|
+
if (!title) {
|
|
117
|
+
throw new Error("interactions add-item requires --title <title>.");
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
input,
|
|
121
|
+
id,
|
|
122
|
+
item: createItemFromOptions(itemId, title, options.values),
|
|
123
|
+
...(options.values.get("after") ? { afterId: options.values.get("after") } : {})
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function parseRemoveItemArgs(args) {
|
|
127
|
+
const [input, id, ...rest] = args;
|
|
128
|
+
if (!input) {
|
|
129
|
+
throw new Error("interactions remove-item requires a .mdx file path.");
|
|
130
|
+
}
|
|
131
|
+
if (!id) {
|
|
132
|
+
throw new Error("interactions remove-item requires a component id.");
|
|
133
|
+
}
|
|
134
|
+
const options = parseOptionMap(rest, ["item-id"], []);
|
|
135
|
+
const itemId = options.values.get("item-id");
|
|
136
|
+
if (!itemId) {
|
|
137
|
+
throw new Error("interactions remove-item requires --item-id <id>.");
|
|
138
|
+
}
|
|
139
|
+
return { input, id, itemId };
|
|
140
|
+
}
|
|
141
|
+
function parseUpdateItemArgs(args) {
|
|
142
|
+
const [input, id, ...rest] = args;
|
|
143
|
+
if (!input) {
|
|
144
|
+
throw new Error("interactions update-item requires a .mdx file path.");
|
|
145
|
+
}
|
|
146
|
+
if (!id) {
|
|
147
|
+
throw new Error("interactions update-item requires a component id.");
|
|
148
|
+
}
|
|
149
|
+
const options = parseOptionMap(rest, ["item-id", "title", "summary", "badge", "tags", "disabled"], ["clear-summary", "clear-badge", "clear-tags"]);
|
|
150
|
+
const itemId = options.values.get("item-id");
|
|
151
|
+
if (!itemId) {
|
|
152
|
+
throw new Error("interactions update-item requires --item-id <id>.");
|
|
153
|
+
}
|
|
154
|
+
const patch = createPatchFromOptions(options.values, options.flags);
|
|
155
|
+
if (Object.keys(patch).length === 0) {
|
|
156
|
+
throw new Error("interactions update-item requires at least one field update.");
|
|
157
|
+
}
|
|
158
|
+
return { input, id, itemId, patch };
|
|
159
|
+
}
|
|
160
|
+
function parseOrderedIds(args) {
|
|
161
|
+
const flagIndex = args.indexOf("--ordered-ids");
|
|
162
|
+
if (flagIndex < 0) {
|
|
163
|
+
throw new Error("interactions set-order requires --ordered-ids <id> [...id].");
|
|
164
|
+
}
|
|
165
|
+
if (flagIndex > 0) {
|
|
166
|
+
throw new Error(`Unexpected interactions set-order argument: ${args[0]}`);
|
|
167
|
+
}
|
|
168
|
+
const orderedIds = args
|
|
169
|
+
.slice(flagIndex + 1)
|
|
170
|
+
.flatMap((value) => value.split(","))
|
|
171
|
+
.map((value) => value.trim())
|
|
172
|
+
.filter(Boolean);
|
|
173
|
+
if (orderedIds.length === 0) {
|
|
174
|
+
throw new Error("interactions set-order requires at least one ordered id.");
|
|
175
|
+
}
|
|
176
|
+
return orderedIds;
|
|
177
|
+
}
|
|
178
|
+
function parseOptionMap(args, valueKeys, flagKeys) {
|
|
179
|
+
const values = new Map();
|
|
180
|
+
const flags = new Set();
|
|
181
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
182
|
+
const name = args[index];
|
|
183
|
+
if (!name?.startsWith("--")) {
|
|
184
|
+
throw new Error(`Unexpected interactions argument: ${name}`);
|
|
185
|
+
}
|
|
186
|
+
const key = name.slice(2);
|
|
187
|
+
if (flagKeys.includes(key)) {
|
|
188
|
+
flags.add(key);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (!valueKeys.includes(key)) {
|
|
192
|
+
throw new Error(`Unknown interactions option: --${key}.`);
|
|
193
|
+
}
|
|
194
|
+
const value = args[index + 1];
|
|
195
|
+
if (value === undefined || value.startsWith("--")) {
|
|
196
|
+
throw new Error(`Missing value for --${key}.`);
|
|
197
|
+
}
|
|
198
|
+
values.set(key, value);
|
|
199
|
+
index += 1;
|
|
200
|
+
}
|
|
201
|
+
return { flags, values };
|
|
202
|
+
}
|
|
203
|
+
function createItemFromOptions(itemId, title, options) {
|
|
204
|
+
return {
|
|
205
|
+
id: itemId,
|
|
206
|
+
title,
|
|
207
|
+
...createPatchFromOptions(options, new Set())
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function createPatchFromOptions(options, flags) {
|
|
211
|
+
return {
|
|
212
|
+
...(options.has("title") ? { title: options.get("title") ?? "" } : {}),
|
|
213
|
+
...(flags.has("clear-summary")
|
|
214
|
+
? { summary: undefined }
|
|
215
|
+
: options.has("summary")
|
|
216
|
+
? { summary: options.get("summary") ?? "" }
|
|
217
|
+
: {}),
|
|
218
|
+
...(flags.has("clear-badge")
|
|
219
|
+
? { badge: undefined }
|
|
220
|
+
: options.has("badge")
|
|
221
|
+
? { badge: options.get("badge") ?? "" }
|
|
222
|
+
: {}),
|
|
223
|
+
...(flags.has("clear-tags") ? { tags: undefined } : options.has("tags") ? { tags: parseTags(options.get("tags") ?? "") } : {}),
|
|
224
|
+
...(options.has("disabled") ? { disabled: parseBooleanOption(options.get("disabled") ?? "", "disabled") } : {})
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function parseTags(value) {
|
|
228
|
+
return value
|
|
229
|
+
.split(",")
|
|
230
|
+
.map((tag) => tag.trim())
|
|
231
|
+
.filter(Boolean);
|
|
232
|
+
}
|
|
233
|
+
function parseBooleanOption(value, name) {
|
|
234
|
+
if (value === "true")
|
|
235
|
+
return true;
|
|
236
|
+
if (value === "false")
|
|
237
|
+
return false;
|
|
238
|
+
throw new Error(`--${name} must be true or false.`);
|
|
239
|
+
}
|
|
240
|
+
function formatInspectResult(result) {
|
|
241
|
+
return [
|
|
242
|
+
"interactions inspect ok",
|
|
243
|
+
`source: ${result.source.path}`,
|
|
244
|
+
`state: ${result.state.path}${result.state.exists ? "" : " (not found)"}`,
|
|
245
|
+
`component: ${result.component.id}`,
|
|
246
|
+
`order source: ${result.order.source}`,
|
|
247
|
+
`items: ${result.component.items.length}`,
|
|
248
|
+
"order:",
|
|
249
|
+
...result.order.orderedIds.map((itemId, index) => {
|
|
250
|
+
const item = result.component.items.find((candidate) => candidate.id === itemId);
|
|
251
|
+
return `${index + 1}. ${itemId}${item ? ` - ${item.title}` : ""}`;
|
|
252
|
+
}),
|
|
253
|
+
...(result.order.appendedIds.length > 0 ? [`appended: ${result.order.appendedIds.join(", ")}`] : []),
|
|
254
|
+
...(result.warnings.length > 0 ? ["warnings:", ...result.warnings.map((warning) => `- ${warning}`)] : [])
|
|
255
|
+
].join("\n");
|
|
256
|
+
}
|
|
257
|
+
function formatMutationResult(result) {
|
|
258
|
+
if (result.action === "promote" && result.status === "noop") {
|
|
259
|
+
return [
|
|
260
|
+
"interactions promote noop",
|
|
261
|
+
`source: ${result.sourcePath}`,
|
|
262
|
+
`component: ${result.component}`,
|
|
263
|
+
`reason: ${result.reason ?? "no runtime overlay"}`
|
|
264
|
+
].join("\n");
|
|
265
|
+
}
|
|
266
|
+
const lines = [
|
|
267
|
+
`interactions ${result.action} ok`,
|
|
268
|
+
...(result.action === "reset" || result.action === "set-order" ? [] : [`source: ${result.sourcePath}`]),
|
|
269
|
+
`state: ${result.statePath}`,
|
|
270
|
+
`component: ${result.component}`
|
|
271
|
+
];
|
|
272
|
+
if (result.itemId) {
|
|
273
|
+
lines.push(`item: ${result.itemId}`);
|
|
274
|
+
}
|
|
275
|
+
if (result.orderedIds) {
|
|
276
|
+
lines.push(`items: ${result.orderedIds.length}`);
|
|
277
|
+
lines.push(`orderedIds: ${result.orderedIds.join(", ")}`);
|
|
278
|
+
}
|
|
279
|
+
return lines.join("\n");
|
|
280
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function reviewCommand(projectRoot: string, args: string[]): Promise<void>;
|