@a-company/atelier 0.28.2 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -420,7 +420,18 @@ function resolveFrame(doc, stateName, frame, overrideDeltas) {
420
420
  }
421
421
  }
422
422
  }
423
- return { id: layer.id, layer, computedProperties };
423
+ const resolvedLayer = { id: layer.id, layer, computedProperties };
424
+ if (layer.visual.type === "video") {
425
+ const video = layer.visual;
426
+ const fps = doc.canvas.fps;
427
+ const startFrame = video.startFrame ?? 0;
428
+ const sourceOffset = video.sourceOffset ?? 0;
429
+ const playbackRate = video.playbackRate ?? 1;
430
+ const relativeFrame = Math.max(0, frame - startFrame);
431
+ const sourceTime = relativeFrame / fps * playbackRate + sourceOffset;
432
+ resolvedLayer.videoSourceTime = video.sourceEnd !== void 0 ? Math.min(sourceTime, video.sourceEnd) : sourceTime;
433
+ }
434
+ return resolvedLayer;
424
435
  });
425
436
  return { frame, stateName, layers: resolvedLayers };
426
437
  }
@@ -1027,6 +1038,14 @@ function renderImage(ctx, eff, imageCache) {
1027
1038
  }
1028
1039
  ctx.drawImage(img, 0, 0, eff.width, eff.height);
1029
1040
  }
