@a-company/atelier 0.27.4 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -1379,6 +1379,7 @@ var init_dist3 = __esm({
1379
1379
  });
1380
1380
 
1381
1381
  // src/cli.ts
1382
+ var import_node_module = require("module");
1382
1383
  var import_commander = require("commander");
1383
1384
 
1384
1385
  // src/commands/validate.ts
@@ -3660,7 +3661,7 @@ function getInlineHTML() {
3660
3661
  }
3661
3662
  function getInlineApp(initialFile) {
3662
3663
  const initialFileStr = initialFile ? JSON.stringify(initialFile) : "null";
3663
- return `import { AtelierStudio } from "@a-company/atelier-studio";
3664
+ return `import { AtelierStudio, exportDocument, ImageCache } from "@a-company/atelier-studio";
3664
3665
  import "@a-company/atelier-studio/styles.css";
3665
3666
  import { parseAtelier, serializeAtelier } from "@a-company/atelier-schema";
3666
3667
 
@@ -3696,6 +3697,100 @@ async function saveFileContent(path: string, content: string): Promise<void> {
3696
3697
  });
3697
3698
  }
3698
3699
 
3700
+ async function saveExportBlob(path: string, blob: Blob): Promise<void> {
3701
+ const buf = await blob.arrayBuffer();
3702
+ await fetch("/api/export?path=" + encodeURIComponent(path), {
3703
+ method: "POST",
3704
+ headers: { "Content-Type": "application/octet-stream" },
3705
+ body: buf,
3706
+ });
3707
+ }
3708
+
3709
+ async function exportAll(format: "gif" | "mp4" | "webm"): Promise<void> {
3710
+ if (files.length === 0) return;
3711
+
3712
+ // Create progress overlay
3713
+ const overlay = document.createElement("div");
3714
+ overlay.style.cssText = "position:fixed;inset:0;background:rgba(0,0,0,0.75);display:flex;align-items:center;justify-content:center;z-index:10000";
3715
+ const card = document.createElement("div");
3716
+ card.style.cssText = "background:#333;border:1px solid #4A4A4A;border-radius:8px;padding:32px 40px;min-width:360px;color:#F5F0EB;font-family:'Cormorant Garamond',Georgia,serif";
3717
+ overlay.appendChild(card);
3718
+ document.body.appendChild(overlay);
3719
+
3720
+ const title = document.createElement("div");
3721
+ title.style.cssText = "font-size:18px;margin-bottom:16px;font-weight:600";
3722
+ title.textContent = "Exporting All Files\u2026";
3723
+ card.appendChild(title);
3724
+
3725
+ const fileLabel = document.createElement("div");
3726
+ fileLabel.style.cssText = "font-size:13px;color:#A89F95;margin-bottom:8px;font-family:'SF Mono','Fira Code',monospace";
3727
+ card.appendChild(fileLabel);
3728
+
3729
+ const progress = document.createElement("progress");
3730
+ progress.style.cssText = "width:100%;height:6px;appearance:none;-webkit-appearance:none";
3731
+ progress.max = files.length;
3732
+ progress.value = 0;
3733
+ card.appendChild(progress);
3734
+
3735
+ const statusText = document.createElement("div");
3736
+ statusText.style.cssText = "font-size:12px;color:#A89F95;margin-top:8px";
3737
+ card.appendChild(statusText);
3738
+
3739
+ let exported = 0;
3740
+ let errors = 0;
3741
+
3742
+ for (const file of files) {
3743
+ fileLabel.textContent = file.path;
3744
+ statusText.textContent = (exported + errors + 1) + " / " + files.length;
3745
+
3746
+ try {
3747
+ const content = await fetchFileContent(file.path);
3748
+ const result = parseAtelier(content);
3749
+ if (!result.success) {
3750
+ errors++;
3751
+ progress.value = exported + errors;
3752
+ continue;
3753
+ }
3754
+
3755
+ const doc = result.data;
3756
+ const w = doc.canvas.width;
3757
+ const h = doc.canvas.height;
3758
+ const canvas = document.createElement("canvas");
3759
+ canvas.width = w;
3760
+ canvas.height = h;
3761
+ const imageCache = new ImageCache();
3762
+
3763
+ const exportResult = await exportDocument(doc, canvas, imageCache, {
3764
+ format,
3765
+ onProgress: ({ percent }) => {
3766
+ statusText.textContent = (exported + errors + 1) + " / " + files.length + " \u2014 " + percent + "%";
3767
+ },
3768
+ });
3769
+
3770
+ // Save alongside the source file: e.g. "dir/my-anim.atelier" \u2192 "dir/my-anim.gif"
3771
+ const outPath = file.path.replace(/\\.atelier$/, "." + format);
3772
+ await saveExportBlob(outPath, exportResult.blob);
3773
+ exported++;
3774
+ } catch (e) {
3775
+ console.error("Export failed:", file.path, e);
3776
+ errors++;
3777
+ }
3778
+ progress.value = exported + errors;
3779
+ }
3780
+
3781
+ // Done
3782
+ title.textContent = "Export Complete";
3783
+ fileLabel.textContent = "";
3784
+ statusText.textContent = exported + " exported" + (errors > 0 ? ", " + errors + " failed" : "");
3785
+ if (errors > 0) console.warn("Export All finished with " + errors + " error(s). Check console for details.");
3786
+
3787
+ const closeBtn = document.createElement("button");
3788
+ closeBtn.style.cssText = "margin-top:16px;padding:6px 20px;background:#3D3D3D;color:#F5F0EB;border:1px solid #4A4A4A;border-radius:4px;cursor:pointer;font-family:inherit;font-size:13px";
3789
+ closeBtn.textContent = "Close";
3790
+ closeBtn.addEventListener("click", () => document.body.removeChild(overlay));
3791
+ card.appendChild(closeBtn);
3792
+ }
3793
+
3699
3794
  // \u2500\u2500 Theme (matches branded theme from showcase) \u2500\u2500
