@mdocui/cli 0.5.1 → 0.6.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.
- package/README.md +10 -4
- package/dist/index.js +886 -50
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -18,15 +18,18 @@ pnpm add -g @mdocui/cli
|
|
|
18
18
|
|
|
19
19
|
### `init`
|
|
20
20
|
|
|
21
|
-
Scaffold a
|
|
21
|
+
Scaffold a complete mdocUI integration. Detects your framework (Next.js, Vite, Remix) and generates all required files.
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
npx @mdocui/cli init
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Creates:
|
|
28
|
-
- `mdocui.config.ts` — component definitions
|
|
29
|
-
- `
|
|
28
|
+
- `mdocui.config.ts` — config with all 24 component definitions
|
|
29
|
+
- `src/lib/mdoc-registry.ts` — component registry setup
|
|
30
|
+
- `src/components/mdoc-message.tsx` — streaming message component using `useRenderer`
|
|
31
|
+
- `.env.example` — API key placeholders
|
|
32
|
+
- `src/app/api/chat/route.ts` — streaming API route (Next.js only)
|
|
30
33
|
|
|
31
34
|
### `generate`
|
|
32
35
|
|
|
@@ -40,12 +43,15 @@ Reads `mdocui.config.ts` and writes the prompt to the configured `output` path (
|
|
|
40
43
|
|
|
41
44
|
### `preview`
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
Start a local dev server with a split-pane editor for live markup rendering and parse validation.
|
|
44
47
|
|
|
45
48
|
```bash
|
|
46
49
|
npx @mdocui/cli preview
|
|
50
|
+
npx @mdocui/cli preview --port 3000
|
|
47
51
|
```
|
|
48
52
|
|
|
53
|
+
Opens a browser UI where you type mdocUI markup on the left and see it rendered on the right in real-time. Great for prompt iteration without burning API calls.
|
|
54
|
+
|
|
49
55
|
## Config File
|
|
50
56
|
|
|
51
57
|
```typescript
|
package/dist/index.js
CHANGED
|
@@ -49,79 +49,911 @@ async function generate(cwd2) {
|
|
|
49
49
|
// src/commands/init.ts
|
|
50
50
|
import fs3 from "fs";
|
|
51
51
|
import path3 from "path";
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
function detectFramework(cwd2) {
|
|
53
|
+
const pkgPath = path3.resolve(cwd2, "package.json");
|
|
54
|
+
const hasSrc = fs3.existsSync(path3.resolve(cwd2, "src"));
|
|
55
|
+
const hasApp = fs3.existsSync(path3.resolve(cwd2, "src/app")) || fs3.existsSync(path3.resolve(cwd2, "app"));
|
|
56
|
+
const hasPages = fs3.existsSync(path3.resolve(cwd2, "src/pages")) || fs3.existsSync(path3.resolve(cwd2, "pages"));
|
|
57
|
+
if (!fs3.existsSync(pkgPath)) {
|
|
58
|
+
return { framework: "unknown", hasSrc, hasApp, hasPages };
|
|
59
|
+
}
|
|
60
|
+
const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
61
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
62
|
+
if (allDeps.next) return { framework: "nextjs", hasSrc, hasApp, hasPages };
|
|
63
|
+
if (allDeps["@remix-run/react"] || allDeps.remix)
|
|
64
|
+
return { framework: "remix", hasSrc, hasApp, hasPages };
|
|
65
|
+
if (allDeps.vite) return { framework: "vite", hasSrc, hasApp, hasPages };
|
|
66
|
+
return { framework: "unknown", hasSrc, hasApp, hasPages };
|
|
67
|
+
}
|
|
68
|
+
function registryTemplate() {
|
|
69
|
+
return `import { createDefaultRegistry } from '@mdocui/react'
|
|
70
|
+
|
|
71
|
+
// Pre-built registry with all 24 built-in mdocUI components:
|
|
72
|
+
// Layout: stack, grid, card, divider, accordion, tabs, tab
|
|
73
|
+
// Interactive: button, button-group, input, textarea, select, checkbox, toggle, form
|
|
74
|
+
// Data: chart, table, stat, progress
|
|
75
|
+
// Content: callout, badge, image, code-block, link
|
|
76
|
+
//
|
|
77
|
+
// To add custom components, use the ComponentRegistry API directly:
|
|
78
|
+
// import { ComponentRegistry } from '@mdocui/core'
|
|
79
|
+
// import { allDefinitions } from '@mdocui/react'
|
|
80
|
+
|
|
81
|
+
export const registry = createDefaultRegistry()
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
84
|
+
function mdocMessageTemplate() {
|
|
85
|
+
return `'use client'
|
|
86
|
+
|
|
87
|
+
import { useCallback, useRef } from 'react'
|
|
88
|
+
import { Renderer, useRenderer, defaultComponents } from '@mdocui/react'
|
|
89
|
+
import type { ActionHandler } from '@mdocui/react'
|
|
90
|
+
import { registry } from '@/lib/mdoc-registry'
|
|
91
|
+
|
|
92
|
+
interface MdocMessageProps {
|
|
93
|
+
/** Called when the user clicks a button or submits a form */
|
|
94
|
+
onAction?: ActionHandler
|
|
95
|
+
/** Called when a "continue" action fires \u2014 typically sends the label as a new user message */
|
|
96
|
+
onSend?: (message: string) => void
|
|
97
|
+
/** Custom prose renderer, e.g. for markdown-to-HTML */
|
|
98
|
+
renderProse?: (content: string, key: string) => React.ReactNode
|
|
99
|
+
/** CSS class overrides per component name */
|
|
100
|
+
classNames?: Record<string, string>
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function MdocMessage({ onAction, onSend, renderProse, classNames }: MdocMessageProps) {
|
|
104
|
+
const { nodes, isStreaming, push, done, reset } = useRenderer({ registry })
|
|
105
|
+
const abortRef = useRef<AbortController | null>(null)
|
|
106
|
+
|
|
107
|
+
const handleAction: ActionHandler = useCallback(
|
|
108
|
+
(action, params) => {
|
|
109
|
+
if (action === 'continue' && onSend) {
|
|
110
|
+
onSend(params?.label ?? action)
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
onAction?.(action, params)
|
|
114
|
+
},
|
|
115
|
+
[onAction, onSend],
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
/** Stream a response from your API endpoint */
|
|
119
|
+
const stream = useCallback(
|
|
120
|
+
async (url: string, body: Record<string, unknown>) => {
|
|
121
|
+
abortRef.current?.abort()
|
|
122
|
+
reset()
|
|
123
|
+
|
|
124
|
+
const controller = new AbortController()
|
|
125
|
+
abortRef.current = controller
|
|
126
|
+
|
|
127
|
+
const res = await fetch(url, {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers: { 'Content-Type': 'application/json' },
|
|
130
|
+
body: JSON.stringify(body),
|
|
131
|
+
signal: controller.signal,
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
if (!res.ok || !res.body) {
|
|
135
|
+
throw new Error(\`Stream request failed: \${res.status}\`)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const reader = res.body.getReader()
|
|
139
|
+
const decoder = new TextDecoder()
|
|
140
|
+
|
|
141
|
+
while (true) {
|
|
142
|
+
const { done: readerDone, value } = await reader.read()
|
|
143
|
+
if (readerDone) break
|
|
144
|
+
push(decoder.decode(value, { stream: true }))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
done()
|
|
148
|
+
},
|
|
149
|
+
[push, done, reset],
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<Renderer
|
|
154
|
+
nodes={nodes}
|
|
155
|
+
components={defaultComponents}
|
|
156
|
+
onAction={handleAction}
|
|
157
|
+
isStreaming={isStreaming}
|
|
158
|
+
renderProse={renderProse}
|
|
159
|
+
classNames={classNames}
|
|
160
|
+
/>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Export the stream helper so parent components can drive it
|
|
165
|
+
export type { MdocMessageProps }
|
|
166
|
+
export { MdocMessage as default }
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
function envExampleTemplate() {
|
|
170
|
+
return `# mdocUI \u2014 LLM API keys
|
|
171
|
+
# Copy this file to .env.local and fill in your keys.
|
|
64
172
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
173
|
+
# Anthropic (Claude)
|
|
174
|
+
ANTHROPIC_API_KEY=
|
|
175
|
+
|
|
176
|
+
# OpenAI
|
|
177
|
+
OPENAI_API_KEY=
|
|
178
|
+
`;
|
|
179
|
+
}
|
|
180
|
+
function nextjsRouteTemplate() {
|
|
181
|
+
return `import { type NextRequest, NextResponse } from 'next/server'
|
|
182
|
+
import { generatePrompt } from '@mdocui/core'
|
|
183
|
+
import { allDefinitions, defaultGroups } from '@mdocui/react'
|
|
184
|
+
|
|
185
|
+
// Build the system prompt once at module level
|
|
186
|
+
const systemPrompt = generatePrompt({
|
|
187
|
+
components: allDefinitions,
|
|
188
|
+
groups: defaultGroups,
|
|
189
|
+
preamble: 'You are a helpful assistant. Use mdocUI components to create rich, interactive responses.',
|
|
73
190
|
})
|
|
74
191
|
|
|
192
|
+
export async function POST(req: NextRequest) {
|
|
193
|
+
const { messages, provider = 'anthropic' } = await req.json()
|
|
194
|
+
|
|
195
|
+
if (provider === 'openai') {
|
|
196
|
+
return streamOpenAI(messages)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return streamAnthropic(messages)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// \u2500\u2500 Anthropic (Claude) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
203
|
+
|
|
204
|
+
async function streamAnthropic(messages: Array<{ role: string; content: string }>) {
|
|
205
|
+
const apiKey = process.env.ANTHROPIC_API_KEY
|
|
206
|
+
if (!apiKey) {
|
|
207
|
+
return NextResponse.json({ error: 'ANTHROPIC_API_KEY not set' }, { status: 500 })
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
211
|
+
method: 'POST',
|
|
212
|
+
headers: {
|
|
213
|
+
'Content-Type': 'application/json',
|
|
214
|
+
'x-api-key': apiKey,
|
|
215
|
+
'anthropic-version': '2023-06-01',
|
|
216
|
+
},
|
|
217
|
+
body: JSON.stringify({
|
|
218
|
+
model: 'claude-sonnet-4-20250514',
|
|
219
|
+
max_tokens: 4096,
|
|
220
|
+
system: systemPrompt,
|
|
221
|
+
messages,
|
|
222
|
+
stream: true,
|
|
223
|
+
}),
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
if (!res.ok || !res.body) {
|
|
227
|
+
const text = await res.text()
|
|
228
|
+
return NextResponse.json({ error: text }, { status: res.status })
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const encoder = new TextEncoder()
|
|
232
|
+
const stream = new ReadableStream({
|
|
233
|
+
async start(controller) {
|
|
234
|
+
const reader = res.body!.getReader()
|
|
235
|
+
const decoder = new TextDecoder()
|
|
236
|
+
let buffer = ''
|
|
237
|
+
|
|
238
|
+
while (true) {
|
|
239
|
+
const { done, value } = await reader.read()
|
|
240
|
+
if (done) break
|
|
241
|
+
buffer += decoder.decode(value, { stream: true })
|
|
242
|
+
|
|
243
|
+
const lines = buffer.split('\\n')
|
|
244
|
+
buffer = lines.pop() ?? ''
|
|
245
|
+
|
|
246
|
+
for (const line of lines) {
|
|
247
|
+
if (!line.startsWith('data: ')) continue
|
|
248
|
+
const data = line.slice(6)
|
|
249
|
+
if (data === '[DONE]') continue
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
const event = JSON.parse(data)
|
|
253
|
+
if (event.type === 'content_block_delta' && event.delta?.text) {
|
|
254
|
+
controller.enqueue(encoder.encode(event.delta.text))
|
|
255
|
+
}
|
|
256
|
+
} catch {
|
|
257
|
+
// skip malformed events
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
controller.close()
|
|
263
|
+
},
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
return new Response(stream, {
|
|
267
|
+
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
|
|
268
|
+
})
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// \u2500\u2500 OpenAI \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
272
|
+
|
|
273
|
+
async function streamOpenAI(messages: Array<{ role: string; content: string }>) {
|
|
274
|
+
const apiKey = process.env.OPENAI_API_KEY
|
|
275
|
+
if (!apiKey) {
|
|
276
|
+
return NextResponse.json({ error: 'OPENAI_API_KEY not set' }, { status: 500 })
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const res = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
280
|
+
method: 'POST',
|
|
281
|
+
headers: {
|
|
282
|
+
'Content-Type': 'application/json',
|
|
283
|
+
Authorization: \`Bearer \${apiKey}\`,
|
|
284
|
+
},
|
|
285
|
+
body: JSON.stringify({
|
|
286
|
+
model: 'gpt-4o',
|
|
287
|
+
messages: [{ role: 'system', content: systemPrompt }, ...messages],
|
|
288
|
+
stream: true,
|
|
289
|
+
}),
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
if (!res.ok || !res.body) {
|
|
293
|
+
const text = await res.text()
|
|
294
|
+
return NextResponse.json({ error: text }, { status: res.status })
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const encoder = new TextEncoder()
|
|
298
|
+
const stream = new ReadableStream({
|
|
299
|
+
async start(controller) {
|
|
300
|
+
const reader = res.body!.getReader()
|
|
301
|
+
const decoder = new TextDecoder()
|
|
302
|
+
let buffer = ''
|
|
303
|
+
|
|
304
|
+
while (true) {
|
|
305
|
+
const { done, value } = await reader.read()
|
|
306
|
+
if (done) break
|
|
307
|
+
buffer += decoder.decode(value, { stream: true })
|
|
308
|
+
|
|
309
|
+
const lines = buffer.split('\\n')
|
|
310
|
+
buffer = lines.pop() ?? ''
|
|
311
|
+
|
|
312
|
+
for (const line of lines) {
|
|
313
|
+
if (!line.startsWith('data: ')) continue
|
|
314
|
+
const data = line.slice(6)
|
|
315
|
+
if (data === '[DONE]') continue
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
const event = JSON.parse(data)
|
|
319
|
+
const content = event.choices?.[0]?.delta?.content
|
|
320
|
+
if (content) {
|
|
321
|
+
controller.enqueue(encoder.encode(content))
|
|
322
|
+
}
|
|
323
|
+
} catch {
|
|
324
|
+
// skip malformed events
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
controller.close()
|
|
330
|
+
},
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
return new Response(stream, {
|
|
334
|
+
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
|
|
335
|
+
})
|
|
336
|
+
}
|
|
337
|
+
`;
|
|
338
|
+
}
|
|
339
|
+
function configTemplate() {
|
|
340
|
+
return `import { allDefinitions, defaultGroups } from '@mdocui/react'
|
|
341
|
+
|
|
75
342
|
export default {
|
|
76
|
-
components:
|
|
343
|
+
components: allDefinitions,
|
|
344
|
+
groups: defaultGroups,
|
|
77
345
|
output: './generated/system-prompt.txt',
|
|
78
|
-
preamble: 'You are a helpful assistant.',
|
|
79
|
-
additionalRules: [
|
|
80
|
-
'End responses with follow-up buttons using action="continue"',
|
|
81
|
-
],
|
|
346
|
+
preamble: 'You are a helpful assistant. Use mdocUI components to create rich, interactive responses.',
|
|
82
347
|
}
|
|
83
348
|
`;
|
|
349
|
+
}
|
|
350
|
+
function writeIfMissing(cwd2, file) {
|
|
351
|
+
const abs = path3.resolve(cwd2, file.relativePath);
|
|
352
|
+
if (fs3.existsSync(abs)) {
|
|
353
|
+
console.log(` skip ${file.relativePath} (already exists)`);
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
fs3.mkdirSync(path3.dirname(abs), { recursive: true });
|
|
357
|
+
fs3.writeFileSync(abs, file.content, "utf-8");
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
84
360
|
async function init(cwd2) {
|
|
85
|
-
const
|
|
361
|
+
const project = detectFramework(cwd2);
|
|
362
|
+
console.log("");
|
|
363
|
+
console.log(` Detected: ${frameworkLabel(project.framework)}`);
|
|
364
|
+
console.log("");
|
|
365
|
+
const files = [];
|
|
366
|
+
files.push({ relativePath: "src/lib/mdoc-registry.ts", content: registryTemplate() });
|
|
367
|
+
files.push({ relativePath: "src/components/mdoc-message.tsx", content: mdocMessageTemplate() });
|
|
368
|
+
files.push({ relativePath: ".env.example", content: envExampleTemplate() });
|
|
369
|
+
files.push({ relativePath: "mdocui.config.ts", content: configTemplate() });
|
|
370
|
+
if (project.framework === "nextjs") {
|
|
371
|
+
const routeDir = project.hasSrc ? "src/app/api/chat" : "app/api/chat";
|
|
372
|
+
files.push({ relativePath: `${routeDir}/route.ts`, content: nextjsRouteTemplate() });
|
|
373
|
+
}
|
|
374
|
+
const created = [];
|
|
375
|
+
const skipped = [];
|
|
376
|
+
for (const file of files) {
|
|
377
|
+
if (writeIfMissing(cwd2, file)) {
|
|
378
|
+
created.push(file.relativePath);
|
|
379
|
+
} else {
|
|
380
|
+
skipped.push(file.relativePath);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
86
383
|
const generatedDir = path3.resolve(cwd2, "generated");
|
|
87
|
-
if (fs3.existsSync(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
fs3.mkdirSync(generatedDir, { recursive: true });
|
|
93
|
-
fs3.writeFileSync(path3.resolve(generatedDir, ".gitkeep"), "", "utf-8");
|
|
94
|
-
console.log("Created:");
|
|
95
|
-
console.log(" mdocui.config.ts \u2014 component definitions + config");
|
|
96
|
-
console.log(" generated/ \u2014 output directory for system prompts");
|
|
384
|
+
if (!fs3.existsSync(generatedDir)) {
|
|
385
|
+
fs3.mkdirSync(generatedDir, { recursive: true });
|
|
386
|
+
fs3.writeFileSync(path3.resolve(generatedDir, ".gitkeep"), "", "utf-8");
|
|
387
|
+
created.push("generated/");
|
|
388
|
+
}
|
|
97
389
|
console.log("");
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
390
|
+
if (created.length > 0) {
|
|
391
|
+
console.log(" Created:");
|
|
392
|
+
for (const f of created) {
|
|
393
|
+
console.log(` + ${f}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (skipped.length > 0) {
|
|
397
|
+
console.log(" Skipped (already exist):");
|
|
398
|
+
for (const f of skipped) {
|
|
399
|
+
console.log(` - ${f}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
console.log("");
|
|
403
|
+
console.log(" Next steps:");
|
|
404
|
+
console.log(" 1. Copy .env.example to .env.local and add your API keys");
|
|
405
|
+
console.log(" 2. Install dependencies:");
|
|
406
|
+
console.log(" npm install @mdocui/core @mdocui/react");
|
|
407
|
+
console.log(" 3. Generate the system prompt:");
|
|
408
|
+
console.log(" npx @mdocui/cli generate");
|
|
409
|
+
if (project.framework === "nextjs") {
|
|
410
|
+
console.log(" 4. Import <MdocMessage /> in your chat page");
|
|
411
|
+
console.log(' and call stream("/api/chat", { messages })');
|
|
412
|
+
} else {
|
|
413
|
+
console.log(" 4. Import <MdocMessage /> in your chat component");
|
|
414
|
+
console.log(" and wire up streaming from your backend");
|
|
415
|
+
}
|
|
416
|
+
console.log("");
|
|
417
|
+
}
|
|
418
|
+
function frameworkLabel(fw) {
|
|
419
|
+
switch (fw) {
|
|
420
|
+
case "nextjs":
|
|
421
|
+
return "Next.js";
|
|
422
|
+
case "vite":
|
|
423
|
+
return "Vite";
|
|
424
|
+
case "remix":
|
|
425
|
+
return "Remix";
|
|
426
|
+
case "unknown":
|
|
427
|
+
return "Unknown framework (generating generic files)";
|
|
428
|
+
}
|
|
101
429
|
}
|
|
102
430
|
|
|
103
431
|
// src/commands/preview.ts
|
|
104
|
-
import
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
432
|
+
import http from "http";
|
|
433
|
+
import { StreamingParser } from "@mdocui/core";
|
|
434
|
+
async function preview(cwd2, options) {
|
|
435
|
+
const port = options?.port ?? 4321;
|
|
436
|
+
let knownTags;
|
|
437
|
+
try {
|
|
438
|
+
const config = await loadConfig(cwd2);
|
|
439
|
+
knownTags = new Set(config.components.map((c) => c.name));
|
|
440
|
+
console.log(`Loaded config with ${knownTags.size} components: ${[...knownTags].join(", ")}`);
|
|
441
|
+
} catch {
|
|
442
|
+
console.log("No config found \u2014 all tags treated as known");
|
|
443
|
+
}
|
|
444
|
+
const server = http.createServer((req, res) => {
|
|
445
|
+
if (req.method === "POST" && req.url === "/api/parse") {
|
|
446
|
+
let body = "";
|
|
447
|
+
req.on("data", (chunk) => {
|
|
448
|
+
body += chunk.toString();
|
|
449
|
+
});
|
|
450
|
+
req.on("end", () => {
|
|
451
|
+
try {
|
|
452
|
+
const { content } = JSON.parse(body);
|
|
453
|
+
const parser = new StreamingParser(
|
|
454
|
+
knownTags ? { knownTags, dropUnknown: false } : void 0
|
|
455
|
+
);
|
|
456
|
+
parser.write(content);
|
|
457
|
+
parser.flush();
|
|
458
|
+
const nodes = parser.getNodes();
|
|
459
|
+
const meta = parser.getMeta();
|
|
460
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
461
|
+
res.end(JSON.stringify({ nodes, meta }));
|
|
462
|
+
} catch (err) {
|
|
463
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
464
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (req.method === "GET" && (req.url === "/" || req.url === "/index.html")) {
|
|
470
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
471
|
+
res.end(buildHTML(knownTags));
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
475
|
+
res.end("Not found");
|
|
476
|
+
});
|
|
477
|
+
server.listen(port, () => {
|
|
478
|
+
console.log(`
|
|
479
|
+
mdocui preview server running at:
|
|
480
|
+
`);
|
|
481
|
+
console.log(` http://localhost:${port}
|
|
482
|
+
`);
|
|
483
|
+
console.log("Press Ctrl+C to stop.\n");
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
function buildHTML(knownTags) {
|
|
487
|
+
const tagList = knownTags ? JSON.stringify([...knownTags]) : "[]";
|
|
488
|
+
return (
|
|
489
|
+
/* html */
|
|
490
|
+
`<!DOCTYPE html>
|
|
491
|
+
<html lang="en">
|
|
492
|
+
<head>
|
|
493
|
+
<meta charset="utf-8" />
|
|
494
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
495
|
+
<title>mdocui preview</title>
|
|
496
|
+
<style>
|
|
497
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
498
|
+
|
|
499
|
+
:root {
|
|
500
|
+
--bg: #0a0a0a;
|
|
501
|
+
--bg-surface: #141414;
|
|
502
|
+
--bg-surface-2: #1e1e1e;
|
|
503
|
+
--border: #2a2a2a;
|
|
504
|
+
--text: #e4e4e4;
|
|
505
|
+
--text-dim: #888;
|
|
506
|
+
--accent: #7c6bff;
|
|
507
|
+
--accent-dim: #5a4fbb;
|
|
508
|
+
--error: #f06;
|
|
509
|
+
--tag-bg: #1a1a2e;
|
|
510
|
+
--tag-border: #2a2a4e;
|
|
511
|
+
--prose-bg: #0f1a0f;
|
|
512
|
+
--prose-border: #1a3a1a;
|
|
513
|
+
--font-mono: 'SF Mono', 'Fira Code', 'JetBrains Mono', Menlo, monospace;
|
|
514
|
+
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
html, body { height: 100%; background: var(--bg); color: var(--text); font-family: var(--font-sans); }
|
|
518
|
+
|
|
519
|
+
.app {
|
|
520
|
+
display: flex;
|
|
521
|
+
flex-direction: column;
|
|
522
|
+
height: 100vh;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
header {
|
|
526
|
+
display: flex;
|
|
527
|
+
align-items: center;
|
|
528
|
+
justify-content: space-between;
|
|
529
|
+
padding: 10px 20px;
|
|
530
|
+
border-bottom: 1px solid var(--border);
|
|
531
|
+
background: var(--bg-surface);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
header h1 {
|
|
535
|
+
font-size: 14px;
|
|
536
|
+
font-weight: 600;
|
|
537
|
+
letter-spacing: 0.5px;
|
|
538
|
+
color: var(--accent);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
header .meta {
|
|
542
|
+
font-size: 12px;
|
|
543
|
+
color: var(--text-dim);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.panels {
|
|
547
|
+
display: flex;
|
|
548
|
+
flex: 1;
|
|
549
|
+
min-height: 0;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.panel {
|
|
553
|
+
flex: 1;
|
|
554
|
+
display: flex;
|
|
555
|
+
flex-direction: column;
|
|
556
|
+
min-width: 0;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.panel + .panel {
|
|
560
|
+
border-left: 1px solid var(--border);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.panel-header {
|
|
564
|
+
padding: 8px 16px;
|
|
565
|
+
font-size: 11px;
|
|
566
|
+
font-weight: 600;
|
|
567
|
+
text-transform: uppercase;
|
|
568
|
+
letter-spacing: 1px;
|
|
569
|
+
color: var(--text-dim);
|
|
570
|
+
background: var(--bg-surface);
|
|
571
|
+
border-bottom: 1px solid var(--border);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
textarea {
|
|
575
|
+
flex: 1;
|
|
576
|
+
width: 100%;
|
|
577
|
+
padding: 16px;
|
|
578
|
+
background: var(--bg);
|
|
579
|
+
color: var(--text);
|
|
580
|
+
border: none;
|
|
581
|
+
resize: none;
|
|
582
|
+
font-family: var(--font-mono);
|
|
583
|
+
font-size: 13px;
|
|
584
|
+
line-height: 1.6;
|
|
585
|
+
outline: none;
|
|
586
|
+
tab-size: 2;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
textarea::placeholder { color: var(--text-dim); }
|
|
590
|
+
|
|
591
|
+
.preview {
|
|
592
|
+
flex: 1;
|
|
593
|
+
overflow-y: auto;
|
|
594
|
+
padding: 16px;
|
|
595
|
+
background: var(--bg);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/* AST node rendering */
|
|
599
|
+
|
|
600
|
+
.node-prose {
|
|
601
|
+
background: var(--prose-bg);
|
|
602
|
+
border: 1px solid var(--prose-border);
|
|
603
|
+
border-radius: 6px;
|
|
604
|
+
padding: 12px 16px;
|
|
605
|
+
margin-bottom: 8px;
|
|
606
|
+
font-size: 14px;
|
|
607
|
+
line-height: 1.7;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.node-prose p { margin-bottom: 0.5em; }
|
|
611
|
+
.node-prose p:last-child { margin-bottom: 0; }
|
|
612
|
+
.node-prose strong { color: #fff; }
|
|
613
|
+
.node-prose em { color: #ccc; font-style: italic; }
|
|
614
|
+
.node-prose code {
|
|
615
|
+
background: var(--bg-surface-2);
|
|
616
|
+
padding: 2px 6px;
|
|
617
|
+
border-radius: 3px;
|
|
618
|
+
font-family: var(--font-mono);
|
|
619
|
+
font-size: 12px;
|
|
620
|
+
}
|
|
621
|
+
.node-prose a { color: var(--accent); text-decoration: underline; }
|
|
622
|
+
.node-prose ul, .node-prose ol { padding-left: 1.5em; margin-bottom: 0.5em; }
|
|
623
|
+
.node-prose h1, .node-prose h2, .node-prose h3 {
|
|
624
|
+
color: #fff;
|
|
625
|
+
margin-bottom: 0.4em;
|
|
626
|
+
}
|
|
627
|
+
.node-prose h1 { font-size: 1.4em; }
|
|
628
|
+
.node-prose h2 { font-size: 1.2em; }
|
|
629
|
+
.node-prose h3 { font-size: 1.05em; }
|
|
630
|
+
|
|
631
|
+
.node-component {
|
|
632
|
+
background: var(--tag-bg);
|
|
633
|
+
border: 1px solid var(--tag-border);
|
|
634
|
+
border-radius: 6px;
|
|
635
|
+
margin-bottom: 8px;
|
|
636
|
+
overflow: hidden;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.node-component-header {
|
|
640
|
+
display: flex;
|
|
641
|
+
align-items: center;
|
|
642
|
+
gap: 8px;
|
|
643
|
+
padding: 8px 12px;
|
|
644
|
+
background: rgba(124, 107, 255, 0.08);
|
|
645
|
+
border-bottom: 1px solid var(--tag-border);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.node-component-name {
|
|
649
|
+
font-family: var(--font-mono);
|
|
650
|
+
font-size: 13px;
|
|
651
|
+
font-weight: 600;
|
|
652
|
+
color: var(--accent);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.node-component-badge {
|
|
656
|
+
font-size: 10px;
|
|
657
|
+
padding: 2px 6px;
|
|
658
|
+
border-radius: 3px;
|
|
659
|
+
background: var(--accent-dim);
|
|
660
|
+
color: #fff;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.node-component-props {
|
|
664
|
+
padding: 8px 12px;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.prop-row {
|
|
668
|
+
display: flex;
|
|
669
|
+
gap: 8px;
|
|
670
|
+
font-size: 12px;
|
|
671
|
+
font-family: var(--font-mono);
|
|
672
|
+
padding: 2px 0;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.prop-key { color: #82aaff; }
|
|
676
|
+
.prop-eq { color: var(--text-dim); }
|
|
677
|
+
.prop-val { color: #c3e88d; }
|
|
678
|
+
|
|
679
|
+
.node-component-children {
|
|
680
|
+
padding: 8px 12px;
|
|
681
|
+
border-top: 1px solid var(--tag-border);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.node-component-children-label {
|
|
685
|
+
font-size: 10px;
|
|
686
|
+
text-transform: uppercase;
|
|
687
|
+
letter-spacing: 0.5px;
|
|
688
|
+
color: var(--text-dim);
|
|
689
|
+
margin-bottom: 6px;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.errors {
|
|
693
|
+
padding: 8px 16px;
|
|
694
|
+
background: rgba(255, 0, 102, 0.08);
|
|
695
|
+
border-top: 1px solid rgba(255, 0, 102, 0.2);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.error-item {
|
|
699
|
+
font-size: 12px;
|
|
700
|
+
color: var(--error);
|
|
701
|
+
font-family: var(--font-mono);
|
|
702
|
+
padding: 2px 0;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.error-item::before { content: '\\26A0 '; }
|
|
706
|
+
|
|
707
|
+
.status-bar {
|
|
708
|
+
display: flex;
|
|
709
|
+
align-items: center;
|
|
710
|
+
justify-content: space-between;
|
|
711
|
+
padding: 4px 16px;
|
|
712
|
+
font-size: 11px;
|
|
713
|
+
color: var(--text-dim);
|
|
714
|
+
border-top: 1px solid var(--border);
|
|
715
|
+
background: var(--bg-surface);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
.empty-state {
|
|
719
|
+
display: flex;
|
|
720
|
+
align-items: center;
|
|
721
|
+
justify-content: center;
|
|
722
|
+
height: 100%;
|
|
723
|
+
color: var(--text-dim);
|
|
724
|
+
font-size: 13px;
|
|
725
|
+
text-align: center;
|
|
726
|
+
line-height: 1.8;
|
|
727
|
+
}
|
|
728
|
+
</style>
|
|
729
|
+
</head>
|
|
730
|
+
<body>
|
|
731
|
+
<div class="app">
|
|
732
|
+
<header>
|
|
733
|
+
<h1>mdocui preview</h1>
|
|
734
|
+
<span class="meta" id="tag-count"></span>
|
|
735
|
+
</header>
|
|
736
|
+
<div class="panels">
|
|
737
|
+
<div class="panel">
|
|
738
|
+
<div class="panel-header">Input</div>
|
|
739
|
+
<textarea id="editor" spellcheck="false" placeholder="Type mdocUI markup here...
|
|
740
|
+
|
|
741
|
+
Example:
|
|
742
|
+
Here is a **weather card**:
|
|
743
|
+
|
|
744
|
+
{% WeatherCard city="San Francisco" temp=72 unit="F" /%}
|
|
745
|
+
|
|
746
|
+
Or with body content:
|
|
747
|
+
|
|
748
|
+
{% Alert type="info" %}
|
|
749
|
+
This is an informational alert with **markdown** inside.
|
|
750
|
+
{% /Alert %}"></textarea>
|
|
751
|
+
</div>
|
|
752
|
+
<div class="panel">
|
|
753
|
+
<div class="panel-header">Preview</div>
|
|
754
|
+
<div class="preview" id="preview">
|
|
755
|
+
<div class="empty-state">Start typing mdocUI markup<br/>to see the live preview</div>
|
|
756
|
+
</div>
|
|
757
|
+
<div class="errors" id="errors" style="display:none"></div>
|
|
758
|
+
</div>
|
|
759
|
+
</div>
|
|
760
|
+
<div class="status-bar">
|
|
761
|
+
<span id="status">Ready</span>
|
|
762
|
+
<span id="node-count"></span>
|
|
763
|
+
</div>
|
|
764
|
+
</div>
|
|
765
|
+
|
|
766
|
+
<script>
|
|
767
|
+
(function() {
|
|
768
|
+
const knownTags = ${tagList};
|
|
769
|
+
const editor = document.getElementById('editor');
|
|
770
|
+
const preview = document.getElementById('preview');
|
|
771
|
+
const errors = document.getElementById('errors');
|
|
772
|
+
const status = document.getElementById('status');
|
|
773
|
+
const nodeCount = document.getElementById('node-count');
|
|
774
|
+
const tagCountEl = document.getElementById('tag-count');
|
|
775
|
+
|
|
776
|
+
if (knownTags.length > 0) {
|
|
777
|
+
tagCountEl.textContent = knownTags.length + ' registered component' + (knownTags.length === 1 ? '' : 's');
|
|
778
|
+
} else {
|
|
779
|
+
tagCountEl.textContent = 'no config \u2014 all tags accepted';
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
let timer = null;
|
|
783
|
+
|
|
784
|
+
editor.addEventListener('input', function() {
|
|
785
|
+
clearTimeout(timer);
|
|
786
|
+
timer = setTimeout(parse, 150);
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
// also handle paste
|
|
790
|
+
editor.addEventListener('paste', function() {
|
|
791
|
+
clearTimeout(timer);
|
|
792
|
+
timer = setTimeout(parse, 50);
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
async function parse() {
|
|
796
|
+
const content = editor.value;
|
|
797
|
+
if (!content.trim()) {
|
|
798
|
+
preview.innerHTML = '<div class="empty-state">Start typing mdocUI markup<br/>to see the live preview</div>';
|
|
799
|
+
errors.style.display = 'none';
|
|
800
|
+
status.textContent = 'Ready';
|
|
801
|
+
nodeCount.textContent = '';
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
status.textContent = 'Parsing...';
|
|
806
|
+
|
|
807
|
+
try {
|
|
808
|
+
const res = await fetch('/api/parse', {
|
|
809
|
+
method: 'POST',
|
|
810
|
+
headers: { 'Content-Type': 'application/json' },
|
|
811
|
+
body: JSON.stringify({ content }),
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
const data = await res.json();
|
|
815
|
+
|
|
816
|
+
if (data.error) {
|
|
817
|
+
status.textContent = 'Error: ' + data.error;
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
preview.innerHTML = renderNodes(data.nodes);
|
|
822
|
+
nodeCount.textContent = data.meta.nodeCount + ' node' + (data.meta.nodeCount === 1 ? '' : 's');
|
|
823
|
+
|
|
824
|
+
if (data.meta.errors && data.meta.errors.length > 0) {
|
|
825
|
+
errors.style.display = 'block';
|
|
826
|
+
errors.innerHTML = data.meta.errors
|
|
827
|
+
.map(function(e) { return '<div class="error-item">' + escapeHtml(e.message) + '</div>'; })
|
|
828
|
+
.join('');
|
|
829
|
+
status.textContent = 'Parsed with ' + data.meta.errors.length + ' warning(s)';
|
|
830
|
+
} else {
|
|
831
|
+
errors.style.display = 'none';
|
|
832
|
+
status.textContent = 'OK';
|
|
833
|
+
}
|
|
834
|
+
} catch (err) {
|
|
835
|
+
status.textContent = 'Fetch error: ' + err.message;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function renderNodes(nodes) {
|
|
840
|
+
if (!nodes || nodes.length === 0) return '<div class="empty-state">No nodes parsed</div>';
|
|
841
|
+
return nodes.map(renderNode).join('');
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function renderNode(node) {
|
|
845
|
+
if (node.type === 'prose') {
|
|
846
|
+
return '<div class="node-prose">' + markdownToHtml(node.content) + '</div>';
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
if (node.type === 'component') {
|
|
850
|
+
var html = '<div class="node-component">';
|
|
851
|
+
html += '<div class="node-component-header">';
|
|
852
|
+
html += '<span class="node-component-name">' + escapeHtml(node.name) + '</span>';
|
|
853
|
+
if (node.selfClosing) {
|
|
854
|
+
html += '<span class="node-component-badge">self-closing</span>';
|
|
855
|
+
}
|
|
856
|
+
html += '</div>';
|
|
857
|
+
|
|
858
|
+
var propKeys = Object.keys(node.props || {});
|
|
859
|
+
if (propKeys.length > 0) {
|
|
860
|
+
html += '<div class="node-component-props">';
|
|
861
|
+
propKeys.forEach(function(key) {
|
|
862
|
+
var val = node.props[key];
|
|
863
|
+
var display = typeof val === 'string' ? '"' + escapeHtml(val) + '"' : String(val);
|
|
864
|
+
html += '<div class="prop-row">';
|
|
865
|
+
html += '<span class="prop-key">' + escapeHtml(key) + '</span>';
|
|
866
|
+
html += '<span class="prop-eq">=</span>';
|
|
867
|
+
html += '<span class="prop-val">' + display + '</span>';
|
|
868
|
+
html += '</div>';
|
|
869
|
+
});
|
|
870
|
+
html += '</div>';
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (node.children && node.children.length > 0) {
|
|
874
|
+
html += '<div class="node-component-children">';
|
|
875
|
+
html += '<div class="node-component-children-label">Children</div>';
|
|
876
|
+
html += renderNodes(node.children);
|
|
877
|
+
html += '</div>';
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
html += '</div>';
|
|
881
|
+
return html;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
return '';
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/** Minimal markdown-to-HTML for prose content */
|
|
888
|
+
function markdownToHtml(text) {
|
|
889
|
+
var html = escapeHtml(text);
|
|
890
|
+
|
|
891
|
+
// headings
|
|
892
|
+
html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
|
|
893
|
+
html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
|
|
894
|
+
html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
|
|
895
|
+
|
|
896
|
+
// bold + italic
|
|
897
|
+
html = html.replace(/\\*\\*\\*(.+?)\\*\\*\\*/g, '<strong><em>$1</em></strong>');
|
|
898
|
+
html = html.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');
|
|
899
|
+
html = html.replace(/\\*(.+?)\\*/g, '<em>$1</em>');
|
|
900
|
+
|
|
901
|
+
// inline code
|
|
902
|
+
html = html.replace(/\`([^\`]+)\`/g, '<code>$1</code>');
|
|
903
|
+
|
|
904
|
+
// links
|
|
905
|
+
html = html.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href="$2" target="_blank">$1</a>');
|
|
906
|
+
|
|
907
|
+
// unordered lists
|
|
908
|
+
html = html.replace(/^- (.+)$/gm, '<li>$1</li>');
|
|
909
|
+
html = html.replace(/(<li>.*<\\/li>\\n?)+/g, function(m) { return '<ul>' + m + '</ul>'; });
|
|
910
|
+
|
|
911
|
+
// paragraphs: split on double newlines
|
|
912
|
+
html = html.split(/\\n\\n+/).map(function(block) {
|
|
913
|
+
block = block.trim();
|
|
914
|
+
if (!block) return '';
|
|
915
|
+
if (/^<(h[1-3]|ul|ol|li)/.test(block)) return block;
|
|
916
|
+
return '<p>' + block.replace(/\\n/g, '<br/>') + '</p>';
|
|
917
|
+
}).join('');
|
|
918
|
+
|
|
919
|
+
return html;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function escapeHtml(str) {
|
|
923
|
+
return String(str)
|
|
924
|
+
.replace(/&/g, '&')
|
|
925
|
+
.replace(/</g, '<')
|
|
926
|
+
.replace(/>/g, '>')
|
|
927
|
+
.replace(/"/g, '"');
|
|
928
|
+
}
|
|
929
|
+
})();
|
|
930
|
+
</script>
|
|
931
|
+
</body>
|
|
932
|
+
</html>`
|
|
933
|
+
);
|
|
111
934
|
}
|
|
112
935
|
|
|
113
936
|
// src/index.ts
|
|
114
937
|
var args = process.argv.slice(2);
|
|
115
938
|
var command = args[0];
|
|
116
939
|
var cwd = process.cwd();
|
|
940
|
+
function parseFlag(flag) {
|
|
941
|
+
const idx = args.indexOf(flag);
|
|
942
|
+
if (idx !== -1 && idx + 1 < args.length) return args[idx + 1];
|
|
943
|
+
const eq = args.find((a) => a.startsWith(`${flag}=`));
|
|
944
|
+
if (eq) return eq.split("=")[1];
|
|
945
|
+
return void 0;
|
|
946
|
+
}
|
|
117
947
|
async function main() {
|
|
118
948
|
switch (command) {
|
|
119
949
|
case "generate":
|
|
120
950
|
await generate(cwd);
|
|
121
951
|
break;
|
|
122
|
-
case "preview":
|
|
123
|
-
|
|
952
|
+
case "preview": {
|
|
953
|
+
const portStr = parseFlag("--port");
|
|
954
|
+
await preview(cwd, portStr ? { port: Number(portStr) } : void 0);
|
|
124
955
|
break;
|
|
956
|
+
}
|
|
125
957
|
case "init":
|
|
126
958
|
await init(cwd);
|
|
127
959
|
break;
|
|
@@ -141,14 +973,18 @@ function printHelp() {
|
|
|
141
973
|
mdocui \u2014 CLI for mdocUI generative UI library
|
|
142
974
|
|
|
143
975
|
Commands:
|
|
144
|
-
init Scaffold
|
|
976
|
+
init Scaffold mdocUI integration (registry, components, API route)
|
|
145
977
|
generate Generate system prompt from component registry
|
|
146
|
-
preview
|
|
978
|
+
preview Start local dev server with live mdocUI markup preview
|
|
979
|
+
|
|
980
|
+
Options:
|
|
981
|
+
--port <n> Port for preview server (default: 4321)
|
|
147
982
|
|
|
148
983
|
Usage:
|
|
149
984
|
npx @mdocui/cli init
|
|
150
985
|
npx @mdocui/cli generate
|
|
151
986
|
npx @mdocui/cli preview
|
|
987
|
+
npx @mdocui/cli preview --port 3000
|
|
152
988
|
`);
|
|
153
989
|
}
|
|
154
990
|
main().catch((err) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/generate.ts","../src/config.ts","../src/commands/init.ts","../src/commands/preview.ts","../src/index.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport { ComponentRegistry, generatePrompt } from '@mdocui/core'\nimport { loadConfig, toPromptOptions } from '../config'\n\nexport async function generate(cwd: string) {\n\tconst config = await loadConfig(cwd)\n\tconst registry = new ComponentRegistry()\n\tregistry.registerAll(config.components)\n\n\tconst prompt = generatePrompt(registry, toPromptOptions(config))\n\tconst outputPath = path.resolve(cwd, config.output ?? 'system-prompt.txt')\n\n\tfs.mkdirSync(path.dirname(outputPath), { recursive: true })\n\tfs.writeFileSync(outputPath, prompt, 'utf-8')\n\n\tconsole.log(`Generated system prompt → ${path.relative(cwd, outputPath)}`)\n\tconsole.log(`${registry.names().length} components, ${prompt.length} chars`)\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ComponentDefinition, ComponentGroup, PromptOptions } from '@mdocui/core'\n\nexport interface MdocUIConfig {\n\tcomponents: ComponentDefinition[]\n\toutput?: string\n\tpreamble?: string\n\tadditionalRules?: string[]\n\texamples?: string[]\n\tgroups?: ComponentGroup[]\n}\n\nconst CONFIG_FILES = ['mdocui.config.ts', 'mdocui.config.js', 'mdocui.config.mjs']\n\nexport async function loadConfig(cwd: string): Promise<MdocUIConfig> {\n\tfor (const name of CONFIG_FILES) {\n\t\tconst configPath = path.resolve(cwd, name)\n\t\tif (!fs.existsSync(configPath)) continue\n\n\t\tconst url = pathToFileURL(configPath).href\n\t\tconst mod = await import(url)\n\t\tconst config = mod.default ?? mod\n\n\t\tif (!config.components || !Array.isArray(config.components)) {\n\t\t\tthrow new Error(`${name}: \"components\" must be an array of ComponentDefinition`)\n\t\t}\n\n\t\treturn config as MdocUIConfig\n\t}\n\n\tthrow new Error(`No config file found. Create one of: ${CONFIG_FILES.join(', ')}`)\n}\n\nexport function toPromptOptions(config: MdocUIConfig): PromptOptions {\n\treturn {\n\t\tpreamble: config.preamble,\n\t\tadditionalRules: config.additionalRules,\n\t\texamples: config.examples,\n\t\tgroups: config.groups,\n\t}\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nconst CONFIG_TEMPLATE = `import { defineComponent } from '@mdocui/core'\nimport { z } from 'zod'\n\nconst button = defineComponent({\n name: 'button',\n description: 'Clickable action button',\n props: z.object({\n action: z.string().describe('Action to perform'),\n label: z.string().describe('Button text'),\n }),\n children: 'none',\n})\n\nconst callout = defineComponent({\n name: 'callout',\n description: 'Highlighted message block',\n props: z.object({\n type: z.enum(['info', 'warning', 'error', 'success']).describe('Severity'),\n title: z.string().optional().describe('Heading'),\n }),\n children: 'any',\n})\n\nexport default {\n components: [button, callout],\n output: './generated/system-prompt.txt',\n preamble: 'You are a helpful assistant.',\n additionalRules: [\n 'End responses with follow-up buttons using action=\"continue\"',\n ],\n}\n`\n\nexport async function init(cwd: string) {\n\tconst configPath = path.resolve(cwd, 'mdocui.config.ts')\n\tconst generatedDir = path.resolve(cwd, 'generated')\n\n\tif (fs.existsSync(configPath)) {\n\t\tconsole.log('mdocui.config.ts already exists — skipping')\n\t\treturn\n\t}\n\n\tfs.writeFileSync(configPath, CONFIG_TEMPLATE, 'utf-8')\n\tfs.mkdirSync(generatedDir, { recursive: true })\n\tfs.writeFileSync(path.resolve(generatedDir, '.gitkeep'), '', 'utf-8')\n\n\tconsole.log('Created:')\n\tconsole.log(' mdocui.config.ts — component definitions + config')\n\tconsole.log(' generated/ — output directory for system prompts')\n\tconsole.log('')\n\tconsole.log('Next steps:')\n\tconsole.log(' 1. Edit mdocui.config.ts — add your components')\n\tconsole.log(' 2. Run: npx @mdocui/cli generate')\n}\n","import { ComponentRegistry, generatePrompt } from '@mdocui/core'\nimport { loadConfig, toPromptOptions } from '../config'\n\nexport async function preview(cwd: string) {\n\tconst config = await loadConfig(cwd)\n\tconst registry = new ComponentRegistry()\n\tregistry.registerAll(config.components)\n\n\tconst prompt = generatePrompt(registry, toPromptOptions(config))\n\tconsole.log(prompt)\n}\n","import { generate } from './commands/generate'\nimport { init } from './commands/init'\nimport { preview } from './commands/preview'\n\nconst args = process.argv.slice(2)\nconst command = args[0]\nconst cwd = process.cwd()\n\nasync function main() {\n\tswitch (command) {\n\t\tcase 'generate':\n\t\t\tawait generate(cwd)\n\t\t\tbreak\n\t\tcase 'preview':\n\t\t\tawait preview(cwd)\n\t\t\tbreak\n\t\tcase 'init':\n\t\t\tawait init(cwd)\n\t\t\tbreak\n\t\tcase '--help':\n\t\tcase '-h':\n\t\tcase undefined:\n\t\t\tprintHelp()\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.error(`Unknown command: ${command}`)\n\t\t\tprintHelp()\n\t\t\tprocess.exit(1)\n\t}\n}\n\nfunction printHelp() {\n\tconsole.log(`\nmdocui — CLI for mdocUI generative UI library\n\nCommands:\n init Scaffold a new mdocui config file\n generate Generate system prompt from component registry\n preview Print generated system prompt to stdout\n\nUsage:\n npx @mdocui/cli init\n npx @mdocui/cli generate\n npx @mdocui/cli preview\n`)\n}\n\nmain().catch((err) => {\n\tconsole.error(err.message)\n\tprocess.exit(1)\n})\n"],"mappings":"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","names":["fs","path","cwd","cwd","path","fs","fs","path","cwd","ComponentRegistry","generatePrompt","cwd","ComponentRegistry","generatePrompt"]}
|
|
1
|
+
{"version":3,"sources":["../src/commands/generate.ts","../src/config.ts","../src/commands/init.ts","../src/commands/preview.ts","../src/index.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport { ComponentRegistry, generatePrompt } from '@mdocui/core'\nimport { loadConfig, toPromptOptions } from '../config'\n\nexport async function generate(cwd: string) {\n\tconst config = await loadConfig(cwd)\n\tconst registry = new ComponentRegistry()\n\tregistry.registerAll(config.components)\n\n\tconst prompt = generatePrompt(registry, toPromptOptions(config))\n\tconst outputPath = path.resolve(cwd, config.output ?? 'system-prompt.txt')\n\n\tfs.mkdirSync(path.dirname(outputPath), { recursive: true })\n\tfs.writeFileSync(outputPath, prompt, 'utf-8')\n\n\tconsole.log(`Generated system prompt → ${path.relative(cwd, outputPath)}`)\n\tconsole.log(`${registry.names().length} components, ${prompt.length} chars`)\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ComponentDefinition, ComponentGroup, PromptOptions } from '@mdocui/core'\n\nexport interface MdocUIConfig {\n\tcomponents: ComponentDefinition[]\n\toutput?: string\n\tpreamble?: string\n\tadditionalRules?: string[]\n\texamples?: string[]\n\tgroups?: ComponentGroup[]\n}\n\nconst CONFIG_FILES = ['mdocui.config.ts', 'mdocui.config.js', 'mdocui.config.mjs']\n\nexport async function loadConfig(cwd: string): Promise<MdocUIConfig> {\n\tfor (const name of CONFIG_FILES) {\n\t\tconst configPath = path.resolve(cwd, name)\n\t\tif (!fs.existsSync(configPath)) continue\n\n\t\tconst url = pathToFileURL(configPath).href\n\t\tconst mod = await import(url)\n\t\tconst config = mod.default ?? mod\n\n\t\tif (!config.components || !Array.isArray(config.components)) {\n\t\t\tthrow new Error(`${name}: \"components\" must be an array of ComponentDefinition`)\n\t\t}\n\n\t\treturn config as MdocUIConfig\n\t}\n\n\tthrow new Error(`No config file found. Create one of: ${CONFIG_FILES.join(', ')}`)\n}\n\nexport function toPromptOptions(config: MdocUIConfig): PromptOptions {\n\treturn {\n\t\tpreamble: config.preamble,\n\t\tadditionalRules: config.additionalRules,\n\t\texamples: config.examples,\n\t\tgroups: config.groups,\n\t}\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\ntype Framework = 'nextjs' | 'vite' | 'remix' | 'unknown'\n\ninterface DetectedProject {\n\tframework: Framework\n\thasSrc: boolean\n\thasApp: boolean // Next.js app router\n\thasPages: boolean // Next.js pages router\n}\n\nfunction detectFramework(cwd: string): DetectedProject {\n\tconst pkgPath = path.resolve(cwd, 'package.json')\n\tconst hasSrc = fs.existsSync(path.resolve(cwd, 'src'))\n\tconst hasApp =\n\t\tfs.existsSync(path.resolve(cwd, 'src/app')) || fs.existsSync(path.resolve(cwd, 'app'))\n\tconst hasPages =\n\t\tfs.existsSync(path.resolve(cwd, 'src/pages')) || fs.existsSync(path.resolve(cwd, 'pages'))\n\n\tif (!fs.existsSync(pkgPath)) {\n\t\treturn { framework: 'unknown', hasSrc, hasApp, hasPages }\n\t}\n\n\tconst pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\n\tif (allDeps.next) return { framework: 'nextjs', hasSrc, hasApp, hasPages }\n\tif (allDeps['@remix-run/react'] || allDeps.remix)\n\t\treturn { framework: 'remix', hasSrc, hasApp, hasPages }\n\tif (allDeps.vite) return { framework: 'vite', hasSrc, hasApp, hasPages }\n\n\treturn { framework: 'unknown', hasSrc, hasApp, hasPages }\n}\n\n// ── File templates ─────────────────────────────────────────\n\nfunction registryTemplate(): string {\n\treturn `import { createDefaultRegistry } from '@mdocui/react'\n\n// Pre-built registry with all 24 built-in mdocUI components:\n// Layout: stack, grid, card, divider, accordion, tabs, tab\n// Interactive: button, button-group, input, textarea, select, checkbox, toggle, form\n// Data: chart, table, stat, progress\n// Content: callout, badge, image, code-block, link\n//\n// To add custom components, use the ComponentRegistry API directly:\n// import { ComponentRegistry } from '@mdocui/core'\n// import { allDefinitions } from '@mdocui/react'\n\nexport const registry = createDefaultRegistry()\n`\n}\n\nfunction mdocMessageTemplate(): string {\n\treturn `'use client'\n\nimport { useCallback, useRef } from 'react'\nimport { Renderer, useRenderer, defaultComponents } from '@mdocui/react'\nimport type { ActionHandler } from '@mdocui/react'\nimport { registry } from '@/lib/mdoc-registry'\n\ninterface MdocMessageProps {\n /** Called when the user clicks a button or submits a form */\n onAction?: ActionHandler\n /** Called when a \"continue\" action fires — typically sends the label as a new user message */\n onSend?: (message: string) => void\n /** Custom prose renderer, e.g. for markdown-to-HTML */\n renderProse?: (content: string, key: string) => React.ReactNode\n /** CSS class overrides per component name */\n classNames?: Record<string, string>\n}\n\nexport function MdocMessage({ onAction, onSend, renderProse, classNames }: MdocMessageProps) {\n const { nodes, isStreaming, push, done, reset } = useRenderer({ registry })\n const abortRef = useRef<AbortController | null>(null)\n\n const handleAction: ActionHandler = useCallback(\n (action, params) => {\n if (action === 'continue' && onSend) {\n onSend(params?.label ?? action)\n return\n }\n onAction?.(action, params)\n },\n [onAction, onSend],\n )\n\n /** Stream a response from your API endpoint */\n const stream = useCallback(\n async (url: string, body: Record<string, unknown>) => {\n abortRef.current?.abort()\n reset()\n\n const controller = new AbortController()\n abortRef.current = controller\n\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n\n if (!res.ok || !res.body) {\n throw new Error(\\`Stream request failed: \\${res.status}\\`)\n }\n\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n\n while (true) {\n const { done: readerDone, value } = await reader.read()\n if (readerDone) break\n push(decoder.decode(value, { stream: true }))\n }\n\n done()\n },\n [push, done, reset],\n )\n\n return (\n <Renderer\n nodes={nodes}\n components={defaultComponents}\n onAction={handleAction}\n isStreaming={isStreaming}\n renderProse={renderProse}\n classNames={classNames}\n />\n )\n}\n\n// Export the stream helper so parent components can drive it\nexport type { MdocMessageProps }\nexport { MdocMessage as default }\n`\n}\n\nfunction envExampleTemplate(): string {\n\treturn `# mdocUI — LLM API keys\n# Copy this file to .env.local and fill in your keys.\n\n# Anthropic (Claude)\nANTHROPIC_API_KEY=\n\n# OpenAI\nOPENAI_API_KEY=\n`\n}\n\nfunction nextjsRouteTemplate(): string {\n\treturn `import { type NextRequest, NextResponse } from 'next/server'\nimport { generatePrompt } from '@mdocui/core'\nimport { allDefinitions, defaultGroups } from '@mdocui/react'\n\n// Build the system prompt once at module level\nconst systemPrompt = generatePrompt({\n components: allDefinitions,\n groups: defaultGroups,\n preamble: 'You are a helpful assistant. Use mdocUI components to create rich, interactive responses.',\n})\n\nexport async function POST(req: NextRequest) {\n const { messages, provider = 'anthropic' } = await req.json()\n\n if (provider === 'openai') {\n return streamOpenAI(messages)\n }\n\n return streamAnthropic(messages)\n}\n\n// ── Anthropic (Claude) ─────────────────────────────────────\n\nasync function streamAnthropic(messages: Array<{ role: string; content: string }>) {\n const apiKey = process.env.ANTHROPIC_API_KEY\n if (!apiKey) {\n return NextResponse.json({ error: 'ANTHROPIC_API_KEY not set' }, { status: 500 })\n }\n\n const res = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: 'claude-sonnet-4-20250514',\n max_tokens: 4096,\n system: systemPrompt,\n messages,\n stream: true,\n }),\n })\n\n if (!res.ok || !res.body) {\n const text = await res.text()\n return NextResponse.json({ error: text }, { status: res.status })\n }\n\n const encoder = new TextEncoder()\n const stream = new ReadableStream({\n async start(controller) {\n const reader = res.body!.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n\n const lines = buffer.split('\\\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6)\n if (data === '[DONE]') continue\n\n try {\n const event = JSON.parse(data)\n if (event.type === 'content_block_delta' && event.delta?.text) {\n controller.enqueue(encoder.encode(event.delta.text))\n }\n } catch {\n // skip malformed events\n }\n }\n }\n\n controller.close()\n },\n })\n\n return new Response(stream, {\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n })\n}\n\n// ── OpenAI ─────────────────────────────────────────────────\n\nasync function streamOpenAI(messages: Array<{ role: string; content: string }>) {\n const apiKey = process.env.OPENAI_API_KEY\n if (!apiKey) {\n return NextResponse.json({ error: 'OPENAI_API_KEY not set' }, { status: 500 })\n }\n\n const res = await fetch('https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: \\`Bearer \\${apiKey}\\`,\n },\n body: JSON.stringify({\n model: 'gpt-4o',\n messages: [{ role: 'system', content: systemPrompt }, ...messages],\n stream: true,\n }),\n })\n\n if (!res.ok || !res.body) {\n const text = await res.text()\n return NextResponse.json({ error: text }, { status: res.status })\n }\n\n const encoder = new TextEncoder()\n const stream = new ReadableStream({\n async start(controller) {\n const reader = res.body!.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n\n const lines = buffer.split('\\\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6)\n if (data === '[DONE]') continue\n\n try {\n const event = JSON.parse(data)\n const content = event.choices?.[0]?.delta?.content\n if (content) {\n controller.enqueue(encoder.encode(content))\n }\n } catch {\n // skip malformed events\n }\n }\n }\n\n controller.close()\n },\n })\n\n return new Response(stream, {\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n })\n}\n`\n}\n\nfunction configTemplate(): string {\n\treturn `import { allDefinitions, defaultGroups } from '@mdocui/react'\n\nexport default {\n components: allDefinitions,\n groups: defaultGroups,\n output: './generated/system-prompt.txt',\n preamble: 'You are a helpful assistant. Use mdocUI components to create rich, interactive responses.',\n}\n`\n}\n\n// ── Scaffolder ─────────────────────────────────────────────\n\ninterface ScaffoldedFile {\n\trelativePath: string\n\tcontent: string\n}\n\nfunction writeIfMissing(cwd: string, file: ScaffoldedFile): boolean {\n\tconst abs = path.resolve(cwd, file.relativePath)\n\tif (fs.existsSync(abs)) {\n\t\tconsole.log(` skip ${file.relativePath} (already exists)`)\n\t\treturn false\n\t}\n\tfs.mkdirSync(path.dirname(abs), { recursive: true })\n\tfs.writeFileSync(abs, file.content, 'utf-8')\n\treturn true\n}\n\nexport async function init(cwd: string) {\n\tconst project = detectFramework(cwd)\n\n\tconsole.log('')\n\tconsole.log(` Detected: ${frameworkLabel(project.framework)}`)\n\tconsole.log('')\n\n\tconst files: ScaffoldedFile[] = []\n\n\t// Always generate these\n\tfiles.push({ relativePath: 'src/lib/mdoc-registry.ts', content: registryTemplate() })\n\tfiles.push({ relativePath: 'src/components/mdoc-message.tsx', content: mdocMessageTemplate() })\n\tfiles.push({ relativePath: '.env.example', content: envExampleTemplate() })\n\tfiles.push({ relativePath: 'mdocui.config.ts', content: configTemplate() })\n\n\t// Framework-specific files\n\tif (project.framework === 'nextjs') {\n\t\tconst routeDir = project.hasSrc ? 'src/app/api/chat' : 'app/api/chat'\n\t\tfiles.push({ relativePath: `${routeDir}/route.ts`, content: nextjsRouteTemplate() })\n\t}\n\n\t// Write files\n\tconst created: string[] = []\n\tconst skipped: string[] = []\n\n\tfor (const file of files) {\n\t\tif (writeIfMissing(cwd, file)) {\n\t\t\tcreated.push(file.relativePath)\n\t\t} else {\n\t\t\tskipped.push(file.relativePath)\n\t\t}\n\t}\n\n\t// Ensure generated/ dir\n\tconst generatedDir = path.resolve(cwd, 'generated')\n\tif (!fs.existsSync(generatedDir)) {\n\t\tfs.mkdirSync(generatedDir, { recursive: true })\n\t\tfs.writeFileSync(path.resolve(generatedDir, '.gitkeep'), '', 'utf-8')\n\t\tcreated.push('generated/')\n\t}\n\n\t// Summary\n\tconsole.log('')\n\tif (created.length > 0) {\n\t\tconsole.log(' Created:')\n\t\tfor (const f of created) {\n\t\t\tconsole.log(` + ${f}`)\n\t\t}\n\t}\n\tif (skipped.length > 0) {\n\t\tconsole.log(' Skipped (already exist):')\n\t\tfor (const f of skipped) {\n\t\t\tconsole.log(` - ${f}`)\n\t\t}\n\t}\n\n\tconsole.log('')\n\tconsole.log(' Next steps:')\n\tconsole.log(' 1. Copy .env.example to .env.local and add your API keys')\n\tconsole.log(' 2. Install dependencies:')\n\tconsole.log(' npm install @mdocui/core @mdocui/react')\n\tconsole.log(' 3. Generate the system prompt:')\n\tconsole.log(' npx @mdocui/cli generate')\n\tif (project.framework === 'nextjs') {\n\t\tconsole.log(' 4. Import <MdocMessage /> in your chat page')\n\t\tconsole.log(' and call stream(\"/api/chat\", { messages })')\n\t} else {\n\t\tconsole.log(' 4. Import <MdocMessage /> in your chat component')\n\t\tconsole.log(' and wire up streaming from your backend')\n\t}\n\tconsole.log('')\n}\n\nfunction frameworkLabel(fw: Framework): string {\n\tswitch (fw) {\n\t\tcase 'nextjs':\n\t\t\treturn 'Next.js'\n\t\tcase 'vite':\n\t\t\treturn 'Vite'\n\t\tcase 'remix':\n\t\t\treturn 'Remix'\n\t\tcase 'unknown':\n\t\t\treturn 'Unknown framework (generating generic files)'\n\t}\n}\n","import http from 'node:http'\nimport type { ASTNode } from '@mdocui/core'\nimport { StreamingParser } from '@mdocui/core'\nimport { loadConfig } from '../config'\n\nexport interface PreviewOptions {\n\tport?: number\n}\n\nexport async function preview(cwd: string, options?: PreviewOptions) {\n\tconst port = options?.port ?? 4321\n\tlet knownTags: Set<string> | undefined\n\n\ttry {\n\t\tconst config = await loadConfig(cwd)\n\t\tknownTags = new Set(config.components.map((c) => c.name))\n\t\tconsole.log(`Loaded config with ${knownTags.size} components: ${[...knownTags].join(', ')}`)\n\t} catch {\n\t\tconsole.log('No config found — all tags treated as known')\n\t}\n\n\tconst server = http.createServer((req, res) => {\n\t\tif (req.method === 'POST' && req.url === '/api/parse') {\n\t\t\tlet body = ''\n\t\t\treq.on('data', (chunk: Buffer) => {\n\t\t\t\tbody += chunk.toString()\n\t\t\t})\n\t\t\treq.on('end', () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst { content } = JSON.parse(body) as { content: string }\n\t\t\t\t\tconst parser = new StreamingParser(\n\t\t\t\t\t\tknownTags ? { knownTags, dropUnknown: false } : undefined,\n\t\t\t\t\t)\n\t\t\t\t\tparser.write(content)\n\t\t\t\t\tparser.flush()\n\t\t\t\t\tconst nodes: ASTNode[] = parser.getNodes()\n\t\t\t\t\tconst meta = parser.getMeta()\n\n\t\t\t\t\tres.writeHead(200, { 'Content-Type': 'application/json' })\n\t\t\t\t\tres.end(JSON.stringify({ nodes, meta }))\n\t\t\t\t} catch (err) {\n\t\t\t\t\tres.writeHead(400, { 'Content-Type': 'application/json' })\n\t\t\t\t\tres.end(JSON.stringify({ error: (err as Error).message }))\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tif (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {\n\t\t\tres.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n\t\t\tres.end(buildHTML(knownTags))\n\t\t\treturn\n\t\t}\n\n\t\tres.writeHead(404, { 'Content-Type': 'text/plain' })\n\t\tres.end('Not found')\n\t})\n\n\tserver.listen(port, () => {\n\t\tconsole.log(`\\nmdocui preview server running at:\\n`)\n\t\tconsole.log(` http://localhost:${port}\\n`)\n\t\tconsole.log('Press Ctrl+C to stop.\\n')\n\t})\n}\n\nfunction buildHTML(knownTags?: Set<string>): string {\n\tconst tagList = knownTags ? JSON.stringify([...knownTags]) : '[]'\n\n\treturn /* html */ `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n<title>mdocui preview</title>\n<style>\n*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n:root {\n --bg: #0a0a0a;\n --bg-surface: #141414;\n --bg-surface-2: #1e1e1e;\n --border: #2a2a2a;\n --text: #e4e4e4;\n --text-dim: #888;\n --accent: #7c6bff;\n --accent-dim: #5a4fbb;\n --error: #f06;\n --tag-bg: #1a1a2e;\n --tag-border: #2a2a4e;\n --prose-bg: #0f1a0f;\n --prose-border: #1a3a1a;\n --font-mono: 'SF Mono', 'Fira Code', 'JetBrains Mono', Menlo, monospace;\n --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\nhtml, body { height: 100%; background: var(--bg); color: var(--text); font-family: var(--font-sans); }\n\n.app {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 20px;\n border-bottom: 1px solid var(--border);\n background: var(--bg-surface);\n}\n\nheader h1 {\n font-size: 14px;\n font-weight: 600;\n letter-spacing: 0.5px;\n color: var(--accent);\n}\n\nheader .meta {\n font-size: 12px;\n color: var(--text-dim);\n}\n\n.panels {\n display: flex;\n flex: 1;\n min-height: 0;\n}\n\n.panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.panel + .panel {\n border-left: 1px solid var(--border);\n}\n\n.panel-header {\n padding: 8px 16px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 1px;\n color: var(--text-dim);\n background: var(--bg-surface);\n border-bottom: 1px solid var(--border);\n}\n\ntextarea {\n flex: 1;\n width: 100%;\n padding: 16px;\n background: var(--bg);\n color: var(--text);\n border: none;\n resize: none;\n font-family: var(--font-mono);\n font-size: 13px;\n line-height: 1.6;\n outline: none;\n tab-size: 2;\n}\n\ntextarea::placeholder { color: var(--text-dim); }\n\n.preview {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n background: var(--bg);\n}\n\n/* AST node rendering */\n\n.node-prose {\n background: var(--prose-bg);\n border: 1px solid var(--prose-border);\n border-radius: 6px;\n padding: 12px 16px;\n margin-bottom: 8px;\n font-size: 14px;\n line-height: 1.7;\n}\n\n.node-prose p { margin-bottom: 0.5em; }\n.node-prose p:last-child { margin-bottom: 0; }\n.node-prose strong { color: #fff; }\n.node-prose em { color: #ccc; font-style: italic; }\n.node-prose code {\n background: var(--bg-surface-2);\n padding: 2px 6px;\n border-radius: 3px;\n font-family: var(--font-mono);\n font-size: 12px;\n}\n.node-prose a { color: var(--accent); text-decoration: underline; }\n.node-prose ul, .node-prose ol { padding-left: 1.5em; margin-bottom: 0.5em; }\n.node-prose h1, .node-prose h2, .node-prose h3 {\n color: #fff;\n margin-bottom: 0.4em;\n}\n.node-prose h1 { font-size: 1.4em; }\n.node-prose h2 { font-size: 1.2em; }\n.node-prose h3 { font-size: 1.05em; }\n\n.node-component {\n background: var(--tag-bg);\n border: 1px solid var(--tag-border);\n border-radius: 6px;\n margin-bottom: 8px;\n overflow: hidden;\n}\n\n.node-component-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background: rgba(124, 107, 255, 0.08);\n border-bottom: 1px solid var(--tag-border);\n}\n\n.node-component-name {\n font-family: var(--font-mono);\n font-size: 13px;\n font-weight: 600;\n color: var(--accent);\n}\n\n.node-component-badge {\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 3px;\n background: var(--accent-dim);\n color: #fff;\n}\n\n.node-component-props {\n padding: 8px 12px;\n}\n\n.prop-row {\n display: flex;\n gap: 8px;\n font-size: 12px;\n font-family: var(--font-mono);\n padding: 2px 0;\n}\n\n.prop-key { color: #82aaff; }\n.prop-eq { color: var(--text-dim); }\n.prop-val { color: #c3e88d; }\n\n.node-component-children {\n padding: 8px 12px;\n border-top: 1px solid var(--tag-border);\n}\n\n.node-component-children-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--text-dim);\n margin-bottom: 6px;\n}\n\n.errors {\n padding: 8px 16px;\n background: rgba(255, 0, 102, 0.08);\n border-top: 1px solid rgba(255, 0, 102, 0.2);\n}\n\n.error-item {\n font-size: 12px;\n color: var(--error);\n font-family: var(--font-mono);\n padding: 2px 0;\n}\n\n.error-item::before { content: '\\\\26A0 '; }\n\n.status-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 4px 16px;\n font-size: 11px;\n color: var(--text-dim);\n border-top: 1px solid var(--border);\n background: var(--bg-surface);\n}\n\n.empty-state {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--text-dim);\n font-size: 13px;\n text-align: center;\n line-height: 1.8;\n}\n</style>\n</head>\n<body>\n<div class=\"app\">\n <header>\n <h1>mdocui preview</h1>\n <span class=\"meta\" id=\"tag-count\"></span>\n </header>\n <div class=\"panels\">\n <div class=\"panel\">\n <div class=\"panel-header\">Input</div>\n <textarea id=\"editor\" spellcheck=\"false\" placeholder=\"Type mdocUI markup here...\n\nExample:\nHere is a **weather card**:\n\n{% WeatherCard city="San Francisco" temp=72 unit="F" /%}\n\nOr with body content:\n\n{% Alert type="info" %}\nThis is an informational alert with **markdown** inside.\n{% /Alert %}\"></textarea>\n </div>\n <div class=\"panel\">\n <div class=\"panel-header\">Preview</div>\n <div class=\"preview\" id=\"preview\">\n <div class=\"empty-state\">Start typing mdocUI markup<br/>to see the live preview</div>\n </div>\n <div class=\"errors\" id=\"errors\" style=\"display:none\"></div>\n </div>\n </div>\n <div class=\"status-bar\">\n <span id=\"status\">Ready</span>\n <span id=\"node-count\"></span>\n </div>\n</div>\n\n<script>\n(function() {\n const knownTags = ${tagList};\n const editor = document.getElementById('editor');\n const preview = document.getElementById('preview');\n const errors = document.getElementById('errors');\n const status = document.getElementById('status');\n const nodeCount = document.getElementById('node-count');\n const tagCountEl = document.getElementById('tag-count');\n\n if (knownTags.length > 0) {\n tagCountEl.textContent = knownTags.length + ' registered component' + (knownTags.length === 1 ? '' : 's');\n } else {\n tagCountEl.textContent = 'no config — all tags accepted';\n }\n\n let timer = null;\n\n editor.addEventListener('input', function() {\n clearTimeout(timer);\n timer = setTimeout(parse, 150);\n });\n\n // also handle paste\n editor.addEventListener('paste', function() {\n clearTimeout(timer);\n timer = setTimeout(parse, 50);\n });\n\n async function parse() {\n const content = editor.value;\n if (!content.trim()) {\n preview.innerHTML = '<div class=\"empty-state\">Start typing mdocUI markup<br/>to see the live preview</div>';\n errors.style.display = 'none';\n status.textContent = 'Ready';\n nodeCount.textContent = '';\n return;\n }\n\n status.textContent = 'Parsing...';\n\n try {\n const res = await fetch('/api/parse', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ content }),\n });\n\n const data = await res.json();\n\n if (data.error) {\n status.textContent = 'Error: ' + data.error;\n return;\n }\n\n preview.innerHTML = renderNodes(data.nodes);\n nodeCount.textContent = data.meta.nodeCount + ' node' + (data.meta.nodeCount === 1 ? '' : 's');\n\n if (data.meta.errors && data.meta.errors.length > 0) {\n errors.style.display = 'block';\n errors.innerHTML = data.meta.errors\n .map(function(e) { return '<div class=\"error-item\">' + escapeHtml(e.message) + '</div>'; })\n .join('');\n status.textContent = 'Parsed with ' + data.meta.errors.length + ' warning(s)';\n } else {\n errors.style.display = 'none';\n status.textContent = 'OK';\n }\n } catch (err) {\n status.textContent = 'Fetch error: ' + err.message;\n }\n }\n\n function renderNodes(nodes) {\n if (!nodes || nodes.length === 0) return '<div class=\"empty-state\">No nodes parsed</div>';\n return nodes.map(renderNode).join('');\n }\n\n function renderNode(node) {\n if (node.type === 'prose') {\n return '<div class=\"node-prose\">' + markdownToHtml(node.content) + '</div>';\n }\n\n if (node.type === 'component') {\n var html = '<div class=\"node-component\">';\n html += '<div class=\"node-component-header\">';\n html += '<span class=\"node-component-name\">' + escapeHtml(node.name) + '</span>';\n if (node.selfClosing) {\n html += '<span class=\"node-component-badge\">self-closing</span>';\n }\n html += '</div>';\n\n var propKeys = Object.keys(node.props || {});\n if (propKeys.length > 0) {\n html += '<div class=\"node-component-props\">';\n propKeys.forEach(function(key) {\n var val = node.props[key];\n var display = typeof val === 'string' ? '\"' + escapeHtml(val) + '\"' : String(val);\n html += '<div class=\"prop-row\">';\n html += '<span class=\"prop-key\">' + escapeHtml(key) + '</span>';\n html += '<span class=\"prop-eq\">=</span>';\n html += '<span class=\"prop-val\">' + display + '</span>';\n html += '</div>';\n });\n html += '</div>';\n }\n\n if (node.children && node.children.length > 0) {\n html += '<div class=\"node-component-children\">';\n html += '<div class=\"node-component-children-label\">Children</div>';\n html += renderNodes(node.children);\n html += '</div>';\n }\n\n html += '</div>';\n return html;\n }\n\n return '';\n }\n\n /** Minimal markdown-to-HTML for prose content */\n function markdownToHtml(text) {\n var html = escapeHtml(text);\n\n // headings\n html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');\n html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');\n html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');\n\n // bold + italic\n html = html.replace(/\\\\*\\\\*\\\\*(.+?)\\\\*\\\\*\\\\*/g, '<strong><em>$1</em></strong>');\n html = html.replace(/\\\\*\\\\*(.+?)\\\\*\\\\*/g, '<strong>$1</strong>');\n html = html.replace(/\\\\*(.+?)\\\\*/g, '<em>$1</em>');\n\n // inline code\n html = html.replace(/\\`([^\\`]+)\\`/g, '<code>$1</code>');\n\n // links\n html = html.replace(/\\\\[([^\\\\]]+)\\\\]\\\\(([^)]+)\\\\)/g, '<a href=\"$2\" target=\"_blank\">$1</a>');\n\n // unordered lists\n html = html.replace(/^- (.+)$/gm, '<li>$1</li>');\n html = html.replace(/(<li>.*<\\\\/li>\\\\n?)+/g, function(m) { return '<ul>' + m + '</ul>'; });\n\n // paragraphs: split on double newlines\n html = html.split(/\\\\n\\\\n+/).map(function(block) {\n block = block.trim();\n if (!block) return '';\n if (/^<(h[1-3]|ul|ol|li)/.test(block)) return block;\n return '<p>' + block.replace(/\\\\n/g, '<br/>') + '</p>';\n }).join('');\n\n return html;\n }\n\n function escapeHtml(str) {\n return String(str)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n }\n})();\n</script>\n</body>\n</html>`\n}\n","import { generate } from './commands/generate'\nimport { init } from './commands/init'\nimport { preview } from './commands/preview'\n\nconst args = process.argv.slice(2)\nconst command = args[0]\nconst cwd = process.cwd()\n\nfunction parseFlag(flag: string): string | undefined {\n\tconst idx = args.indexOf(flag)\n\tif (idx !== -1 && idx + 1 < args.length) return args[idx + 1]\n\tconst eq = args.find((a) => a.startsWith(`${flag}=`))\n\tif (eq) return eq.split('=')[1]\n\treturn undefined\n}\n\nasync function main() {\n\tswitch (command) {\n\t\tcase 'generate':\n\t\t\tawait generate(cwd)\n\t\t\tbreak\n\t\tcase 'preview': {\n\t\t\tconst portStr = parseFlag('--port')\n\t\t\tawait preview(cwd, portStr ? { port: Number(portStr) } : undefined)\n\t\t\tbreak\n\t\t}\n\t\tcase 'init':\n\t\t\tawait init(cwd)\n\t\t\tbreak\n\t\tcase '--help':\n\t\tcase '-h':\n\t\tcase undefined:\n\t\t\tprintHelp()\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.error(`Unknown command: ${command}`)\n\t\t\tprintHelp()\n\t\t\tprocess.exit(1)\n\t}\n}\n\nfunction printHelp() {\n\tconsole.log(`\nmdocui — CLI for mdocUI generative UI library\n\nCommands:\n init Scaffold mdocUI integration (registry, components, API route)\n generate Generate system prompt from component registry\n preview Start local dev server with live mdocUI markup preview\n\nOptions:\n --port <n> Port for preview server (default: 4321)\n\nUsage:\n npx @mdocui/cli init\n npx @mdocui/cli generate\n npx @mdocui/cli preview\n npx @mdocui/cli preview --port 3000\n`)\n}\n\nmain().catch((err) => {\n\tconsole.error(err.message)\n\tprocess.exit(1)\n})\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAsRG,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqK7B;;;AC3fA,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UAAU,KAAK,CAAC;AACtB,IAAM,MAAM,QAAQ,IAAI;AAExB,SAAS,UAAU,MAAkC;AACpD,QAAM,MAAM,KAAK,QAAQ,IAAI;AAC7B,MAAI,QAAQ,MAAM,MAAM,IAAI,KAAK,OAAQ,QAAO,KAAK,MAAM,CAAC;AAC5D,QAAM,KAAK,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,CAAC;AACpD,MAAI,GAAI,QAAO,GAAG,MAAM,GAAG,EAAE,CAAC;AAC9B,SAAO;AACR;AAEA,eAAe,OAAO;AACrB,UAAQ,SAAS;AAAA,IAChB,KAAK;AACJ,YAAM,SAAS,GAAG;AAClB;AAAA,IACD,KAAK,WAAW;AACf,YAAM,UAAU,UAAU,QAAQ;AAClC,YAAM,QAAQ,KAAK,UAAU,EAAE,MAAM,OAAO,OAAO,EAAE,IAAI,MAAS;AAClE;AAAA,IACD;AAAA,IACA,KAAK;AACJ,YAAM,KAAK,GAAG;AACd;AAAA,IACD,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACJ,gBAAU;AACV;AAAA,IACD;AACC,cAAQ,MAAM,oBAAoB,OAAO,EAAE;AAC3C,gBAAU;AACV,cAAQ,KAAK,CAAC;AAAA,EAChB;AACD;AAEA,SAAS,YAAY;AACpB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAgBZ;AACD;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACrB,UAAQ,MAAM,IAAI,OAAO;AACzB,UAAQ,KAAK,CAAC;AACf,CAAC;","names":["fs","path","cwd","cwd","path","fs","fs","path","cwd","cwd"]}
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mdocui/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "CLI for mdocUI — scaffold, generate system prompts, preview",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mdocui": "./dist/index.js"
|
|
8
8
|
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
9
15
|
"main": "./dist/index.js",
|
|
10
16
|
"types": "./dist/index.d.ts",
|
|
11
17
|
"files": [
|
|
@@ -13,7 +19,7 @@
|
|
|
13
19
|
],
|
|
14
20
|
"dependencies": {
|
|
15
21
|
"zod": "^4.3.0",
|
|
16
|
-
"@mdocui/core": "0.
|
|
22
|
+
"@mdocui/core": "0.6.1"
|
|
17
23
|
},
|
|
18
24
|
"devDependencies": {
|
|
19
25
|
"@types/node": "^25.5.0",
|