1041
+ function renderVideo(ctx, eff, sourceTime, provider) {
1042
+ const visual = eff.visual;
1043
+ const src = visual.src;
1044
+ if (!src) return;
1045
+ const frame = provider(src, sourceTime, eff.width, eff.height);
1046
+ if (!frame) return;
1047
+ ctx.drawImage(frame, 0, 0, eff.width, eff.height);
1048
+ }
1030
1049
  function renderRef(ctx, eff, opts, _parentDoc) {
1031
1050
  const visual = eff.visual;
1032
1051
  const resolver = opts?.documentResolver;
@@ -1124,6 +1143,7 @@ function renderPlaceholder(ctx, eff, label) {
1124
1143
  function renderFrame(ctx, resolvedFrame, doc, optsOrCache) {
1125
1144
  let imageCache;
1126
1145
  let documentResolver;
1146
+ let videoFrameProvider;
1127
1147
  let maxRefDepth = 4;
1128
1148
  if (optsOrCache && typeof optsOrCache.get === "function") {
1129
1149
  imageCache = optsOrCache;
@@ -1131,6 +1151,7 @@ function renderFrame(ctx, resolvedFrame, doc, optsOrCache) {
1131
1151
  const opts = optsOrCache;
1132
1152
  imageCache = opts.imageCache;
1133
1153
  documentResolver = opts.documentResolver;
1154
+ videoFrameProvider = opts.videoFrameProvider;
1134
1155
  maxRefDepth = opts.maxRefDepth ?? 4;
1135
1156
  }
1136
1157
  const { width, height } = doc.canvas;
@@ -1138,10 +1159,14 @@ function renderFrame(ctx, resolvedFrame, doc, optsOrCache) {
1138
1159
  ctx.fillRect(0, 0, width, height);
1139
1160
  const effMap = /* @__PURE__ */ new Map();
1140
1161
  const effList = [];
1162
+ const videoSourceTimeMap = /* @__PURE__ */ new Map();
1141
1163
  for (const resolvedLayer of resolvedFrame.layers) {
1142
1164
  const eff = buildEffectiveLayer(resolvedLayer, width, height);
1143
1165
  effMap.set(resolvedLayer.layer.id, eff);
1144
1166
  effList.push(eff);
1167
+ if (resolvedLayer.videoSourceTime !== void 0) {
1168
+ videoSourceTimeMap.set(resolvedLayer.layer.id, resolvedLayer.videoSourceTime);
1169
+ }
1145
1170
  }
1146
1171
  for (const eff of effList) {
1147
1172
  const { layer } = eff;
@@ -1153,6 +1178,12 @@ function renderFrame(ctx, resolvedFrame, doc, optsOrCache) {
1153
1178
  iv.src = doc.assets[iv.assetId].src;
1154
1179
  }
1155
1180
  }
1181
+ if (layer.visual.type === "video") {
1182
+ const vv = eff.visual;
1183
+ if (!vv.src && vv.assetId && doc.assets?.[vv.assetId]) {
1184
+ vv.src = doc.assets[vv.assetId].src;
1185
+ }
1186
+ }
1156
1187
  ctx.save();
1157
1188
  applyAncestorTransforms(ctx, layer.id, effMap, doc);
1158
1189
  ctx.globalAlpha = eff.opacity;
@@ -1200,6 +1231,11 @@ function renderFrame(ctx, resolvedFrame, doc, optsOrCache) {
1200
1231
  case "image":
1201
1232
  if (imageCache) renderImage(renderCtx, eff, imageCache);
1202
1233
  break;
1234
+ case "video":
1235
+ if (videoFrameProvider) {
1236
+ renderVideo(renderCtx, eff, videoSourceTimeMap.get(layer.id) ?? 0, videoFrameProvider);
1237
+ }
1238
+ break;
1203
1239
  case "group":
1204
1240
  break;
1205
1241
  case "ref":
@@ -1225,6 +1261,9 @@ function renderFrame(ctx, resolvedFrame, doc, optsOrCache) {
1225
1261
  case "image":
1226
1262
  if (imageCache) renderImage(offCtx, eff, imageCache);
1227
1263
  break;
1264
+ case "video":
1265
+ if (videoFrameProvider) renderVideo(offCtx, eff, videoSourceTimeMap.get(layer.id) ?? 0, videoFrameProvider);
1266
+ break;
1228
1267
  case "ref":
1229
1268
  renderRef(offCtx, eff, refOpts, doc);
1230
1269
  break;
@@ -1240,6 +1279,9 @@ function renderFrame(ctx, resolvedFrame, doc, optsOrCache) {
1240
1279
  case "image":
1241
1280
  if (imageCache) renderImage(ctx, eff, imageCache);
1242
1281
  break;
1282
+ case "video":
1283
+ if (videoFrameProvider) renderVideo(ctx, eff, videoSourceTimeMap.get(layer.id) ?? 0, videoFrameProvider);
1284
+ break;
1243
1285
  case "ref":
1244
1286
  renderRef(ctx, eff, refOpts, doc);
1245
1287
  break;
@@ -1642,6 +1684,18 @@ var ImageVisualSchema = import_zod7.z.object({
1642
1684
  spritesheet: SpritesheetConfigSchema.optional(),
1643
1685
  frameIndex: import_zod7.z.number().int().min(0).optional()
1644
1686
  });
1687
+ var VideoVisualSchema = import_zod7.z.object({
1688
+ type: import_zod7.z.literal("video"),
1689
+ assetId: import_zod7.z.string().min(1, "assetId is required"),
1690
+ src: import_zod7.z.string().optional(),
1691
+ startFrame: import_zod7.z.number().int().min(0).optional(),
1692
+ sourceOffset: import_zod7.z.number().min(0).optional(),
1693
+ sourceEnd: import_zod7.z.number().positive().optional(),
1694
+ playbackRate: import_zod7.z.number().positive().optional(),
1695
+ volume: import_zod7.z.number().min(0).max(1).optional(),
1696
+ muted: import_zod7.z.boolean().optional(),
1697
+ objectFit: import_zod7.z.enum(["contain", "cover", "fill"]).optional()
1698
+ });
1645
1699
  var GroupVisualSchema = import_zod7.z.object({
1646
1700
  type: import_zod7.z.literal("group")
1647
1701
  });
@@ -1655,6 +1709,7 @@ var VisualSchema = import_zod7.z.discriminatedUnion("type", [
1655
1709
  ShapeVisualSchema,
1656
1710
  TextVisualSchema,
1657
1711
  ImageVisualSchema,
1712
+ VideoVisualSchema,
1658
1713
  GroupVisualSchema,
1659
1714
  RefVisualSchema
1660
1715
  ]);
@@ -1774,7 +1829,7 @@ var VariableSchema = import_zod12.z.object({
1774
1829
  default: import_zod12.z.unknown().optional(),
1775
1830
  description: import_zod12.z.string().optional()
1776
1831
  });
1777
- var AssetTypeSchema = import_zod13.z.enum(["image", "svg", "font", "animation", "audio"]);
1832
+ var AssetTypeSchema = import_zod13.z.enum(["image", "svg", "font", "animation", "audio", "video"]);
1778
1833
  var AssetSchema = import_zod13.z.object({
1779
1834
  type: AssetTypeSchema,
1780
1835
  src: import_zod13.z.string().min(1, "Asset src is required"),
@@ -1785,6 +1840,12 @@ var AssetSchema = import_zod13.z.object({
1785
1840
  frameCount: import_zod13.z.number().int().positive().optional(),
1786
1841
  frameWidth: import_zod13.z.number().positive(),
1787
1842
  frameHeight: import_zod13.z.number().positive()
1843
+ }).optional(),
1844
+ videoMeta: import_zod13.z.object({
1845
+ duration: import_zod13.z.number().positive("videoMeta.duration must be positive"),
1846
+ fps: import_zod13.z.number().positive("videoMeta.fps must be positive"),
1847
+ width: import_zod13.z.number().int().positive(),
1848
+ height: import_zod13.z.number().int().positive()
1788
1849
  }).optional()
1789
1850
  });
1790
1851
  var CanvasSchema = import_zod14.z.object({
@@ -1818,6 +1879,35 @@ function validateDocument(input) {
1818
1879
  }
1819
1880
  return { success: false, errors: formatErrors(result.error) };
1820
1881
  }
1882
+ function validateVideoLayer(visual, videoMetaDuration) {
1883
+ const errors = [];
1884
+ if (!visual.assetId) {
1885
+ errors.push({ path: "assetId", message: "assetId is required" });
1886
+ }
1887
+ const sourceOffset = visual.sourceOffset ?? 0;
1888
+ if (visual.sourceEnd !== void 0) {
1889
+ if (visual.sourceEnd <= sourceOffset) {
1890
+ errors.push({
1891
+ path: "sourceEnd",
1892
+ message: `sourceEnd (${visual.sourceEnd}) must be greater than sourceOffset (${sourceOffset})`
1893
+ });
1894
+ }
1895
+ if (videoMetaDuration !== void 0 && visual.sourceEnd > videoMetaDuration) {
1896
+ errors.push({
1897
+ path: "sourceEnd",
1898
+ message: `sourceEnd (${visual.sourceEnd}) exceeds asset duration (${videoMetaDuration})`
1899
+ });
1900
+ }
1901
+ }
1902
+ if (videoMetaDuration !== void 0 && sourceOffset >= videoMetaDuration) {
1903
+ errors.push({
1904
+ path: "sourceOffset",
1905
+ message: `sourceOffset (${sourceOffset}) is at or beyond asset duration (${videoMetaDuration})`
1906
+ });
1907
+ }
1908
+ if (errors.length > 0) return { success: false, errors };
1909
+ return { success: true, data: visual };
1910
+ }
1821
1911
  function parseAtelier(yamlString) {
1822
1912
  let parsed;
1823
1913
  try {
@@ -1877,9 +1967,105 @@ function validateCommand(program2) {
1877
1967
  });
1878
1968
  }
1879
1969
 
1880
- // src/commands/info.ts
1970
+ // src/commands/lint.ts
1881
1971
  var import_node_fs2 = require("fs");
1882
1972
  var import_node_path2 = require("path");
1973
+ init_dist2();
1974
+ function lintFile(filePath) {
1975
+ const absPath = (0, import_node_path2.resolve)(filePath);
1976
+ let content;
1977
+ try {
1978
+ content = (0, import_node_fs2.readFileSync)(absPath, "utf-8");
1979
+ } catch {
1980
+ return {
1981
+ file: absPath,
1982
+ valid: false,
1983
+ gates: [
1984
+ {
1985
+ gate: "^valid-document",
1986
+ pass: false,
1987
+ errors: [`Cannot read file: ${absPath}`]
1988
+ }
1989
+ ]
1990
+ };
1991
+ }
1992
+ const gates = [];
1993
+ const parseResult = parseAtelier(content);
1994
+ if (!parseResult.success) {
1995
+ gates.push({
1996
+ gate: "^valid-document",
1997
+ pass: false,
1998
+ errors: parseResult.errors.map((e) => `${e.path}: ${e.message}`)
1999
+ });
2000
+ return { file: absPath, valid: false, gates };
2001
+ }
2002
+ gates.push({ gate: "^valid-document", pass: true, errors: [] });
2003
+ const doc = parseResult.data;
2004
+ const deltaErrors = [];
2005
+ for (const [stateName, state] of Object.entries(doc.states)) {
2006
+ const overlaps = validateAllDeltas(state.deltas);
2007
+ for (const overlap of overlaps) {
2008
+ deltaErrors.push(`State "${stateName}": ${overlap.message}`);
2009
+ }
2010
+ }
2011
+ gates.push({
2012
+ gate: "^valid-delta",
2013
+ pass: deltaErrors.length === 0,
2014
+ errors: deltaErrors
2015
+ });
2016
+ const videoErrors = [];
2017
+ for (const layer of doc.layers) {
2018
+ if (layer.visual.type !== "video") continue;
2019
+ const visual = layer.visual;
2020
+ const duration = doc.assets?.[visual.assetId]?.videoMeta?.duration;
2021
+ const result = validateVideoLayer(visual, duration);
2022
+ if (!result.success) {
2023
+ for (const err of result.errors) {
2024
+ videoErrors.push(`Layer "${layer.id}" (${err.path}): ${err.message}`);
2025
+ }
2026
+ }
2027
+ }
2028
+ gates.push({
2029
+ gate: "^valid-video-layer",
2030
+ pass: videoErrors.length === 0,
2031
+ errors: videoErrors
2032
+ });
2033
+ const valid = gates.every((g) => g.pass);
2034
+ return { file: absPath, valid, gates };
2035
+ }
2036
+ function formatResult(result) {
2037
+ const lines = [];
2038
+ const status = result.valid ? "PASS" : "FAIL";
2039
+ lines.push(`${status} ${result.file}`);
2040
+ for (const gate of result.gates) {
2041
+ const gateStatus = gate.pass ? " \u2713" : " \u2717";
2042
+ lines.push(`${gateStatus} ${gate.gate}`);
2043
+ for (const err of gate.errors) {
2044
+ lines.push(` ${err}`);
2045
+ }
2046
+ }
2047
+ return lines.join("\n");
2048
+ }
2049
+ function lintCommand(program2) {
2050
+ program2.command("lint <files...>").description(
2051
+ "Lint .atelier files against all gates (^valid-document, ^valid-delta, ^valid-video-layer)"
2052
+ ).option("--json", "Output results as JSON array").action((files, opts) => {
2053
+ const results = files.map(lintFile);
2054
+ if (opts.json) {
2055
+ console.log(JSON.stringify(results, null, 2));
2056
+ } else {
2057
+ for (const result of results) {
2058
+ console.log(formatResult(result));
2059
+ }
2060
+ }
2061
+ const allValid = results.every((r) => r.valid);
2062
+ if (!allValid) process.exit(1);
2063
+ });
2064
+ }
2065
+
2066
+ // src/commands/info.ts
2067
+ var import_node_fs3 = require("fs");
2068
+ var import_node_path3 = require("path");
1883
2069
  function getInfo(doc) {
1884
2070
  return {
1885
2071
  name: doc.name,
@@ -1936,10 +2122,10 @@ function formatInfo(info) {
1936
2122
  return lines.join("\n");
1937
2123
  }
1938
2124
  function readAndParse(file) {
1939
- const absPath = (0, import_node_path2.resolve)(file);
2125
+ const absPath = (0, import_node_path3.resolve)(file);
1940
2126
  let content;
1941
2127
  try {
1942
- content = (0, import_node_fs2.readFileSync)(absPath, "utf-8");
2128
+ content = (0, import_node_fs3.readFileSync)(absPath, "utf-8");
1943
2129
  } catch {
1944
2130
  console.error(`Cannot read file: ${absPath}`);
1945
2131
  return process.exit(1);
@@ -1963,8 +2149,8 @@ function infoCommand(program2) {
1963
2149
  }
1964
2150
 
1965
2151
  // src/commands/still.ts
1966
- var import_node_fs3 = require("fs");
1967
- var import_node_path3 = require("path");
2152
+ var import_node_fs4 = require("fs");
2153
+ var import_node_path4 = require("path");
1968
2154
  init_dist2();
1969
2155
  function resolveStill(doc, stateName, frame) {
1970
2156
  const stateNames = Object.keys(doc.states);
@@ -1981,10 +2167,10 @@ function resolveStill(doc, stateName, frame) {
1981
2167
  return resolveFrame(doc, resolvedStateName, resolvedFrame);
1982
2168
  }
1983
2169
  function readAndParse2(file) {
1984
- const absPath = (0, import_node_path3.resolve)(file);
2170
+ const absPath = (0, import_node_path4.resolve)(file);
1985
2171
  let content;
1986
2172
  try {
1987
- content = (0, import_node_fs3.readFileSync)(absPath, "utf-8");
2173
+ content = (0, import_node_fs4.readFileSync)(absPath, "utf-8");
1988
2174
  } catch {
1989
2175
  console.error(`Cannot read file: ${absPath}`);
1990
2176
  return process.exit(1);
@@ -2035,7 +2221,7 @@ function stillCommand(program2) {
2035
2221
  if (options.format === "json") {
2036
2222
  const json = JSON.stringify(resolved, null, 2);
2037
2223
  if (options.output) {
2038
- (0, import_node_fs3.writeFileSync)((0, import_node_path3.resolve)(options.output), json, "utf-8");
2224
+ (0, import_node_fs4.writeFileSync)((0, import_node_path4.resolve)(options.output), json, "utf-8");
2039
2225
  } else {
2040
2226
  console.log(json);
2041
2227
  }
@@ -2055,7 +2241,7 @@ function stillCommand(program2) {
2055
2241
  renderFrame2(ctx, resolved, doc);
2056
2242
  const buffer = cvs.toBuffer("image/png");
2057
2243
  if (options.output) {
2058
- (0, import_node_fs3.writeFileSync)((0, import_node_path3.resolve)(options.output), buffer);
2244
+ (0, import_node_fs4.writeFileSync)((0, import_node_path4.resolve)(options.output), buffer);
2059
2245
  } else {
2060
2246
  process.stdout.write(buffer);
2061
2247
  }
@@ -2071,18 +2257,18 @@ function stillCommand(program2) {
2071
2257
  }
2072
2258
 
2073
2259
  // src/commands/render.ts
2074
- var import_node_fs4 = require("fs");
2075
- var import_node_path4 = require("path");
2260
+ var import_node_fs5 = require("fs");
2261
+ var import_node_path5 = require("path");
2076
2262
 
2077
2263
  // src/commands/render-pipeline.ts
2078
2264
  var import_node_child_process = require("child_process");
2079
2265
  init_dist2();
2080
2266
  init_dist3();
2081
2267
  async function checkFfmpeg() {
2082
- return new Promise((resolve10) => {
2268
+ return new Promise((resolve11) => {
2083
2269
  const proc = (0, import_node_child_process.spawn)("ffmpeg", ["-version"], { stdio: "pipe" });
2084
- proc.on("error", () => resolve10(false));
2085
- proc.on("close", (code) => resolve10(code === 0));
2270
+ proc.on("error", () => resolve11(false));
2271
+ proc.on("close", (code) => resolve11(code === 0));
2086
2272
  });
2087
2273
  }
2088
2274
  function buildFfmpegArgs(width, height, fps, format, output) {
@@ -2162,7 +2348,7 @@ async function preloadImages(doc, loadImage) {
2162
2348
  for (const src of preloaded.keys()) {
2163
2349
  imageCache.load(src);
2164
2350
  }
2165
- await new Promise((resolve10) => process.nextTick(resolve10));
2351
+ await new Promise((resolve11) => process.nextTick(resolve11));
2166
2352
  return imageCache;
2167
2353
  }
2168
2354
  async function renderDocument(doc, opts) {
@@ -2226,7 +2412,7 @@ async function renderDocument(doc, opts) {
2226
2412
  const canWrite = ffmpeg.stdin.write(raw);
2227
2413
  if (!canWrite) {
2228
2414
  await new Promise(
2229
- (resolve10) => ffmpeg.stdin.once("drain", resolve10)
2415
+ (resolve11) => ffmpeg.stdin.once("drain", resolve11)
2230
2416
  );
2231
2417
  }
2232
2418
  frameIndex++;
@@ -2239,8 +2425,8 @@ async function renderDocument(doc, opts) {
2239
2425
  }
2240
2426
  }
2241
2427
  ffmpeg.stdin.end();
2242
- const exitCode = await new Promise((resolve10) => {
2243
- ffmpeg.on("close", resolve10);
2428
+ const exitCode = await new Promise((resolve11) => {
2429
+ ffmpeg.on("close", resolve11);
2244
2430
  });
2245
2431
  if (exitCode !== 0) {
2246
2432
  throw new Error(
@@ -2259,10 +2445,10 @@ ${stderrOutput.slice(-500)}`
2259
2445
 
2260
2446
  // src/commands/render.ts
2261
2447
  function readAndParse3(file) {
2262
- const absPath = (0, import_node_path4.resolve)(file);
2448
+ const absPath = (0, import_node_path5.resolve)(file);
2263
2449
  let content;
2264
2450
  try {
2265
- content = (0, import_node_fs4.readFileSync)(absPath, "utf-8");
2451
+ content = (0, import_node_fs5.readFileSync)(absPath, "utf-8");
2266
2452
  } catch {
2267
2453
  console.error(`Cannot read file: ${absPath}`);
2268
2454
  return process.exit(1);
@@ -2279,7 +2465,7 @@ function readAndParse3(file) {
2279
2465
  }
2280
2466
  function inferFormat(output) {
2281
2467
  if (!output) return "mp4";
2282
- const ext = (0, import_node_path4.extname)(output).toLowerCase();
2468
+ const ext = (0, import_node_path5.extname)(output).toLowerCase();
2283
2469
  if (ext === ".gif") return "gif";
2284
2470
  return "mp4";
2285
2471
  }
@@ -2313,12 +2499,12 @@ function renderCommand(program2) {
2313
2499
  } else {
2314
2500
  format = inferFormat(options.output);
2315
2501
  }
2316
- const inputName = (0, import_node_path4.basename)(file, (0, import_node_path4.extname)(file));
2502
+ const inputName = (0, import_node_path5.basename)(file, (0, import_node_path5.extname)(file));
2317
2503
  const output = options.output ?? `${inputName}.${format}`;
2318
2504
  const startTime = Date.now();
2319
2505
  try {
2320
2506
  const result = await renderDocument(doc, {
2321
- output: (0, import_node_path4.resolve)(output),
2507
+ output: (0, import_node_path5.resolve)(output),
2322
2508
  format,
2323
2509
  states: options.state,
2324
2510
  onProgress: ({ frame, totalFrames, state, percent }) => {
@@ -2344,8 +2530,8 @@ function renderCommand(program2) {
2344
2530
  }
2345
2531
 
2346
2532
  // src/commands/export-svg.ts
2347
- var import_node_fs5 = require("fs");
2348
- var import_node_path5 = require("path");
2533
+ var import_node_fs6 = require("fs");
2534
+ var import_node_path6 = require("path");
2349
2535
 
2350
2536
  // ../svg/dist/index.js
2351
2537
  init_dist2();
@@ -2810,10 +2996,10 @@ function renderRefSVG(eff, visual, _parentDoc, opts, _depth, _visitedRefs) {
2810
2996
 
2811
2997
  // src/commands/export-svg.ts
2812
2998
  function readAndParse4(file) {
2813
- const absPath = (0, import_node_path5.resolve)(file);
2999
+ const absPath = (0, import_node_path6.resolve)(file);
2814
3000
  let content;
2815
3001
  try {
2816
- content = (0, import_node_fs5.readFileSync)(absPath, "utf-8");
3002
+ content = (0, import_node_fs6.readFileSync)(absPath, "utf-8");
2817
3003
  } catch {
2818
3004
  console.error(`Cannot read file: ${absPath}`);
2819
3005
  return process.exit(1);
@@ -2855,7 +3041,7 @@ function exportSvgCommand(program2) {
2855
3041
  xmlDeclaration: options.xmlDeclaration
2856
3042
  });
2857
3043
  if (options.output) {
2858
- (0, import_node_fs5.writeFileSync)((0, import_node_path5.resolve)(options.output), svg, "utf-8");
3044
+ (0, import_node_fs6.writeFileSync)((0, import_node_path6.resolve)(options.output), svg, "utf-8");
2859
3045
  } else {
2860
3046
  console.log(svg);
2861
3047
  }
@@ -2868,8 +3054,8 @@ function exportSvgCommand(program2) {
2868
3054
  }
2869
3055
 
2870
3056
  // src/commands/export-lottie.ts
2871
- var import_node_fs6 = require("fs");
2872
- var import_node_path6 = require("path");
3057
+ var import_node_fs7 = require("fs");
3058
+ var import_node_path7 = require("path");
2873
3059
 
2874
3060
  // ../lottie/dist/index.js
2875
3061
  function colorToLottie(color) {
@@ -3438,10 +3624,10 @@ function exportToLottie(doc, opts) {
3438
3624
 
3439
3625
  // src/commands/export-lottie.ts
3440
3626
  function readAndParse5(file) {
3441
- const absPath = (0, import_node_path6.resolve)(file);
3627
+ const absPath = (0, import_node_path7.resolve)(file);
3442
3628
  let content;
3443
3629
  try {
3444
- content = (0, import_node_fs6.readFileSync)(absPath, "utf-8");
3630
+ content = (0, import_node_fs7.readFileSync)(absPath, "utf-8");
3445
3631
  } catch {
3446
3632
  console.error(`Cannot read file: ${absPath}`);
3447
3633
  return process.exit(1);
@@ -3469,7 +3655,7 @@ function exportLottieCommand(program2) {
3469
3655
  }
3470
3656
  const output = JSON.stringify(json, null, 2);
3471
3657
  if (options.output) {
3472
- (0, import_node_fs6.writeFileSync)((0, import_node_path6.resolve)(options.output), output, "utf-8");
3658
+ (0, import_node_fs7.writeFileSync)((0, import_node_path7.resolve)(options.output), output, "utf-8");
3473
3659
  } else {
3474
3660
  console.log(output);
3475
3661
  }
@@ -3482,8 +3668,8 @@ function exportLottieCommand(program2) {
3482
3668
  }
3483
3669
 
3484
3670
  // src/commands/assets.ts
3485
- var import_node_fs7 = require("fs");
3486
- var import_node_path7 = require("path");
3671
+ var import_node_fs8 = require("fs");
3672
+ var import_node_path8 = require("path");
3487
3673
  function getAssets(doc) {
3488
3674
  const assets = doc.assets ?? {};
3489
3675
  return Object.entries(assets).map(([assetId, asset]) => {
@@ -3515,10 +3701,10 @@ function formatAssets(assets) {
3515
3701
  return lines.join("\n");
3516
3702
  }
3517
3703
  function readAndParse6(file) {
3518
- const absPath = (0, import_node_path7.resolve)(file);
3704
+ const absPath = (0, import_node_path8.resolve)(file);
3519
3705
  let content;
3520
3706
  try {
3521
- content = (0, import_node_fs7.readFileSync)(absPath, "utf-8");
3707
+ content = (0, import_node_fs8.readFileSync)(absPath, "utf-8");
3522
3708
  } catch {
3523
3709
  console.error(`Cannot read file: ${absPath}`);
3524
3710
  return process.exit(1);
@@ -3542,8 +3728,8 @@ function assetsCommand(program2) {
3542
3728
  }
3543
3729
 
3544
3730
  // src/commands/variables.ts
3545
- var import_node_fs8 = require("fs");
3546
- var import_node_path8 = require("path");
3731
+ var import_node_fs9 = require("fs");
3732
+ var import_node_path9 = require("path");
3547
3733
  init_dist2();
3548
3734
  function getVariables(doc) {
3549
3735
  const variables = doc.variables ?? {};
@@ -3579,10 +3765,10 @@ function formatVariables(info) {
3579
3765
  return lines.join("\n");
3580
3766
  }
3581
3767
  function readAndParse7(file) {
3582
- const absPath = (0, import_node_path8.resolve)(file);
3768
+ const absPath = (0, import_node_path9.resolve)(file);
3583
3769
  let content;
3584
3770
  try {
3585
- content = (0, import_node_fs8.readFileSync)(absPath, "utf-8");
3771
+ content = (0, import_node_fs9.readFileSync)(absPath, "utf-8");
3586
3772
  } catch {
3587
3773
  console.error(`Cannot read file: ${absPath}`);
3588
3774
  return process.exit(1);
@@ -3606,8 +3792,8 @@ function variablesCommand(program2) {
3606
3792
  }
3607
3793
 
3608
3794
  // src/commands/studio.ts
3609
- var import_node_path9 = require("path");
3610
- var import_node_fs9 = require("fs");
3795
+ var import_node_path10 = require("path");
3796
+ var import_node_fs10 = require("fs");
3611
3797
  var import_node_os = require("os");
3612
3798
  var import_node_crypto = require("crypto");
3613
3799
  var import_node_child_process2 = require("child_process");
@@ -3616,30 +3802,30 @@ function findAtelierFiles(dir, base = dir) {
3616
3802
  const results = [];
3617
3803
  let entries;
3618
3804
  try {
3619
- entries = (0, import_node_fs9.readdirSync)(dir);
3805
+ entries = (0, import_node_fs10.readdirSync)(dir);
3620
3806
  } catch {
3621
3807
  return results;
3622
3808
  }
3623
3809
  for (const entry of entries) {
3624
3810
  if (entry === "node_modules" || entry === "dist" || entry === ".git") continue;
3625
- const full = (0, import_node_path9.join)(dir, entry);
3811
+ const full = (0, import_node_path10.join)(dir, entry);
3626
3812
  let stat;
3627
3813
  try {
3628
- stat = (0, import_node_fs9.statSync)(full);
3814
+ stat = (0, import_node_fs10.statSync)(full);
3629
3815
  } catch {
3630
3816
  continue;
3631
3817
  }
3632
3818
  if (stat.isDirectory()) {
3633
3819
  results.push(...findAtelierFiles(full, base));
3634
3820
  } else if (entry.endsWith(".atelier")) {
3635
- results.push((0, import_node_path9.relative)(base, full));
3821
+ results.push((0, import_node_path10.relative)(base, full));
3636
3822
  }
3637
3823
  }
3638
3824
  return results.sort();
3639
3825
  }
3640
3826
  function isSafePath(filePath) {
3641
3827
  if (!filePath || filePath.includes("..") || filePath.startsWith("/")) return false;
3642
- const resolved = (0, import_node_path9.resolve)(process.cwd(), filePath);
3828
+ const resolved = (0, import_node_path10.resolve)(process.cwd(), filePath);
3643
3829
  return resolved.startsWith(process.cwd());
3644
3830
  }
3645
3831
  function getInlineHTML() {
@@ -4093,17 +4279,17 @@ function studioCommand(program2) {
4093
4279
  process.exit(1);
4094
4280
  }
4095
4281
  const cwd = process.cwd();
4096
- const cliPackageDir = (0, import_node_path9.resolve)((0, import_node_path9.dirname)(new URL(import_meta.url).pathname), "..");
4282
+ const cliPackageDir = (0, import_node_path10.resolve)((0, import_node_path10.dirname)(new URL(import_meta.url).pathname), "..");
4097
4283
  const tmpId = (0, import_node_crypto.randomBytes)(4).toString("hex");
4098
- const tmpDirRaw = (0, import_node_path9.join)((0, import_node_os.tmpdir)(), `atelier-studio-${tmpId}`);
4099
- (0, import_node_fs9.mkdirSync)(tmpDirRaw, { recursive: true });
4100
- const tmpDir = (0, import_node_fs9.realpathSync)(tmpDirRaw);
4101
- (0, import_node_fs9.writeFileSync)((0, import_node_path9.join)(tmpDir, "index.html"), getInlineHTML());
4102
- (0, import_node_fs9.writeFileSync)((0, import_node_path9.join)(tmpDir, "main.ts"), getInlineApp(file ?? null));
4103
- const cliNodeModules = (0, import_node_path9.join)(cliPackageDir, "node_modules");
4104
- if ((0, import_node_fs9.existsSync)(cliNodeModules)) {
4284
+ const tmpDirRaw = (0, import_node_path10.join)((0, import_node_os.tmpdir)(), `atelier-studio-${tmpId}`);
4285
+ (0, import_node_fs10.mkdirSync)(tmpDirRaw, { recursive: true });
4286
+ const tmpDir = (0, import_node_fs10.realpathSync)(tmpDirRaw);
4287
+ (0, import_node_fs10.writeFileSync)((0, import_node_path10.join)(tmpDir, "index.html"), getInlineHTML());
4288
+ (0, import_node_fs10.writeFileSync)((0, import_node_path10.join)(tmpDir, "main.ts"), getInlineApp(file ?? null));
4289
+ const cliNodeModules = (0, import_node_path10.join)(cliPackageDir, "node_modules");
4290
+ if ((0, import_node_fs10.existsSync)(cliNodeModules)) {
4105
4291
  try {
4106
- (0, import_node_fs9.symlinkSync)(cliNodeModules, (0, import_node_path9.join)(tmpDir, "node_modules"), "dir");
4292
+ (0, import_node_fs10.symlinkSync)(cliNodeModules, (0, import_node_path10.join)(tmpDir, "node_modules"), "dir");
4107
4293
  } catch {
4108
4294
  }
4109
4295
  }
@@ -4154,10 +4340,10 @@ function studioCommand(program2) {
4154
4340
  res.end("Invalid path");
4155
4341
  return;
4156
4342
  }
4157
- const absPath = (0, import_node_path9.resolve)(cwd, filePath);
4343
+ const absPath = (0, import_node_path10.resolve)(cwd, filePath);
4158
4344
  if (req.method === "GET") {
4159
4345
  try {
4160
- const content = (0, import_node_fs9.readFileSync)(absPath, "utf-8");
4346
+ const content = (0, import_node_fs10.readFileSync)(absPath, "utf-8");
4161
4347
  res.setHeader("Content-Type", "text/plain");
4162
4348
  res.end(content);
4163
4349
  } catch {
@@ -4173,7 +4359,7 @@ function studioCommand(program2) {
4173
4359
  });
4174
4360
  req.on("end", () => {
4175
4361
  try {
4176
- (0, import_node_fs9.writeFileSync)(absPath, body, "utf-8");
4362
+ (0, import_node_fs10.writeFileSync)(absPath, body, "utf-8");
4177
4363
  res.end("OK");
4178
4364
  } catch {
4179
4365
  res.statusCode = 500;
@@ -4190,15 +4376,15 @@ function studioCommand(program2) {
4190
4376
  res.end("Invalid path");
4191
4377
  return;
4192
4378
  }
4193
- const absPath = (0, import_node_path9.resolve)(cwd, filePath);
4379
+ const absPath = (0, import_node_path10.resolve)(cwd, filePath);
4194
4380
  const chunks = [];
4195
4381
  req.on("data", (chunk) => {
4196
4382
  chunks.push(chunk);
4197
4383
  });
4198
4384
  req.on("end", () => {
4199
4385
  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));
4386
+ (0, import_node_fs10.mkdirSync)((0, import_node_path10.dirname)(absPath), { recursive: true });
4387
+ (0, import_node_fs10.writeFileSync)(absPath, Buffer.concat(chunks));
4202
4388
  res.end("OK");
4203
4389
  } catch {
4204
4390
  res.statusCode = 500;
@@ -4237,7 +4423,7 @@ function studioCommand(program2) {
4237
4423
  console.log("\nShutting down...");
4238
4424
  server.close();
4239
4425
  try {
4240
- (0, import_node_fs9.rmSync)(tmpDir, { recursive: true, force: true });
4426
+ (0, import_node_fs10.rmSync)(tmpDir, { recursive: true, force: true });
4241
4427
  } catch {
4242
4428
  }
4243
4429
  process.exit(0);
@@ -4253,6 +4439,7 @@ var import_meta2 = {};
4253
4439
  var program = new import_commander.Command();
4254
4440
  program.name("atelier").description("Atelier animation CLI").version((0, import_node_module.createRequire)(import_meta2.url)("../package.json").version);
4255
4441
  validateCommand(program);
4442
+ lintCommand(program);
4256
4443
  infoCommand(program);
4257
4444
  stillCommand(program);
4258
4445
  renderCommand(program);