@popcraft/cli 0.53.1 → 0.53.3

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 (2) hide show
  1. package/dist/popcraft.mjs +422 -175
  2. package/package.json +2 -2
package/dist/popcraft.mjs CHANGED
@@ -1196,7 +1196,7 @@ function operations(spec = openapi) {
1196
1196
  }
1197
1197
  async function call(target2, method, path, opts = {}) {
1198
1198
  const qs = new URLSearchParams(opts.query ?? {}).toString();
1199
- const headers = { "user-agent": `popcraft-cli/${"0.53.1"}`, ...opts.headers };
1199
+ const headers = { "user-agent": `popcraft-cli/${"0.53.3"}`, ...opts.headers };
1200
1200
  if (target2.token) headers.authorization = `Bearer ${target2.token}`;
1201
1201
  if (opts.contentType) headers["content-type"] = opts.contentType;
1202
1202
  try {
@@ -6786,8 +6786,8 @@ function currentPrintPage(doc2, pageId2, o = {}) {
6786
6786
  }
6787
6787
  const c2 = o.centre;
6788
6788
  if (c2) {
6789
- const inside2 = pages2.find((p2) => c2.x >= p2.x && c2.x <= p2.x + p2.width && c2.y >= p2.y && c2.y <= p2.y + p2.height);
6790
- if (inside2) return inside2;
6789
+ const inside3 = pages2.find((p2) => c2.x >= p2.x && c2.x <= p2.x + p2.width && c2.y >= p2.y && c2.y <= p2.y + p2.height);
6790
+ if (inside3) return inside3;
6791
6791
  let best = pages2[0], bestD = Infinity;
6792
6792
  for (const p2 of pages2) {
6793
6793
  const dx = Math.max(p2.x - c2.x, 0, c2.x - (p2.x + p2.width));
@@ -94509,7 +94509,6 @@ function hasMessage(key5) {
94509
94509
  var current2, loading, listeners, formats, asts;
94510
94510
  var init_runtime = __esm({
94511
94511
  "lib/i18n/runtime.ts"() {
94512
- "use strict";
94513
94512
  init_intl_messageformat();
94514
94513
  init_icu_messageformat_parser();
94515
94514
  init_catalogs();
@@ -96212,7 +96211,6 @@ function repairDocumentPaints(doc2) {
96212
96211
  var broken;
96213
96212
  var init_paint_repair = __esm({
96214
96213
  "lib/document/paint-repair.ts"() {
96215
- "use strict";
96216
96214
  init_shader_graph();
96217
96215
  broken = (p2) => {
96218
96216
  if (!p2 || typeof p2 !== "object") return false;
@@ -97386,6 +97384,7 @@ function selectionWorldBounds(nodes, ids2, scrollState) {
97386
97384
  }
97387
97385
  var init_selection_bounds = __esm({
97388
97386
  "lib/editor/selection-bounds.ts"() {
97387
+ "use strict";
97389
97388
  init_scene_graph();
97390
97389
  init_transform();
97391
97390
  }
@@ -97577,6 +97576,7 @@ function relinkCopies(doc2, copies) {
97577
97576
  var TEXT_PATH_SOURCE_TYPES, isPathSource;
97578
97577
  var init_text_links = __esm({
97579
97578
  "lib/document/text-links.ts"() {
97579
+ "use strict";
97580
97580
  init_transform();
97581
97581
  TEXT_PATH_SOURCE_TYPES = ["VECTOR", "ELLIPSE", "RECTANGLE", "POLYGON", "STAR", "LINE", "ARROW", "FRAME"];
97582
97582
  isPathSource = (n) => !!n && TEXT_PATH_SOURCE_TYPES.includes(n.type);
@@ -248025,6 +248025,7 @@ function repairCompositions(comps, nodes) {
248025
248025
  var isObj5, finite6, optFinite, isVec2, TIMELINE_COLOR_IDS, TIMELINE_COLORS, UNITS, ORDERS, PROP_KEYS, ANIMATOR_ID, MOTION_TYPES, TRANSITIONS, isFraction, MEDIA_HASH;
248026
248026
  var init_invariants = __esm({
248027
248027
  "lib/motion/invariants.ts"() {
248028
+ "use strict";
248028
248029
  init_easing();
248029
248030
  init_properties();
248030
248031
  init_markers();
@@ -248121,6 +248122,7 @@ function resyncText(doc2, ids2) {
248121
248122
  var isVirtualId;
248122
248123
  var init_invariants2 = __esm({
248123
248124
  "lib/document/invariants.ts"() {
248125
+ "use strict";
248124
248126
  init_auto_layout();
248125
248127
  init_scene_graph();
248126
248128
  init_text_bounds();
@@ -251991,12 +251993,65 @@ var require_polygon_clipping_cjs = __commonJS({
251991
251993
  }
251992
251994
  });
251993
251995
 
251996
+ // lib/engine/geometry/winding.ts
251997
+ function interiorPoint(r) {
251998
+ let cx = 0, cy = 0;
251999
+ for (const [x2, y] of r) {
252000
+ cx += x2;
252001
+ cy += y;
252002
+ }
252003
+ const centroid2 = [cx / r.length, cy / r.length];
252004
+ if (inside(centroid2, r)) return centroid2;
252005
+ const [a, b, c2] = [r[0], r[1], r[2] ?? r[0]];
252006
+ return [(a[0] + b[0] + c2[0]) / 3, (a[1] + b[1] + c2[1]) / 3];
252007
+ }
252008
+ function nonzeroMultiPoly(rings) {
252009
+ const usable = rings.filter((r) => r.length >= 3);
252010
+ if (!usable.length) return [];
252011
+ const polys = usable.map((r) => [r]);
252012
+ if (usable.length === 1) return import_polygon_clipping.default.union(polys[0]);
252013
+ const areas = usable.map(signedArea);
252014
+ const add3 = [], sub2 = [];
252015
+ usable.forEach((r, i2) => {
252016
+ const p2 = interiorPoint(r);
252017
+ let w = Math.sign(areas[i2]);
252018
+ usable.forEach((o, j) => {
252019
+ if (j !== i2 && Math.abs(areas[j]) > Math.abs(areas[i2]) && inside(p2, o)) w += Math.sign(areas[j]);
252020
+ });
252021
+ (w === 0 ? sub2 : add3).push(polys[i2]);
252022
+ });
252023
+ if (!add3.length) return [];
252024
+ const filled2 = import_polygon_clipping.default.union(add3[0], ...add3.slice(1));
252025
+ return sub2.length ? import_polygon_clipping.default.difference(filled2, ...sub2) : filled2;
252026
+ }
252027
+ var import_polygon_clipping, signedArea, inside;
252028
+ var init_winding = __esm({
252029
+ "lib/engine/geometry/winding.ts"() {
252030
+ "use strict";
252031
+ import_polygon_clipping = __toESM(require_polygon_clipping_cjs());
252032
+ signedArea = (r) => {
252033
+ let a = 0;
252034
+ for (let i2 = 0, j = r.length - 1; i2 < r.length; j = i2++) a += (r[j][0] - r[i2][0]) * (r[j][1] + r[i2][1]);
252035
+ return a / 2;
252036
+ };
252037
+ inside = ([x2, y], r) => {
252038
+ let c2 = false;
252039
+ for (let i2 = 0, j = r.length - 1; i2 < r.length; j = i2++) {
252040
+ const [xi, yi] = r[i2], [xj, yj] = r[j];
252041
+ if (yi > y !== yj > y && x2 < (xj - xi) * (y - yi) / (yj - yi) + xi) c2 = !c2;
252042
+ }
252043
+ return c2;
252044
+ };
252045
+ }
252046
+ });
252047
+
251994
252048
  // lib/engine/geometry/triangulate.ts
251995
- var import_polygon_clipping;
252049
+ var import_polygon_clipping2;
251996
252050
  var init_triangulate = __esm({
251997
252051
  "lib/engine/geometry/triangulate.ts"() {
251998
252052
  "use strict";
251999
- import_polygon_clipping = __toESM(require_polygon_clipping_cjs());
252053
+ import_polygon_clipping2 = __toESM(require_polygon_clipping_cjs());
252054
+ init_winding();
252000
252055
  }
252001
252056
  });
252002
252057
 
@@ -252020,7 +252075,7 @@ function computeBooleanMultiPoly(node2, allNodes) {
252020
252075
  const asPolys = rings.map((r) => [r]);
252021
252076
  let mp;
252022
252077
  try {
252023
- mp = asPolys.length > 1 ? import_polygon_clipping2.default.union(asPolys[0], ...asPolys.slice(1)) : asPolys;
252078
+ mp = asPolys.length > 1 ? import_polygon_clipping3.default.union(asPolys[0], ...asPolys.slice(1)) : asPolys;
252024
252079
  } catch {
252025
252080
  mp = asPolys;
252026
252081
  }
@@ -252032,25 +252087,25 @@ function computeBooleanMultiPoly(node2, allNodes) {
252032
252087
  try {
252033
252088
  const rest = operands.slice(1);
252034
252089
  if (rest.length > 0) {
252035
- if (op4 === "SUBTRACT") result = import_polygon_clipping2.default.difference(operands[0], ...rest);
252036
- else if (op4 === "INTERSECT") result = import_polygon_clipping2.default.intersection(operands[0], ...rest);
252037
- else if (op4 === "EXCLUDE") result = import_polygon_clipping2.default.xor(operands[0], ...rest);
252038
- else result = import_polygon_clipping2.default.union(operands[0], ...rest);
252090
+ if (op4 === "SUBTRACT") result = import_polygon_clipping3.default.difference(operands[0], ...rest);
252091
+ else if (op4 === "INTERSECT") result = import_polygon_clipping3.default.intersection(operands[0], ...rest);
252092
+ else if (op4 === "EXCLUDE") result = import_polygon_clipping3.default.xor(operands[0], ...rest);
252093
+ else result = import_polygon_clipping3.default.union(operands[0], ...rest);
252039
252094
  }
252040
252095
  } catch {
252041
252096
  try {
252042
- result = import_polygon_clipping2.default.union(operands[0], ...operands.slice(1));
252097
+ result = import_polygon_clipping3.default.union(operands[0], ...operands.slice(1));
252043
252098
  } catch {
252044
252099
  result = operands[0];
252045
252100
  }
252046
252101
  }
252047
252102
  return result;
252048
252103
  }
252049
- var import_polygon_clipping2;
252104
+ var import_polygon_clipping3;
252050
252105
  var init_vector_geometry_cache = __esm({
252051
252106
  "lib/engine/vector-geometry-cache.ts"() {
252052
252107
  "use strict";
252053
- import_polygon_clipping2 = __toESM(require_polygon_clipping_cjs());
252108
+ import_polygon_clipping3 = __toESM(require_polygon_clipping_cjs());
252054
252109
  init_flatten();
252055
252110
  init_triangulate();
252056
252111
  init_shape_outline();
@@ -252069,11 +252124,11 @@ function arrangementFaces(shapes) {
252069
252124
  const next2 = [];
252070
252125
  let rest = shape2;
252071
252126
  for (const f2 of faces2) {
252072
- const inside2 = op2(import_polygon_clipping3.default.intersection, f2.mp, shape2);
252073
- if (solid(inside2)) next2.push({ mp: inside2, sources: [...f2.sources, i2] });
252074
- const outside = op2(import_polygon_clipping3.default.difference, f2.mp, shape2);
252127
+ const inside3 = op2(import_polygon_clipping4.default.intersection, f2.mp, shape2);
252128
+ if (solid(inside3)) next2.push({ mp: inside3, sources: [...f2.sources, i2] });
252129
+ const outside = op2(import_polygon_clipping4.default.difference, f2.mp, shape2);
252075
252130
  if (solid(outside)) next2.push({ mp: outside, sources: f2.sources });
252076
- if (solid(inside2)) rest = op2(import_polygon_clipping3.default.difference, rest, f2.mp);
252131
+ if (solid(inside3)) rest = op2(import_polygon_clipping4.default.difference, rest, f2.mp);
252077
252132
  }
252078
252133
  if (solid(rest)) next2.push({ mp: rest, sources: [i2] });
252079
252134
  faces2 = next2;
@@ -252082,15 +252137,15 @@ function arrangementFaces(shapes) {
252082
252137
  }
252083
252138
  function pointInMultiPoly(mp, x2, y) {
252084
252139
  for (const poly of mp) {
252085
- let inside2 = false;
252140
+ let inside3 = false;
252086
252141
  for (const ring of poly) {
252087
252142
  for (let i2 = 0, j = ring.length - 1; i2 < ring.length; j = i2++) {
252088
252143
  const [xi, yi] = ring[i2];
252089
252144
  const [xj, yj] = ring[j];
252090
- if (yi > y !== yj > y && x2 < (xj - xi) * (y - yi) / (yj - yi) + xi) inside2 = !inside2;
252145
+ if (yi > y !== yj > y && x2 < (xj - xi) * (y - yi) / (yj - yi) + xi) inside3 = !inside3;
252091
252146
  }
252092
252147
  }
252093
- if (inside2) return true;
252148
+ if (inside3) return true;
252094
252149
  }
252095
252150
  return false;
252096
252151
  }
@@ -252102,16 +252157,16 @@ function unionFaces(faces2, picked) {
252102
252157
  const parts = picked.map((i2) => faces2[i2]?.mp).filter(solid);
252103
252158
  if (parts.length === 0) return [];
252104
252159
  try {
252105
- return parts.length > 1 ? import_polygon_clipping3.default.union(parts[0], ...parts.slice(1)) : parts[0];
252160
+ return parts.length > 1 ? import_polygon_clipping4.default.union(parts[0], ...parts.slice(1)) : parts[0];
252106
252161
  } catch {
252107
252162
  return parts[0];
252108
252163
  }
252109
252164
  }
252110
- var import_polygon_clipping3, MAX_SHAPE_BUILDER_SHAPES, area2, MIN_FACE_AREA, solid, op2;
252165
+ var import_polygon_clipping4, MAX_SHAPE_BUILDER_SHAPES, area2, MIN_FACE_AREA, solid, op2;
252111
252166
  var init_regions = __esm({
252112
252167
  "lib/engine/geometry/regions.ts"() {
252113
252168
  "use strict";
252114
- import_polygon_clipping3 = __toESM(require_polygon_clipping_cjs());
252169
+ import_polygon_clipping4 = __toESM(require_polygon_clipping_cjs());
252115
252170
  MAX_SHAPE_BUILDER_SHAPES = 12;
252116
252171
  area2 = (mp) => {
252117
252172
  let a = 0;
@@ -252213,7 +252268,7 @@ function bufferPolylines(lines2, width) {
252213
252268
  }
252214
252269
  if (pieces.length === 0) return [];
252215
252270
  try {
252216
- return pieces.length > 1 ? import_polygon_clipping4.default.union(pieces[0], ...pieces.slice(1)) : [pieces[0]];
252271
+ return pieces.length > 1 ? import_polygon_clipping5.default.union(pieces[0], ...pieces.slice(1)) : [pieces[0]];
252217
252272
  } catch {
252218
252273
  return pieces.map((p2) => [p2[0]]);
252219
252274
  }
@@ -252222,7 +252277,7 @@ function combineRings(rings, mode2) {
252222
252277
  const polys = rings.filter((r) => r.length >= 3).map((r) => [r]);
252223
252278
  if (polys.length === 0) return [];
252224
252279
  try {
252225
- return polys.length > 1 ? import_polygon_clipping4.default[mode2](polys[0], ...polys.slice(1)) : [polys[0]];
252280
+ return polys.length > 1 ? import_polygon_clipping5.default[mode2](polys[0], ...polys.slice(1)) : [polys[0]];
252226
252281
  } catch {
252227
252282
  return polys.map((p2) => [p2[0]]);
252228
252283
  }
@@ -252236,8 +252291,15 @@ function nodeWorldMultiPoly(doc2, id3) {
252236
252291
  return mp.map((poly) => poly.map((ring) => ring.map(([x2, y]) => place4(x2, y))));
252237
252292
  }
252238
252293
  const rings = nodeWorldRings(doc2, id3);
252239
- const mode2 = node2.type === "VECTOR" && node2.fillRule === "EVENODD" ? "xor" : "union";
252240
- return combineRings(rings, mode2);
252294
+ if (node2.type === "VECTOR") {
252295
+ if (node2.fillRule === "EVENODD") return combineRings(rings, "xor");
252296
+ try {
252297
+ return nonzeroMultiPoly(rings);
252298
+ } catch {
252299
+ return combineRings(rings, "union");
252300
+ }
252301
+ }
252302
+ return combineRings(rings, "union");
252241
252303
  }
252242
252304
  function nodeAreaMultiPoly(doc2, id3) {
252243
252305
  if (!doc2.nodes[id3]) return [];
@@ -252247,13 +252309,23 @@ function nodeAreaMultiPoly(doc2, id3) {
252247
252309
  }
252248
252310
  function flattenNodesMultiPoly(doc2, ids2) {
252249
252311
  const operands = [];
252250
- for (const id3 of ids2) {
252251
- const mp = nodeAreaMultiPoly(doc2, id3);
252252
- if (mp.length > 0) operands.push(mp);
252253
- }
252312
+ const visit2 = (id3) => {
252313
+ const n = doc2.nodes[id3];
252314
+ if (!n || n.visible === false) return;
252315
+ if (n.type !== "GROUP") {
252316
+ const mp = n.type === "FRAME" || n.type === "COMPONENT" || n.type === "INSTANCE" || n.type === "SECTION" ? (n.fills ?? []).some((f2) => f2.visible !== false) ? nodeWorldMultiPoly(doc2, id3) : [] : nodeAreaMultiPoly(doc2, id3);
252317
+ if (mp.length > 0) operands.push(mp);
252318
+ }
252319
+ if (n.type !== "BOOLEAN") for (const c2 of n.childIds ?? []) visit2(c2);
252320
+ };
252321
+ ids2.forEach(visit2);
252322
+ return unionAll(operands);
252323
+ }
252324
+ function unionAll(mps) {
252325
+ const operands = mps.filter((mp) => mp.length > 0);
252254
252326
  if (operands.length === 0) return [];
252255
252327
  try {
252256
- return operands.length > 1 ? import_polygon_clipping4.default.union(operands[0], ...operands.slice(1)) : operands[0];
252328
+ return operands.length > 1 ? import_polygon_clipping5.default.union(operands[0], ...operands.slice(1)) : operands[0];
252257
252329
  } catch {
252258
252330
  return operands[0];
252259
252331
  }
@@ -252279,7 +252351,7 @@ function outlineStrokeMultiPoly(doc2, id3) {
252279
252351
  const buffer2 = bufferPolylines(lines2, width * 2);
252280
252352
  if (fill.length === 0 || buffer2.length === 0) return bufferPolylines(lines2, width);
252281
252353
  try {
252282
- return alignment === "INSIDE" ? import_polygon_clipping4.default.intersection(buffer2, fill) : import_polygon_clipping4.default.difference(buffer2, fill);
252354
+ return alignment === "INSIDE" ? import_polygon_clipping5.default.intersection(buffer2, fill) : import_polygon_clipping5.default.difference(buffer2, fill);
252283
252355
  } catch {
252284
252356
  return bufferPolylines(lines2, width);
252285
252357
  }
@@ -252311,16 +252383,17 @@ function shapeBuilderFaces(doc2, ids2) {
252311
252383
  facesCache = { nodes: doc2.nodes, key: key5, faces: faces2 };
252312
252384
  return faces2;
252313
252385
  }
252314
- var import_polygon_clipping4, JOIN_CIRCLE_SEGMENTS, facesCache;
252386
+ var import_polygon_clipping5, JOIN_CIRCLE_SEGMENTS, facesCache;
252315
252387
  var init_vector_ops = __esm({
252316
252388
  "lib/engine/geometry/vector-ops.ts"() {
252317
252389
  "use strict";
252318
- import_polygon_clipping4 = __toESM(require_polygon_clipping_cjs());
252390
+ import_polygon_clipping5 = __toESM(require_polygon_clipping_cjs());
252319
252391
  init_scene_graph();
252320
252392
  init_transform();
252321
252393
  init_shape_outline();
252322
252394
  init_flatten();
252323
252395
  init_vector_geometry_cache();
252396
+ init_winding();
252324
252397
  init_regions();
252325
252398
  JOIN_CIRCLE_SEGMENTS = 16;
252326
252399
  facesCache = null;
@@ -252374,15 +252447,15 @@ var init_brush_along_path = __esm({
252374
252447
 
252375
252448
  // lib/engine/geometry/polygon.ts
252376
252449
  function pointInPolygon(p2, poly) {
252377
- let inside2 = false;
252450
+ let inside3 = false;
252378
252451
  for (let i2 = 0, j = poly.length - 1; i2 < poly.length; j = i2++) {
252379
252452
  const a = poly[i2], b = poly[j];
252380
252453
  if (a.y > p2.y !== b.y > p2.y) {
252381
252454
  const xCross = a.x + (p2.y - a.y) * (b.x - a.x) / (b.y - a.y);
252382
- if (p2.x < xCross) inside2 = !inside2;
252455
+ if (p2.x < xCross) inside3 = !inside3;
252383
252456
  }
252384
252457
  }
252385
- return inside2;
252458
+ return inside3;
252386
252459
  }
252387
252460
  var init_polygon = __esm({
252388
252461
  "lib/engine/geometry/polygon.ts"() {
@@ -252422,8 +252495,8 @@ function enclosedRegion(shapes, pt, view2) {
252422
252495
  }
252423
252496
  try {
252424
252497
  let region = rectMP(view2);
252425
- for (const c2 of containing) region = import_polygon_clipping5.default.intersection(region, c2);
252426
- if (subtract.length) region = import_polygon_clipping5.default.difference(region, ...subtract);
252498
+ for (const c2 of containing) region = import_polygon_clipping6.default.intersection(region, c2);
252499
+ if (subtract.length) region = import_polygon_clipping6.default.difference(region, ...subtract);
252427
252500
  const hit = region.find((p2) => pointInPoly(pt, p2));
252428
252501
  if (!hit) return { ok: false, reason: "EMPTY" };
252429
252502
  if (!containing.length && touchesRect(hit, view2)) return { ok: false, reason: "OPEN" };
@@ -252474,11 +252547,11 @@ function findEnclosedRegion(doc2, pageId2, pt, view2) {
252474
252547
  walk4(pageId2);
252475
252548
  return enclosedRegion(shapes, pt, view2);
252476
252549
  }
252477
- var import_polygon_clipping5, ringPts, pointInMP, rectMP;
252550
+ var import_polygon_clipping6, ringPts, pointInMP, rectMP;
252478
252551
  var init_bucket = __esm({
252479
252552
  "lib/engine/geometry/bucket.ts"() {
252480
252553
  "use strict";
252481
- import_polygon_clipping5 = __toESM(require_polygon_clipping_cjs());
252554
+ import_polygon_clipping6 = __toESM(require_polygon_clipping_cjs());
252482
252555
  init_scene_graph();
252483
252556
  init_vector_ops();
252484
252557
  init_polygon();
@@ -256120,6 +256193,7 @@ fn shade(uv: vec2<f32>, res: vec2<f32>, tm: f32) -> vec4<f32> {
256120
256193
  var SHADER_PRESETS, shaderPreset, legacyPresetOf, DEFAULT_SHADER_PRESET, DEFAULT_TEXTURE_PARAMS, f, vec4, NOISE_LIB;
256121
256194
  var init_shader_presets = __esm({
256122
256195
  "lib/engine/shader-presets.ts"() {
256196
+ "use strict";
256123
256197
  init_shader_graph();
256124
256198
  init_shader_graph_presets();
256125
256199
  init_shader_presets_legacy();
@@ -256670,7 +256744,6 @@ function mergeFonts(fonts, incoming) {
256670
256744
  var fontWeightOf, fontStyleOf, sameSlot, hashOf, documentFontHashes;
256671
256745
  var init_document_fonts = __esm({
256672
256746
  "lib/fonts/document-fonts.ts"() {
256673
- "use strict";
256674
256747
  init_media_store();
256675
256748
  init_media_hash();
256676
256749
  fontWeightOf = (f2) => f2.weight ?? 400;
@@ -256816,7 +256889,6 @@ var init_apply2 = __esm({
256816
256889
  var HYPERY_CLIENT_ID, HYPERY_GATEWAY_URL, HYPERY_AI_SCOPES, plansEnabled, HYPERY_BILLING_SCOPES, HYPERY_AUDIO_SCOPES, HYPERY_SCOPES, cloudEnabled;
256817
256890
  var init_config2 = __esm({
256818
256891
  "lib/client/cloud/config.ts"() {
256819
- "use strict";
256820
256892
  HYPERY_CLIENT_ID = process.env.NEXT_PUBLIC_HYPERY_CLIENT_ID ?? "";
256821
256893
  HYPERY_GATEWAY_URL = process.env.NEXT_PUBLIC_HYPERY_GATEWAY_URL ?? "https://hypery.ai";
256822
256894
  HYPERY_AI_SCOPES = ["ai:chat", "ai:completions", "ai:models", "ai:images"];
@@ -257639,10 +257711,10 @@ function collapse(doc2) {
257639
257711
  else groups.set(h, [id3]);
257640
257712
  }
257641
257713
  const chosen = [];
257642
- const inside2 = /* @__PURE__ */ new Set();
257714
+ const inside3 = /* @__PURE__ */ new Set();
257643
257715
  const sorted = [...groups.values()].filter((g) => g.length >= MIN_COPIES).sort((a, b) => (size6.get(b[0]) ?? 0) - (size6.get(a[0]) ?? 0));
257644
257716
  const within3 = (id3) => {
257645
- for (let p2 = nodes[id3]?.parentId, guard2 = 0; p2 && guard2 < 200; p2 = nodes[p2]?.parentId, guard2++) if (inside2.has(p2)) return true;
257717
+ for (let p2 = nodes[id3]?.parentId, guard2 = 0; p2 && guard2 < 200; p2 = nodes[p2]?.parentId, guard2++) if (inside3.has(p2)) return true;
257646
257718
  return false;
257647
257719
  };
257648
257720
  for (const g of sorted) {
@@ -257650,7 +257722,7 @@ function collapse(doc2) {
257650
257722
  const copies = g.filter((id3) => !within3(id3) && (id3 === rep || sameContents(nodes, rep, id3)));
257651
257723
  if (copies.length < MIN_COPIES || !copies.includes(rep)) continue;
257652
257724
  chosen.push(copies);
257653
- for (const id3 of copies) inside2.add(id3);
257725
+ for (const id3 of copies) inside3.add(id3);
257654
257726
  }
257655
257727
  if (!chosen.length) return doc2;
257656
257728
  const out = { ...nodes };
@@ -258012,6 +258084,64 @@ function scaleVectorPaths(paths, sx, sy, rebuild) {
258012
258084
  return { ...path, points, data: rebuild(points, path.closed) };
258013
258085
  });
258014
258086
  }
258087
+ function scaledChildProps(orig, sx, sy, rebuild) {
258088
+ return {
258089
+ x: orig.x * sx,
258090
+ y: orig.y * sy,
258091
+ width: orig.width * sx,
258092
+ height: orig.height * sy,
258093
+ ...orig.type === "VECTOR" ? { paths: scaleVectorPaths(orig.paths, sx, sy, rebuild) } : {}
258094
+ };
258095
+ }
258096
+ function rebuildVectorData(points, closed) {
258097
+ if (points.length < 2) return [];
258098
+ const data = [];
258099
+ data.push("M", points[0].x, points[0].y);
258100
+ for (let i2 = 1; i2 < points.length; i2++) {
258101
+ const prev = points[i2 - 1];
258102
+ const curr = points[i2];
258103
+ if (prev.handleOut && curr.handleIn) {
258104
+ data.push("C", prev.handleOut.x, prev.handleOut.y, curr.handleIn.x, curr.handleIn.y, curr.x, curr.y);
258105
+ } else if (prev.handleOut) {
258106
+ data.push("Q", prev.handleOut.x, prev.handleOut.y, curr.x, curr.y);
258107
+ } else if (curr.handleIn) {
258108
+ data.push("Q", curr.handleIn.x, curr.handleIn.y, curr.x, curr.y);
258109
+ } else {
258110
+ data.push("L", curr.x, curr.y);
258111
+ }
258112
+ }
258113
+ if (closed) {
258114
+ const prev = points[points.length - 1];
258115
+ const curr = points[0];
258116
+ if (prev.handleOut && curr.handleIn) {
258117
+ data.push("C", prev.handleOut.x, prev.handleOut.y, curr.handleIn.x, curr.handleIn.y, curr.x, curr.y);
258118
+ } else if (prev.handleOut) {
258119
+ data.push("Q", prev.handleOut.x, prev.handleOut.y, curr.x, curr.y);
258120
+ } else if (curr.handleIn) {
258121
+ data.push("Q", curr.handleIn.x, curr.handleIn.y, curr.x, curr.y);
258122
+ }
258123
+ data.push("Z");
258124
+ }
258125
+ return data;
258126
+ }
258127
+ function scaleSubtreePatches(nodes, id3, sx, sy, includeSelf = true) {
258128
+ const out = /* @__PURE__ */ new Map();
258129
+ const root2 = nodes[id3];
258130
+ if (!root2 || sx === 1 && sy === 1) return out;
258131
+ if (includeSelf && root2.type === "VECTOR") out.set(id3, { paths: scaleVectorPaths(root2.paths, sx, sy, rebuildVectorData) });
258132
+ const walk4 = (parent) => {
258133
+ for (const cid of parent.childIds ?? []) {
258134
+ const c2 = nodes[cid];
258135
+ if (!c2) continue;
258136
+ const patch2 = { ...scaledChildProps(c2, sx, sy, rebuildVectorData) };
258137
+ if (c2.type === "TEXT") patch2.fontSize = (c2.fontSize ?? 16) * Math.min(sx, sy);
258138
+ out.set(cid, patch2);
258139
+ walk4(c2);
258140
+ }
258141
+ };
258142
+ if (root2.type === "GROUP" || root2.type === "BOOLEAN" || !includeSelf) walk4(root2);
258143
+ return out;
258144
+ }
258015
258145
  var scalePoint;
258016
258146
  var init_scale_geometry = __esm({
258017
258147
  "lib/document/scale-geometry.ts"() {
@@ -306868,6 +306998,7 @@ function decodeSource(key5, label3) {
306868
306998
  var DECODE_CACHE_BYTES, cache7, pending4, held, sizeOf, lengths, decodedLengths, DECODE_TIMEOUT_MS, decodedAudio, decodedSource, pcmOf;
306869
306999
  var init_audio_decode = __esm({
306870
307000
  "lib/motion/audio-decode.ts"() {
307001
+ "use strict";
306871
307002
  init_media_store();
306872
307003
  init_audio_plan();
306873
307004
  init_time_stretch();
@@ -310254,6 +310385,7 @@ function importSVGFile(file2, document2, parentId) {
310254
310385
  var ENTITIES, decode3, I, mul2, apply2, scaleOf, NUM2, nums, near, NAMED, INHERITED, PRESENTATION, len2, CAP, JOIN, SKIPPED, refId;
310255
310386
  var init_svg_import = __esm({
310256
310387
  "lib/io/svg-import.ts"() {
310388
+ "use strict";
310257
310389
  init_dist_node();
310258
310390
  init_types();
310259
310391
  ENTITIES = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", nbsp: "\xA0" };
@@ -311857,7 +311989,6 @@ async function dispatchShaped(key5, options) {
311857
311989
  var onViolation;
311858
311990
  var init_dispatch = __esm({
311859
311991
  "lib/commands/dispatch.ts"() {
311860
- "use strict";
311861
311992
  init_errors();
311862
311993
  init_registry();
311863
311994
  init_confirm();
@@ -312919,7 +313050,6 @@ function applyTextPath(layout2, path, tp, rebase = true) {
312919
313050
  var defaultCircleRadius, ownsPath, mapPoint;
312920
313051
  var init_text_path = __esm({
312921
313052
  "lib/engine/text/text-path.ts"() {
312922
- "use strict";
312923
313053
  init_scene_graph();
312924
313054
  init_transform();
312925
313055
  init_shape_to_path();
@@ -315574,12 +315704,12 @@ function textStrokeSVG(node2) {
315574
315704
  const w = Array.isArray(stroke3.width) ? Math.max(...stroke3.width) : stroke3.width;
315575
315705
  if (!(w > 0)) return null;
315576
315706
  const behind = strokeBehindFill(node2) || stroke3.alignment === "OUTSIDE";
315577
- const inside2 = stroke3.alignment === "INSIDE";
315578
- if (inside2 && behind) return null;
315707
+ const inside3 = stroke3.alignment === "INSIDE";
315708
+ if (inside3 && behind) return null;
315579
315709
  const alpha2 = (stroke3.color.a ?? 1) * (stroke3.opacity ?? 1);
315580
315710
  const join13 = stroke3.join === "ROUND" ? "round" : stroke3.join === "BEVEL" ? "bevel" : "miter";
315581
315711
  const attrs = `stroke="${colorToSVG(stroke3.color)}"${alpha2 < 1 ? ` stroke-opacity="${fmt2(alpha2)}"` : ""} stroke-width="${fmt2(stroke3.alignment === "CENTER" ? w : w * 2)}" stroke-linejoin="${join13}"${behind ? ' paint-order="stroke fill"' : ""}`;
315582
- return { attrs, behind, inside: inside2 };
315712
+ return { attrs, behind, inside: inside3 };
315583
315713
  }
315584
315714
  function textFillAttr(resolved, node2, ctx2) {
315585
315715
  const fill0 = resolved.fills?.[0];
@@ -316503,12 +316633,12 @@ Q
316503
316633
  };
316504
316634
  const paintStrokes = () => {
316505
316635
  for (const s of item.strokes) {
316506
- const inside2 = s.alignment === "INSIDE";
316507
- const wdt = inside2 ? s.width * 2 : s.width;
316636
+ const inside3 = s.alignment === "INSIDE";
316637
+ const wdt = inside3 ? s.width * 2 : s.width;
316508
316638
  this.w(`q /${this.gs(1, opacity * s.alpha, blend2)} gs ${this.res.color.stroke(s.color)} ${num7(wdt)} w ${CAP2[s.cap]} J ${JOIN2[s.join]} j`);
316509
316639
  this.w(s.dash ? ` [${num7(s.dash[0])} ${num7(s.dash[1])}] 0 d
316510
316640
  ` : " [] 0 d\n");
316511
- if (inside2) this.w(`${ops2}${clipOp} n
316641
+ if (inside3) this.w(`${ops2}${clipOp} n
316512
316642
  `);
316513
316643
  this.w(`${ops2}S
316514
316644
  Q
@@ -316681,8 +316811,8 @@ function removeOverlaps(cmds) {
316681
316811
  break;
316682
316812
  }
316683
316813
  if (!touching) return cmds;
316684
- const pos = rings.filter((r) => signedArea(r) > 0).map((r) => [r]), neg = rings.filter((r) => signedArea(r) < 0).map((r) => [r]);
316685
- const clip = import_polygon_clipping6.default;
316814
+ const pos = rings.filter((r) => signedArea2(r) > 0).map((r) => [r]), neg = rings.filter((r) => signedArea2(r) < 0).map((r) => [r]);
316815
+ const clip = import_polygon_clipping7.default;
316686
316816
  let result;
316687
316817
  try {
316688
316818
  const a = pos.length ? clip.union(...pos) : [], b = neg.length ? clip.union(...neg) : [];
@@ -316795,12 +316925,12 @@ function buildCFF(name2, unitsPerEm, glyphs) {
316795
316925
  }
316796
316926
  return out;
316797
316927
  }
316798
- var import_polygon_clipping6, signedArea, bbox, intersects;
316928
+ var import_polygon_clipping7, signedArea2, bbox, intersects;
316799
316929
  var init_cff = __esm({
316800
316930
  "lib/io/pdf/cff.ts"() {
316801
316931
  "use strict";
316802
- import_polygon_clipping6 = __toESM(require_polygon_clipping_cjs());
316803
- signedArea = (p2) => p2.reduce((a, [x2, y], i2) => {
316932
+ import_polygon_clipping7 = __toESM(require_polygon_clipping_cjs());
316933
+ signedArea2 = (p2) => p2.reduce((a, [x2, y], i2) => {
316804
316934
  const [nx, ny] = p2[(i2 + 1) % p2.length];
316805
316935
  return a + x2 * ny - nx * y;
316806
316936
  }, 0) / 2;
@@ -317381,7 +317511,6 @@ async function exportSettingFile(renderer, node2, setting, doc2, styles) {
317381
317511
  var MIME, DEFAULT_EXPORT, exportSettingsOf;
317382
317512
  var init_export = __esm({
317383
317513
  "lib/io/export.ts"() {
317384
- "use strict";
317385
317514
  init_download();
317386
317515
  init_scene_graph();
317387
317516
  init_codecs();
@@ -318316,7 +318445,6 @@ function agentToolList() {
318316
318445
  var AGENT_CAPABILITIES, legacyCommandId, legacyTools, blobToDataUrl3, provided, legacyTool, legacyToolList;
318317
318446
  var init_agent = __esm({
318318
318447
  "lib/commands/agent.ts"() {
318319
- "use strict";
318320
318448
  init_types5();
318321
318449
  init_registry();
318322
318450
  init_errors();
@@ -318354,6 +318482,7 @@ __export(tools_exports, {
318354
318482
  allTools: () => allTools,
318355
318483
  defineTool: () => defineTool,
318356
318484
  getTool: () => getTool,
318485
+ parseToolArgs: () => parseToolArgs,
318357
318486
  registerTools: () => registerTools,
318358
318487
  runTool: () => runTool,
318359
318488
  toolSpecs: () => toolSpecs
@@ -318374,14 +318503,53 @@ function toolSpecs(names) {
318374
318503
  function: { name: t3.name, description: t3.description, parameters: toolParameters(t3.parameters) }
318375
318504
  }));
318376
318505
  }
318506
+ function parseToolArgs(text) {
318507
+ try {
318508
+ return JSON.parse(text);
318509
+ } catch {
318510
+ }
318511
+ let depth = 0, inStr = false, esc3 = false;
318512
+ const stack = [];
318513
+ for (let i2 = 0; i2 < text.length; i2++) {
318514
+ const c2 = text[i2];
318515
+ if (inStr) {
318516
+ if (esc3) esc3 = false;
318517
+ else if (c2 === "\\") esc3 = true;
318518
+ else if (c2 === '"') inStr = false;
318519
+ continue;
318520
+ }
318521
+ if (c2 === '"') inStr = true;
318522
+ else if (c2 === "{" || c2 === "[") {
318523
+ stack.push(c2 === "{" ? "}" : "]");
318524
+ depth++;
318525
+ } else if (c2 === "}" || c2 === "]") {
318526
+ stack.pop();
318527
+ depth--;
318528
+ if (depth === 0) {
318529
+ try {
318530
+ return JSON.parse(text.slice(0, i2 + 1));
318531
+ } catch {
318532
+ return void 0;
318533
+ }
318534
+ }
318535
+ }
318536
+ }
318537
+ if (!stack.length) return void 0;
318538
+ try {
318539
+ return JSON.parse(text + (inStr ? '"' : "") + stack.reverse().join(""));
318540
+ } catch {
318541
+ return void 0;
318542
+ }
318543
+ }
318377
318544
  async function runTool(name2, argsText, ctx2) {
318378
318545
  let raw = {};
318379
318546
  if (argsText.trim()) {
318380
- try {
318381
- raw = JSON.parse(argsText);
318382
- } catch {
318383
- throw new ToolError(`The arguments for ${name2} were not valid JSON.`, "invalid_params");
318547
+ const parsed2 = parseToolArgs(argsText);
318548
+ if (parsed2 === void 0) {
318549
+ console.warn(`[agent] unparseable arguments for ${name2}:`, argsText.slice(0, 500));
318550
+ throw new ToolError(`The arguments for ${name2} were not valid JSON. Send one JSON object.`, "invalid_params");
318384
318551
  }
318552
+ raw = parsed2;
318385
318553
  }
318386
318554
  return dispatch2(name2, {
318387
318555
  surface: "agent",
@@ -318898,7 +319066,10 @@ var init_doc_helpers = __esm({
318898
319066
  init_invariants2();
318899
319067
  MAX_NODES = 200;
318900
319068
  plural2 = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
318901
- hexOf = (c2) => c2 ? "#" + [c2.r, c2.g, c2.b].map((v) => Math.round(Math.max(0, Math.min(1, v)) * 255).toString(16).padStart(2, "0")).join("") : "";
319069
+ hexOf = (c2) => c2 ? "#" + [c2.r, c2.g, c2.b].map((v) => {
319070
+ const n = Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : 0;
319071
+ return Math.round(n * 255).toString(16).padStart(2, "0");
319072
+ }).join("") : "";
318902
319073
  isVirtualId2 = (id3) => id3.includes("::");
318903
319074
  near3 = (a, b, tol) => !!a && Math.abs(a.r - b.r) <= tol && Math.abs(a.g - b.g) <= tol && Math.abs(a.b - b.b) <= tol;
318904
319075
  localBox = (doc2, ids2) => {
@@ -319099,16 +319270,28 @@ function placeSvgLayers(doc2, parentId, svg, place4) {
319099
319270
  if (n.type === "IMAGE" && !doc2.nodes[n.id] && url4?.startsWith("data:")) nodes[n.id] = { ...n, imageUrl: place4.intern(url4) };
319100
319271
  }
319101
319272
  }
319102
- const minX = Math.min(...kids.map((k2) => k2.x)), minY = Math.min(...kids.map((k2) => k2.y));
319103
- const maxX = Math.max(...kids.map((k2) => k2.x + k2.width)), maxY = Math.max(...kids.map((k2) => k2.y + k2.height));
319104
- for (const k2 of kids) nodes[k2.id] = { ...nodes[k2.id], x: k2.x - minX, y: k2.y - minY };
319105
- const w = Math.max(1, maxX - minX), h = Math.max(1, maxY - minY);
319106
- nodes[frame3.id] = { ...nodes[frame3.id], x: Math.round(place4.cx - w / 2), y: Math.round(place4.cy - h / 2), width: w, height: h };
319273
+ const minX = Math.min(...kids.map((k3) => k3.x)), minY = Math.min(...kids.map((k3) => k3.y));
319274
+ const maxX = Math.max(...kids.map((k3) => k3.x + k3.width)), maxY = Math.max(...kids.map((k3) => k3.y + k3.height));
319275
+ for (const k3 of kids) nodes[k3.id] = { ...nodes[k3.id], x: k3.x - minX, y: k3.y - minY };
319276
+ let w = Math.max(1, maxX - minX), h = Math.max(1, maxY - minY);
319277
+ const f2 = place4.fit;
319278
+ const k2 = f2 && (f2.width || f2.height) ? Math.min(f2.width ? f2.width / w : Infinity, f2.height ? f2.height / h : Infinity) : 1;
319279
+ if (k2 !== 1 && Number.isFinite(k2) && k2 > 0) {
319280
+ const wrapper = { childIds: kids.map((c2) => c2.id) };
319281
+ for (const [id3, patch2] of scaleSubtreePatches({ ...nodes, [frame3.id]: { ...nodes[frame3.id], ...wrapper } }, frame3.id, k2, k2, false)) {
319282
+ nodes[id3] = { ...nodes[id3], ...patch2 };
319283
+ }
319284
+ w *= k2;
319285
+ h *= k2;
319286
+ }
319287
+ const topLeft = place4.anchor === "top-left";
319288
+ nodes[frame3.id] = { ...nodes[frame3.id], x: Math.round(topLeft ? place4.cx : place4.cx - w / 2), y: Math.round(topLeft ? place4.cy : place4.cy - h / 2), width: w, height: h };
319107
319289
  return { doc: { ...next2, nodes }, id: frame3.id };
319108
319290
  }
319109
319291
  var MAX_SVG_LAYER_BYTES;
319110
319292
  var init_svg_place = __esm({
319111
319293
  "lib/io/svg-place.ts"() {
319294
+ init_scale_geometry();
319112
319295
  init_scene_graph();
319113
319296
  init_svg_import();
319114
319297
  MAX_SVG_LAYER_BYTES = 8 * 1024 * 1024;
@@ -319128,6 +319311,49 @@ function descendants(doc2, rootId, skipRoot = true) {
319128
319311
  }
319129
319312
  return out;
319130
319313
  }
319314
+ function geometryMismatch(doc2, id3) {
319315
+ const n = doc2.nodes[id3];
319316
+ if (!n || n.type !== "VECTOR" || !n.paths?.length) return void 0;
319317
+ const pb = pathBounds(n.paths);
319318
+ if (!pb) return void 0;
319319
+ const gw = pb.maxX - pb.minX, gh = pb.maxY - pb.minY;
319320
+ if (Math.abs(gw - n.width) <= 1 && Math.abs(gh - n.height) <= 1) return void 0;
319321
+ return `box is ${Math.round(n.width)}\xD7${Math.round(n.height)} but the artwork is ${Math.round(gw)}\xD7${Math.round(gh)} \u2014 resize_nodes to reconcile them`;
319322
+ }
319323
+ function normalizeColor(c2, key5) {
319324
+ if (typeof c2 === "string") return parseHex(c2);
319325
+ const o = c2;
319326
+ const ch = o && ["r", "g", "b"].map((k2) => o[k2]);
319327
+ if (!ch || !ch.every((v) => typeof v === "number" && Number.isFinite(v))) {
319328
+ throw new ToolError(`${key5} has a colour without numeric r, g and b. Give 0\u20131 channels, or use set_fills with a hex colour.`);
319329
+ }
319330
+ const nums2 = ch;
319331
+ const scale = nums2.some((v) => v > 1) ? 255 : 1;
319332
+ const a = typeof o.a === "number" && Number.isFinite(o.a) ? o.a > 1 ? o.a / 255 : o.a : 1;
319333
+ return createColor(nums2[0] / scale, nums2[1] / scale, nums2[2] / scale, a);
319334
+ }
319335
+ function normalizeSegments(v) {
319336
+ if (!Array.isArray(v)) return v;
319337
+ return v.map((seg) => {
319338
+ if (!seg || typeof seg !== "object") return seg;
319339
+ const s = { ...seg };
319340
+ for (const k2 of ["fills", "strokes", "textDecorationColor"]) if (k2 in s) s[k2] = normalizePaints(s[k2], k2);
319341
+ return s;
319342
+ });
319343
+ }
319344
+ function normalizePaints(v, key5) {
319345
+ if (!Array.isArray(v)) throw new ToolError(`${key5} must be an array of paints.`);
319346
+ return v.map((p2) => {
319347
+ const paint = p2;
319348
+ if (!paint || typeof paint !== "object") throw new ToolError(`${key5} must be an array of paint objects.`);
319349
+ const out = { ...paint };
319350
+ if ("color" in paint) out.color = normalizeColor(paint.color, key5);
319351
+ if (Array.isArray(paint.gradientStops)) {
319352
+ out.gradientStops = paint.gradientStops.map((s) => ({ ...s, color: normalizeColor(s.color, key5) }));
319353
+ }
319354
+ return out;
319355
+ });
319356
+ }
319131
319357
  var nodeFind, nodeGet, nodeMeasure, docOutline, CREATABLE, nodeCreate, FORBIDDEN_PROPS, nodeUpdate, nodeDelete, nodeMove, nodeResize, nodeReparent, nodeGroup, nodeUngroup, nodeOrder, nodeAlign, nodeDistribute, nodeRename, nodeDuplicate, nodeImportSvg, nodeApplyAutoLayout, nodeSetLock, nodeSetVisibility, nodeFlip, nodeRotate;
319132
319358
  var init_node = __esm({
319133
319359
  "lib/commands/node.ts"() {
@@ -319139,7 +319365,9 @@ var init_node = __esm({
319139
319365
  init_selection_bounds();
319140
319366
  init_scene_graph();
319141
319367
  init_types();
319368
+ init_svg_import();
319142
319369
  init_errors();
319370
+ init_scale_geometry();
319143
319371
  init_plugin_shim();
319144
319372
  init_types5();
319145
319373
  init_ui();
@@ -319203,10 +319431,12 @@ var init_node = __esm({
319203
319431
  hits.push(n);
319204
319432
  if (hits.length >= limit) break;
319205
319433
  }
319434
+ const truncated = hits.length >= limit;
319206
319435
  return {
319207
- summary: `${plural2(hits.length, "match", "matches")} for ${q || type || args2.name || "everything here"}`,
319436
+ summary: `${plural2(hits.length, "match", "matches")}${truncated ? "+ (capped)" : ""} for ${q || type || args2.name || "everything here"}`,
319208
319437
  data: {
319209
319438
  count: hits.length,
319439
+ truncated,
319210
319440
  nodes: args2.detail === "full" ? plain(hits) : hits.map((n) => ({ ...brief(n), page: pageOf2(doc2, n.id) }))
319211
319441
  }
319212
319442
  };
@@ -319248,7 +319478,10 @@ var init_node = __esm({
319248
319478
  return n2 ? plain(n2) : null;
319249
319479
  }
319250
319480
  const n = resolvedNodeOrThrow(doc2, id3);
319251
- return args2.detail === "full" ? plain(n) : describeProps(doc2, n);
319481
+ if (args2.detail === "full") return plain(n);
319482
+ const props = describeProps(doc2, n);
319483
+ const warning = geometryMismatch(doc2, n.id);
319484
+ return warning ? { ...props, warning } : props;
319252
319485
  });
319253
319486
  return { summary: `Read ${plural2(nodes.length, "layer")}`, data: { nodes } };
319254
319487
  }
@@ -319281,7 +319514,8 @@ var init_node = __esm({
319281
319514
  });
319282
319515
  const each = ids2.map((id3) => {
319283
319516
  const rb = getWorldRenderBounds(doc2, id3);
319284
- return { id: id3, name: doc2.nodes[id3].name, bounds: rb ? round5(rb) : null };
319517
+ const warning = geometryMismatch(doc2, id3);
319518
+ return { id: id3, name: doc2.nodes[id3].name, bounds: rb ? round5(rb) : null, ...warning ? { warning } : {} };
319285
319519
  });
319286
319520
  const combined = round5(box2);
319287
319521
  return { summary: `Bounds: ${combined.width}\xD7${combined.height} at ${combined.x},${combined.y}`, data: { combined, nodes: each } };
@@ -319474,13 +319708,34 @@ var init_node = __esm({
319474
319708
  for (const [k2, v] of Object.entries(u.props)) {
319475
319709
  if (args2.strict && FORBIDDEN_PROPS.has(k2)) throw new ToolError(`update_nodes cannot change "${k2}". Use reparent, delete_nodes or create_node instead.`);
319476
319710
  if (FORBIDDEN_PROPS.has(k2) || v === void 0) continue;
319477
- patch2[k2] = v;
319711
+ patch2[k2] = k2 === "fills" || k2 === "strokes" ? normalizePaints(v, k2) : k2 === "styledSegments" ? normalizeSegments(v) : v;
319478
319712
  changedKeys.add(k2);
319479
319713
  }
319714
+ if ("fills" in patch2) patch2.fillStyleId = void 0;
319715
+ if ("strokes" in patch2) patch2.strokeStyleId = void 0;
319480
319716
  if (args2.strict && !Object.keys(patch2).length) throw new ToolError(`No properties given for "${u.id}".`);
319481
319717
  patches.set(u.id, patch2);
319482
319718
  }
319483
- commitUpdates(ctx2, doc2, patches);
319719
+ for (const [id3, patch2] of patches) {
319720
+ const p2 = patch2;
319721
+ if (!("width" in p2) && !("height" in p2)) continue;
319722
+ const n = doc2.nodes[id3];
319723
+ if (n.width <= 0 || n.height <= 0) continue;
319724
+ const w = p2.width ?? n.width, h = p2.height ?? n.height;
319725
+ if (n.type === "FRAME" || n.type === "COMPONENT" || n.type === "SECTION") continue;
319726
+ for (const [pid, sub2] of scaleSubtreePatches(doc2.nodes, id3, w / n.width, h / n.height)) {
319727
+ patches.set(pid, { ...patches.get(pid), ...sub2 });
319728
+ }
319729
+ }
319730
+ let committed = doc2;
319731
+ for (const [id3, patch2] of patches) {
319732
+ const p2 = patch2;
319733
+ const n = doc2.nodes[id3];
319734
+ if (("width" in p2 || "height" in p2) && (n.type === "FRAME" || n.type === "COMPONENT" || n.type === "SECTION")) {
319735
+ committed = applyConstraints(committed, id3, n.width, n.height, p2.width ?? n.width, p2.height ?? n.height);
319736
+ }
319737
+ }
319738
+ commitUpdates(ctx2, committed, patches);
319484
319739
  return { summary: `Updated ${plural2(patches.size, "layer")} (${[...changedKeys].slice(0, 6).join(", ")})`, data: { ids: [...patches.keys()] } };
319485
319740
  }
319486
319741
  });
@@ -319589,9 +319844,21 @@ var init_node = __esm({
319589
319844
  const n = doc2.nodes[id3];
319590
319845
  const w = args2.width ?? (args2.scale ? n.width * args2.scale : n.width);
319591
319846
  const h = args2.height ?? (args2.scale ? n.height * args2.scale : n.height);
319592
- patches.set(id3, { width: Math.max(AI_LIMITS.minSize, w), height: Math.max(AI_LIMITS.minSize, h) });
319847
+ const width = Math.max(AI_LIMITS.minSize, w), height = Math.max(AI_LIMITS.minSize, h);
319848
+ patches.set(id3, { width, height });
319849
+ if (n.width <= 0 || n.height <= 0) continue;
319850
+ for (const [pid, patch2] of scaleSubtreePatches(doc2.nodes, id3, width / n.width, height / n.height)) {
319851
+ patches.set(pid, { ...patches.get(pid), ...patch2 });
319852
+ }
319593
319853
  }
319594
- commitUpdates(ctx2, doc2, patches);
319854
+ let next2 = doc2;
319855
+ for (const id3 of ids2) {
319856
+ const n = doc2.nodes[id3], p2 = patches.get(id3);
319857
+ if (n.type === "FRAME" || n.type === "COMPONENT" || n.type === "SECTION") {
319858
+ next2 = applyConstraints(next2, id3, n.width, n.height, p2.width, p2.height);
319859
+ }
319860
+ }
319861
+ commitUpdates(ctx2, next2, patches);
319595
319862
  return { summary: `Resized ${plural2(ids2.length, "layer")}`, data: { ids: ids2 } };
319596
319863
  }
319597
319864
  });
@@ -319611,7 +319878,7 @@ var init_node = __esm({
319611
319878
  plugin: { method: "reparentNodes" },
319612
319879
  agent: {
319613
319880
  name: "reparent",
319614
- description: "Move layers into another parent (frame, group or page), optionally at a child index. Positions stay in the new parent's coordinates, so move_nodes afterwards if they land off-screen."
319881
+ description: "Move layers into another parent (frame, group or page), optionally at a child index. Each layer keeps its position on screen (its coordinates are rebased into the new parent)."
319615
319882
  }
319616
319883
  },
319617
319884
  run: (args2, ctx2) => {
@@ -319622,6 +319889,8 @@ var init_node = __esm({
319622
319889
  for (const id3 of args2.ids) {
319623
319890
  nodeOrThrow(doc2, id3);
319624
319891
  if (id3 === args2.parentId) throw new ToolError("A layer cannot be its own parent.");
319892
+ const placed2 = placementUnder(next2, id3, args2.parentId);
319893
+ if (placed2) next2 = updateNode(next2, id3, placed2);
319625
319894
  next2 = reparentNode(next2, id3, args2.parentId, args2.index);
319626
319895
  }
319627
319896
  ctx2.commit(applyInvariants(next2, [...args2.ids, args2.parentId]));
@@ -319928,6 +320197,9 @@ var init_node = __esm({
319928
320197
  svg: external_exports.string().min(8).optional().describe("The full <svg>\u2026</svg> markup."),
319929
320198
  parentId: external_exports.string().optional(),
319930
320199
  at: external_exports.object({ x: external_exports.number(), y: external_exports.number() }).optional().describe("Place the layers in one frame centred on this point (parent coordinates) instead of at the SVG's own coordinates."),
320200
+ anchor: external_exports.enum(["center", "top-left"]).optional().describe("Whether `at` is the frame's centre (default) or its top-left corner."),
320201
+ width: external_exports.number().positive().optional().describe("With `at`: scale the artwork to fit this width (keeps proportions)."),
320202
+ height: external_exports.number().positive().optional().describe("With `at`: scale the artwork to fit this height (keeps proportions)."),
319931
320203
  name: external_exports.string().max(200).optional().describe("Name of that frame (with `at`)."),
319932
320204
  more: external_exports.array(external_exports.object({ svg: external_exports.string().min(8), name: external_exports.string().max(200).optional() })).max(50).optional().describe("Further SVGs placed in the same step (with `at`), each 24 px further down and right.")
319933
320205
  }),
@@ -319935,7 +320207,7 @@ var init_node = __esm({
319935
320207
  plugin: { method: "importSvg" },
319936
320208
  agent: {
319937
320209
  name: "import_svg",
319938
- description: "Import SVG markup as editable layers under a parent (default: the current page). Pass `at` to place them in one frame centred on a point. Returns the ids of the new top-level layers."
320210
+ description: 'Import SVG markup as editable layers under a parent (default: the current page). Pass `at` (in the PARENT\'s coordinates) to place them in one frame centred on that point, or with its top-left there when anchor is "top-left"; add width and/or height to scale the artwork to fit that box. Returns the ids of the new top-level layers.'
319939
320211
  },
319940
320212
  // Paste, drop and File › Import: one undo step, and the ones that would not convert reported back so the
319941
320213
  // caller can place them as pictures instead (lib/editor/svg-paste.ts).
@@ -319953,7 +320225,7 @@ var init_node = __esm({
319953
320225
  const added2 = [];
319954
320226
  const failed2 = [];
319955
320227
  items.forEach((item, i2) => {
319956
- const placed2 = placeSvgLayers2(next3, parentId, item.svg, { name: item.name || "SVG", cx: args2.at.x + i2 * 24, cy: args2.at.y + i2 * 24, intern: (url4) => internDataUrl2(url4) });
320228
+ const placed2 = placeSvgLayers2(next3, parentId, item.svg, { name: item.name || "SVG", cx: args2.at.x + i2 * 24, cy: args2.at.y + i2 * 24, anchor: args2.anchor, fit: { width: args2.width, height: args2.height }, intern: (url4) => internDataUrl2(url4) });
319957
320229
  if (placed2) {
319958
320230
  next3 = placed2.doc;
319959
320231
  added2.push(placed2.id);
@@ -320771,7 +321043,7 @@ var init_paint = __esm({
320771
321043
  else if (mode2 === "replace" || !existing.length) fills = [paint];
320772
321044
  else if (mode2 === "add") fills = [...existing, paint];
320773
321045
  else fills = [paint, ...existing.slice(1)];
320774
- patches.set(id3, { fills });
321046
+ patches.set(id3, { fills, ...args2.remove ? {} : { fillStyleId: void 0 } });
320775
321047
  }
320776
321048
  const what = args2.gradient ? `a ${args2.gradient.type.toLowerCase()} gradient` : args2.image ? "an image" : args2.color;
320777
321049
  commitUpdates(ctx2, doc2, patches);
@@ -320838,7 +321110,7 @@ var init_paint = __esm({
320838
321110
  ...args2.join !== void 0 ? { join: args2.join } : {},
320839
321111
  ...args2.dashPattern !== void 0 ? { dashPattern: args2.dashPattern } : {}
320840
321112
  };
320841
- patches.set(id3, { strokes: [merged, ...existing.slice(1)], ...order2 });
321113
+ patches.set(id3, { strokes: [merged, ...existing.slice(1)], strokeStyleId: void 0, ...order2 });
320842
321114
  }
320843
321115
  commitUpdates(ctx2, doc2, patches);
320844
321116
  return {
@@ -320948,7 +321220,8 @@ var init_paint = __esm({
320948
321220
  const rec = n;
320949
321221
  const patch2 = {};
320950
321222
  let hit = 0;
320951
- const paints = rec.fills;
321223
+ const fillStyle = rec.fillStyleId ? doc2.styles?.[rec.fillStyleId] : void 0;
321224
+ const paints = fillStyle?.type === "PAINT" ? fillStyle.paints : rec.fills;
320952
321225
  if (paints?.length) {
320953
321226
  let changed = false;
320954
321227
  const next2 = paints.map((p2) => {
@@ -320975,7 +321248,10 @@ var init_paint = __esm({
320975
321248
  }
320976
321249
  return out;
320977
321250
  });
320978
- if (changed) patch2.fills = next2;
321251
+ if (changed) {
321252
+ patch2.fills = next2;
321253
+ if (rec.fillStyleId) patch2.fillStyleId = void 0;
321254
+ }
320979
321255
  }
320980
321256
  const strokes = rec.strokes;
320981
321257
  if (strokes?.length) {
@@ -320987,7 +321263,10 @@ var init_paint = __esm({
320987
321263
  hit++;
320988
321264
  return { ...s, color: c2 };
320989
321265
  });
320990
- if (changed) patch2.strokes = next2;
321266
+ if (changed) {
321267
+ patch2.strokes = next2;
321268
+ if (rec.strokeStyleId) patch2.strokeStyleId = void 0;
321269
+ }
320991
321270
  }
320992
321271
  const effects = effectsOf(n);
320993
321272
  if (effects.length) {
@@ -321018,7 +321297,7 @@ var init_paint = __esm({
321018
321297
  }
321019
321298
  }
321020
321299
  if (!swaps) throw new ToolError(`No ${args2.from} found under ${args2.rootId ?? "the selection"}. Check the color with get_node, or raise "tolerance".`);
321021
- ctx2.commit({ ...doc2, nodes });
321300
+ ctx2.commit(applyInvariants({ ...doc2, nodes }, touched));
321022
321301
  return { summary: `Replaced ${args2.from} with ${args2.to} in ${plural2(touched.length, "layer")} (${swaps} paints)`, data: { ids: touched.slice(0, 50), swaps } };
321023
321302
  }
321024
321303
  });
@@ -328471,7 +328750,7 @@ var init_package = __esm({
328471
328750
  "packages/runtime/package.json"() {
328472
328751
  package_default = {
328473
328752
  name: "@popcraft/runtime",
328474
- version: "0.53.1",
328753
+ version: "0.53.3",
328475
328754
  description: "What code exported from PopCraft shares: the meaning of expressions (operators and functions) and, as it grows, the parts of a design's behaviour that are common code rather than generated React.",
328476
328755
  license: "MIT",
328477
328756
  homepage: "https://popcraft.app/docs/exporting/nextjs",
@@ -355184,7 +355463,7 @@ function reading(doc2, ids2) {
355184
355463
  }
355185
355464
  function boardColumns(doc2, roots, looseTitle, within3) {
355186
355465
  const areas = within3?.map((n) => boxOf(doc2, n));
355187
- const wanted = (p2) => !areas || areas.some((a) => inside(p2, a));
355466
+ const wanted = (p2) => !areas || areas.some((a) => inside2(p2, a));
355188
355467
  const cards = [];
355189
355468
  const boxes = [];
355190
355469
  const texts = [];
@@ -355215,7 +355494,7 @@ function boardColumns(doc2, roots, looseTitle, within3) {
355215
355494
  const band = b.box.minY + (b.box.maxY - b.box.minY) * 0.25;
355216
355495
  const top = texts.filter((t3) => {
355217
355496
  const c2 = centre(t3.box);
355218
- return inside(c2, b.box) && c2.y <= band;
355497
+ return inside2(c2, b.box) && c2.y <= band;
355219
355498
  }).sort((x2, y) => x2.box.minY - y.box.minY)[0];
355220
355499
  if (top) titles.set(b.node.id, { text: top.text, id: top.id });
355221
355500
  else if (!DEFAULT_NAMES.test(b.node.name.trim())) titles.set(b.node.id, { text: b.node.name });
@@ -355223,7 +355502,7 @@ function boardColumns(doc2, roots, looseTitle, within3) {
355223
355502
  const columnAt = (p2) => {
355224
355503
  let best = null;
355225
355504
  for (const b of boxes) {
355226
- if (!inside(p2, b.box)) continue;
355505
+ if (!inside2(p2, b.box)) continue;
355227
355506
  if (!best) {
355228
355507
  best = b;
355229
355508
  continue;
@@ -355266,7 +355545,7 @@ function boardCsv(columns) {
355266
355545
  const rows = Math.max(0, ...columns.map((c2) => c2.items.length));
355267
355546
  return toCsv([columns.map((c2) => c2.title), ...Array.from({ length: rows }, (_, i2) => columns.map((c2) => c2.items[i2] ?? ""))]);
355268
355547
  }
355269
- var MARGIN, MAX_BOARD_EXPORT_PX, textOf, boxOf, centre, inside, area3, textLayer, COLUMN_TYPES;
355548
+ var MARGIN, MAX_BOARD_EXPORT_PX, textOf, boxOf, centre, inside2, area3, textLayer, COLUMN_TYPES;
355270
355549
  var init_board_export = __esm({
355271
355550
  "lib/popcorn/board-export.ts"() {
355272
355551
  init_scene_graph();
@@ -355276,7 +355555,7 @@ var init_board_export = __esm({
355276
355555
  textOf = (n) => (n.text ?? "").replace(/\s+/g, " ").trim();
355277
355556
  boxOf = (doc2, n) => uprightBounds(getWorldTransform(doc2, n.id), n.width, n.height);
355278
355557
  centre = (b) => ({ x: (b.minX + b.maxX) / 2, y: (b.minY + b.maxY) / 2 });
355279
- inside = (p2, b) => p2.x >= b.minX && p2.x <= b.maxX && p2.y >= b.minY && p2.y <= b.maxY;
355558
+ inside2 = (p2, b) => p2.x >= b.minX && p2.x <= b.maxX && p2.y >= b.minY && p2.y <= b.maxY;
355280
355559
  area3 = (b) => (b.maxX - b.minX) * (b.maxY - b.minY);
355281
355560
  textLayer = (n) => n.type === "TEXT" ? (n.characters ?? "").replace(/\s+/g, " ").trim() : "";
355282
355561
  COLUMN_TYPES = /* @__PURE__ */ new Set(["FRAME", "SECTION", "GROUP", "RECTANGLE", "INSTANCE", "COMPONENT", "ELLIPSE"]);
@@ -358625,11 +358904,11 @@ function mapCompilerMessages(messages, offset, lines2) {
358625
358904
  for (const m of messages) {
358626
358905
  if (m.type !== "error" && m.type !== "warning") continue;
358627
358906
  const line = m.lineNum - offset;
358628
- const inside2 = m.lineNum > 0 && line >= 1 && line <= lines2;
358907
+ const inside3 = m.lineNum > 0 && line >= 1 && line <= lines2;
358629
358908
  out.push({
358630
- line: inside2 ? line : null,
358631
- column: inside2 && m.linePos > 0 ? m.linePos : null,
358632
- message: inside2 ? m.message : harnessMessage(m.message),
358909
+ line: inside3 ? line : null,
358910
+ column: inside3 && m.linePos > 0 ? m.linePos : null,
358911
+ message: inside3 ? m.message : harnessMessage(m.message),
358633
358912
  severity: m.type === "error" ? "error" : "warning"
358634
358913
  });
358635
358914
  }
@@ -359144,9 +359423,9 @@ var init_motion_rich = __esm({
359144
359423
  if (def.type !== "NUMBER" && def.type !== "COLOR" && def.type !== "IMAGE") throw new ToolError("Only a NUMBER, COLOR or IMAGE property feeds a layer input.");
359145
359424
  const layer2 = nodeOrThrow(doc2, op4.layer);
359146
359425
  const owner = target2.type === "COMPONENT_SET" ? target2.id : target2.id;
359147
- let inside2 = false;
359148
- for (let n = layer2; n; n = n.parentId ? doc2.nodes[n.parentId] : void 0) if (n.id === owner) inside2 = true;
359149
- if (!inside2 || layer2.id === owner) throw new ToolError(`"${layer2.name}" is not a layer inside "${target2.name}".`);
359426
+ let inside3 = false;
359427
+ for (let n = layer2; n; n = n.parentId ? doc2.nodes[n.parentId] : void 0) if (n.id === owner) inside3 = true;
359428
+ if (!inside3 || layer2.id === owner) throw new ToolError(`"${layer2.name}" is not a layer inside "${target2.name}".`);
359150
359429
  const props = layerInputPatch(layer2, op4.name, op4.path);
359151
359430
  if (!props) throw new ToolError(`"${layer2.name}" has no ${op4.path}.`);
359152
359431
  commitUpdates(ctx2, doc2, /* @__PURE__ */ new Map([[layer2.id, props]]));
@@ -361276,7 +361555,14 @@ var init_media = __esm({
361276
361555
  },
361277
361556
  run: async (args2) => {
361278
361557
  const { listLibraryMedia: listLibraryMedia2 } = await Promise.resolve().then(() => (init_client5(), client_exports3));
361279
- const page = await listLibraryMedia2(args2);
361558
+ const q = { ...args2 };
361559
+ if (!q.workspace) {
361560
+ const { syncStatus: syncStatus2 } = await Promise.resolve().then(() => (init_sync2(), sync_exports2));
361561
+ const link2 = syncStatus2().link;
361562
+ if (!link2) throw new ToolError("This document is not saved to the cloud, so it has no team media library. Pass workspace.");
361563
+ q.workspace = (await listLibraryMedia2({ file: link2.key, limit: 1 })).workspaceId;
361564
+ }
361565
+ const page = await listLibraryMedia2(q);
361280
361566
  return { summary: `${page.items.length} media item${page.items.length === 1 ? "" : "s"}${page.nextCursor ? " (more)" : ""}`, data: { workspaceId: page.workspaceId, items: page.items.map(brief3), nextCursor: page.nextCursor } };
361281
361567
  }
361282
361568
  });
@@ -362221,7 +362507,6 @@ var init_delete_current_file = __esm({
362221
362507
  var storeOpsSeam;
362222
362508
  var init_seams = __esm({
362223
362509
  "lib/commands/seams.ts"() {
362224
- "use strict";
362225
362510
  storeOpsSeam = (st) => ({
362226
362511
  booleanOperation: (op4) => st().booleanOperation(op4),
362227
362512
  flatten: () => st().flattenSelection(),
@@ -401277,6 +401562,7 @@ function placeDesign(doc2, built, source3, size6, name2, at4) {
401277
401562
  var DESIGN_NAMESPACES, DERIVED2, templateOfBuild;
401278
401563
  var init_build3 = __esm({
401279
401564
  "lib/agent/design-script/build.ts"() {
401565
+ "use strict";
401280
401566
  init_hash();
401281
401567
  init_scene_graph();
401282
401568
  init_libraries();
@@ -401306,6 +401592,7 @@ function designFrameOf(doc2, id3) {
401306
401592
  }
401307
401593
  var init_locate = __esm({
401308
401594
  "lib/agent/design-script/locate.ts"() {
401595
+ "use strict";
401309
401596
  }
401310
401597
  });
401311
401598
 
@@ -401852,6 +402139,10 @@ async function drive(meta3, start2) {
401852
402139
  return [...notes, ...runtime.drainParentNotifications()];
401853
402140
  },
401854
402141
  maxParallel: MAX_PARALLEL_TOOLS,
402142
+ // The loop's default cut every result at 8000 characters AFTER the tool had honoured its own limit, so
402143
+ // export_svg with maxChars 40000 still came back without its last paths and the model could not
402144
+ // rebuild the artwork. Tools bound their own output; this is only the backstop.
402145
+ maxResultChars: MAX_TOOL_RESULT_CHARS,
401855
402146
  maxRounds: TURN_MAX_ROUNDS,
401856
402147
  modelRetries: 3,
401857
402148
  isRecoverable: (err2) => !abort.signal.aborted && isInterruption(err2),
@@ -401977,7 +402268,7 @@ async function drive(meta3, start2) {
401977
402268
  useAgentStore.setState({ running: false, resuming: false, abort: null });
401978
402269
  }
401979
402270
  }
401980
- var ALWAYS_ALLOWED, MAX_HISTORY_MESSAGES, MAX_PARALLEL_TOOLS, TURN_MAX_ROUNDS, registerTurnTools, specsFor, loopTools, runs, agentRuns, setAgentRuns, RESUME_WINDOW_MS, MAX_INTERRUPTIONS, SUSPEND_RESTART_MS, STOPPED_MESSAGE;
402271
+ var ALWAYS_ALLOWED, MAX_HISTORY_MESSAGES, MAX_PARALLEL_TOOLS, MAX_TOOL_RESULT_CHARS, TURN_MAX_ROUNDS, registerTurnTools, specsFor, loopTools, runs, agentRuns, setAgentRuns, RESUME_WINDOW_MS, MAX_INTERRUPTIONS, SUSPEND_RESTART_MS, STOPPED_MESSAGE;
401981
402272
  var init_runner = __esm({
401982
402273
  "lib/agent/runner.ts"() {
401983
402274
  init_editor_store();
@@ -402003,6 +402294,7 @@ var init_runner = __esm({
402003
402294
  ALWAYS_ALLOWED = ["list_skills", "read_skill", "manage_todos", "notify", "get_selection", "task", "await_job", "send_message", "stop_agent", "list_agents", "list_profiles", "read_profile", "ask_user"];
402004
402295
  MAX_HISTORY_MESSAGES = 24;
402005
402296
  MAX_PARALLEL_TOOLS = 4;
402297
+ MAX_TOOL_RESULT_CHARS = 48e3;
402006
402298
  TURN_MAX_ROUNDS = 500;
402007
402299
  registerTurnTools = (deps) => {
402008
402300
  registerBuiltinTools();
@@ -406487,7 +406779,6 @@ function registerBuiltinCommands() {
406487
406779
  var builtinCommands, uiOnlyCommands;
406488
406780
  var init_builtin4 = __esm({
406489
406781
  "lib/commands/builtin.ts"() {
406490
- "use strict";
406491
406782
  init_registry();
406492
406783
  init_node();
406493
406784
  init_node();
@@ -407369,7 +407660,6 @@ __export(editor_store_exports, {
407369
407660
  rebuildVectorData: () => rebuildVectorData,
407370
407661
  recoverPointsFromData: () => recoverPointsFromData,
407371
407662
  resetLibraryDocuments: () => resetLibraryDocuments,
407372
- scaleChildrenRecursively: () => scaleChildrenRecursively,
407373
407663
  tabPrefsOnly: () => tabPrefsOnly,
407374
407664
  useActiveTool: () => useActiveTool,
407375
407665
  useCamera: () => useCamera,
@@ -407542,37 +407832,6 @@ function stripImagePlaceholderFills(nodes, prevNodes, changed) {
407542
407832
  }
407543
407833
  return out ?? nodes;
407544
407834
  }
407545
- function rebuildVectorData(points, closed) {
407546
- if (points.length < 2) return [];
407547
- const data = [];
407548
- data.push("M", points[0].x, points[0].y);
407549
- for (let i2 = 1; i2 < points.length; i2++) {
407550
- const prev = points[i2 - 1];
407551
- const curr = points[i2];
407552
- if (prev.handleOut && curr.handleIn) {
407553
- data.push("C", prev.handleOut.x, prev.handleOut.y, curr.handleIn.x, curr.handleIn.y, curr.x, curr.y);
407554
- } else if (prev.handleOut) {
407555
- data.push("Q", prev.handleOut.x, prev.handleOut.y, curr.x, curr.y);
407556
- } else if (curr.handleIn) {
407557
- data.push("Q", curr.handleIn.x, curr.handleIn.y, curr.x, curr.y);
407558
- } else {
407559
- data.push("L", curr.x, curr.y);
407560
- }
407561
- }
407562
- if (closed) {
407563
- const prev = points[points.length - 1];
407564
- const curr = points[0];
407565
- if (prev.handleOut && curr.handleIn) {
407566
- data.push("C", prev.handleOut.x, prev.handleOut.y, curr.handleIn.x, curr.handleIn.y, curr.x, curr.y);
407567
- } else if (prev.handleOut) {
407568
- data.push("Q", prev.handleOut.x, prev.handleOut.y, curr.x, curr.y);
407569
- } else if (curr.handleIn) {
407570
- data.push("Q", curr.handleIn.x, curr.handleIn.y, curr.x, curr.y);
407571
- }
407572
- data.push("Z");
407573
- }
407574
- return data;
407575
- }
407576
407835
  function fixCorruptedVectors(doc2) {
407577
407836
  let changed = false;
407578
407837
  const newNodes = { ...doc2.nodes };
@@ -407722,30 +407981,6 @@ function recoverPointsFromData(data) {
407722
407981
  }
407723
407982
  return points;
407724
407983
  }
407725
- function scaleChildrenRecursively(doc2, parentId, scaleX, scaleY) {
407726
- const parent = doc2.nodes[parentId];
407727
- if (!parent || !parent.childIds) return doc2;
407728
- const newDoc = { ...doc2, nodes: { ...doc2.nodes } };
407729
- const processNode = (nodeId2) => {
407730
- const node2 = newDoc.nodes[nodeId2];
407731
- if (!node2) return;
407732
- const updates = {
407733
- x: node2.x * scaleX,
407734
- y: node2.y * scaleY,
407735
- width: node2.width * scaleX,
407736
- height: node2.height * scaleY
407737
- };
407738
- if (node2.type === "TEXT") {
407739
- updates.fontSize = (node2.fontSize ?? 16) * Math.min(scaleX, scaleY);
407740
- }
407741
- newDoc.nodes[nodeId2] = { ...node2, ...updates };
407742
- if (node2.childIds) {
407743
- node2.childIds.forEach(processNode);
407744
- }
407745
- };
407746
- parent.childIds.forEach(processNode);
407747
- return newDoc;
407748
- }
407749
407984
  function placeInstance(document2, mainComponentId, pageId2, x2, y) {
407750
407985
  const main2 = document2.nodes[mainComponentId];
407751
407986
  if (!main2 || main2.type !== "COMPONENT") return null;
@@ -409352,10 +409587,10 @@ var init_editor_store = __esm({
409352
409587
  if ("childIds" in node2 && node2.childIds.length > 0) {
409353
409588
  newDoc = applyConstraints(newDoc, id3, node2.width, node2.height, newWidth, newHeight);
409354
409589
  }
409355
- if (node2.type === "GROUP" && node2.width > 0 && node2.height > 0) {
409356
- const scaleX = newWidth / node2.width;
409357
- const scaleY = newHeight / node2.height;
409358
- newDoc = scaleChildrenRecursively(newDoc, id3, scaleX, scaleY);
409590
+ if ((node2.type === "GROUP" || node2.type === "VECTOR") && node2.width > 0 && node2.height > 0) {
409591
+ for (const [pid, patch2] of scaleSubtreePatches(newDoc.nodes, id3, newWidth / node2.width, newHeight / node2.height)) {
409592
+ newDoc = updateNode(newDoc, pid, patch2);
409593
+ }
409359
409594
  } else if (node2.type === "TABLE" && node2.width > 0 && node2.height > 0) {
409360
409595
  const table3 = node2;
409361
409596
  const totalW = (table3.colWidths || []).reduce((a, b) => a + b, 0) || 1;
@@ -410364,23 +410599,35 @@ var init_editor_store = __esm({
410364
410599
  const geo = multiPolyToVectorGeometry(mp);
410365
410600
  if (!geo) return;
410366
410601
  const first2 = doc2.nodes[state.selection[0]];
410602
+ const firstFills = (id3) => {
410603
+ const n = doc2.nodes[id3];
410604
+ if (!n) return void 0;
410605
+ if (n.fills?.length && n.type !== "GROUP") return n.fills;
410606
+ for (const c2 of n.childIds ?? []) {
410607
+ const f2 = firstFills(c2);
410608
+ if (f2) return f2;
410609
+ }
410610
+ return void 0;
410611
+ };
410612
+ const fills = state.selection.map(firstFills).find(Boolean);
410367
410613
  const vectorNode = createVector({
410368
410614
  ...geo,
410369
- fills: first2?.fills?.length ? first2.fills : [{ type: "SOLID", color: { r: 0.85, g: 0.85, b: 0.85, a: 1 }, visible: true }],
410615
+ fills: fills ?? [{ type: "SOLID", color: { r: 0.85, g: 0.85, b: 0.85, a: 1 }, visible: true }],
410370
410616
  strokes: [],
410371
410617
  fillRule: "EVENODD",
410372
410618
  name: state.selection.length > 1 ? "Flattened" : `${first2?.name ?? "Vector"} (flattened)`
410373
410619
  });
410374
410620
  doc2 = addNode(doc2, vectorNode, state.currentPageId);
410621
+ const parentId = first2?.parentId;
410622
+ if (parentId && parentId !== state.currentPageId && doc2.nodes[parentId]) {
410623
+ const placed2 = placementUnder(doc2, vectorNode.id, parentId);
410624
+ if (placed2) doc2 = reparentNode(updateNode(doc2, vectorNode.id, placed2), vectorNode.id, parentId);
410625
+ }
410375
410626
  state.selection.forEach((id3) => {
410376
410627
  if (doc2.nodes[id3]) doc2 = removeNode(doc2, id3);
410377
410628
  });
410378
- const command = createCommand("FLATTEN", "Flatten selection", documentBefore, doc2);
410379
- set4({
410380
- document: doc2,
410381
- history: pushCommand(state.history, command),
410382
- selection: [vectorNode.id]
410383
- });
410629
+ if (documentBefore !== doc2) get4().commitDocument(doc2, "Flatten selection");
410630
+ set4({ selection: [vectorNode.id] });
410384
410631
  },
410385
410632
  joinPaths: () => {
410386
410633
  const state = get4();
@@ -414092,7 +414339,7 @@ init_resources();
414092
414339
  init_desktop();
414093
414340
  init_hypery();
414094
414341
  import { createInterface } from "node:readline";
414095
- var VERSION2 = "0.53.1";
414342
+ var VERSION2 = "0.53.3";
414096
414343
  var HELP = `popcraft ${VERSION2} \u2014 the PopCraft command line
414097
414344
 
414098
414345
  Files (headless, on .popcraft files):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@popcraft/cli",
3
- "version": "0.53.1",
3
+ "version": "0.53.3",
4
4
  "description": "The PopCraft command line: edit .popcraft files headlessly, call the REST API, publish to the marketplace and manage the desktop app's local plugins, themes, fonts, skills and agents.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://popcraft.app/docs/api/cli",
@@ -34,7 +34,7 @@
34
34
  "esbuild": "0.28.2"
35
35
  },
36
36
  "dependencies": {
37
- "@popcraft/kit": "0.53.1"
37
+ "@popcraft/kit": "0.53.3"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "node build.mjs",