contentrain 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +168 -0
  3. package/dist/context-An3xrvcP.mjs +50 -0
  4. package/dist/diff-otz0kGqT.mjs +116 -0
  5. package/dist/doctor-DyKjAIKH.mjs +187 -0
  6. package/dist/generate-DDEDRLdy.mjs +76 -0
  7. package/dist/index.mjs +21 -0
  8. package/dist/init-D5L_7bKE.mjs +415 -0
  9. package/dist/serve-CaCZMeHF.mjs +111 -0
  10. package/dist/serve-ui/assets/AgentPromptGroup.vue_vue_type_script_setup_true_lang-Dy-5bpEY.js +16 -0
  11. package/dist/serve-ui/assets/AppLayout-CJFq2ApF.js +16 -0
  12. package/dist/serve-ui/assets/BranchDetailPage-DzSu3JlE.js +17 -0
  13. package/dist/serve-ui/assets/BranchesPage-ZSG-l44J.js +6 -0
  14. package/dist/serve-ui/assets/CollapsibleTrigger.vue_vue_type_script_setup_true_lang-C4oejwcu.js +1 -0
  15. package/dist/serve-ui/assets/Collection-zzx9pNd2.js +6 -0
  16. package/dist/serve-ui/assets/ConfigProvider-BGjDLViI.js +1 -0
  17. package/dist/serve-ui/assets/ContentListPage-DTFaQB8N.js +51 -0
  18. package/dist/serve-ui/assets/ContentListPage-vXxAeSg8.css +1 -0
  19. package/dist/serve-ui/assets/ContentPage-4sQsAlY2.js +1 -0
  20. package/dist/serve-ui/assets/DashboardPage-CZDlnvAE.js +21 -0
  21. package/dist/serve-ui/assets/DialogTitle.vue_vue_type_script_setup_true_lang-CBu_dmA6.js +1 -0
  22. package/dist/serve-ui/assets/Input.vue_vue_type_script_setup_true_lang-otPltlZG.js +6 -0
  23. package/dist/serve-ui/assets/ModelDetailPage-BMquKCtH.js +49 -0
  24. package/dist/serve-ui/assets/ModelsPage-C_K-aWXf.js +1 -0
  25. package/dist/serve-ui/assets/NormalizePage-CHkT6yLV.js +11 -0
  26. package/dist/serve-ui/assets/PageHeader.vue_vue_type_script_setup_true_lang-C7aWxNSw.js +11 -0
  27. package/dist/serve-ui/assets/Presence-cbB6AqkQ.js +3 -0
  28. package/dist/serve-ui/assets/Separator.vue_vue_type_script_setup_true_lang-fePFl24S.js +1 -0
  29. package/dist/serve-ui/assets/StudioHint.vue_vue_type_script_setup_true_lang-B45TG2r1.js +6 -0
  30. package/dist/serve-ui/assets/TrustBadge.vue_vue_type_script_setup_true_lang-CgoQGS6k.js +11 -0
  31. package/dist/serve-ui/assets/ValidatePage-BW8DO6WC.js +21 -0
  32. package/dist/serve-ui/assets/arrow-down-DqOfGL7C.js +6 -0
  33. package/dist/serve-ui/assets/arrow-left-BRvgeveW.js +6 -0
  34. package/dist/serve-ui/assets/book-marked-D8StxRZP.js +6 -0
  35. package/dist/serve-ui/assets/book-open-BrGAe3tk.js +6 -0
  36. package/dist/serve-ui/assets/box-7olVZr5D.js +6 -0
  37. package/dist/serve-ui/assets/chevron-down-B9YdCtsj.js +6 -0
  38. package/dist/serve-ui/assets/chevron-right-DFKsmZVd.js +6 -0
  39. package/dist/serve-ui/assets/content-BQE3NEm5.js +1 -0
  40. package/dist/serve-ui/assets/database-BGEFn4Oq.js +6 -0
  41. package/dist/serve-ui/assets/file-code-BGiS4dun.js +6 -0
  42. package/dist/serve-ui/assets/file-text-BDHXQ95W.js +6 -0
  43. package/dist/serve-ui/assets/getActiveElement-FUKBWQwh.js +1 -0
  44. package/dist/serve-ui/assets/git-branch-DdE7bk9-.js +6 -0
  45. package/dist/serve-ui/assets/git-merge-iStuJS69.js +6 -0
  46. package/dist/serve-ui/assets/hash-B-ibLpM1.js +6 -0
  47. package/dist/serve-ui/assets/index-C-RjMzFm.css +1 -0
  48. package/dist/serve-ui/assets/index-CoUshYEB.js +80 -0
  49. package/dist/serve-ui/assets/index-WHgvdYOs.js +1 -0
  50. package/dist/serve-ui/assets/languages-BTcd2Rl1.js +6 -0
  51. package/dist/serve-ui/assets/model-empty-state-B6AM4bil.js +1 -0
  52. package/dist/serve-ui/assets/plus-D8Nd3YnG.js +11 -0
  53. package/dist/serve-ui/assets/project-B1qSm0Fz.js +1 -0
  54. package/dist/serve-ui/assets/scan-search-DGPL_VJu.js +6 -0
  55. package/dist/serve-ui/assets/settings-DL-0RaxD.js +6 -0
  56. package/dist/serve-ui/assets/shield-check-CUL7KkSA.js +6 -0
  57. package/dist/serve-ui/assets/trash-2-83QhL6-T.js +11 -0
  58. package/dist/serve-ui/assets/useDirection-Cd3C7VdT.js +1 -0
  59. package/dist/serve-ui/assets/useFormatters-DgWFpsAv.js +1 -0
  60. package/dist/serve-ui/assets/useId-C0uR6mdj.js +1 -0
  61. package/dist/serve-ui/assets/useNonce-DTGvzOJz.js +1 -0
  62. package/dist/serve-ui/assets/useWatch-COpVIQvm.js +1 -0
  63. package/dist/serve-ui/favicon.svg +19 -0
  64. package/dist/serve-ui/for-developers-1.svg +65 -0
  65. package/dist/serve-ui/icon-color.svg +18 -0
  66. package/dist/serve-ui/index.html +17 -0
  67. package/dist/serve-ui/logo/color-icon-black-text.svg +65 -0
  68. package/dist/serve-ui/logo/color-icon-white-text.svg +61 -0
  69. package/dist/serve-ui/merge-1.svg +131 -0
  70. package/dist/serve-ui/merge-2.svg +161 -0
  71. package/dist/serve-ui/model-empty-state.svg +12 -0
  72. package/dist/serve-ui/select-data-type.svg +23 -0
  73. package/dist/server-DW0KXB0T.mjs +607 -0
  74. package/dist/status-kF5miVty.mjs +122 -0
  75. package/dist/ui-B5mXontH.mjs +33 -0
  76. package/dist/validate-DnVamp3p.mjs +155 -0
  77. package/package.json +66 -0