3700
3795
  const theme = {
3701
3796
  bg: "#2C2C2C",
@@ -3842,6 +3937,26 @@ const sidebarList = document.createElement("div");
3842
3937
  sidebarList.className = "sidebar__list";
3843
3938
  sidebar.appendChild(sidebarList);
3844
3939
 
3940
+ const sidebarFooter = document.createElement("div");
3941
+ sidebarFooter.style.cssText = "padding:12px 16px;border-top:1px solid #4A4A4A;display:flex;gap:8px;align-items:center";
3942
+ const exportAllSelect = document.createElement("select");
3943
+ exportAllSelect.style.cssText = "flex:1;background:#3D3D3D;color:#F5F0EB;border:1px solid #4A4A4A;border-radius:4px;padding:4px 8px;font-size:11px;font-family:'SF Mono','Fira Code',monospace;cursor:pointer";
3944
+ for (const [val, label] of [["gif","GIF"],["mp4","MP4"],["webm","WebM"]] as const) {
3945
+ const o = document.createElement("option");
3946
+ o.value = val;
3947
+ o.textContent = label;
3948
+ exportAllSelect.appendChild(o);
3949
+ }
3950
+ sidebarFooter.appendChild(exportAllSelect);
3951
+ const exportAllBtn = document.createElement("button");
3952
+ exportAllBtn.style.cssText = "background:#C75B39;color:#F5F0EB;border:none;border-radius:4px;padding:5px 12px;font-size:11px;font-family:inherit;cursor:pointer;white-space:nowrap";
3953
+ exportAllBtn.textContent = "Export All";
3954
+ exportAllBtn.addEventListener("click", () => {
3955
+ exportAll(exportAllSelect.value as "gif" | "mp4" | "webm");
3956
+ });
3957
+ sidebarFooter.appendChild(exportAllBtn);
3958
+ sidebar.appendChild(sidebarFooter);
3959
+
3845
3960
  const main = document.createElement("div");
3846
3961
  main.className = "main";
3847
3962
 
@@ -3906,6 +4021,9 @@ async function loadFile(path: string): Promise<void> {
3906
4021
  studio = null;
3907
4022
  }
3908
4023
 
4024
+ // Set filename for export downloads (strip path and .atelier extension)
4025
+ const baseName = path.split("/").pop()?.replace(/\\.atelier$/, "") || null;
4026
+
3909
4027
  studio = new AtelierStudio(editorContainer, {
3910
4028
  mode: "full",
3911
4029
  initialTab: "yaml",
@@ -3932,6 +4050,7 @@ async function loadFile(path: string): Promise<void> {
3932
4050
  },
3933
4051
  });
3934
4052
  studio.setTheme(theme);
4053
+ studio.setFilename(baseName);
3935
4054
  studio.loadDocument(result.data);
3936
4055
  }
3937
4056
 
@@ -4064,6 +4183,30 @@ function studioCommand(program2) {
4064
4183
  return;
4065
4184
  }
4066
4185
  }
4186
+ if (url2.pathname === "/api/export" && req.method === "POST") {
4187
+ const filePath = url2.searchParams.get("path");
4188
+ if (!filePath || !isSafePath(filePath)) {
4189
+ res.statusCode = 400;
4190
+ res.end("Invalid path");
4191
+ return;
4192
+ }
4193
+ const absPath = (0, import_node_path9.resolve)(cwd, filePath);
4194
+ const chunks = [];
4195
+ req.on("data", (chunk) => {
4196
+ chunks.push(chunk);
4197
+ });
4198
+ req.on("end", () => {
4199
+ try {
4200
+ (0, import_node_fs9.mkdirSync)((0, import_node_path9.dirname)(absPath), { recursive: true });
4201
+ (0, import_node_fs9.writeFileSync)(absPath, Buffer.concat(chunks));
4202
+ res.end("OK");
4203
+ } catch {
4204
+ res.statusCode = 500;
4205
+ res.end("Write failed");
4206
+ }
4207
+ });
4208
+ return;
4209
+ }
4067
4210
  if (url2.pathname === "/api/cwd") {
4068
4211
  res.setHeader("Content-Type", "application/json");
4069
4212
  res.end(JSON.stringify({ cwd }));
@@ -4106,8 +4249,9 @@ function studioCommand(program2) {
4106
4249
  }
4107
4250
 
4108
4251
  // src/cli.ts
4252
+ var import_meta2 = {};
4109
4253
  var program = new import_commander.Command();
4110
- program.name("atelier").description("Atelier animation CLI").version("0.1.0");
4254
+ program.name("atelier").description("Atelier animation CLI").version((0, import_node_module.createRequire)(import_meta2.url)("../package.json").version);
4111
4255
  validateCommand(program);
4112
4256
  infoCommand(program);
4113
4257
  stillCommand(program);