@a-company/atelier 0.27.2 → 0.27.4
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/dist/{chunk-PHS7IW52.js → chunk-C5DBTHXB.js} +4 -2
- package/dist/chunk-C5DBTHXB.js.map +1 -0
- package/dist/{chunk-OKHQNXN4.js → chunk-LC7ICNMN.js} +4 -4
- package/dist/chunk-LC7ICNMN.js.map +1 -0
- package/dist/cli.cjs +12 -15
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +11 -16
- package/dist/cli.js.map +1 -1
- package/dist/{dist-3PO6F556.js → dist-6IHF7WA7.js} +2 -2
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/mcp.cjs +4 -2
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +4 -2
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-OKHQNXN4.js.map +0 -1
- package/dist/chunk-PHS7IW52.js.map +0 -1
- /package/dist/{dist-3PO6F556.js.map → dist-6IHF7WA7.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -8,15 +8,15 @@ import {
|
|
|
8
8
|
stillCommand,
|
|
9
9
|
validateCommand,
|
|
10
10
|
variablesCommand
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-LC7ICNMN.js";
|
|
12
|
+
import "./chunk-C5DBTHXB.js";
|
|
13
13
|
|
|
14
14
|
// src/cli.ts
|
|
15
15
|
import { Command } from "commander";
|
|
16
16
|
|
|
17
17
|
// src/commands/studio.ts
|
|
18
18
|
import { resolve, join, relative, dirname } from "path";
|
|
19
|
-
import { mkdirSync, writeFileSync, rmSync, readFileSync, readdirSync, statSync, realpathSync } from "fs";
|
|
19
|
+
import { mkdirSync, writeFileSync, rmSync, readFileSync, readdirSync, statSync, realpathSync, symlinkSync, existsSync } from "fs";
|
|
20
20
|
import { tmpdir } from "os";
|
|
21
21
|
import { randomBytes } from "crypto";
|
|
22
22
|
import { exec } from "child_process";
|
|
@@ -384,13 +384,19 @@ function studioCommand(program2) {
|
|
|
384
384
|
}
|
|
385
385
|
const cwd = process.cwd();
|
|
386
386
|
const cliPackageDir = resolve(dirname(new URL(import.meta.url).pathname), "..");
|
|
387
|
-
const packagesDir = resolve(cliPackageDir, "..");
|
|
388
387
|
const tmpId = randomBytes(4).toString("hex");
|
|
389
388
|
const tmpDirRaw = join(tmpdir(), `atelier-studio-${tmpId}`);
|
|
390
389
|
mkdirSync(tmpDirRaw, { recursive: true });
|
|
391
390
|
const tmpDir = realpathSync(tmpDirRaw);
|
|
392
391
|
writeFileSync(join(tmpDir, "index.html"), getInlineHTML());
|
|
393
392
|
writeFileSync(join(tmpDir, "main.ts"), getInlineApp(file ?? null));
|
|
393
|
+
const cliNodeModules = join(cliPackageDir, "node_modules");
|
|
394
|
+
if (existsSync(cliNodeModules)) {
|
|
395
|
+
try {
|
|
396
|
+
symlinkSync(cliNodeModules, join(tmpDir, "node_modules"), "dir");
|
|
397
|
+
} catch {
|
|
398
|
+
}
|
|
399
|
+
}
|
|
394
400
|
console.log(`Starting Atelier Studio...`);
|
|
395
401
|
console.log(` Working directory: ${cwd}`);
|
|
396
402
|
let vite;
|
|
@@ -408,18 +414,7 @@ function studioCommand(program2) {
|
|
|
408
414
|
port,
|
|
409
415
|
strictPort: false,
|
|
410
416
|
fs: {
|
|
411
|
-
|
|
412
|
-
}
|
|
413
|
-
},
|
|
414
|
-
resolve: {
|
|
415
|
-
alias: {
|
|
416
|
-
"@a-company/atelier-studio/styles.css": resolve(packagesDir, "studio/dist/index.css"),
|
|
417
|
-
"@a-company/atelier-studio": resolve(packagesDir, "studio/src/index.ts"),
|
|
418
|
-
"@a-company/atelier-schema": resolve(packagesDir, "schema/src/index.ts"),
|
|
419
|
-
"@a-company/atelier-types": resolve(packagesDir, "types/src/index.ts"),
|
|
420
|
-
"@a-company/atelier-math": resolve(packagesDir, "math/src/index.ts"),
|
|
421
|
-
"@a-company/atelier-core": resolve(packagesDir, "core/src/index.ts"),
|
|
422
|
-
"@a-company/atelier-canvas": resolve(packagesDir, "canvas/src/index.ts")
|
|
417
|
+
strict: false
|
|
423
418
|
}
|
|
424
419
|
},
|
|
425
420
|
plugins: [
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/studio.ts"],"sourcesContent":["#!/usr/bin/env node\n// @a-company/atelier-cli — Entry point for the `atelier` command\n\nimport { Command } from \"commander\";\nimport { validateCommand } from \"./commands/validate.js\";\nimport { infoCommand } from \"./commands/info.js\";\nimport { stillCommand } from \"./commands/still.js\";\nimport { renderCommand } from \"./commands/render.js\";\nimport { exportSvgCommand } from \"./commands/export-svg.js\";\nimport { exportLottieCommand } from \"./commands/export-lottie.js\";\nimport { assetsCommand } from \"./commands/assets.js\";\nimport { variablesCommand } from \"./commands/variables.js\";\nimport { studioCommand } from \"./commands/studio.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"atelier\")\n .description(\"Atelier animation CLI\")\n .version(\"0.1.0\");\n\n// Register commands\nvalidateCommand(program);\ninfoCommand(program);\nstillCommand(program);\nrenderCommand(program);\nexportSvgCommand(program);\nexportLottieCommand(program);\nassetsCommand(program);\nvariablesCommand(program);\nstudioCommand(program);\n\nprogram.parse();\n","/**\n * `atelier studio [file]` — launch the browser-based Atelier editor.\n *\n * Spins up a Vite dev server with a temporary app that imports AtelierStudio,\n * provides a file API for reading/writing .atelier files from CWD, and opens\n * the browser.\n *\n * Usage:\n * atelier studio → browse all .atelier files in CWD\n * atelier studio my-animation.atelier → open specific file\n * atelier studio --port 8080 → custom port\n * atelier studio --no-open → don't auto-open browser\n */\n\nimport { resolve, join, relative, dirname } from \"node:path\";\nimport { mkdirSync, writeFileSync, rmSync, readFileSync, readdirSync, statSync, realpathSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { randomBytes } from \"node:crypto\";\nimport { exec } from \"node:child_process\";\nimport type { Command } from \"commander\";\n\n/** Recursively glob for .atelier files under a directory. */\nfunction findAtelierFiles(dir: string, base: string = dir): string[] {\n const results: string[] = [];\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return results;\n }\n for (const entry of entries) {\n if (entry === \"node_modules\" || entry === \"dist\" || entry === \".git\") continue;\n const full = join(dir, entry);\n let stat;\n try {\n stat = statSync(full);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n results.push(...findAtelierFiles(full, base));\n } else if (entry.endsWith(\".atelier\")) {\n results.push(relative(base, full));\n }\n }\n return results.sort();\n}\n\n/** Validate that a file path is safe (relative, no traversal). */\nfunction isSafePath(filePath: string): boolean {\n if (!filePath || filePath.includes(\"..\") || filePath.startsWith(\"/\")) return false;\n const resolved = resolve(process.cwd(), filePath);\n return resolved.startsWith(process.cwd());\n}\n\nfunction getInlineHTML(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Atelier Studio</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&display=swap\" rel=\"stylesheet\">\n </head>\n <body>\n <div id=\"studio\"></div>\n <script type=\"module\" src=\"/main.ts\"></script>\n </body>\n</html>`;\n}\n\nfunction getInlineApp(initialFile: string | null): string {\n const initialFileStr = initialFile ? JSON.stringify(initialFile) : \"null\";\n return `import { AtelierStudio } from \"@a-company/atelier-studio\";\nimport \"@a-company/atelier-studio/styles.css\";\nimport { parseAtelier, serializeAtelier } from \"@a-company/atelier-schema\";\n\n// ── Types ──\ninterface FileEntry {\n path: string;\n name: string;\n folder: string;\n}\n\n// ── State ──\nlet studio: AtelierStudio | null = null;\nlet currentFile: string | null = null;\nlet files: FileEntry[] = [];\nlet saveTimeout: ReturnType<typeof setTimeout> | null = null;\n\n// ── API helpers ──\nasync function fetchFiles(): Promise<FileEntry[]> {\n const res = await fetch(\"/api/files\");\n return res.json();\n}\n\nasync function fetchFileContent(path: string): Promise<string> {\n const res = await fetch(\"/api/file?path=\" + encodeURIComponent(path));\n return res.text();\n}\n\nasync function saveFileContent(path: string, content: string): Promise<void> {\n await fetch(\"/api/file?path=\" + encodeURIComponent(path), {\n method: \"POST\",\n headers: { \"Content-Type\": \"text/plain\" },\n body: content,\n });\n}\n\n// ── Theme (matches branded theme from showcase) ──\nconst theme = {\n bg: \"#2C2C2C\",\n bgSecondary: \"#333333\",\n bgTertiary: \"#3D3D3D\",\n text: \"#F5F0EB\",\n textMuted: \"#A89F95\",\n textAccent: \"#F5F0EB\",\n border: \"#4A4A4A\",\n buttonBg: \"#3D3D3D\",\n buttonHover: \"#4A4A4A\",\n buttonActive: \"#555555\",\n accent: \"#C75B39\",\n accentHover: \"#D4724E\",\n sliderTrack: \"#4A4A4A\",\n sliderThumb: \"#C75B39\",\n fontFamily: \"'Cormorant Garamond', Georgia, serif\",\n fontMono: \"'SF Mono', 'Fira Code', monospace\",\n canvasShadow: \"0 4px 60px rgba(199, 91, 57, 0.12), 0 0 40px rgba(0,0,0,0.4)\",\n};\n\n// ── Styles ──\nconst style = document.createElement(\"style\");\nstyle.textContent = \\`\n * { margin: 0; padding: 0; box-sizing: border-box; }\n html, body { height: 100%; overflow: hidden; background: #2C2C2C; color: #F5F0EB; }\n body { font-family: 'Cormorant Garamond', Georgia, serif; }\n #studio { display: flex; height: 100vh; width: 100vw; }\n\n .sidebar {\n width: 260px;\n min-width: 260px;\n background: #333333;\n border-right: 1px solid #4A4A4A;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n .sidebar__header {\n padding: 16px 20px;\n border-bottom: 1px solid #4A4A4A;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: #A89F95;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .sidebar__header span {\n color: #C75B39;\n font-size: 13px;\n }\n .sidebar__list {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n }\n .sidebar__list::-webkit-scrollbar { width: 6px; }\n .sidebar__list::-webkit-scrollbar-track { background: transparent; }\n .sidebar__list::-webkit-scrollbar-thumb { background: #4A4A4A; border-radius: 3px; }\n\n .sidebar__folder {\n padding: 10px 20px 4px;\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 1.5px;\n text-transform: uppercase;\n color: #A89F95;\n }\n .sidebar__item {\n padding: 8px 20px 8px 28px;\n font-size: 13px;\n cursor: pointer;\n color: #A89F95;\n transition: background 0.15s, color 0.15s;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 11.5px;\n }\n .sidebar__item:hover { background: #363636; color: #F5F0EB; }\n .sidebar__item--active {\n background: rgba(199, 91, 57, 0.12) !important;\n color: #C75B39 !important;\n }\n\n .main {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n .main__status {\n height: 32px;\n min-height: 32px;\n display: flex;\n align-items: center;\n padding: 0 16px;\n background: #333333;\n border-bottom: 1px solid #4A4A4A;\n font-size: 11px;\n color: #A89F95;\n font-family: 'SF Mono', 'Fira Code', monospace;\n gap: 12px;\n }\n .main__status .save-indicator {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-left: auto;\n transition: opacity 0.3s;\n }\n .main__status .save-indicator--saving { color: #C75B39; }\n .main__status .save-indicator--saved { color: #6B8E6B; }\n .main__editor {\n flex: 1;\n overflow: hidden;\n }\n .main__empty {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #A89F95;\n font-size: 18px;\n }\n\\`;\ndocument.head.appendChild(style);\n\n// ── Build UI ──\nconst root = document.getElementById(\"studio\")!;\nconst sidebar = document.createElement(\"div\");\nsidebar.className = \"sidebar\";\n\nconst sidebarHeader = document.createElement(\"div\");\nsidebarHeader.className = \"sidebar__header\";\nsidebarHeader.innerHTML = '<span>◆</span> ATELIER STUDIO';\nsidebar.appendChild(sidebarHeader);\n\nconst sidebarList = document.createElement(\"div\");\nsidebarList.className = \"sidebar__list\";\nsidebar.appendChild(sidebarList);\n\nconst main = document.createElement(\"div\");\nmain.className = \"main\";\n\nconst statusBar = document.createElement(\"div\");\nstatusBar.className = \"main__status\";\nmain.appendChild(statusBar);\n\nconst editorContainer = document.createElement(\"div\");\neditorContainer.className = \"main__editor\";\nmain.appendChild(editorContainer);\n\nroot.appendChild(sidebar);\nroot.appendChild(main);\n\n// ── File list rendering ──\nfunction renderFileList(): void {\n sidebarList.innerHTML = \"\";\n let lastFolder = \"\";\n\n for (const file of files) {\n if (file.folder && file.folder !== lastFolder) {\n lastFolder = file.folder;\n const folder = document.createElement(\"div\");\n folder.className = \"sidebar__folder\";\n folder.textContent = file.folder;\n sidebarList.appendChild(folder);\n }\n\n const item = document.createElement(\"div\");\n item.className = \"sidebar__item\" + (file.path === currentFile ? \" sidebar__item--active\" : \"\");\n item.textContent = file.name;\n item.title = file.path;\n item.addEventListener(\"click\", () => loadFile(file.path));\n sidebarList.appendChild(item);\n }\n}\n\n// ── Load a file into the studio ──\nasync function loadFile(path: string): Promise<void> {\n currentFile = path;\n renderFileList();\n\n const content = await fetchFileContent(path);\n const result = parseAtelier(content);\n\n if (!result.success) {\n editorContainer.innerHTML = \"\";\n const err = document.createElement(\"div\");\n err.className = \"main__empty\";\n err.style.flexDirection = \"column\";\n err.style.gap = \"8px\";\n err.innerHTML = '<div style=\"color:#C75B39\">Parse Error</div><div style=\"font-size:13px;font-family:monospace\">' +\n result.errors.map(e => e.path + \": \" + e.message).join(\"<br>\") + \"</div>\";\n editorContainer.appendChild(err);\n return;\n }\n\n statusBar.innerHTML = '<span>' + path + '</span><span class=\"save-indicator save-indicator--saved\">✓ saved</span>';\n\n if (studio) {\n studio.destroy();\n studio = null;\n }\n\n studio = new AtelierStudio(editorContainer, {\n mode: \"full\",\n initialTab: \"yaml\",\n allowSave: true,\n onDocumentChange: (doc) => {\n // Auto-save with debounce\n const indicator = statusBar.querySelector(\".save-indicator\");\n if (indicator) {\n indicator.className = \"save-indicator save-indicator--saving\";\n indicator.innerHTML = \"● saving...\";\n }\n\n if (saveTimeout) clearTimeout(saveTimeout);\n saveTimeout = setTimeout(async () => {\n if (!currentFile) return;\n const yaml = serializeAtelier(doc);\n await saveFileContent(currentFile, yaml);\n const ind = statusBar.querySelector(\".save-indicator\");\n if (ind) {\n ind.className = \"save-indicator save-indicator--saved\";\n ind.innerHTML = \"✓ saved\";\n }\n }, 800);\n },\n });\n studio.setTheme(theme);\n studio.loadDocument(result.data);\n}\n\n// ── Boot ──\nasync function boot(): Promise<void> {\n files = await fetchFiles();\n\n if (files.length === 0) {\n editorContainer.innerHTML = \"\";\n const empty = document.createElement(\"div\");\n empty.className = \"main__empty\";\n empty.textContent = \"No .atelier files found in this directory\";\n editorContainer.appendChild(empty);\n statusBar.textContent = \"No files\";\n renderFileList();\n return;\n }\n\n renderFileList();\n\n const initialFile = ${initialFileStr};\n const target = initialFile\n ? files.find(f => f.path === initialFile || f.path.endsWith(initialFile))\n : files[0];\n\n if (target) {\n await loadFile(target.path);\n }\n}\n\nboot();\n`;\n}\n\n/** Register the `studio` subcommand on the Commander program. */\nexport function studioCommand(program: Command): void {\n program\n .command(\"studio [file]\")\n .description(\"Launch the browser-based Atelier editor\")\n .option(\"-p, --port <number>\", \"Port to serve on\", \"4321\")\n .option(\"--no-open\", \"Don't auto-open browser\")\n .action(\n async (\n file: string | undefined,\n options: { port: string; open: boolean },\n ) => {\n const port = parseInt(options.port, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n console.error(`Invalid port: ${options.port}`);\n process.exit(1);\n }\n\n const cwd = process.cwd();\n\n // Resolve the monorepo package paths for Vite aliases.\n // This file lives at packages/cli/src/commands/studio.ts (or dist/cli.js).\n // We need to find sibling packages relative to packages/cli.\n const cliPackageDir = resolve(dirname(new URL(import.meta.url).pathname), \"..\");\n const packagesDir = resolve(cliPackageDir, \"..\");\n\n // Create temp directory with inline app.\n // Use realpathSync to resolve macOS /var -> /private/var symlink,\n // which Vite normalizes internally when resolving file paths.\n const tmpId = randomBytes(4).toString(\"hex\");\n const tmpDirRaw = join(tmpdir(), `atelier-studio-${tmpId}`);\n mkdirSync(tmpDirRaw, { recursive: true });\n const tmpDir = realpathSync(tmpDirRaw);\n\n writeFileSync(join(tmpDir, \"index.html\"), getInlineHTML());\n writeFileSync(join(tmpDir, \"main.ts\"), getInlineApp(file ?? null));\n\n console.log(`Starting Atelier Studio...`);\n console.log(` Working directory: ${cwd}`);\n\n // Dynamically import Vite (it's a peer/optional dep)\n let vite: typeof import(\"vite\");\n try {\n vite = await import(\"vite\");\n } catch {\n console.error(\"Vite is required for `atelier studio`.\");\n console.error(\"Install it: pnpm add -D vite\");\n process.exit(1);\n return;\n }\n\n const server = await vite.createServer({\n root: tmpDir,\n server: {\n port,\n strictPort: false,\n fs: {\n allow: [tmpDir, packagesDir, cwd],\n },\n },\n resolve: {\n alias: {\n \"@a-company/atelier-studio/styles.css\": resolve(packagesDir, \"studio/dist/index.css\"),\n \"@a-company/atelier-studio\": resolve(packagesDir, \"studio/src/index.ts\"),\n \"@a-company/atelier-schema\": resolve(packagesDir, \"schema/src/index.ts\"),\n \"@a-company/atelier-types\": resolve(packagesDir, \"types/src/index.ts\"),\n \"@a-company/atelier-math\": resolve(packagesDir, \"math/src/index.ts\"),\n \"@a-company/atelier-core\": resolve(packagesDir, \"core/src/index.ts\"),\n \"@a-company/atelier-canvas\": resolve(packagesDir, \"canvas/src/index.ts\"),\n },\n },\n plugins: [\n {\n name: \"atelier-api\",\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const url = new URL(req.url ?? \"/\", `http://localhost:${port}`);\n\n if (url.pathname === \"/api/files\") {\n const atelierFiles = findAtelierFiles(cwd);\n const entries = atelierFiles.map((p) => {\n const parts = p.split(\"/\");\n return {\n path: p,\n name: parts[parts.length - 1].replace(\".atelier\", \"\"),\n folder: parts.length > 1 ? parts.slice(0, -1).join(\"/\") : \"\",\n };\n });\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify(entries));\n return;\n }\n\n if (url.pathname === \"/api/file\") {\n const filePath = url.searchParams.get(\"path\");\n if (!filePath || !isSafePath(filePath)) {\n res.statusCode = 400;\n res.end(\"Invalid path\");\n return;\n }\n\n const absPath = resolve(cwd, filePath);\n\n if (req.method === \"GET\") {\n try {\n const content = readFileSync(absPath, \"utf-8\");\n res.setHeader(\"Content-Type\", \"text/plain\");\n res.end(content);\n } catch {\n res.statusCode = 404;\n res.end(\"File not found\");\n }\n return;\n }\n\n if (req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk: Buffer) => { body += chunk.toString(); });\n req.on(\"end\", () => {\n try {\n writeFileSync(absPath, body, \"utf-8\");\n res.end(\"OK\");\n } catch {\n res.statusCode = 500;\n res.end(\"Write failed\");\n }\n });\n return;\n }\n }\n\n if (url.pathname === \"/api/cwd\") {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify({ cwd }));\n return;\n }\n\n next();\n });\n },\n },\n ],\n logLevel: \"warn\",\n });\n\n await server.listen();\n\n const resolvedUrl = server.resolvedUrls?.local[0] ?? `http://localhost:${port}`;\n const url = resolvedUrl;\n\n console.log(` Server running at: ${url}`);\n\n const atelierFiles = findAtelierFiles(cwd);\n console.log(` Found ${atelierFiles.length} .atelier file(s)`);\n\n if (file) {\n console.log(` Opening: ${file}`);\n }\n\n console.log(` Press Ctrl+C to stop\\n`);\n\n if (options.open) {\n exec(`open \"${url}\"`);\n }\n\n // Keep alive and handle cleanup\n const cleanup = () => {\n console.log(\"\\nShutting down...\");\n server.close();\n try {\n rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // ignore cleanup errors\n }\n process.exit(0);\n };\n\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAGA,SAAS,eAAe;;;ACWxB,SAAS,SAAS,MAAM,UAAU,eAAe;AACjD,SAAS,WAAW,eAAe,QAAQ,cAAc,aAAa,UAAU,oBAAoB;AACpG,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AAIrB,SAAS,iBAAiB,KAAa,OAAe,KAAe;AACnE,QAAM,UAAoB,CAAC;AAC3B,MAAI;AACJ,MAAI;AACF,cAAU,YAAY,GAAG;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,aAAW,SAAS,SAAS;AAC3B,QAAI,UAAU,kBAAkB,UAAU,UAAU,UAAU,OAAQ;AACtE,UAAM,OAAO,KAAK,KAAK,KAAK;AAC5B,QAAI;AACJ,QAAI;AACF,aAAO,SAAS,IAAI;AAAA,IACtB,QAAQ;AACN;AAAA,IACF;AACA,QAAI,KAAK,YAAY,GAAG;AACtB,cAAQ,KAAK,GAAG,iBAAiB,MAAM,IAAI,CAAC;AAAA,IAC9C,WAAW,MAAM,SAAS,UAAU,GAAG;AACrC,cAAQ,KAAK,SAAS,MAAM,IAAI,CAAC;AAAA,IACnC;AAAA,EACF;AACA,SAAO,QAAQ,KAAK;AACtB;AAGA,SAAS,WAAW,UAA2B;AAC7C,MAAI,CAAC,YAAY,SAAS,SAAS,IAAI,KAAK,SAAS,WAAW,GAAG,EAAG,QAAO;AAC7E,QAAM,WAAW,QAAQ,QAAQ,IAAI,GAAG,QAAQ;AAChD,SAAO,SAAS,WAAW,QAAQ,IAAI,CAAC;AAC1C;AAEA,SAAS,gBAAwB;AAC/B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeT;AAEA,SAAS,aAAa,aAAoC;AACxD,QAAM,iBAAiB,cAAc,KAAK,UAAU,WAAW,IAAI;AACnE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["program","server","url","atelierFiles"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/studio.ts"],"sourcesContent":["#!/usr/bin/env node\n// @a-company/atelier-cli — Entry point for the `atelier` command\n\nimport { Command } from \"commander\";\nimport { validateCommand } from \"./commands/validate.js\";\nimport { infoCommand } from \"./commands/info.js\";\nimport { stillCommand } from \"./commands/still.js\";\nimport { renderCommand } from \"./commands/render.js\";\nimport { exportSvgCommand } from \"./commands/export-svg.js\";\nimport { exportLottieCommand } from \"./commands/export-lottie.js\";\nimport { assetsCommand } from \"./commands/assets.js\";\nimport { variablesCommand } from \"./commands/variables.js\";\nimport { studioCommand } from \"./commands/studio.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"atelier\")\n .description(\"Atelier animation CLI\")\n .version(\"0.1.0\");\n\n// Register commands\nvalidateCommand(program);\ninfoCommand(program);\nstillCommand(program);\nrenderCommand(program);\nexportSvgCommand(program);\nexportLottieCommand(program);\nassetsCommand(program);\nvariablesCommand(program);\nstudioCommand(program);\n\nprogram.parse();\n","/**\n * `atelier studio [file]` — launch the browser-based Atelier editor.\n *\n * Spins up a Vite dev server with a temporary app that imports AtelierStudio,\n * provides a file API for reading/writing .atelier files from CWD, and opens\n * the browser.\n *\n * Usage:\n * atelier studio → browse all .atelier files in CWD\n * atelier studio my-animation.atelier → open specific file\n * atelier studio --port 8080 → custom port\n * atelier studio --no-open → don't auto-open browser\n */\n\nimport { resolve, join, relative, dirname } from \"node:path\";\nimport { mkdirSync, writeFileSync, rmSync, readFileSync, readdirSync, statSync, realpathSync, symlinkSync, existsSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { randomBytes } from \"node:crypto\";\nimport { exec } from \"node:child_process\";\nimport type { Command } from \"commander\";\n\n/** Recursively glob for .atelier files under a directory. */\nfunction findAtelierFiles(dir: string, base: string = dir): string[] {\n const results: string[] = [];\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return results;\n }\n for (const entry of entries) {\n if (entry === \"node_modules\" || entry === \"dist\" || entry === \".git\") continue;\n const full = join(dir, entry);\n let stat;\n try {\n stat = statSync(full);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n results.push(...findAtelierFiles(full, base));\n } else if (entry.endsWith(\".atelier\")) {\n results.push(relative(base, full));\n }\n }\n return results.sort();\n}\n\n/** Validate that a file path is safe (relative, no traversal). */\nfunction isSafePath(filePath: string): boolean {\n if (!filePath || filePath.includes(\"..\") || filePath.startsWith(\"/\")) return false;\n const resolved = resolve(process.cwd(), filePath);\n return resolved.startsWith(process.cwd());\n}\n\nfunction getInlineHTML(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Atelier Studio</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&display=swap\" rel=\"stylesheet\">\n </head>\n <body>\n <div id=\"studio\"></div>\n <script type=\"module\" src=\"/main.ts\"></script>\n </body>\n</html>`;\n}\n\nfunction getInlineApp(initialFile: string | null): string {\n const initialFileStr = initialFile ? JSON.stringify(initialFile) : \"null\";\n return `import { AtelierStudio } from \"@a-company/atelier-studio\";\nimport \"@a-company/atelier-studio/styles.css\";\nimport { parseAtelier, serializeAtelier } from \"@a-company/atelier-schema\";\n\n// ── Types ──\ninterface FileEntry {\n path: string;\n name: string;\n folder: string;\n}\n\n// ── State ──\nlet studio: AtelierStudio | null = null;\nlet currentFile: string | null = null;\nlet files: FileEntry[] = [];\nlet saveTimeout: ReturnType<typeof setTimeout> | null = null;\n\n// ── API helpers ──\nasync function fetchFiles(): Promise<FileEntry[]> {\n const res = await fetch(\"/api/files\");\n return res.json();\n}\n\nasync function fetchFileContent(path: string): Promise<string> {\n const res = await fetch(\"/api/file?path=\" + encodeURIComponent(path));\n return res.text();\n}\n\nasync function saveFileContent(path: string, content: string): Promise<void> {\n await fetch(\"/api/file?path=\" + encodeURIComponent(path), {\n method: \"POST\",\n headers: { \"Content-Type\": \"text/plain\" },\n body: content,\n });\n}\n\n// ── Theme (matches branded theme from showcase) ──\nconst theme = {\n bg: \"#2C2C2C\",\n bgSecondary: \"#333333\",\n bgTertiary: \"#3D3D3D\",\n text: \"#F5F0EB\",\n textMuted: \"#A89F95\",\n textAccent: \"#F5F0EB\",\n border: \"#4A4A4A\",\n buttonBg: \"#3D3D3D\",\n buttonHover: \"#4A4A4A\",\n buttonActive: \"#555555\",\n accent: \"#C75B39\",\n accentHover: \"#D4724E\",\n sliderTrack: \"#4A4A4A\",\n sliderThumb: \"#C75B39\",\n fontFamily: \"'Cormorant Garamond', Georgia, serif\",\n fontMono: \"'SF Mono', 'Fira Code', monospace\",\n canvasShadow: \"0 4px 60px rgba(199, 91, 57, 0.12), 0 0 40px rgba(0,0,0,0.4)\",\n};\n\n// ── Styles ──\nconst style = document.createElement(\"style\");\nstyle.textContent = \\`\n * { margin: 0; padding: 0; box-sizing: border-box; }\n html, body { height: 100%; overflow: hidden; background: #2C2C2C; color: #F5F0EB; }\n body { font-family: 'Cormorant Garamond', Georgia, serif; }\n #studio { display: flex; height: 100vh; width: 100vw; }\n\n .sidebar {\n width: 260px;\n min-width: 260px;\n background: #333333;\n border-right: 1px solid #4A4A4A;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n .sidebar__header {\n padding: 16px 20px;\n border-bottom: 1px solid #4A4A4A;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: #A89F95;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .sidebar__header span {\n color: #C75B39;\n font-size: 13px;\n }\n .sidebar__list {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n }\n .sidebar__list::-webkit-scrollbar { width: 6px; }\n .sidebar__list::-webkit-scrollbar-track { background: transparent; }\n .sidebar__list::-webkit-scrollbar-thumb { background: #4A4A4A; border-radius: 3px; }\n\n .sidebar__folder {\n padding: 10px 20px 4px;\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 1.5px;\n text-transform: uppercase;\n color: #A89F95;\n }\n .sidebar__item {\n padding: 8px 20px 8px 28px;\n font-size: 13px;\n cursor: pointer;\n color: #A89F95;\n transition: background 0.15s, color 0.15s;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 11.5px;\n }\n .sidebar__item:hover { background: #363636; color: #F5F0EB; }\n .sidebar__item--active {\n background: rgba(199, 91, 57, 0.12) !important;\n color: #C75B39 !important;\n }\n\n .main {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n .main__status {\n height: 32px;\n min-height: 32px;\n display: flex;\n align-items: center;\n padding: 0 16px;\n background: #333333;\n border-bottom: 1px solid #4A4A4A;\n font-size: 11px;\n color: #A89F95;\n font-family: 'SF Mono', 'Fira Code', monospace;\n gap: 12px;\n }\n .main__status .save-indicator {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-left: auto;\n transition: opacity 0.3s;\n }\n .main__status .save-indicator--saving { color: #C75B39; }\n .main__status .save-indicator--saved { color: #6B8E6B; }\n .main__editor {\n flex: 1;\n overflow: hidden;\n }\n .main__empty {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #A89F95;\n font-size: 18px;\n }\n\\`;\ndocument.head.appendChild(style);\n\n// ── Build UI ──\nconst root = document.getElementById(\"studio\")!;\nconst sidebar = document.createElement(\"div\");\nsidebar.className = \"sidebar\";\n\nconst sidebarHeader = document.createElement(\"div\");\nsidebarHeader.className = \"sidebar__header\";\nsidebarHeader.innerHTML = '<span>◆</span> ATELIER STUDIO';\nsidebar.appendChild(sidebarHeader);\n\nconst sidebarList = document.createElement(\"div\");\nsidebarList.className = \"sidebar__list\";\nsidebar.appendChild(sidebarList);\n\nconst main = document.createElement(\"div\");\nmain.className = \"main\";\n\nconst statusBar = document.createElement(\"div\");\nstatusBar.className = \"main__status\";\nmain.appendChild(statusBar);\n\nconst editorContainer = document.createElement(\"div\");\neditorContainer.className = \"main__editor\";\nmain.appendChild(editorContainer);\n\nroot.appendChild(sidebar);\nroot.appendChild(main);\n\n// ── File list rendering ──\nfunction renderFileList(): void {\n sidebarList.innerHTML = \"\";\n let lastFolder = \"\";\n\n for (const file of files) {\n if (file.folder && file.folder !== lastFolder) {\n lastFolder = file.folder;\n const folder = document.createElement(\"div\");\n folder.className = \"sidebar__folder\";\n folder.textContent = file.folder;\n sidebarList.appendChild(folder);\n }\n\n const item = document.createElement(\"div\");\n item.className = \"sidebar__item\" + (file.path === currentFile ? \" sidebar__item--active\" : \"\");\n item.textContent = file.name;\n item.title = file.path;\n item.addEventListener(\"click\", () => loadFile(file.path));\n sidebarList.appendChild(item);\n }\n}\n\n// ── Load a file into the studio ──\nasync function loadFile(path: string): Promise<void> {\n currentFile = path;\n renderFileList();\n\n const content = await fetchFileContent(path);\n const result = parseAtelier(content);\n\n if (!result.success) {\n editorContainer.innerHTML = \"\";\n const err = document.createElement(\"div\");\n err.className = \"main__empty\";\n err.style.flexDirection = \"column\";\n err.style.gap = \"8px\";\n err.innerHTML = '<div style=\"color:#C75B39\">Parse Error</div><div style=\"font-size:13px;font-family:monospace\">' +\n result.errors.map(e => e.path + \": \" + e.message).join(\"<br>\") + \"</div>\";\n editorContainer.appendChild(err);\n return;\n }\n\n statusBar.innerHTML = '<span>' + path + '</span><span class=\"save-indicator save-indicator--saved\">✓ saved</span>';\n\n if (studio) {\n studio.destroy();\n studio = null;\n }\n\n studio = new AtelierStudio(editorContainer, {\n mode: \"full\",\n initialTab: \"yaml\",\n allowSave: true,\n onDocumentChange: (doc) => {\n // Auto-save with debounce\n const indicator = statusBar.querySelector(\".save-indicator\");\n if (indicator) {\n indicator.className = \"save-indicator save-indicator--saving\";\n indicator.innerHTML = \"● saving...\";\n }\n\n if (saveTimeout) clearTimeout(saveTimeout);\n saveTimeout = setTimeout(async () => {\n if (!currentFile) return;\n const yaml = serializeAtelier(doc);\n await saveFileContent(currentFile, yaml);\n const ind = statusBar.querySelector(\".save-indicator\");\n if (ind) {\n ind.className = \"save-indicator save-indicator--saved\";\n ind.innerHTML = \"✓ saved\";\n }\n }, 800);\n },\n });\n studio.setTheme(theme);\n studio.loadDocument(result.data);\n}\n\n// ── Boot ──\nasync function boot(): Promise<void> {\n files = await fetchFiles();\n\n if (files.length === 0) {\n editorContainer.innerHTML = \"\";\n const empty = document.createElement(\"div\");\n empty.className = \"main__empty\";\n empty.textContent = \"No .atelier files found in this directory\";\n editorContainer.appendChild(empty);\n statusBar.textContent = \"No files\";\n renderFileList();\n return;\n }\n\n renderFileList();\n\n const initialFile = ${initialFileStr};\n const target = initialFile\n ? files.find(f => f.path === initialFile || f.path.endsWith(initialFile))\n : files[0];\n\n if (target) {\n await loadFile(target.path);\n }\n}\n\nboot();\n`;\n}\n\n/** Register the `studio` subcommand on the Commander program. */\nexport function studioCommand(program: Command): void {\n program\n .command(\"studio [file]\")\n .description(\"Launch the browser-based Atelier editor\")\n .option(\"-p, --port <number>\", \"Port to serve on\", \"4321\")\n .option(\"--no-open\", \"Don't auto-open browser\")\n .action(\n async (\n file: string | undefined,\n options: { port: string; open: boolean },\n ) => {\n const port = parseInt(options.port, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n console.error(`Invalid port: ${options.port}`);\n process.exit(1);\n }\n\n const cwd = process.cwd();\n\n // Find the CLI package directory (where node_modules lives).\n // This file is at packages/cli/dist/cli.js (or src/commands/studio.ts in dev).\n const cliPackageDir = resolve(dirname(new URL(import.meta.url).pathname), \"..\");\n\n // Create temp directory with inline app.\n // Use realpathSync to resolve macOS /var -> /private/var symlink,\n // which Vite normalizes internally when resolving file paths.\n const tmpId = randomBytes(4).toString(\"hex\");\n const tmpDirRaw = join(tmpdir(), `atelier-studio-${tmpId}`);\n mkdirSync(tmpDirRaw, { recursive: true });\n const tmpDir = realpathSync(tmpDirRaw);\n\n writeFileSync(join(tmpDir, \"index.html\"), getInlineHTML());\n writeFileSync(join(tmpDir, \"main.ts\"), getInlineApp(file ?? null));\n\n // Symlink node_modules into temp dir so Vite can resolve @a-company/* packages.\n // Works for both monorepo (pnpm workspace links) and npm global install.\n const cliNodeModules = join(cliPackageDir, \"node_modules\");\n if (existsSync(cliNodeModules)) {\n try {\n symlinkSync(cliNodeModules, join(tmpDir, \"node_modules\"), \"dir\");\n } catch {\n // Non-fatal: aliases will handle resolution if symlink fails\n }\n }\n\n console.log(`Starting Atelier Studio...`);\n console.log(` Working directory: ${cwd}`);\n\n // Dynamically import Vite (it's a peer/optional dep)\n let vite: typeof import(\"vite\");\n try {\n vite = await import(\"vite\");\n } catch {\n console.error(\"Vite is required for `atelier studio`.\");\n console.error(\"Install it: pnpm add -D vite\");\n process.exit(1);\n return;\n }\n\n const server = await vite.createServer({\n root: tmpDir,\n server: {\n port,\n strictPort: false,\n fs: {\n strict: false,\n },\n },\n plugins: [\n {\n name: \"atelier-api\",\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const url = new URL(req.url ?? \"/\", `http://localhost:${port}`);\n\n if (url.pathname === \"/api/files\") {\n const atelierFiles = findAtelierFiles(cwd);\n const entries = atelierFiles.map((p) => {\n const parts = p.split(\"/\");\n return {\n path: p,\n name: parts[parts.length - 1].replace(\".atelier\", \"\"),\n folder: parts.length > 1 ? parts.slice(0, -1).join(\"/\") : \"\",\n };\n });\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify(entries));\n return;\n }\n\n if (url.pathname === \"/api/file\") {\n const filePath = url.searchParams.get(\"path\");\n if (!filePath || !isSafePath(filePath)) {\n res.statusCode = 400;\n res.end(\"Invalid path\");\n return;\n }\n\n const absPath = resolve(cwd, filePath);\n\n if (req.method === \"GET\") {\n try {\n const content = readFileSync(absPath, \"utf-8\");\n res.setHeader(\"Content-Type\", \"text/plain\");\n res.end(content);\n } catch {\n res.statusCode = 404;\n res.end(\"File not found\");\n }\n return;\n }\n\n if (req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk: Buffer) => { body += chunk.toString(); });\n req.on(\"end\", () => {\n try {\n writeFileSync(absPath, body, \"utf-8\");\n res.end(\"OK\");\n } catch {\n res.statusCode = 500;\n res.end(\"Write failed\");\n }\n });\n return;\n }\n }\n\n if (url.pathname === \"/api/cwd\") {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify({ cwd }));\n return;\n }\n\n next();\n });\n },\n },\n ],\n logLevel: \"warn\",\n });\n\n await server.listen();\n\n const resolvedUrl = server.resolvedUrls?.local[0] ?? `http://localhost:${port}`;\n const url = resolvedUrl;\n\n console.log(` Server running at: ${url}`);\n\n const atelierFiles = findAtelierFiles(cwd);\n console.log(` Found ${atelierFiles.length} .atelier file(s)`);\n\n if (file) {\n console.log(` Opening: ${file}`);\n }\n\n console.log(` Press Ctrl+C to stop\\n`);\n\n if (options.open) {\n exec(`open \"${url}\"`);\n }\n\n // Keep alive and handle cleanup\n const cleanup = () => {\n console.log(\"\\nShutting down...\");\n server.close();\n try {\n rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // ignore cleanup errors\n }\n process.exit(0);\n };\n\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAGA,SAAS,eAAe;;;ACWxB,SAAS,SAAS,MAAM,UAAU,eAAe;AACjD,SAAS,WAAW,eAAe,QAAQ,cAAc,aAAa,UAAU,cAAc,aAAa,kBAAkB;AAC7H,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AAIrB,SAAS,iBAAiB,KAAa,OAAe,KAAe;AACnE,QAAM,UAAoB,CAAC;AAC3B,MAAI;AACJ,MAAI;AACF,cAAU,YAAY,GAAG;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,aAAW,SAAS,SAAS;AAC3B,QAAI,UAAU,kBAAkB,UAAU,UAAU,UAAU,OAAQ;AACtE,UAAM,OAAO,KAAK,KAAK,KAAK;AAC5B,QAAI;AACJ,QAAI;AACF,aAAO,SAAS,IAAI;AAAA,IACtB,QAAQ;AACN;AAAA,IACF;AACA,QAAI,KAAK,YAAY,GAAG;AACtB,cAAQ,KAAK,GAAG,iBAAiB,MAAM,IAAI,CAAC;AAAA,IAC9C,WAAW,MAAM,SAAS,UAAU,GAAG;AACrC,cAAQ,KAAK,SAAS,MAAM,IAAI,CAAC;AAAA,IACnC;AAAA,EACF;AACA,SAAO,QAAQ,KAAK;AACtB;AAGA,SAAS,WAAW,UAA2B;AAC7C,MAAI,CAAC,YAAY,SAAS,SAAS,IAAI,KAAK,SAAS,WAAW,GAAG,EAAG,QAAO;AAC7E,QAAM,WAAW,QAAQ,QAAQ,IAAI,GAAG,QAAQ;AAChD,SAAO,SAAS,WAAW,QAAQ,IAAI,CAAC;AAC1C;AAEA,SAAS,gBAAwB;AAC/B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeT;AAEA,SAAS,aAAa,aAAoC;AACxD,QAAM,iBAAiB,cAAc,KAAK,UAAU,WAAW,IAAI;AACnE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["program","server","url","atelierFiles"]}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
renderRef,
|
|
10
10
|
renderShape,
|
|
11
11
|
renderText
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-C5DBTHXB.js";
|
|
13
13
|
export {
|
|
14
14
|
ImageCache,
|
|
15
15
|
applyFill,
|
|
@@ -22,4 +22,4 @@ export {
|
|
|
22
22
|
renderShape,
|
|
23
23
|
renderText
|
|
24
24
|
};
|
|
25
|
-
//# sourceMappingURL=dist-
|
|
25
|
+
//# sourceMappingURL=dist-6IHF7WA7.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -223,6 +223,8 @@ function resolveEasing(easing) {
|
|
|
223
223
|
if (!easing) return linear;
|
|
224
224
|
if (typeof easing === "string") {
|
|
225
225
|
switch (easing) {
|
|
226
|
+
case "linear":
|
|
227
|
+
return linear;
|
|
226
228
|
case "ease-in":
|
|
227
229
|
return easeIn;
|
|
228
230
|
case "ease-out":
|
|
@@ -440,7 +442,7 @@ function groupDeltas(deltas) {
|
|
|
440
442
|
return map;
|
|
441
443
|
}
|
|
442
444
|
function rangesOverlap(a, b) {
|
|
443
|
-
return a[0]
|
|
445
|
+
return a[0] < b[1] && b[0] < a[1];
|
|
444
446
|
}
|
|
445
447
|
function validateAllDeltas(deltas) {
|
|
446
448
|
const errors = [];
|
|
@@ -1540,7 +1542,7 @@ var StepEasingSchema = import_zod5.z.object({
|
|
|
1540
1542
|
steps: import_zod5.z.number().int().positive(),
|
|
1541
1543
|
position: import_zod5.z.enum(["start", "end"]).optional()
|
|
1542
1544
|
});
|
|
1543
|
-
var EasingPresetSchema = import_zod5.z.enum(["ease-in", "ease-out", "ease-in-out"]);
|
|
1545
|
+
var EasingPresetSchema = import_zod5.z.enum(["linear", "ease-in", "ease-out", "ease-in-out"]);
|
|
1544
1546
|
var EasingSchema = import_zod5.z.union([
|
|
1545
1547
|
LinearEasingSchema,
|
|
1546
1548
|
CubicBezierEasingSchema,
|