@@ -0,0 +1,415 @@
1
+ import { r as resolveProjectRoot, t as loadProjectContext } from "./context-An3xrvcP.mjs";
2
+ import { i as pc } from "./ui-B5mXontH.mjs";
3
+ import { defineCommand } from "citty";
4
+ import { confirm, intro, isCancel, log, multiselect, outro, select, spinner } from "@clack/prompts";
5
+ import { simpleGit } from "simple-git";
6
+ import { join } from "node:path";
7
+ import { writeModel } from "@contentrain/mcp/core/model-manager";
8
+ import { ensureDir, pathExists, writeJson } from "@contentrain/mcp/util/fs";
9
+ import { appendFile, readFile, writeFile } from "node:fs/promises";
10
+ import { detectStackInfo } from "@contentrain/mcp/util/detect";
11
+ import { getTemplate, listTemplates } from "@contentrain/mcp/templates";
12
+ import { buildBranchName, createTransaction } from "@contentrain/mcp/git/transaction";
13
+ import { scanSummary } from "@contentrain/mcp/core/scanner";
14
+ //#region src/commands/init.ts
15
+ const COMMON_LOCALES = [
16
+ {
17
+ value: "en",
18
+ label: "English"
19
+ },
20
+ {
21
+ value: "tr",
22
+ label: "Turkish"
23
+ },
24
+ {
25
+ value: "de",
26
+ label: "German"
27
+ },
28
+ {
29
+ value: "fr",
30
+ label: "French"
31
+ },
32
+ {
33
+ value: "es",
34
+ label: "Spanish"
35
+ },
36
+ {
37
+ value: "pt",
38
+ label: "Portuguese"
39
+ },
40
+ {
41
+ value: "it",
42
+ label: "Italian"
43
+ },
44
+ {
45
+ value: "nl",
46
+ label: "Dutch"
47
+ },
48
+ {
49
+ value: "ja",
50
+ label: "Japanese"
51
+ },
52
+ {
53
+ value: "ko",
54
+ label: "Korean"
55
+ },
56
+ {
57
+ value: "zh",
58
+ label: "Chinese"
59
+ },
60
+ {
61
+ value: "ar",
62
+ label: "Arabic"
63
+ },
64
+ {
65
+ value: "ru",
66
+ label: "Russian"
67
+ }
68
+ ];
69
+ var init_default = defineCommand({
70
+ meta: {
71
+ name: "init",
72
+ description: "Initialize Contentrain in your project"
73
+ },
74
+ args: {
75
+ root: {
76
+ type: "string",
77
+ description: "Project root path",
78
+ required: false
79
+ },
80
+ yes: {
81
+ type: "boolean",
82
+ description: "Skip prompts, use defaults",
83
+ required: false
84
+ }
85
+ },
86
+ async run({ args }) {
87
+ const projectRoot = await resolveProjectRoot(args.root);
88
+ intro(pc.bold("contentrain init"));
89
+ if ((await loadProjectContext(projectRoot)).initialized) {
90
+ log.warning("Project already initialized.");
91
+ log.message(`Run ${pc.cyan("contentrain status")} to see current state.`);
92
+ outro("");
93
+ return;
94
+ }
95
+ const s = spinner();
96
+ s.start("Detecting project stack...");
97
+ const info = await detectStackInfo(projectRoot);
98
+ s.stop(`Detected: ${pc.cyan(info.name)} — ${info.description}`);
99
+ if (info.monorepo) log.info(`Monorepo: ${pc.green("Yes")}${info.monorepoTool ? ` (${info.monorepoTool})` : ""}`);
100
+ if (info.features.length > 0) log.info(`Features: ${info.features.map((f) => pc.dim(f)).join(", ")}`);
101
+ if (args.yes) {
102
+ await executeInit(projectRoot, {
103
+ stack: info.stack,
104
+ locales: ["en"],
105
+ domains: [
106
+ "marketing",
107
+ "blog",
108
+ "system"
109
+ ],
110
+ workflow: "auto-merge",
111
+ template: null
112
+ });
113
+ outro(pc.green("Initialized with defaults!"));
114
+ return;
115
+ }
116
+ const flatStacks = [
117
+ {
118
+ group: "Meta-frameworks",
119
+ items: [
120
+ "nuxt",
121
+ "next",
122
+ "astro",
123
+ "sveltekit",
124
+ "remix",
125
+ "analog"
126
+ ]
127
+ },
128
+ {
129
+ group: "Frameworks",
130
+ items: [
131
+ "vue",
132
+ "react",
133
+ "svelte",
134
+ "solid",
135
+ "angular"
136
+ ]
137
+ },
138
+ {
139
+ group: "Mobile",
140
+ items: [
141
+ "react-native",
142
+ "expo",
143
+ "flutter"
144
+ ]
145
+ },
146
+ {
147
+ group: "Backend",
148
+ items: [
149
+ "node",
150
+ "express",
151
+ "fastify",
152
+ "nestjs",
153
+ "django",
154
+ "rails",
155
+ "laravel",
156
+ "go",
157
+ "rust",
158
+ "dotnet"
159
+ ]
160
+ },
161
+ {
162
+ group: "Static",
163
+ items: [
164
+ "hugo",
165
+ "jekyll",
166
+ "eleventy"
167
+ ]
168
+ },
169
+ {
170
+ group: "Desktop",
171
+ items: ["electron", "tauri"]
172
+ }
173
+ ].flatMap((g) => g.items);
174
+ const stackChoice = await select({
175
+ message: "Project framework",
176
+ options: [
177
+ {
178
+ value: info.stack,
179
+ label: `${info.name} (detected)`
180
+ },
181
+ ...flatStacks.filter((v) => v !== info.stack).map((v) => ({
182
+ value: v,
183
+ label: v
184
+ })),
185
+ {
186
+ value: "other",
187
+ label: "Other"
188
+ }
189
+ ],
190
+ initialValue: info.stack
191
+ });
192
+ if (isCancel(stackChoice)) return handleCancel();
193
+ const localeChoices = await multiselect({
194
+ message: "Supported locales",
195
+ options: COMMON_LOCALES.map((l) => ({
196
+ value: l.value,
197
+ label: `${l.label} (${l.value})`
198
+ })),
199
+ initialValues: ["en"],
200
+ required: true
201
+ });
202
+ if (isCancel(localeChoices)) return handleCancel();
203
+ const domainChoices = await multiselect({
204
+ message: "Content domains",
205
+ options: [
206
+ {
207
+ value: "marketing",
208
+ label: "Marketing — landing pages, CTAs, testimonials"
209
+ },
210
+ {
211
+ value: "blog",
212
+ label: "Blog — posts, categories, authors"
213
+ },
214
+ {
215
+ value: "docs",
216
+ label: "Docs — documentation, guides, API"
217
+ },
218
+ {
219
+ value: "system",
220
+ label: "System — error messages, labels, notifications"
221
+ },
222
+ {
223
+ value: "ecommerce",
224
+ label: "E-commerce — products, categories, reviews"
225
+ }
226
+ ],
227
+ initialValues: [
228
+ "marketing",
229
+ "blog",
230
+ "system"
231
+ ],
232
+ required: true
233
+ });
234
+ if (isCancel(domainChoices)) return handleCancel();
235
+ const workflowChoice = await select({
236
+ message: "Content workflow",
237
+ options: [{
238
+ value: "auto-merge",
239
+ label: "Auto-merge — changes apply immediately"
240
+ }, {
241
+ value: "review",
242
+ label: "Review — changes go to branches for review"
243
+ }],
244
+ initialValue: "auto-merge"
245
+ });
246
+ if (isCancel(workflowChoice)) return handleCancel();
247
+ const templateChoice = await select({
248
+ message: "Start with a template?",
249
+ options: [{
250
+ value: "none",
251
+ label: "Empty — just the base setup"
252
+ }, ...listTemplates().map((t) => ({
253
+ value: t,
254
+ label: `${t.charAt(0).toUpperCase() + t.slice(1)} template`
255
+ }))],
256
+ initialValue: "none"
257
+ });
258
+ if (isCancel(templateChoice)) return handleCancel();
259
+ s.start("Scanning project for hardcoded strings...");
260
+ try {
261
+ const summary = await scanSummary(projectRoot);
262
+ s.stop(`Found ~${pc.yellow(String(summary.total_candidates_estimate))} hardcoded strings in ${summary.total_files} files`);
263
+ } catch {
264
+ s.stop("Scan preview skipped (not a recognized project structure)");
265
+ }
266
+ const proceed = await confirm({ message: "Initialize Contentrain?" });
267
+ if (isCancel(proceed) || !proceed) return handleCancel();
268
+ await executeInit(projectRoot, {
269
+ stack: stackChoice,
270
+ locales: localeChoices,
271
+ domains: domainChoices,
272
+ workflow: workflowChoice,
273
+ template: templateChoice === "none" ? null : templateChoice
274
+ });
275
+ log.success("Project initialized!");
276
+ log.info(pc.bold("Next steps:"));
277
+ log.message(` ${pc.cyan("contentrain status")} — see your project overview`);
278
+ log.message(` ${pc.cyan("contentrain doctor")} — verify setup health`);
279
+ if (templateChoice === "none") log.message(` ${pc.cyan("contentrain serve")} — open the local review and normalize UI`);
280
+ log.message(` ${pc.cyan("contentrain generate")} — generate SDK client`);
281
+ outro("");
282
+ }
283
+ });
284
+ async function executeInit(projectRoot, opts) {
285
+ const s = spinner();
286
+ s.start("Initializing...");
287
+ if (!await pathExists(join(projectRoot, ".git"))) {
288
+ const git = simpleGit(projectRoot);
289
+ await git.init();
290
+ await git.add(".");
291
+ await git.commit("initial commit", { "--allow-empty": null });
292
+ }
293
+ const tx = await createTransaction(projectRoot, buildBranchName("new", "init"));
294
+ try {
295
+ await tx.write(async (wt) => {
296
+ const wtCrDir = join(wt, ".contentrain");
297
+ await Promise.all([
298
+ "models",
299
+ "content",
300
+ "meta"
301
+ ].map(async (dir) => {
302
+ await ensureDir(join(wtCrDir, dir));
303
+ await writeFile(join(wtCrDir, dir, ".gitkeep"), "", "utf-8");
304
+ }));
305
+ const config = {
306
+ version: 1,
307
+ stack: opts.stack,
308
+ workflow: opts.workflow,
309
+ locales: {
310
+ default: opts.locales[0] ?? "en",
311
+ supported: opts.locales
312
+ },
313
+ domains: opts.domains
314
+ };
315
+ await writeJson(join(wtCrDir, "config.json"), config);
316
+ await writeJson(join(wtCrDir, "vocabulary.json"), {
317
+ version: 1,
318
+ terms: {}
319
+ });
320
+ await writeJson(join(wtCrDir, "context.json"), {
321
+ version: "1",
322
+ lastOperation: {
323
+ tool: "contentrain_init",
324
+ model: "",
325
+ locale: opts.locales[0] ?? "en",
326
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
327
+ source: "mcp-local"
328
+ },
329
+ stats: {
330
+ models: 0,
331
+ entries: 0,
332
+ locales: opts.locales,
333
+ lastSync: (/* @__PURE__ */ new Date()).toISOString()
334
+ }
335
+ });
336
+ await updateGitignore(wt);
337
+ if (opts.template) {
338
+ const tmpl = getTemplate(opts.template);
339
+ if (tmpl) for (const model of tmpl.models) await writeModel(wt, model);
340
+ }
341
+ });
342
+ await tx.commit(`[contentrain] init: ${opts.stack} project setup`);
343
+ await tx.complete();
344
+ s.stop("Initialized");
345
+ await installRules(projectRoot);
346
+ } catch (error) {
347
+ s.stop("Failed");
348
+ throw error;
349
+ } finally {
350
+ await tx.cleanup();
351
+ }
352
+ }
353
+ async function installRules(projectRoot) {
354
+ try {
355
+ const ruleFiles = {
356
+ "claude-code": "ide/claude-code/contentrain.md",
357
+ "cursor": "ide/cursor/contentrain.cursorrules",
358
+ "windsurf": "ide/windsurf/contentrain.windsurfrules",
359
+ "generic": "ide/generic/contentrain.md"
360
+ };
361
+ const { createRequire } = await import("node:module");
362
+ const require = createRequire(import.meta.url);
363
+ const resolveRuleFile = (rulePath) => require.resolve(`@contentrain/rules/${rulePath}`);
364
+ const targets = [];
365
+ if (await pathExists(join(projectRoot, "CLAUDE.md")) || await pathExists(join(projectRoot, ".claude"))) targets.push({
366
+ ide: "Claude Code",
367
+ source: resolveRuleFile(ruleFiles["claude-code"]),
368
+ dest: join(projectRoot, "CLAUDE.md"),
369
+ append: true
370
+ });
371
+ if (await pathExists(join(projectRoot, ".cursorrules")) || await pathExists(join(projectRoot, ".cursor"))) targets.push({
372
+ ide: "Cursor",
373
+ source: resolveRuleFile(ruleFiles["cursor"]),
374
+ dest: join(projectRoot, ".cursorrules")
375
+ });
376
+ if (await pathExists(join(projectRoot, ".windsurf"))) targets.push({
377
+ ide: "Windsurf",
378
+ source: resolveRuleFile(ruleFiles["windsurf"]),
379
+ dest: join(projectRoot, ".windsurfrules")
380
+ });
381
+ if (targets.length === 0) targets.push({
382
+ ide: "Generic",
383
+ source: resolveRuleFile(ruleFiles["generic"]),
384
+ dest: join(projectRoot, "CLAUDE.md")
385
+ });
386
+ for (const target of targets) {
387
+ const ruleContent = await readFile(target.source, "utf-8");
388
+ const marker = "# Contentrain AI Rules";
389
+ if (target.append && await pathExists(target.dest)) {
390
+ if ((await readFile(target.dest, "utf-8")).includes(marker)) continue;
391
+ await appendFile(target.dest, `\n\n${ruleContent}\n`);
392
+ } else await writeFile(target.dest, ruleContent, "utf-8");
393
+ const git = simpleGit(projectRoot);
394
+ await git.add(target.dest);
395
+ try {
396
+ await git.commit(`[contentrain] install ${target.ide} rules`);
397
+ } catch {}
398
+ }
399
+ } catch {}
400
+ }
401
+ async function updateGitignore(projectRoot) {
402
+ const gitignorePath = join(projectRoot, ".gitignore");
403
+ const ignoreEntries = [".contentrain/.cache/", ".contentrain/client/"];
404
+ if (await pathExists(gitignorePath)) {
405
+ const content = await readFile(gitignorePath, "utf-8");
406
+ const missing = ignoreEntries.filter((e) => !content.includes(e));
407
+ if (missing.length > 0) await appendFile(gitignorePath, `\n# Contentrain\n${missing.join("\n")}\n`);
408
+ } else await writeFile(gitignorePath, `# Contentrain\n${ignoreEntries.join("\n")}\n`, "utf-8");
409
+ }
410
+ function handleCancel() {
411
+ outro(pc.dim("Cancelled"));
412
+ process.exit(0);
413
+ }
414
+ //#endregion
415
+ export { init_default as default };
@@ -0,0 +1,111 @@
1
+ import { r as resolveProjectRoot } from "./context-An3xrvcP.mjs";
2
+ import { defineCommand } from "citty";
3
+ import { createServer } from "@contentrain/mcp/server";
4
+ import consola from "consola";
5
+ //#region src/commands/serve.ts
6
+ var serve_default = defineCommand({
7
+ meta: {
8
+ name: "serve",
9
+ description: "Start local content viewer or MCP stdio server"
10
+ },
11
+ args: {
12
+ root: {
13
+ type: "string",
14
+ description: "Project root path",
15
+ required: false
16
+ },
17
+ stdio: {
18
+ type: "boolean",
19
+ description: "Use stdio MCP transport (for IDE integration)",
20
+ required: false
21
+ },
22
+ port: {
23
+ type: "string",
24
+ description: "HTTP server port (default: 3333)",
25
+ required: false
26
+ },
27
+ open: {
28
+ type: "boolean",
29
+ description: "Open browser automatically",
30
+ required: false
31
+ },
32
+ host: {
33
+ type: "string",
34
+ description: "Bind address (default: localhost)",
35
+ required: false
36
+ }
37
+ },
38
+ async run({ args }) {
39
+ const projectRoot = await resolveProjectRoot(args.root);
40
+ process.env["CONTENTRAIN_PROJECT_ROOT"] = projectRoot;
41
+ process.env["CONTENTRAIN_SOURCE"] = "mcp-local";
42
+ try {
43
+ const { simpleGit } = await import("simple-git");
44
+ const config = await simpleGit(projectRoot).listConfig();
45
+ const name = config.all["user.name"];
46
+ const email = config.all["user.email"];
47
+ if (name && typeof name === "string") process.env["CONTENTRAIN_AUTHOR_NAME"] = name;
48
+ if (email && typeof email === "string") process.env["CONTENTRAIN_AUTHOR_EMAIL"] = email;
49
+ } catch {}
50
+ if (args.stdio) {
51
+ const server = createServer(projectRoot);
52
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
53
+ const transport = new StdioServerTransport();
54
+ consola.info(`Contentrain MCP server starting (stdio) — ${projectRoot}`);
55
+ await server.connect(transport);
56
+ return;
57
+ }
58
+ const port = Number(args.port) || 3333;
59
+ const host = args.host ?? "localhost";
60
+ const shouldOpen = args.open !== false;
61
+ const { join, dirname } = await import("node:path");
62
+ const { existsSync } = await import("node:fs");
63
+ const { fileURLToPath } = await import("node:url");
64
+ const uiDir = join(dirname(fileURLToPath(import.meta.url)), "serve-ui");
65
+ if (!existsSync(uiDir)) {
66
+ consola.warn("Serve UI not found. Running in API-only mode.");
67
+ consola.info("UI will be available after building: cd packages/cli/src/serve-ui && pnpm build");
68
+ }
69
+ const { createServeApp } = await import("./server-DW0KXB0T.mjs");
70
+ const { app, handleUpgrade } = await createServeApp({
71
+ projectRoot,
72
+ port,
73
+ uiDir
74
+ });
75
+ const { createServer: createHttpServer } = await import("node:http");
76
+ const { toNodeListener } = await import("h3");
77
+ const h3Listener = toNodeListener(app);
78
+ const httpServer = createHttpServer((req, res) => {
79
+ if (req.url === "/ws") {
80
+ if (!req.headers.upgrade) {
81
+ res.writeHead(400);
82
+ res.end("WebSocket upgrade required");
83
+ }
84
+ return;
85
+ }
86
+ h3Listener(req, res);
87
+ });
88
+ httpServer.on("upgrade", (req, socket, head) => {
89
+ handleUpgrade(req, socket, head);
90
+ });
91
+ httpServer.listen(port, host, () => {
92
+ const url = `http://${host}:${port}`;
93
+ consola.box({
94
+ title: "Contentrain Serve",
95
+ message: [
96
+ `Local: ${url}`,
97
+ `Watching: .contentrain/`,
98
+ `Root: ${projectRoot}`,
99
+ "",
100
+ "Press Ctrl+C to stop"
101
+ ].join("\n")
102
+ });
103
+ if (shouldOpen) import("node:child_process").then(({ exec }) => {
104
+ exec(`${process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open"} ${url}`);
105
+ });
106
+ });
107
+ if (host === "0.0.0.0") consola.warn("Server is accessible from the network. No authentication is configured.");
108
+ }
109
+ });
110
+ //#endregion
111
+ export { serve_default as default };
@@ -0,0 +1,16 @@
1
+ import{c as i,a as p,o as t,p as o,q as s,j as u,u as a,B as n,g as m,b as l,r as x,E as d,f as g}from"./index-CoUshYEB.js";/**
2
+ * @license lucide-vue-next v0.474.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const h=i("BotIcon",[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]]);/**
7
+ * @license lucide-vue-next v0.474.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const f=i("CheckIcon",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);/**
12
+ * @license lucide-vue-next v0.474.0 - ISC
13
+ *
14
+ * This source code is licensed under the ISC license.
15
+ * See the LICENSE file in the root directory of this source tree.
16
+ */const k=i("CopyIcon",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]),_={class:"flex size-7 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary transition-colors group-hover:bg-primary/15"},b={class:"flex-1 min-w-0"},v={key:0,class:"block text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60 mb-0.5"},C={class:"block text-xs text-foreground/80 leading-relaxed"},w={class:"shrink-0 mt-0.5 text-muted-foreground/40 transition-colors group-hover:text-primary/60"},P=p({__name:"AgentPrompt",props:{prompt:{},label:{}},setup(e){const c=e,r=x(!1);async function y(){try{await navigator.clipboard.writeText(c.prompt),r.value=!0,d.success("Prompt copied — paste it in your AI agent"),setTimeout(()=>{r.value=!1},2e3)}catch{d.error("Failed to copy prompt")}}return(I,V)=>(t(),o("button",{class:"group flex w-full items-start gap-3 rounded-lg border border-border/60 bg-card px-3.5 py-2.5 text-left transition-all hover:border-primary/30 hover:bg-primary/[0.03] active:scale-[0.995]",onClick:y},[s("div",_,[u(a(h),{class:"size-3.5"})]),s("div",b,[e.label?(t(),o("span",v,n(e.label),1)):m("",!0),s("span",C,n(e.prompt),1)]),s("div",w,[r.value?(t(),l(a(f),{key:0,class:"size-3.5 text-status-success"})):(t(),l(a(k),{key:1,class:"size-3.5"}))])]))}}),z={class:"space-y-2"},B={key:0,class:"flex items-center gap-2 text-xs font-medium text-muted-foreground"},M={class:"grid gap-1.5"},j=p({__name:"AgentPromptGroup",props:{title:{}},setup(e){return(c,r)=>(t(),o("div",z,[e.title?(t(),o("div",B,[u(a(h),{class:"size-3.5"}),s("span",null,n(e.title),1)])):m("",!0),s("div",M,[g(c.$slots,"default")])]))}});export{f as C,j as _,P as a,k as b};
@@ -0,0 +1,16 @@
1
+ import{c as re,d as Ae,r as w,w as F,a as E,t as le,o as v,b as g,e as b,f as C,n as G,u as t,m as A,g as N,h as x,i as z,j as k,k as X,l as Y,p as $,q as S,s as M,F as K,v as J,x as ae,R as ve,y as V,z as B,A as he,B as P,C as Le,D as de}from"./index-CoUshYEB.js";import{u as Pe,S as $e,_ as Re}from"./Input.vue_vue_type_script_setup_true_lang-otPltlZG.js";import{u as Z}from"./project-B1qSm0Fz.js";import{_ as oe}from"./index-WHgvdYOs.js";import{B as be}from"./box-7olVZr5D.js";import{F as me}from"./file-text-BDHXQ95W.js";import{S as _e}from"./shield-check-CUL7KkSA.js";import{G as ge}from"./git-branch-DdE7bk9-.js";import{S as Te}from"./scan-search-DGPL_VJu.js";import{u as R,c as ne,a as q,P as Q,b as se,d as Se,w as De}from"./Presence-cbB6AqkQ.js";import{u as Ie}from"./useDirection-Cd3C7VdT.js";import{P as W,r as ye,d as ie,u as we}from"./useWatch-COpVIQvm.js";import{c as ze,u as He}from"./useNonce-DTGvzOJz.js";import"./ConfigProvider-BGjDLViI.js";/**
2
+ * @license lucide-vue-next v0.474.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const xe=re("LayoutDashboardIcon",[["rect",{width:"7",height:"9",x:"3",y:"3",rx:"1",key:"10lvy0"}],["rect",{width:"7",height:"5",x:"14",y:"3",rx:"1",key:"16une8"}],["rect",{width:"7",height:"9",x:"14",y:"12",rx:"1",key:"1hutg5"}],["rect",{width:"7",height:"5",x:"3",y:"16",rx:"1",key:"ldoo1y"}]]);/**
7
+ * @license lucide-vue-next v0.474.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const Oe=re("MoonIcon",[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z",key:"a7tn18"}]]);/**
12
+ * @license lucide-vue-next v0.474.0 - ISC
13
+ *
14
+ * This source code is licensed under the ISC license.
15
+ * See the LICENSE file in the root directory of this source tree.
16
+ */const Ne=re("SunIcon",[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]]),Be="/icon-color.svg",Me="/logo/color-icon-black-text.svg",Ve="/logo/color-icon-white-text.svg",qe=Ae("ui",()=>{const u=Pe({attribute:"class"}),e=w(!1),o=w(JSON.parse(localStorage.getItem("cr-dismissed-hints")??"{}"));function a(){u.value=u.value==="dark"?"light":"dark"}function n(l){o.value[l]=Date.now(),localStorage.setItem("cr-dismissed-hints",JSON.stringify(o.value))}function s(l){const r=o.value[l];if(!r)return!1;const c=10080*60*1e3;return Date.now()-r<c}return F(e,l=>{localStorage.setItem("cr-sidebar-collapsed",String(l))}),localStorage.getItem("cr-sidebar-collapsed")==="true"&&(e.value=!0),{colorMode:u,sidebarCollapsed:e,toggleTheme:a,dismissHint:n,isHintDismissed:s}}),[T,We]=ne("ScrollAreaRoot");var Xe=E({__name:"ScrollAreaRoot",props:{type:{type:String,required:!1,default:"hover"},dir:{type:String,required:!1},scrollHideDelay:{type:Number,required:!1,default:600},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},setup(u,{expose:e}){const o=u,a=w(0),n=w(0),s=w(),i=w(),l=w(),r=w(),c=w(!1),d=w(!1),{type:m,dir:f,scrollHideDelay:y}=le(o),L=Ie(f);function D(){var h;(h=s.value)==null||h.scrollTo({top:0})}function I(){var h;(h=s.value)==null||h.scrollTo({top:0,left:0})}e({viewport:s,scrollTop:D,scrollTopLeft:I});const{forwardRef:p,currentElement:_}=R();return We({type:m,dir:L,scrollHideDelay:y,scrollArea:_,viewport:s,onViewportChange:h=>{s.value=h||void 0},content:i,onContentChange:h=>{i.value=h},scrollbarX:l,scrollbarXEnabled:c,scrollbarY:r,scrollbarYEnabled:d,onScrollbarXChange:h=>{l.value=h||void 0},onScrollbarYChange:h=>{r.value=h||void 0},onScrollbarXEnabledChange:h=>{c.value=h},onScrollbarYEnabledChange:h=>{d.value=h},onCornerWidthChange:h=>{a.value=h},onCornerHeightChange:h=>{n.value=h}}),(h,H)=>(v(),g(t(W),{ref:t(p),"as-child":o.asChild,as:h.as,dir:t(L),style:G({position:"relative","--reka-scroll-area-corner-width":`${a.value}px`,"--reka-scroll-area-corner-height":`${n.value}px`})},{default:b(()=>[C(h.$slots,"default")]),_:3},8,["as-child","as","dir","style"]))}}),Ye=Xe,je=E({__name:"ScrollAreaCornerImpl",setup(u){const e=T(),o=w(0),a=w(0),n=x(()=>!!o.value&&!!a.value);function s(){var r;const l=((r=e.scrollbarX.value)==null?void 0:r.offsetHeight)||0;e.onCornerHeightChange(l),a.value=l}function i(){var r;const l=((r=e.scrollbarY.value)==null?void 0:r.offsetWidth)||0;e.onCornerWidthChange(l),o.value=l}return q(e.scrollbarX.value,s),q(e.scrollbarY.value,i),F(()=>e.scrollbarX.value,s),F(()=>e.scrollbarY.value,i),(l,r)=>{var c;return n.value?(v(),g(t(W),A({key:0,style:{width:`${o.value}px`,height:`${a.value}px`,position:"absolute",right:t(e).dir.value==="ltr"?0:void 0,left:t(e).dir.value==="rtl"?0:void 0,bottom:0}},(c=l.$parent)==null?void 0:c.$props),{default:b(()=>[C(l.$slots,"default")]),_:3},16,["style"])):N("v-if",!0)}}}),Ue=je,Fe=E({__name:"ScrollAreaCorner",props:{asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},setup(u){const e=u,{forwardRef:o}=R(),a=T(),n=x(()=>!!a.scrollbarX.value&&!!a.scrollbarY.value),s=x(()=>a.type.value!=="scroll"&&n.value);return(i,l)=>s.value?(v(),g(Ue,A({key:0},e,{ref:t(o)}),{default:b(()=>[C(i.$slots,"default")]),_:3},16)):N("v-if",!0)}}),Ge=Fe;function Ce(u,e){return o=>{if(u[0]===u[1]||e[0]===e[1])return e[0];const a=(e[1]-e[0])/(u[1]-u[0]);return e[0]+a*(o-u[0])}}function ee(u){const e=ke(u.viewport,u.content),o=u.scrollbar.paddingStart+u.scrollbar.paddingEnd,a=(u.scrollbar.size-o)*e;return Math.max(a,18)}function ke(u,e){const o=u/e;return Number.isNaN(o)?0:o}function Ke(u,e=()=>{}){let o={left:u.scrollLeft,top:u.scrollTop},a=0;return(function n(){const s={left:u.scrollLeft,top:u.scrollTop},i=o.left!==s.left,l=o.top!==s.top;(i||l)&&e(),o=s,a=window.requestAnimationFrame(n)})(),()=>window.cancelAnimationFrame(a)}function pe(u,e,o="ltr"){const a=ee(e),n=e.scrollbar.paddingStart+e.scrollbar.paddingEnd,s=e.scrollbar.size-n,i=e.content-e.viewport,l=s-a,r=o==="ltr"?[0,i]:[i*-1,0],c=ze(u,r[0],r[1]);return Ce([0,i],[0,l])(c)}function U(u){return u?Number.parseInt(u,10):0}function Je(u,e,o,a="ltr"){const n=ee(o),s=n/2,i=e||s,l=n-i,r=o.scrollbar.paddingStart+i,c=o.scrollbar.size-o.scrollbar.paddingEnd-l,d=o.content-o.viewport,m=a==="ltr"?[0,d]:[d*-1,0];return Ce([r,c],m)(u)}function fe(u,e){return u>0&&u<e}var Ze=E({__name:"ScrollAreaScrollbarX",setup(u){const e=T(),o=te(),{forwardRef:a,currentElement:n}=R();z(()=>{n.value&&e.onScrollbarXChange(n.value)});const s=x(()=>o.sizes.value);return(i,l)=>(v(),g(Ee,{ref:t(a),"is-horizontal":!0,"data-orientation":"horizontal",style:G({bottom:0,left:t(e).dir.value==="rtl"?"var(--reka-scroll-area-corner-width)":0,right:t(e).dir.value==="ltr"?"var(--reka-scroll-area-corner-width)":0,"--reka-scroll-area-thumb-width":s.value?`${t(ee)(s.value)}px`:void 0}),onOnDragScroll:l[0]||(l[0]=r=>t(o).onDragScroll(r.x))},{default:b(()=>[C(i.$slots,"default")]),_:3},8,["style"]))}}),Qe=Ze,et=E({__name:"ScrollAreaScrollbarY",setup(u){const e=T(),o=te(),{forwardRef:a,currentElement:n}=R();z(()=>{n.value&&e.onScrollbarYChange(n.value)});const s=x(()=>o.sizes.value);return(i,l)=>(v(),g(Ee,{ref:t(a),"is-horizontal":!1,"data-orientation":"vertical",style:G({top:0,right:t(e).dir.value==="ltr"?0:void 0,left:t(e).dir.value==="rtl"?0:void 0,bottom:"var(--reka-scroll-area-corner-height)","--reka-scroll-area-thumb-height":s.value?`${t(ee)(s.value)}px`:void 0}),onOnDragScroll:l[0]||(l[0]=r=>t(o).onDragScroll(r.y))},{default:b(()=>[C(i.$slots,"default")]),_:3},8,["style"]))}}),tt=et,ot=E({__name:"ScrollAreaScrollbarAuto",props:{forceMount:{type:Boolean,required:!1}},setup(u){const e=T(),o=j(),{forwardRef:a}=R(),n=w(!1),s=se(()=>{if(e.viewport.value){const i=e.viewport.value.offsetWidth<e.viewport.value.scrollWidth,l=e.viewport.value.offsetHeight<e.viewport.value.scrollHeight;n.value=o.isHorizontal.value?i:l}},10);return z(()=>s()),q(e.viewport,s),q(e.content,s),(i,l)=>(v(),g(t(Q),{present:i.forceMount||n.value},{default:b(()=>[k(ue,A(i.$attrs,{ref:t(a),"data-state":n.value?"visible":"hidden"}),{default:b(()=>[C(i.$slots,"default")]),_:3},16,["data-state"])]),_:3},8,["present"]))}}),ce=ot,rt=E({inheritAttrs:!1,__name:"ScrollAreaScrollbarGlimpse",props:{forceMount:{type:Boolean,required:!1}},setup(u){const e=T(),o=j(),{forwardRef:a}=R(),{state:n,dispatch:s}=Se("hidden",{hidden:{POINTER_ENTER:"glimpse",SCROLL:"scrolling"},glimpse:{HIDE:"hidden",POINTER_LEAVE:"hidden",SCROLL:"scrolling",POINTER_ENTER:"glimpse"},scrolling:{SCROLL_END:"idle",POINTER_ENTER:"interacting"},interacting:{SCROLL:"interacting",POINTER_LEAVE:"idle"},idle:{HIDE:"hidden",SCROLL:"scrolling",POINTER_ENTER:"interacting"}}),i=x(()=>n.value!=="hidden");function l(){s("POINTER_ENTER")}function r(){s("POINTER_LEAVE")}const c=se(()=>s("SCROLL_END"),100);return X(d=>{if(n.value==="glimpse"){const m=window.setTimeout(()=>s("HIDE"),e.scrollHideDelay.value);d(()=>{window.clearTimeout(m)})}}),X(d=>{if(n.value==="idle"){const m=window.setTimeout(()=>s("HIDE"),e.scrollHideDelay.value);d(()=>{window.clearTimeout(m)})}}),X(d=>{const m=e.viewport.value,f=o.isHorizontal.value?"scrollLeft":"scrollTop";if(m){let y=m[f];const L=()=>{const D=m[f];y!==D&&(s("SCROLL"),c()),y=D};m.addEventListener("scroll",L),d(()=>{m.removeEventListener("scroll",L)})}}),z(()=>{const d=e.scrollArea.value;d&&(d.addEventListener("pointerenter",l),d.addEventListener("pointerleave",r))}),Y(()=>{const d=e.scrollArea.value;d&&(d.removeEventListener("pointerenter",l),d.removeEventListener("pointerleave",r))}),(d,m)=>(v(),g(t(Q),{present:d.forceMount||i.value},{default:b(()=>[k(ce,A(d.$attrs,{ref:t(a),"data-state":i.value?"visible":"hidden"}),{default:b(()=>[C(d.$slots,"default")]),_:3},16,["data-state"])]),_:3},8,["present"]))}}),lt=rt,at=E({inheritAttrs:!1,__name:"ScrollAreaScrollbarHover",props:{forceMount:{type:Boolean,required:!1}},setup(u){const e=T(),{forwardRef:o}=R();let a;const n=w(!1);function s(){window.clearTimeout(a),n.value=!0}function i(){a=window.setTimeout(()=>{n.value=!1},e.scrollHideDelay.value)}return z(()=>{const l=e.scrollArea.value;l&&(l.addEventListener("pointerenter",s),l.addEventListener("pointerleave",i))}),Y(()=>{const l=e.scrollArea.value;l&&(window.clearTimeout(a),l.removeEventListener("pointerenter",s),l.removeEventListener("pointerleave",i))}),(l,r)=>(v(),g(t(Q),{present:l.forceMount||n.value},{default:b(()=>[k(ce,A(l.$attrs,{ref:t(o),"data-state":n.value?"visible":"hidden"}),{default:b(()=>[C(l.$slots,"default")]),_:3},16,["data-state"])]),_:3},8,["present"]))}}),nt=at,st=E({__name:"ScrollAreaScrollbarScroll",props:{forceMount:{type:Boolean,required:!1}},setup(u){const e=T(),o=j(),{forwardRef:a}=R(),{state:n,dispatch:s}=Se("hidden",{hidden:{SCROLL:"scrolling"},scrolling:{SCROLL_END:"idle",POINTER_ENTER:"interacting"},interacting:{SCROLL:"interacting",POINTER_LEAVE:"idle"},idle:{HIDE:"hidden",SCROLL:"scrolling",POINTER_ENTER:"interacting"}}),i=x(()=>n.value!=="hidden");X(r=>{if(n.value==="idle"){const c=window.setTimeout(()=>s("HIDE"),e.scrollHideDelay.value);r(()=>{window.clearTimeout(c)})}});const l=se(()=>s("SCROLL_END"),100);return X(r=>{const c=e.viewport.value,d=o.isHorizontal.value?"scrollLeft":"scrollTop";if(c){let m=c[d];const f=()=>{const y=c[d];m!==y&&(s("SCROLL"),l()),m=y};c.addEventListener("scroll",f),r(()=>{c.removeEventListener("scroll",f)})}}),(r,c)=>(v(),g(t(Q),{present:r.forceMount||i.value},{default:b(()=>[k(ue,A(r.$attrs,{ref:t(a),"data-state":i.value?"visible":"hidden"}),{default:b(()=>[C(r.$slots,"default")]),_:3},16,["data-state"])]),_:3},8,["present"]))}}),it=st;const[j,ct]=ne("ScrollAreaScrollbar");var ut=E({inheritAttrs:!1,__name:"ScrollAreaScrollbar",props:{orientation:{type:String,required:!1,default:"vertical"},forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:"div"}},setup(u){const e=u,{forwardRef:o}=R(),a=T(),n=x(()=>e.orientation==="horizontal");F(n,()=>{n.value?a.onScrollbarXEnabledChange(!0):a.onScrollbarYEnabledChange(!0)},{immediate:!0}),Y(()=>{a.onScrollbarXEnabledChange(!1),a.onScrollbarYEnabledChange(!1)});const{orientation:s,forceMount:i,asChild:l,as:r}=le(e);return ct({orientation:s,forceMount:i,isHorizontal:n,as:r,asChild:l}),(c,d)=>t(a).type.value==="hover"?(v(),g(nt,A({key:0},c.$attrs,{ref:t(o),"force-mount":t(i)}),{default:b(()=>[C(c.$slots,"default")]),_:3},16,["force-mount"])):t(a).type.value==="scroll"?(v(),g(it,A({key:1},c.$attrs,{ref:t(o),"force-mount":t(i)}),{default:b(()=>[C(c.$slots,"default")]),_:3},16,["force-mount"])):t(a).type.value==="glimpse"?(v(),g(lt,A({key:2},c.$attrs,{ref:t(o),"force-mount":t(i)}),{default:b(()=>[C(c.$slots,"default")]),_:3},16,["force-mount"])):t(a).type.value==="auto"?(v(),g(ce,A({key:3},c.$attrs,{ref:t(o),"force-mount":t(i)}),{default:b(()=>[C(c.$slots,"default")]),_:3},16,["force-mount"])):t(a).type.value==="always"?(v(),g(ue,A({key:4},c.$attrs,{ref:t(o),"data-state":"visible"}),{default:b(()=>[C(c.$slots,"default")]),_:3},16)):N("v-if",!0)}}),dt=ut;const[te,pt]=ne("ScrollAreaScrollbarVisible");var ft=E({__name:"ScrollAreaScrollbarVisible",setup(u){const e=T(),o=j(),{forwardRef:a}=R(),n=w({content:0,viewport:0,scrollbar:{size:0,paddingStart:0,paddingEnd:0}}),s=x(()=>{const p=ke(n.value.viewport,n.value.content);return p>0&&p<1}),i=w(),l=w(0);function r(p,_){if(y.value){const h=e.viewport.value.scrollLeft+p.deltaY;e.viewport.value.scrollLeft=h,fe(h,_)&&p.preventDefault()}else{const h=e.viewport.value.scrollTop+p.deltaY;e.viewport.value.scrollTop=h,fe(h,_)&&p.preventDefault()}}function c(p,_){y.value?l.value=_.x:l.value=_.y}function d(p){l.value=0}function m(p){n.value=p}function f(p,_){return Je(p,l.value,n.value,_)}const y=x(()=>o.isHorizontal.value);function L(p){y.value?e.viewport.value.scrollLeft=f(p,e.dir.value):e.viewport.value.scrollTop=f(p)}function D(){if(y.value){if(e.viewport.value&&i.value){const p=e.viewport.value.scrollLeft,_=pe(p,n.value,e.dir.value);i.value.style.transform=`translate3d(${_}px, 0, 0)`}}else if(e.viewport.value&&i.value){const p=e.viewport.value.scrollTop,_=pe(p,n.value);i.value.style.transform=`translate3d(0, ${_}px, 0)`}}function I(p){i.value=p}return pt({sizes:n,hasThumb:s,handleWheelScroll:r,handleThumbDown:c,handleThumbUp:d,handleSizeChange:m,onThumbPositionChange:D,onThumbChange:I,onDragScroll:L}),(p,_)=>y.value?(v(),g(Qe,A({key:0},p.$attrs,{ref:t(a)}),{default:b(()=>[C(p.$slots,"default")]),_:3},16)):(v(),g(tt,A({key:1},p.$attrs,{ref:t(a)}),{default:b(()=>[C(p.$slots,"default")]),_:3},16))}}),ue=ft,vt=E({__name:"ScrollAreaScrollbarImpl",props:{isHorizontal:{type:Boolean,required:!0}},emits:["onDragScroll","onWheelScroll","onThumbPointerDown"],setup(u,{emit:e}){const o=u,a=e,n=T(),s=te(),i=j(),{forwardRef:l,currentElement:r}=R(),c=w(""),d=w();function m(p){var _,h;if(d.value){const H=p.clientX-((_=d.value)==null?void 0:_.left),O=p.clientY-((h=d.value)==null?void 0:h.top);a("onDragScroll",{x:H,y:O})}}function f(p){p.button===0&&(p.target.setPointerCapture(p.pointerId),d.value=r.value.getBoundingClientRect(),c.value=document.body.style.webkitUserSelect,document.body.style.webkitUserSelect="none",n.viewport&&(n.viewport.value.style.scrollBehavior="auto"),m(p))}function y(p){m(p)}function L(p){const _=p.target;_.hasPointerCapture(p.pointerId)&&_.releasePointerCapture(p.pointerId),document.body.style.webkitUserSelect=c.value,n.viewport&&(n.viewport.value.style.scrollBehavior=""),d.value=void 0}function D(p){var O;const _=p.target,h=(O=r.value)==null?void 0:O.contains(_),H=s.sizes.value.content-s.sizes.value.viewport;h&&s.handleWheelScroll(p,H)}z(()=>{document.addEventListener("wheel",D,{passive:!1})}),Y(()=>{document.removeEventListener("wheel",D)});function I(){var p,_,h,H,O;r.value&&(o.isHorizontal?s.handleSizeChange({content:((p=n.viewport.value)==null?void 0:p.scrollWidth)??0,viewport:((_=n.viewport.value)==null?void 0:_.offsetWidth)??0,scrollbar:{size:r.value.clientWidth??0,paddingStart:U(getComputedStyle(r.value).paddingLeft),paddingEnd:U(getComputedStyle(r.value).paddingRight)}}):s.handleSizeChange({content:((h=n.viewport.value)==null?void 0:h.scrollHeight)??0,viewport:((H=n.viewport.value)==null?void 0:H.offsetHeight)??0,scrollbar:{size:((O=r.value)==null?void 0:O.clientHeight)??0,paddingStart:U(getComputedStyle(r.value).paddingTop),paddingEnd:U(getComputedStyle(r.value).paddingBottom)}}),s.onThumbPositionChange())}return q(r,I),q(n.content,I),(p,_)=>(v(),g(t(W),{ref:t(l),style:{position:"absolute"},"data-scrollbarimpl":"",as:t(i).as.value,"as-child":t(i).asChild.value,onPointerdown:f,onPointermove:y,onPointerup:L},{default:b(()=>[C(p.$slots,"default")]),_:3},8,["as","as-child"]))}}),Ee=vt,ht=E({__name:"ScrollAreaThumb",props:{asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},setup(u){const e=u,o=T(),a=te();function n(f){const L=f.target.getBoundingClientRect(),D=f.clientX-L.left,I=f.clientY-L.top;a.handleThumbDown(f,{x:D,y:I})}function s(f){a.handleThumbUp(f)}const{forwardRef:i,currentElement:l}=R(),r=w(),c=x(()=>o.viewport.value);function d(){if(!r.value){const f=Ke(c.value,a.onThumbPositionChange);r.value=f,a.onThumbPositionChange()}}const m=x(()=>a.sizes.value);return De(m,()=>{a.onThumbChange(l.value),c.value&&(a.onThumbPositionChange(),c.value.addEventListener("scroll",d))}),Y(()=>{var f;c.value.removeEventListener("scroll",d),(f=o.viewport.value)==null||f.removeEventListener("scroll",d)}),(f,y)=>(v(),g(t(W),{ref:t(i),"data-state":t(a).hasThumb?"visible":"hidden",style:{width:"var(--reka-scroll-area-thumb-width)",height:"var(--reka-scroll-area-thumb-height)"},"as-child":e.asChild,as:f.as,onPointerdown:n,onPointerup:s},{default:b(()=>[C(f.$slots,"default")]),_:3},8,["data-state","as-child","as"]))}}),bt=ht,mt=E({inheritAttrs:!1,__name:"ScrollAreaViewport",props:{nonce:{type:String,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},setup(u,{expose:e}){const o=u,{nonce:a}=le(o),n=He(a),s=T(),i=w();z(()=>{s.onViewportChange(i.value),s.onContentChange(r.value)}),e({viewportElement:i});const{forwardRef:l,currentElement:r}=R();return(c,d)=>(v(),$(K,null,[S("div",A({ref_key:"viewportElement",ref:i,"data-reka-scroll-area-viewport":"",style:{overflowX:t(s).scrollbarXEnabled.value?"scroll":"hidden",overflowY:t(s).scrollbarYEnabled.value?"scroll":"hidden"}},c.$attrs,{tabindex:0}),[k(t(W),{ref:t(l),style:G({minWidth:t(s).scrollbarXEnabled.value?"fit-content":void 0}),"as-child":o.asChild,as:c.as},{default:b(()=>[C(c.$slots,"default")]),_:3},8,["style","as-child","as"])],16),k(t(W),{as:"style",nonce:t(n)},{default:b(()=>d[0]||(d[0]=[M(" /* Hide scrollbars cross-browser and enable momentum scroll for touch devices */ [data-reka-scroll-area-viewport] { scrollbar-width:none; -ms-overflow-style:none; -webkit-overflow-scrolling:touch; } [data-reka-scroll-area-viewport]::-webkit-scrollbar { display:none; } ")])),_:1,__:[0]},8,["nonce"])],64))}}),_t=mt;const gt={class:"group flex h-screen w-18 min-w-18 flex-col overflow-hidden border-r border-border bg-card transition-all duration-200 ease-in-out hover:w-55 hover:min-w-55 xl:w-55 xl:min-w-55"},St={class:"flex flex-1 flex-col gap-1 px-3 pt-4"},yt={class:"truncate opacity-0 transition-opacity duration-200 group-hover:opacity-100 xl:opacity-100"},wt={class:"flex items-center gap-3 px-3 pb-4 mt-auto"},xt={class:"truncate opacity-0 transition-opacity duration-200 group-hover:opacity-100 xl:opacity-100"},Ct=E({__name:"PrimarySidebar",setup(u){const e=J(),o=qe(),a=Z(),n=[{icon:xe,label:"Dashboard",to:"/",exact:!0},{icon:be,label:"Models",to:"/models"},{icon:me,label:"Content",to:"/content"},{icon:_e,label:"Validate",to:"/validate"},{icon:ge,label:"Branches",to:"/branches"},{icon:Te,label:"Normalize",to:"/normalize"}];function s(r){return r.exact?e.path===r.to:e.path.startsWith(r.to)}const i=x(()=>{var r,c;return((c=(r=a.status)==null?void 0:r.branches)==null?void 0:c.unmerged)??0}),l=x(()=>{var c;const r=(c=a.status)==null?void 0:c.validation;return r?r.errors+r.warnings:0});return(r,c)=>(v(),$("aside",gt,[c[1]||(c[1]=S("div",{class:"flex h-14 items-center px-4 shrink-0"},[S("img",{src:Be,alt:"Contentrain",class:"size-8 shrink-0 block group-hover:hidden xl:hidden"}),S("img",{src:Me,alt:"Contentrain",class:"h-7 shrink-0 hidden group-hover:block xl:block dark:hidden"}),S("img",{src:Ve,alt:"Contentrain",class:"h-7 shrink-0 hidden dark:group-hover:block dark:xl:block"})],-1)),S("nav",St,[(v(),$(K,null,ae(n,d=>k(t(ve),{key:d.to,to:d.to,class:V(t(B)("flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm transition-all duration-150",s(d)?"bg-background text-primary font-medium shadow-sm border border-border":"text-muted-foreground hover:bg-background/60 hover:text-foreground"))},{default:b(()=>[(v(),g(he(d.icon),{class:"size-5 shrink-0"})),S("span",yt,P(d.label),1),d.to==="/branches"&&i.value>0?(v(),g(t(oe),{key:0,variant:"secondary",class:"ml-auto text-[10px] h-5 px-1.5 opacity-0 group-hover:opacity-100 xl:opacity-100 transition-opacity"},{default:b(()=>[M(P(i.value),1)]),_:1})):N("",!0),d.to==="/validate"&&l.value>0?(v(),g(t(oe),{key:1,class:"ml-auto text-[10px] h-5 px-1.5 bg-status-error/10 text-status-error border-0 opacity-0 group-hover:opacity-100 xl:opacity-100 transition-opacity"},{default:b(()=>[M(P(l.value),1)]),_:1})):N("",!0)]),_:2},1032,["to","class"])),64))]),S("div",wt,[S("button",{class:V(t(B)("flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm w-full transition-all duration-150","text-muted-foreground hover:bg-background/60 hover:text-foreground")),onClick:c[0]||(c[0]=d=>t(o).toggleTheme())},[t(o).colorMode==="dark"?(v(),g(t(Ne),{key:0,class:"size-5 shrink-0"})):(v(),g(t(Oe),{key:1,class:"size-5 shrink-0"})),S("span",xt,P(t(o).colorMode==="dark"?"Light mode":"Dark mode"),1)],2)])]))}}),kt=E({__name:"ScrollBar",props:{orientation:{default:"vertical"},forceMount:{type:Boolean},asChild:{type:Boolean},as:{},class:{type:[Boolean,null,String,Object,Array]}},setup(u){const e=u,o=ye(e,"class");return(a,n)=>(v(),g(t(dt),A({"data-slot":"scroll-area-scrollbar"},t(o),{class:t(B)("flex touch-none p-px transition-colors select-none",u.orientation==="vertical"&&"h-full w-2.5 border-l border-l-transparent",u.orientation==="horizontal"&&"h-2.5 flex-col border-t border-t-transparent",e.class)}),{default:b(()=>[k(t(bt),{"data-slot":"scroll-area-thumb",class:"bg-border relative flex-1 rounded-full"})]),_:1},16,["class"]))}}),Et=E({__name:"ScrollArea",props:{type:{},dir:{},scrollHideDelay:{},asChild:{type:Boolean},as:{},class:{type:[Boolean,null,String,Object,Array]}},setup(u){const e=u,o=ye(e,"class");return(a,n)=>(v(),g(t(Ye),A({"data-slot":"scroll-area"},t(o),{class:t(B)("relative",e.class)}),{default:b(()=>[k(t(_t),{"data-slot":"scroll-area-viewport",class:"focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"},{default:b(()=>[C(a.$slots,"default")]),_:3}),k(kt),k(t(Ge))]),_:3},16,["class"]))}}),At={class:"flex h-full"},Lt={class:"hidden w-55 shrink-0 flex-col border-r border-border sidebar-gradient lg:flex"},Pt={class:"p-3"},$t={class:"relative"},Rt=["onClick"],Tt={class:"truncate flex-1"},Dt={key:0,class:"px-3 py-6 text-center text-xs text-muted-foreground"},It={class:"flex-1 min-w-0 overflow-y-auto custom-scrollbar"},zt=E({__name:"SubSidebarLayout",props:{basePath:{},paramKey:{}},setup(u){const e=ie("serve-ui-texts").locale("en").get(),o=u,a=J(),n=Le(),s=Z(),i=w(""),l=x(()=>{var f;const d=((f=s.status)==null?void 0:f.models)??[];if(!i.value)return d;const m=i.value.toLowerCase();return d.filter(y=>y.id.toLowerCase().includes(m)||y.domain.toLowerCase().includes(m))}),r=x(()=>a.params[o.paramKey]),c={collection:"bg-primary/10 text-primary",singleton:"bg-status-info/10 text-status-info",document:"bg-status-success/10 text-status-success",dictionary:"bg-status-warning/10 text-status-warning"};return(d,m)=>(v(),$("div",At,[S("aside",Lt,[S("div",Pt,[S("div",$t,[k(t($e),{class:"absolute left-2.5 top-2.5 size-3.5 text-muted-foreground"}),k(t(Re),{modelValue:i.value,"onUpdate:modelValue":m[0]||(m[0]=f=>i.value=f),placeholder:t(e)["sub-sidebar-layout.search"],class:"h-8 bg-background pl-8 text-xs"},null,8,["modelValue","placeholder"])])]),k(t(Et),{class:"flex-1 px-1.5"},{default:b(()=>[(v(!0),$(K,null,ae(l.value,f=>{var y;return v(),$("button",{key:f.id,class:V(t(B)("flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm transition-colors",r.value===f.id?"bg-background font-medium text-primary shadow-sm border border-border":"text-muted-foreground hover:bg-background/60 hover:text-foreground")),onClick:L=>t(n).push(`${u.basePath}/${f.id}`)},[S("span",{class:V(t(B)("size-1.5 rounded-full shrink-0",((y=c[f.kind])==null?void 0:y.split(" ")[0])??"bg-muted"))},null,2),S("span",Tt,P(f.id),1),k(t(oe),{variant:"secondary",class:"text-[10px] shrink-0 h-4 px-1"},{default:b(()=>[M(P(f.kind==="singleton"?"1":f.fields),1)]),_:2},1024)],10,Rt)}),128)),l.value.length===0?(v(),$("div",Dt,P(t(e)["sub-sidebar-layout.no-models-found"]),1)):N("",!0)]),_:1})]),S("div",It,[C(d.$slots,"default")])]))}}),Ht={class:"flex items-center gap-4 border-t border-border bg-background px-6 py-1.5 text-xs text-muted-foreground"},Ot={class:"flex items-center gap-1.5"},Nt={key:0,class:"ml-auto"},Bt=E({__name:"StatusBar",setup(u){const e=ie("serve-ui-texts").locale("en").get(),o=Z(),{connected:a,lastUpdate:n}=we(),s=x(()=>{var r;return((r=o.status)==null?void 0:r.models.length)??0}),i=x(()=>{var r,c;return((c=(r=o.status)==null?void 0:r.config)==null?void 0:c.locales.supported.length)??0}),l=x(()=>{if(!n.value)return"";const r=Date.now()-n.value.getTime();return r<5e3?"just now":r<6e4?`${Math.floor(r/1e3)}s ago`:r<36e5?`${Math.floor(r/6e4)}m ago`:n.value.toLocaleTimeString()});return(r,c)=>(v(),$("footer",Ht,[S("span",Ot,[S("span",{class:V(["size-1.5 rounded-full",t(a)?"bg-status-success":"bg-status-error"])},null,2),M(" "+P(t(a)?"watching":"disconnected"),1)]),c[0]||(c[0]=S("span",{class:"h-3 w-px bg-border"},null,-1)),S("span",null,P(s.value)+" "+P(t(e)["status-bar.models"]),1),c[1]||(c[1]=S("span",{class:"h-3 w-px bg-border"},null,-1)),S("span",null,P(i.value)+" "+P(t(e)["status-bar.locales"]),1),l.value?(v(),$("span",Nt,P(l.value),1)):N("",!0)]))}}),Mt={class:"fixed bottom-0 left-0 right-0 z-50 border-t border-border bg-background"},Vt={class:"flex items-center justify-around py-2"},qt=E({__name:"MobileNav",setup(u){const e=ie("serve-ui-texts").locale("en").get(),o=J(),a=[{icon:xe,label:e["mobile-nav.dash"],to:"/",exact:!0},{icon:be,label:e["mobile-nav.models"],to:"/models"},{icon:me,label:e["mobile-nav.content"],to:"/content"},{icon:_e,label:e["mobile-nav.valid"],to:"/validate"},{icon:ge,label:e["mobile-nav.branch"],to:"/branches"}];function n(s){return s.exact?o.path===s.to:o.path.startsWith(s.to)}return(s,i)=>(v(),$("nav",Mt,[S("div",Vt,[(v(),$(K,null,ae(a,l=>k(t(ve),{key:l.to,to:l.to,class:V(t(B)("flex flex-col items-center gap-0.5 px-3 py-1 text-[10px]",n(l)?"text-primary":"text-muted-foreground"))},{default:b(()=>[(v(),g(he(l.icon),{class:"size-5"})),M(" "+P(l.label),1)]),_:2},1032,["to","class"])),64))])]))}}),Wt={class:"flex h-screen max-h-screen w-full max-w-480 mx-auto"},Xt={class:"flex flex-1 flex-col min-w-0"},Yt={class:"flex-1 overflow-hidden pb-16 md:pb-0"},jt={key:1,class:"h-full overflow-y-auto custom-scrollbar"},so=E({__name:"AppLayout",setup(u){const e=J(),o=Z(),a=x(()=>e.meta.subSidebar===!0),n=x(()=>e.meta.basePath??""),s=x(()=>e.meta.paramKey??"");return we(i=>{(i.type==="config:changed"||i.type==="model:changed"||i.type==="context:changed")&&o.fetchStatus()}),z(()=>{o.fetchStatus()}),(i,l)=>(v(),$("div",Wt,[k(Ct,{class:"hidden md:flex"}),S("div",Xt,[S("main",Yt,[a.value?(v(),g(zt,{key:0,"base-path":n.value,"param-key":s.value},{default:b(()=>[k(t(de))]),_:1},8,["base-path","param-key"])):(v(),$("div",jt,[k(t(de))]))]),k(Bt,{class:"hidden md:flex"})]),k(qt,{class:"md:hidden"})]))}});export{so as default};