@pooder/kit 6.2.1 → 6.3.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/index.mjs CHANGED
@@ -1192,6 +1192,442 @@ import {
1192
1192
  controlsUtils
1193
1193
  } from "fabric";
1194
1194
 
1195
+ // src/shared/scene/frame.ts
1196
+ function emptyFrameRect() {
1197
+ return { left: 0, top: 0, width: 0, height: 0 };
1198
+ }
1199
+ function resolveCutFrameRect(canvasService, configService) {
1200
+ if (!canvasService || !configService) {
1201
+ return emptyFrameRect();
1202
+ }
1203
+ const sizeState = readSizeState(configService);
1204
+ const layout = computeSceneLayout(canvasService, sizeState);
1205
+ if (!layout) {
1206
+ return emptyFrameRect();
1207
+ }
1208
+ return canvasService.toSceneRect({
1209
+ left: layout.cutRect.left,
1210
+ top: layout.cutRect.top,
1211
+ width: layout.cutRect.width,
1212
+ height: layout.cutRect.height
1213
+ });
1214
+ }
1215
+ function toLayoutSceneRect(rect) {
1216
+ return {
1217
+ left: rect.left,
1218
+ top: rect.top,
1219
+ width: rect.width,
1220
+ height: rect.height,
1221
+ space: "scene"
1222
+ };
1223
+ }
1224
+
1225
+ // src/shared/runtime/sessionState.ts
1226
+ function cloneWithJson(value) {
1227
+ return JSON.parse(JSON.stringify(value));
1228
+ }
1229
+ function applyCommittedSnapshot(session, nextCommitted, options) {
1230
+ const clone = options.clone;
1231
+ session.committed = clone(nextCommitted);
1232
+ const shouldPreserveDirtyWorking = options.toolActive && options.preserveDirtyWorking !== false && session.hasWorkingChanges;
1233
+ if (!shouldPreserveDirtyWorking) {
1234
+ session.working = clone(session.committed);
1235
+ session.hasWorkingChanges = false;
1236
+ }
1237
+ }
1238
+ function runDeferredConfigUpdate(state, action, cooldownMs = 0) {
1239
+ state.isUpdatingConfig = true;
1240
+ action();
1241
+ if (cooldownMs <= 0) {
1242
+ state.isUpdatingConfig = false;
1243
+ return;
1244
+ }
1245
+ setTimeout(() => {
1246
+ state.isUpdatingConfig = false;
1247
+ }, cooldownMs);
1248
+ }
1249
+
1250
+ // src/extensions/image/commands.ts
1251
+ function createImageCommands(tool) {
1252
+ return [
1253
+ {
1254
+ command: "addImage",
1255
+ id: "addImage",
1256
+ title: "Add Image",
1257
+ handler: async (url, options) => {
1258
+ const result = await tool.upsertImageEntry(url, {
1259
+ mode: "add",
1260
+ addOptions: options
1261
+ });
1262
+ return result.id;
1263
+ }
1264
+ },
1265
+ {
1266
+ command: "upsertImage",
1267
+ id: "upsertImage",
1268
+ title: "Upsert Image",
1269
+ handler: async (url, options = {}) => {
1270
+ return await tool.upsertImageEntry(url, options);
1271
+ }
1272
+ },
1273
+ {
1274
+ command: "applyImageOperation",
1275
+ id: "applyImageOperation",
1276
+ title: "Apply Image Operation",
1277
+ handler: async (id, operation, options = {}) => {
1278
+ await tool.applyImageOperation(id, operation, options);
1279
+ }
1280
+ },
1281
+ {
1282
+ command: "getImageViewState",
1283
+ id: "getImageViewState",
1284
+ title: "Get Image View State",
1285
+ handler: () => {
1286
+ return tool.getImageViewState();
1287
+ }
1288
+ },
1289
+ {
1290
+ command: "setImageTransform",
1291
+ id: "setImageTransform",
1292
+ title: "Set Image Transform",
1293
+ handler: async (id, updates, options = {}) => {
1294
+ await tool.setImageTransform(id, updates, options);
1295
+ }
1296
+ },
1297
+ {
1298
+ command: "imageSessionReset",
1299
+ id: "imageSessionReset",
1300
+ title: "Reset Image Session",
1301
+ handler: () => {
1302
+ tool.resetImageSession();
1303
+ }
1304
+ },
1305
+ {
1306
+ command: "completeImages",
1307
+ id: "completeImages",
1308
+ title: "Complete Images",
1309
+ handler: async () => {
1310
+ return await tool.commitWorkingImagesAsCropped();
1311
+ }
1312
+ },
1313
+ {
1314
+ command: "exportUserCroppedImage",
1315
+ id: "exportUserCroppedImage",
1316
+ title: "Export User Cropped Image",
1317
+ handler: async (options = {}) => {
1318
+ return await tool.exportUserCroppedImage(options);
1319
+ }
1320
+ },
1321
+ {
1322
+ command: "focusImage",
1323
+ id: "focusImage",
1324
+ title: "Focus Image",
1325
+ handler: (id, options = {}) => {
1326
+ return tool.setImageFocus(id, options);
1327
+ }
1328
+ },
1329
+ {
1330
+ command: "removeImage",
1331
+ id: "removeImage",
1332
+ title: "Remove Image",
1333
+ handler: (id) => {
1334
+ const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
1335
+ const removed = sourceItems.find((item) => item.id === id);
1336
+ const next = sourceItems.filter((item) => item.id !== id);
1337
+ if (next.length !== sourceItems.length) {
1338
+ tool.purgeSourceSizeCacheForItem(removed);
1339
+ if (tool.focusedImageId === id) {
1340
+ tool.setImageFocus(null, {
1341
+ syncCanvasSelection: true,
1342
+ skipRender: true
1343
+ });
1344
+ }
1345
+ if (tool.isToolActive) {
1346
+ tool.workingItems = tool.cloneItems(next);
1347
+ tool.hasWorkingChanges = true;
1348
+ tool.updateImages();
1349
+ tool.emitWorkingChange(id);
1350
+ return;
1351
+ }
1352
+ tool.updateConfig(next);
1353
+ }
1354
+ }
1355
+ },
1356
+ {
1357
+ command: "updateImage",
1358
+ id: "updateImage",
1359
+ title: "Update Image",
1360
+ handler: async (id, updates, options = {}) => {
1361
+ await tool.updateImage(id, updates, options);
1362
+ }
1363
+ },
1364
+ {
1365
+ command: "clearImages",
1366
+ id: "clearImages",
1367
+ title: "Clear Images",
1368
+ handler: () => {
1369
+ tool.sourceSizeCache.clear();
1370
+ tool.setImageFocus(null, {
1371
+ syncCanvasSelection: true,
1372
+ skipRender: true
1373
+ });
1374
+ if (tool.isToolActive) {
1375
+ tool.workingItems = [];
1376
+ tool.hasWorkingChanges = true;
1377
+ tool.updateImages();
1378
+ tool.emitWorkingChange();
1379
+ return;
1380
+ }
1381
+ tool.updateConfig([]);
1382
+ }
1383
+ },
1384
+ {
1385
+ command: "bringToFront",
1386
+ id: "bringToFront",
1387
+ title: "Bring Image to Front",
1388
+ handler: (id) => {
1389
+ const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
1390
+ const index = sourceItems.findIndex((item) => item.id === id);
1391
+ if (index !== -1 && index < sourceItems.length - 1) {
1392
+ const next = [...sourceItems];
1393
+ const [item] = next.splice(index, 1);
1394
+ next.push(item);
1395
+ if (tool.isToolActive) {
1396
+ tool.workingItems = tool.cloneItems(next);
1397
+ tool.hasWorkingChanges = true;
1398
+ tool.updateImages();
1399
+ tool.emitWorkingChange(id);
1400
+ return;
1401
+ }
1402
+ tool.updateConfig(next);
1403
+ }
1404
+ }
1405
+ },
1406
+ {
1407
+ command: "sendToBack",
1408
+ id: "sendToBack",
1409
+ title: "Send Image to Back",
1410
+ handler: (id) => {
1411
+ const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
1412
+ const index = sourceItems.findIndex((item) => item.id === id);
1413
+ if (index > 0) {
1414
+ const next = [...sourceItems];
1415
+ const [item] = next.splice(index, 1);
1416
+ next.unshift(item);
1417
+ if (tool.isToolActive) {
1418
+ tool.workingItems = tool.cloneItems(next);
1419
+ tool.hasWorkingChanges = true;
1420
+ tool.updateImages();
1421
+ tool.emitWorkingChange(id);
1422
+ return;
1423
+ }
1424
+ tool.updateConfig(next);
1425
+ }
1426
+ }
1427
+ }
1428
+ ];
1429
+ }
1430
+
1431
+ // src/extensions/image/config.ts
1432
+ function createImageConfigurations() {
1433
+ return [
1434
+ {
1435
+ id: "image.items",
1436
+ type: "array",
1437
+ label: "Images",
1438
+ default: []
1439
+ },
1440
+ {
1441
+ id: "image.debug",
1442
+ type: "boolean",
1443
+ label: "Image Debug Log",
1444
+ default: false
1445
+ },
1446
+ {
1447
+ id: "image.control.cornerSize",
1448
+ type: "number",
1449
+ label: "Image Control Corner Size",
1450
+ min: 4,
1451
+ max: 64,
1452
+ step: 1,
1453
+ default: 14
1454
+ },
1455
+ {
1456
+ id: "image.control.touchCornerSize",
1457
+ type: "number",
1458
+ label: "Image Control Touch Corner Size",
1459
+ min: 8,
1460
+ max: 96,
1461
+ step: 1,
1462
+ default: 24
1463
+ },
1464
+ {
1465
+ id: "image.control.cornerStyle",
1466
+ type: "select",
1467
+ label: "Image Control Corner Style",
1468
+ options: ["circle", "rect"],
1469
+ default: "circle"
1470
+ },
1471
+ {
1472
+ id: "image.control.cornerColor",
1473
+ type: "color",
1474
+ label: "Image Control Corner Color",
1475
+ default: "#ffffff"
1476
+ },
1477
+ {
1478
+ id: "image.control.cornerStrokeColor",
1479
+ type: "color",
1480
+ label: "Image Control Corner Stroke Color",
1481
+ default: "#1677ff"
1482
+ },
1483
+ {
1484
+ id: "image.control.transparentCorners",
1485
+ type: "boolean",
1486
+ label: "Image Control Transparent Corners",
1487
+ default: false
1488
+ },
1489
+ {
1490
+ id: "image.control.borderColor",
1491
+ type: "color",
1492
+ label: "Image Control Border Color",
1493
+ default: "#1677ff"
1494
+ },
1495
+ {
1496
+ id: "image.control.borderScaleFactor",
1497
+ type: "number",
1498
+ label: "Image Control Border Width",
1499
+ min: 0.5,
1500
+ max: 8,
1501
+ step: 0.1,
1502
+ default: 1.5
1503
+ },
1504
+ {
1505
+ id: "image.control.padding",
1506
+ type: "number",
1507
+ label: "Image Control Padding",
1508
+ min: 0,
1509
+ max: 64,
1510
+ step: 1,
1511
+ default: 0
1512
+ },
1513
+ {
1514
+ id: "image.frame.strokeColor",
1515
+ type: "color",
1516
+ label: "Image Frame Stroke Color",
1517
+ default: "#808080"
1518
+ },
1519
+ {
1520
+ id: "image.frame.strokeWidth",
1521
+ type: "number",
1522
+ label: "Image Frame Stroke Width",
1523
+ min: 0,
1524
+ max: 20,
1525
+ step: 0.5,
1526
+ default: 2
1527
+ },
1528
+ {
1529
+ id: "image.frame.strokeStyle",
1530
+ type: "select",
1531
+ label: "Image Frame Stroke Style",
1532
+ options: ["solid", "dashed", "hidden"],
1533
+ default: "dashed"
1534
+ },
1535
+ {
1536
+ id: "image.frame.dashLength",
1537
+ type: "number",
1538
+ label: "Image Frame Dash Length",
1539
+ min: 1,
1540
+ max: 40,
1541
+ step: 1,
1542
+ default: 8
1543
+ },
1544
+ {
1545
+ id: "image.frame.innerBackground",
1546
+ type: "color",
1547
+ label: "Image Frame Inner Background",
1548
+ default: "rgba(0,0,0,0)"
1549
+ },
1550
+ {
1551
+ id: "image.frame.outerBackground",
1552
+ type: "color",
1553
+ label: "Image Frame Outer Background",
1554
+ default: "#f5f5f5"
1555
+ }
1556
+ ];
1557
+ }
1558
+
1559
+ // src/extensions/image/imageOperations.ts
1560
+ function clampNormalizedAnchor(value) {
1561
+ return Math.max(-1, Math.min(2, value));
1562
+ }
1563
+ function toNormalizedAnchor(center, start, size) {
1564
+ return clampNormalizedAnchor((center - start) / Math.max(1, size));
1565
+ }
1566
+ function resolveAbsoluteScale(operation, area, source) {
1567
+ const widthScale = Math.max(1, area.width) / Math.max(1, source.width);
1568
+ const heightScale = Math.max(1, area.height) / Math.max(1, source.height);
1569
+ switch (operation.type) {
1570
+ case "cover":
1571
+ return Math.max(widthScale, heightScale);
1572
+ case "contain":
1573
+ return Math.min(widthScale, heightScale);
1574
+ case "maximizeWidth":
1575
+ return widthScale;
1576
+ case "maximizeHeight":
1577
+ return heightScale;
1578
+ default:
1579
+ return null;
1580
+ }
1581
+ }
1582
+ function resolveImageOperationArea(args) {
1583
+ const spec = args.area || { type: "frame" };
1584
+ if (spec.type === "custom") {
1585
+ return {
1586
+ width: Math.max(1, spec.width),
1587
+ height: Math.max(1, spec.height),
1588
+ centerX: spec.centerX,
1589
+ centerY: spec.centerY
1590
+ };
1591
+ }
1592
+ if (spec.type === "viewport") {
1593
+ return {
1594
+ width: Math.max(1, args.viewport.width),
1595
+ height: Math.max(1, args.viewport.height),
1596
+ centerX: args.viewport.left + args.viewport.width / 2,
1597
+ centerY: args.viewport.top + args.viewport.height / 2
1598
+ };
1599
+ }
1600
+ return {
1601
+ width: Math.max(1, args.frame.width),
1602
+ height: Math.max(1, args.frame.height),
1603
+ centerX: args.frame.left + args.frame.width / 2,
1604
+ centerY: args.frame.top + args.frame.height / 2
1605
+ };
1606
+ }
1607
+ function computeImageOperationUpdates(args) {
1608
+ const { frame, source, operation, area } = args;
1609
+ if (operation.type === "resetTransform") {
1610
+ return {
1611
+ scale: 1,
1612
+ left: 0.5,
1613
+ top: 0.5,
1614
+ angle: 0
1615
+ };
1616
+ }
1617
+ const left = toNormalizedAnchor(area.centerX, frame.left, frame.width);
1618
+ const top = toNormalizedAnchor(area.centerY, frame.top, frame.height);
1619
+ if (operation.type === "center") {
1620
+ return { left, top };
1621
+ }
1622
+ const absoluteScale = resolveAbsoluteScale(operation, area, source);
1623
+ const coverScale = getCoverScale(frame, source);
1624
+ return {
1625
+ scale: Math.max(0.05, (absoluteScale || coverScale) / coverScale),
1626
+ left,
1627
+ top
1628
+ };
1629
+ }
1630
+
1195
1631
  // src/extensions/geometry.ts
1196
1632
  import paper from "paper";
1197
1633
 
@@ -1768,391 +2204,177 @@ function generateBleedZonePath(originalOptions, offsetOptions, offset) {
1768
2204
  }
1769
2205
  const pathData = bleedZone.pathData;
1770
2206
  shapeOriginal.remove();
1771
- shapeOffset.remove();
1772
- bleedZone.remove();
1773
- return pathData;
1774
- }
1775
- function getLowestPointOnDieline(options) {
1776
- ensurePaper(options.width * 2, options.height * 2);
1777
- paper.project.activeLayer.removeChildren();
1778
- const shape = createBaseShape(options);
1779
- const bounds = shape.bounds;
1780
- const result = {
1781
- x: bounds.center.x,
1782
- y: bounds.bottom
1783
- };
1784
- shape.remove();
1785
- return result;
1786
- }
1787
- function getNearestPointOnDieline(point, options) {
1788
- ensurePaper(options.width * 2, options.height * 2);
1789
- paper.project.activeLayer.removeChildren();
1790
- const shape = createBaseShape(options);
1791
- const p = new paper.Point(point.x, point.y);
1792
- const location = shape.getNearestLocation(p);
1793
- const result = {
1794
- x: location.point.x,
1795
- y: location.point.y,
1796
- normal: location.normal ? { x: location.normal.x, y: location.normal.y } : void 0
1797
- };
1798
- shape.remove();
1799
- return result;
1800
- }
1801
- function getPathBounds(pathData) {
1802
- const path = new paper.Path();
1803
- path.pathData = pathData;
1804
- const bounds = path.bounds;
1805
- path.remove();
1806
- return {
1807
- x: bounds.x,
1808
- y: bounds.y,
1809
- width: bounds.width,
1810
- height: bounds.height
1811
- };
1812
- }
1813
-
1814
- // src/shared/scene/frame.ts
1815
- function emptyFrameRect() {
1816
- return { left: 0, top: 0, width: 0, height: 0 };
1817
- }
1818
- function resolveCutFrameRect(canvasService, configService) {
1819
- if (!canvasService || !configService) {
1820
- return emptyFrameRect();
1821
- }
1822
- const sizeState = readSizeState(configService);
1823
- const layout = computeSceneLayout(canvasService, sizeState);
1824
- if (!layout) {
1825
- return emptyFrameRect();
1826
- }
1827
- return canvasService.toSceneRect({
1828
- left: layout.cutRect.left,
1829
- top: layout.cutRect.top,
1830
- width: layout.cutRect.width,
1831
- height: layout.cutRect.height
1832
- });
1833
- }
1834
- function toLayoutSceneRect(rect) {
1835
- return {
1836
- left: rect.left,
1837
- top: rect.top,
1838
- width: rect.width,
1839
- height: rect.height,
1840
- space: "scene"
1841
- };
1842
- }
1843
-
1844
- // src/shared/runtime/sessionState.ts
1845
- function cloneWithJson(value) {
1846
- return JSON.parse(JSON.stringify(value));
1847
- }
1848
- function applyCommittedSnapshot(session, nextCommitted, options) {
1849
- const clone = options.clone;
1850
- session.committed = clone(nextCommitted);
1851
- const shouldPreserveDirtyWorking = options.toolActive && options.preserveDirtyWorking !== false && session.hasWorkingChanges;
1852
- if (!shouldPreserveDirtyWorking) {
1853
- session.working = clone(session.committed);
1854
- session.hasWorkingChanges = false;
1855
- }
1856
- }
1857
- function runDeferredConfigUpdate(state, action, cooldownMs = 0) {
1858
- state.isUpdatingConfig = true;
1859
- action();
1860
- if (cooldownMs <= 0) {
1861
- state.isUpdatingConfig = false;
1862
- return;
1863
- }
1864
- setTimeout(() => {
1865
- state.isUpdatingConfig = false;
1866
- }, cooldownMs);
1867
- }
1868
-
1869
- // src/extensions/image/commands.ts
1870
- function createImageCommands(tool) {
1871
- return [
1872
- {
1873
- command: "addImage",
1874
- id: "addImage",
1875
- title: "Add Image",
1876
- handler: async (url, options) => {
1877
- const result = await tool.upsertImageEntry(url, {
1878
- mode: "add",
1879
- addOptions: options
1880
- });
1881
- return result.id;
1882
- }
1883
- },
1884
- {
1885
- command: "upsertImage",
1886
- id: "upsertImage",
1887
- title: "Upsert Image",
1888
- handler: async (url, options = {}) => {
1889
- return await tool.upsertImageEntry(url, options);
1890
- }
1891
- },
1892
- {
1893
- command: "getWorkingImages",
1894
- id: "getWorkingImages",
1895
- title: "Get Working Images",
1896
- handler: () => {
1897
- return tool.cloneItems(tool.workingItems);
1898
- }
1899
- },
1900
- {
1901
- command: "setWorkingImage",
1902
- id: "setWorkingImage",
1903
- title: "Set Working Image",
1904
- handler: (id, updates) => {
1905
- tool.updateImageInWorking(id, updates);
1906
- }
1907
- },
1908
- {
1909
- command: "resetWorkingImages",
1910
- id: "resetWorkingImages",
1911
- title: "Reset Working Images",
1912
- handler: () => {
1913
- tool.workingItems = tool.cloneItems(tool.items);
1914
- tool.hasWorkingChanges = false;
1915
- tool.updateImages();
1916
- tool.emitWorkingChange();
1917
- }
1918
- },
1919
- {
1920
- command: "completeImages",
1921
- id: "completeImages",
1922
- title: "Complete Images",
1923
- handler: async () => {
1924
- return await tool.commitWorkingImagesAsCropped();
1925
- }
1926
- },
1927
- {
1928
- command: "exportUserCroppedImage",
1929
- id: "exportUserCroppedImage",
1930
- title: "Export User Cropped Image",
1931
- handler: async (options = {}) => {
1932
- return await tool.exportUserCroppedImage(options);
1933
- }
1934
- },
1935
- {
1936
- command: "fitImageToArea",
1937
- id: "fitImageToArea",
1938
- title: "Fit Image to Area",
1939
- handler: async (id, area) => {
1940
- await tool.fitImageToArea(id, area);
1941
- }
1942
- },
1943
- {
1944
- command: "fitImageToDefaultArea",
1945
- id: "fitImageToDefaultArea",
1946
- title: "Fit Image to Default Area",
1947
- handler: async (id) => {
1948
- await tool.fitImageToDefaultArea(id);
1949
- }
1950
- },
1951
- {
1952
- command: "focusImage",
1953
- id: "focusImage",
1954
- title: "Focus Image",
1955
- handler: (id, options = {}) => {
1956
- return tool.setImageFocus(id, options);
1957
- }
1958
- },
1959
- {
1960
- command: "removeImage",
1961
- id: "removeImage",
1962
- title: "Remove Image",
1963
- handler: (id) => {
1964
- const removed = tool.items.find((item) => item.id === id);
1965
- const next = tool.items.filter((item) => item.id !== id);
1966
- if (next.length !== tool.items.length) {
1967
- tool.purgeSourceSizeCacheForItem(removed);
1968
- if (tool.focusedImageId === id) {
1969
- tool.setImageFocus(null, {
1970
- syncCanvasSelection: true,
1971
- skipRender: true
1972
- });
1973
- }
1974
- tool.updateConfig(next);
1975
- }
1976
- }
1977
- },
1978
- {
1979
- command: "updateImage",
1980
- id: "updateImage",
1981
- title: "Update Image",
1982
- handler: async (id, updates, options = {}) => {
1983
- await tool.updateImage(id, updates, options);
1984
- }
1985
- },
1986
- {
1987
- command: "clearImages",
1988
- id: "clearImages",
1989
- title: "Clear Images",
1990
- handler: () => {
1991
- tool.sourceSizeCache.clear();
1992
- tool.setImageFocus(null, {
1993
- syncCanvasSelection: true,
1994
- skipRender: true
1995
- });
1996
- tool.updateConfig([]);
1997
- }
1998
- },
1999
- {
2000
- command: "bringToFront",
2001
- id: "bringToFront",
2002
- title: "Bring Image to Front",
2003
- handler: (id) => {
2004
- const index = tool.items.findIndex((item) => item.id === id);
2005
- if (index !== -1 && index < tool.items.length - 1) {
2006
- const next = [...tool.items];
2007
- const [item] = next.splice(index, 1);
2008
- next.push(item);
2009
- tool.updateConfig(next);
2010
- }
2011
- }
2012
- },
2013
- {
2014
- command: "sendToBack",
2015
- id: "sendToBack",
2016
- title: "Send Image to Back",
2017
- handler: (id) => {
2018
- const index = tool.items.findIndex((item) => item.id === id);
2019
- if (index > 0) {
2020
- const next = [...tool.items];
2021
- const [item] = next.splice(index, 1);
2022
- next.unshift(item);
2023
- tool.updateConfig(next);
2024
- }
2025
- }
2026
- }
2027
- ];
2207
+ shapeOffset.remove();
2208
+ bleedZone.remove();
2209
+ return pathData;
2210
+ }
2211
+ function getLowestPointOnDieline(options) {
2212
+ ensurePaper(options.width * 2, options.height * 2);
2213
+ paper.project.activeLayer.removeChildren();
2214
+ const shape = createBaseShape(options);
2215
+ const bounds = shape.bounds;
2216
+ const result = {
2217
+ x: bounds.center.x,
2218
+ y: bounds.bottom
2219
+ };
2220
+ shape.remove();
2221
+ return result;
2222
+ }
2223
+ function getNearestPointOnDieline(point, options) {
2224
+ ensurePaper(options.width * 2, options.height * 2);
2225
+ paper.project.activeLayer.removeChildren();
2226
+ const shape = createBaseShape(options);
2227
+ const p = new paper.Point(point.x, point.y);
2228
+ const location = shape.getNearestLocation(p);
2229
+ const result = {
2230
+ x: location.point.x,
2231
+ y: location.point.y,
2232
+ normal: location.normal ? { x: location.normal.x, y: location.normal.y } : void 0
2233
+ };
2234
+ shape.remove();
2235
+ return result;
2028
2236
  }
2029
2237
 
2030
- // src/extensions/image/config.ts
2031
- function createImageConfigurations() {
2238
+ // src/extensions/image/sessionOverlay.ts
2239
+ var EPSILON = 1e-4;
2240
+ var SHAPE_OUTLINE_COLOR = "rgba(255, 0, 0, 0.9)";
2241
+ var DEFAULT_HATCH_FILL = "rgba(255, 0, 0, 0.22)";
2242
+ function buildRectPath(width, height) {
2243
+ return `M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z`;
2244
+ }
2245
+ function buildViewportMaskPath(viewport, cutRect) {
2246
+ const cutLeft = cutRect.left - viewport.left;
2247
+ const cutTop = cutRect.top - viewport.top;
2032
2248
  return [
2033
- {
2034
- id: "image.items",
2035
- type: "array",
2036
- label: "Images",
2037
- default: []
2038
- },
2039
- {
2040
- id: "image.debug",
2041
- type: "boolean",
2042
- label: "Image Debug Log",
2043
- default: false
2044
- },
2045
- {
2046
- id: "image.control.cornerSize",
2047
- type: "number",
2048
- label: "Image Control Corner Size",
2049
- min: 4,
2050
- max: 64,
2051
- step: 1,
2052
- default: 14
2053
- },
2054
- {
2055
- id: "image.control.touchCornerSize",
2056
- type: "number",
2057
- label: "Image Control Touch Corner Size",
2058
- min: 8,
2059
- max: 96,
2060
- step: 1,
2061
- default: 24
2062
- },
2063
- {
2064
- id: "image.control.cornerStyle",
2065
- type: "select",
2066
- label: "Image Control Corner Style",
2067
- options: ["circle", "rect"],
2068
- default: "circle"
2069
- },
2070
- {
2071
- id: "image.control.cornerColor",
2072
- type: "color",
2073
- label: "Image Control Corner Color",
2074
- default: "#ffffff"
2075
- },
2076
- {
2077
- id: "image.control.cornerStrokeColor",
2078
- type: "color",
2079
- label: "Image Control Corner Stroke Color",
2080
- default: "#1677ff"
2081
- },
2082
- {
2083
- id: "image.control.transparentCorners",
2084
- type: "boolean",
2085
- label: "Image Control Transparent Corners",
2086
- default: false
2087
- },
2088
- {
2089
- id: "image.control.borderColor",
2090
- type: "color",
2091
- label: "Image Control Border Color",
2092
- default: "#1677ff"
2093
- },
2094
- {
2095
- id: "image.control.borderScaleFactor",
2096
- type: "number",
2097
- label: "Image Control Border Width",
2098
- min: 0.5,
2099
- max: 8,
2100
- step: 0.1,
2101
- default: 1.5
2102
- },
2103
- {
2104
- id: "image.control.padding",
2105
- type: "number",
2106
- label: "Image Control Padding",
2107
- min: 0,
2108
- max: 64,
2109
- step: 1,
2110
- default: 0
2111
- },
2112
- {
2113
- id: "image.frame.strokeColor",
2114
- type: "color",
2115
- label: "Image Frame Stroke Color",
2116
- default: "#808080"
2117
- },
2118
- {
2119
- id: "image.frame.strokeWidth",
2120
- type: "number",
2121
- label: "Image Frame Stroke Width",
2122
- min: 0,
2123
- max: 20,
2124
- step: 0.5,
2125
- default: 2
2126
- },
2127
- {
2128
- id: "image.frame.strokeStyle",
2129
- type: "select",
2130
- label: "Image Frame Stroke Style",
2131
- options: ["solid", "dashed", "hidden"],
2132
- default: "dashed"
2133
- },
2134
- {
2135
- id: "image.frame.dashLength",
2136
- type: "number",
2137
- label: "Image Frame Dash Length",
2138
- min: 1,
2139
- max: 40,
2140
- step: 1,
2141
- default: 8
2142
- },
2143
- {
2144
- id: "image.frame.innerBackground",
2145
- type: "color",
2146
- label: "Image Frame Inner Background",
2147
- default: "rgba(0,0,0,0)"
2148
- },
2149
- {
2150
- id: "image.frame.outerBackground",
2151
- type: "color",
2152
- label: "Image Frame Outer Background",
2153
- default: "#f5f5f5"
2249
+ buildRectPath(viewport.width, viewport.height),
2250
+ `M ${cutLeft} ${cutTop} L ${cutLeft + cutRect.width} ${cutTop} L ${cutLeft + cutRect.width} ${cutTop + cutRect.height} L ${cutLeft} ${cutTop + cutRect.height} Z`
2251
+ ].join(" ");
2252
+ }
2253
+ function resolveCutShapeRadiusPx(geometry, cutRect) {
2254
+ const visualRadius = Number.isFinite(geometry.radius) ? Math.max(0, geometry.radius) : 0;
2255
+ const visualOffset = Number.isFinite(geometry.offset) ? geometry.offset : 0;
2256
+ const rawCutRadius = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
2257
+ const maxRadius = Math.max(0, Math.min(cutRect.width, cutRect.height) / 2);
2258
+ return Math.max(0, Math.min(maxRadius, rawCutRadius));
2259
+ }
2260
+ function buildBuiltinShapeOverlayPaths(cutRect, geometry) {
2261
+ if (!geometry || geometry.shape === "custom") {
2262
+ return null;
2263
+ }
2264
+ const radius = resolveCutShapeRadiusPx(geometry, cutRect);
2265
+ if (geometry.shape === "rect" && radius <= EPSILON) {
2266
+ return null;
2267
+ }
2268
+ const shapePathData = generateDielinePath({
2269
+ shape: geometry.shape,
2270
+ shapeStyle: geometry.shapeStyle,
2271
+ width: Math.max(1, cutRect.width),
2272
+ height: Math.max(1, cutRect.height),
2273
+ radius,
2274
+ x: cutRect.width / 2,
2275
+ y: cutRect.height / 2,
2276
+ features: [],
2277
+ canvasWidth: Math.max(1, cutRect.width),
2278
+ canvasHeight: Math.max(1, cutRect.height)
2279
+ });
2280
+ if (!shapePathData) {
2281
+ return null;
2282
+ }
2283
+ return {
2284
+ shapePathData,
2285
+ hatchPathData: `${buildRectPath(cutRect.width, cutRect.height)} ${shapePathData}`
2286
+ };
2287
+ }
2288
+ function buildImageSessionOverlaySpecs(args) {
2289
+ const { viewport, layout, geometry, visual, hatchPattern } = args;
2290
+ const cutRect = layout.cutRect;
2291
+ const specs = [];
2292
+ specs.push({
2293
+ id: "image.cropMask.rect",
2294
+ type: "path",
2295
+ space: "screen",
2296
+ data: { id: "image.cropMask.rect", zIndex: 1 },
2297
+ props: {
2298
+ pathData: buildViewportMaskPath(viewport, cutRect),
2299
+ left: viewport.left,
2300
+ top: viewport.top,
2301
+ originX: "left",
2302
+ originY: "top",
2303
+ fill: visual.outerBackground,
2304
+ stroke: null,
2305
+ fillRule: "evenodd",
2306
+ selectable: false,
2307
+ evented: false,
2308
+ excludeFromExport: true,
2309
+ objectCaching: false
2154
2310
  }
2155
- ];
2311
+ });
2312
+ const shapeOverlay = buildBuiltinShapeOverlayPaths(cutRect, geometry);
2313
+ if (shapeOverlay) {
2314
+ specs.push({
2315
+ id: "image.cropShapeHatch",
2316
+ type: "path",
2317
+ space: "screen",
2318
+ data: { id: "image.cropShapeHatch", zIndex: 5 },
2319
+ props: {
2320
+ pathData: shapeOverlay.hatchPathData,
2321
+ left: cutRect.left,
2322
+ top: cutRect.top,
2323
+ originX: "left",
2324
+ originY: "top",
2325
+ fill: hatchPattern || DEFAULT_HATCH_FILL,
2326
+ opacity: hatchPattern ? 1 : 0.8,
2327
+ stroke: null,
2328
+ fillRule: "evenodd",
2329
+ selectable: false,
2330
+ evented: false,
2331
+ excludeFromExport: true,
2332
+ objectCaching: false
2333
+ }
2334
+ });
2335
+ specs.push({
2336
+ id: "image.cropShapeOutline",
2337
+ type: "path",
2338
+ space: "screen",
2339
+ data: { id: "image.cropShapeOutline", zIndex: 6 },
2340
+ props: {
2341
+ pathData: shapeOverlay.shapePathData,
2342
+ left: cutRect.left,
2343
+ top: cutRect.top,
2344
+ originX: "left",
2345
+ originY: "top",
2346
+ fill: "transparent",
2347
+ stroke: SHAPE_OUTLINE_COLOR,
2348
+ strokeWidth: 1,
2349
+ selectable: false,
2350
+ evented: false,
2351
+ excludeFromExport: true,
2352
+ objectCaching: false
2353
+ }
2354
+ });
2355
+ }
2356
+ specs.push({
2357
+ id: "image.cropFrame",
2358
+ type: "rect",
2359
+ space: "screen",
2360
+ data: { id: "image.cropFrame", zIndex: 7 },
2361
+ props: {
2362
+ left: cutRect.left,
2363
+ top: cutRect.top,
2364
+ width: cutRect.width,
2365
+ height: cutRect.height,
2366
+ originX: "left",
2367
+ originY: "top",
2368
+ fill: visual.innerBackground,
2369
+ stroke: visual.strokeStyle === "hidden" ? "rgba(0,0,0,0)" : visual.strokeColor,
2370
+ strokeWidth: visual.strokeStyle === "hidden" ? 0 : visual.strokeWidth,
2371
+ strokeDashArray: visual.strokeStyle === "dashed" ? [visual.dashLength, visual.dashLength] : void 0,
2372
+ selectable: false,
2373
+ evented: false,
2374
+ excludeFromExport: true
2375
+ }
2376
+ });
2377
+ return specs;
2156
2378
  }
2157
2379
 
2158
2380
  // src/extensions/image/ImageTool.ts
@@ -2440,6 +2662,7 @@ var ImageTool = class {
2440
2662
  this.clearRenderedImages();
2441
2663
  (_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
2442
2664
  this.renderProducerDisposable = void 0;
2665
+ this.emitImageStateChange();
2443
2666
  if (this.canvasService) {
2444
2667
  void this.canvasService.flushRenderFromProducers();
2445
2668
  this.canvasService = void 0;
@@ -2614,11 +2837,21 @@ var ImageTool = class {
2614
2837
  (_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
2615
2838
  }
2616
2839
  }
2840
+ clearSnapGuideContext() {
2841
+ var _a;
2842
+ const topContext = (_a = this.canvasService) == null ? void 0 : _a.canvas.contextTop;
2843
+ if (!this.canvasService || !topContext) return;
2844
+ this.canvasService.canvas.clearContext(topContext);
2845
+ }
2617
2846
  clearSnapPreview() {
2618
2847
  var _a;
2848
+ const shouldClearCanvas = this.hasRenderedSnapGuides || !!this.activeSnapX || !!this.activeSnapY;
2619
2849
  this.activeSnapX = null;
2620
2850
  this.activeSnapY = null;
2621
2851
  this.hasRenderedSnapGuides = false;
2852
+ if (shouldClearCanvas) {
2853
+ this.clearSnapGuideContext();
2854
+ }
2622
2855
  (_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
2623
2856
  }
2624
2857
  endMoveSnapInteraction() {
@@ -2839,9 +3072,9 @@ var ImageTool = class {
2839
3072
  name: "Image",
2840
3073
  interaction: "session",
2841
3074
  commands: {
2842
- begin: "resetWorkingImages",
3075
+ begin: "imageSessionReset",
2843
3076
  commit: "completeImages",
2844
- rollback: "resetWorkingImages"
3077
+ rollback: "imageSessionReset"
2845
3078
  },
2846
3079
  session: {
2847
3080
  autoBegin: true,
@@ -2875,6 +3108,28 @@ var ImageTool = class {
2875
3108
  cloneItems(items) {
2876
3109
  return this.normalizeItems((items || []).map((i) => ({ ...i })));
2877
3110
  }
3111
+ getViewItems() {
3112
+ return this.isToolActive ? this.workingItems : this.items;
3113
+ }
3114
+ getImageViewState() {
3115
+ this.syncToolActiveFromWorkbench();
3116
+ const items = this.cloneItems(this.getViewItems());
3117
+ const focusedItem = this.focusedImageId == null ? null : items.find((item) => item.id === this.focusedImageId) || null;
3118
+ return {
3119
+ items,
3120
+ hasAnyImage: items.length > 0,
3121
+ focusedId: this.focusedImageId,
3122
+ focusedItem,
3123
+ isToolActive: this.isToolActive,
3124
+ isImageSelectionActive: this.isImageSelectionActive,
3125
+ hasWorkingChanges: this.hasWorkingChanges,
3126
+ source: this.isToolActive ? "working" : "committed"
3127
+ };
3128
+ }
3129
+ emitImageStateChange() {
3130
+ var _a;
3131
+ (_a = this.context) == null ? void 0 : _a.eventBus.emit("image:state:change", this.getImageViewState());
3132
+ }
2878
3133
  emitWorkingChange(changedId = null) {
2879
3134
  var _a;
2880
3135
  (_a = this.context) == null ? void 0 : _a.eventBus.emit("image:working:change", {
@@ -2910,10 +3165,13 @@ var ImageTool = class {
2910
3165
  }
2911
3166
  if (!options.skipRender) {
2912
3167
  this.updateImages();
3168
+ } else {
3169
+ this.emitImageStateChange();
2913
3170
  }
2914
3171
  return { ok: true, id };
2915
3172
  }
2916
- async addImageEntry(url, options, fitOnAdd = true) {
3173
+ async addImageEntry(url, options, operation) {
3174
+ this.syncToolActiveFromWorkbench();
2917
3175
  const id = this.generateId();
2918
3176
  const newItem = this.normalizeItem({
2919
3177
  id,
@@ -2921,13 +3179,20 @@ var ImageTool = class {
2921
3179
  opacity: 1,
2922
3180
  ...options
2923
3181
  });
2924
- const sessionDirtyBeforeAdd = this.isToolActive && this.hasWorkingChanges;
2925
3182
  const waitLoaded = this.waitImageLoaded(id, true);
2926
- this.updateConfig([...this.items, newItem]);
2927
- this.addItemToWorkingSessionIfNeeded(newItem, sessionDirtyBeforeAdd);
3183
+ if (this.isToolActive) {
3184
+ this.workingItems = this.cloneItems([...this.workingItems, newItem]);
3185
+ this.hasWorkingChanges = true;
3186
+ this.updateImages();
3187
+ this.emitWorkingChange(id);
3188
+ } else {
3189
+ this.updateConfig([...this.items, newItem]);
3190
+ }
2928
3191
  const loaded = await waitLoaded;
2929
- if (loaded && fitOnAdd) {
2930
- await this.fitImageToDefaultArea(id);
3192
+ if (loaded && operation) {
3193
+ await this.applyImageOperation(id, operation, {
3194
+ target: this.isToolActive ? "working" : "config"
3195
+ });
2931
3196
  }
2932
3197
  if (loaded) {
2933
3198
  this.setImageFocus(id);
@@ -2935,8 +3200,8 @@ var ImageTool = class {
2935
3200
  return id;
2936
3201
  }
2937
3202
  async upsertImageEntry(url, options = {}) {
3203
+ this.syncToolActiveFromWorkbench();
2938
3204
  const mode = options.mode || (options.id ? "replace" : "add");
2939
- const fitOnAdd = options.fitOnAdd !== false;
2940
3205
  if (mode === "replace") {
2941
3206
  if (!options.id) {
2942
3207
  throw new Error("replace-target-id-required");
@@ -2945,19 +3210,31 @@ var ImageTool = class {
2945
3210
  if (!this.hasImageItem(targetId)) {
2946
3211
  throw new Error("replace-target-not-found");
2947
3212
  }
2948
- await this.updateImageInConfig(targetId, { url });
3213
+ if (this.isToolActive) {
3214
+ const current = this.workingItems.find((item) => item.id === targetId) || this.items.find((item) => item.id === targetId);
3215
+ this.purgeSourceSizeCacheForItem(current);
3216
+ this.updateImageInWorking(targetId, {
3217
+ url,
3218
+ sourceUrl: url,
3219
+ committedUrl: void 0
3220
+ });
3221
+ } else {
3222
+ await this.updateImageInConfig(targetId, { url });
3223
+ }
3224
+ const loaded = await this.waitImageLoaded(targetId, true);
3225
+ if (loaded && options.operation) {
3226
+ await this.applyImageOperation(targetId, options.operation, {
3227
+ target: this.isToolActive ? "working" : "config"
3228
+ });
3229
+ }
3230
+ if (loaded) {
3231
+ this.setImageFocus(targetId);
3232
+ }
2949
3233
  return { id: targetId, mode: "replace" };
2950
3234
  }
2951
- const id = await this.addImageEntry(url, options.addOptions, fitOnAdd);
3235
+ const id = await this.addImageEntry(url, options.addOptions, options.operation);
2952
3236
  return { id, mode: "add" };
2953
3237
  }
2954
- addItemToWorkingSessionIfNeeded(item, sessionDirtyBeforeAdd) {
2955
- if (!sessionDirtyBeforeAdd || !this.isToolActive) return;
2956
- if (this.workingItems.some((existing) => existing.id === item.id)) return;
2957
- this.workingItems = this.cloneItems([...this.workingItems, item]);
2958
- this.updateImages();
2959
- this.emitWorkingChange(item.id);
2960
- }
2961
3238
  async updateImage(id, updates, options = {}) {
2962
3239
  this.syncToolActiveFromWorkbench();
2963
3240
  const target = options.target || "auto";
@@ -3018,40 +3295,9 @@ var ImageTool = class {
3018
3295
  }
3019
3296
  getFrameRectScreen(frame) {
3020
3297
  if (!this.canvasService) {
3021
- return { left: 0, top: 0, width: 0, height: 0 };
3022
- }
3023
- return this.canvasService.toScreenRect(frame || this.getFrameRect());
3024
- }
3025
- toLayoutSceneRect(rect) {
3026
- return toLayoutSceneRect(rect);
3027
- }
3028
- async resolveDefaultFitArea() {
3029
- if (!this.canvasService) return null;
3030
- const frame = this.getFrameRect();
3031
- if (frame.width <= 0 || frame.height <= 0) return null;
3032
- return {
3033
- width: Math.max(1, frame.width),
3034
- height: Math.max(1, frame.height),
3035
- left: frame.left + frame.width / 2,
3036
- top: frame.top + frame.height / 2
3037
- };
3038
- }
3039
- async fitImageToDefaultArea(id) {
3040
- if (!this.canvasService) return;
3041
- const area = await this.resolveDefaultFitArea();
3042
- if (area) {
3043
- await this.fitImageToArea(id, area);
3044
- return;
3298
+ return { left: 0, top: 0, width: 0, height: 0 };
3045
3299
  }
3046
- const viewport = this.canvasService.getSceneViewportRect();
3047
- const canvasW = Math.max(1, viewport.width || 0);
3048
- const canvasH = Math.max(1, viewport.height || 0);
3049
- await this.fitImageToArea(id, {
3050
- width: canvasW,
3051
- height: canvasH,
3052
- left: viewport.left + canvasW / 2,
3053
- top: viewport.top + canvasH / 2
3054
- });
3300
+ return this.canvasService.toScreenRect(frame || this.getFrameRect());
3055
3301
  }
3056
3302
  getImageObjects() {
3057
3303
  if (!this.canvasService) return [];
@@ -3151,74 +3397,37 @@ var ImageTool = class {
3151
3397
  outerBackground: this.getConfig("image.frame.outerBackground", "#f5f5f5") || "#f5f5f5"
3152
3398
  };
3153
3399
  }
3154
- toSceneGeometryLike(raw) {
3155
- const shape = raw == null ? void 0 : raw.shape;
3156
- if (!isDielineShape(shape)) {
3400
+ resolveSessionOverlayState() {
3401
+ if (!this.canvasService || !this.context) {
3157
3402
  return null;
3158
3403
  }
3159
- const radiusRaw = Number(raw == null ? void 0 : raw.radius);
3160
- const offsetRaw = Number(raw == null ? void 0 : raw.offset);
3161
- const unit = typeof (raw == null ? void 0 : raw.unit) === "string" ? raw.unit : "px";
3162
- const radius = unit === "scene" || !this.canvasService ? radiusRaw : this.canvasService.toSceneLength(radiusRaw);
3163
- const offset = unit === "scene" || !this.canvasService ? offsetRaw : this.canvasService.toSceneLength(offsetRaw);
3164
- return {
3165
- shape,
3166
- shapeStyle: normalizeShapeStyle(raw == null ? void 0 : raw.shapeStyle),
3167
- radius: Number.isFinite(radius) ? radius : 0,
3168
- offset: Number.isFinite(offset) ? offset : 0
3169
- };
3170
- }
3171
- async resolveSceneGeometryForOverlay() {
3172
- if (!this.context) return null;
3173
- const commandService = this.context.services.get("CommandService");
3174
- if (commandService) {
3175
- try {
3176
- const raw = await Promise.resolve(
3177
- commandService.executeCommand("getSceneGeometry")
3178
- );
3179
- const geometry2 = this.toSceneGeometryLike(raw);
3180
- if (geometry2) {
3181
- this.debug("overlay:sceneGeometry:command", geometry2);
3182
- return geometry2;
3183
- }
3184
- this.debug("overlay:sceneGeometry:command:invalid", { raw });
3185
- } catch (error) {
3186
- this.debug("overlay:sceneGeometry:command:error", {
3187
- error: error instanceof Error ? error.message : String(error)
3188
- });
3189
- }
3190
- }
3191
- if (!this.canvasService) return null;
3192
3404
  const configService = this.context.services.get(
3193
3405
  "ConfigurationService"
3194
3406
  );
3195
- if (!configService) return null;
3196
- const sizeState = readSizeState(configService);
3197
- const layout = computeSceneLayout(this.canvasService, sizeState);
3198
- if (!layout) {
3199
- this.debug("overlay:sceneGeometry:fallback:missing-layout");
3407
+ if (!configService) {
3200
3408
  return null;
3201
3409
  }
3202
- const geometry = this.toSceneGeometryLike(
3203
- buildSceneGeometry(configService, layout)
3410
+ const layout = computeSceneLayout(
3411
+ this.canvasService,
3412
+ readSizeState(configService)
3204
3413
  );
3205
- if (geometry) {
3206
- this.debug("overlay:sceneGeometry:fallback", geometry);
3414
+ if (!layout) {
3415
+ this.debug("overlay:layout:missing");
3416
+ return null;
3207
3417
  }
3208
- return geometry;
3209
- }
3210
- resolveCutShapeRadius(geometry, frame) {
3211
- const visualRadius = Number.isFinite(geometry.radius) ? Math.max(0, geometry.radius) : 0;
3212
- const visualOffset = Number.isFinite(geometry.offset) ? geometry.offset : 0;
3213
- const rawCutRadius = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
3214
- const maxRadius = Math.max(0, Math.min(frame.width, frame.height) / 2);
3215
- return Math.max(0, Math.min(maxRadius, rawCutRadius));
3418
+ const geometry = buildSceneGeometry(configService, layout);
3419
+ this.debug("overlay:state:resolved", {
3420
+ cutRect: layout.cutRect,
3421
+ shape: geometry.shape,
3422
+ shapeStyle: geometry.shapeStyle,
3423
+ radius: geometry.radius,
3424
+ offset: geometry.offset
3425
+ });
3426
+ return { layout, geometry };
3216
3427
  }
3217
3428
  getCropShapeHatchPattern(color = "rgba(255, 0, 0, 0.6)") {
3218
- var _a;
3219
3429
  if (typeof document === "undefined") return void 0;
3220
- const sceneScale = ((_a = this.canvasService) == null ? void 0 : _a.getSceneScale()) || 1;
3221
- const cacheKey = `${color}::${sceneScale.toFixed(6)}`;
3430
+ const cacheKey = color;
3222
3431
  if (this.cropShapeHatchPattern && this.cropShapeHatchPatternColor === color && this.cropShapeHatchPatternKey === cacheKey) {
3223
3432
  return this.cropShapeHatchPattern;
3224
3433
  }
@@ -3248,138 +3457,11 @@ var ImageTool = class {
3248
3457
  // @ts-ignore: Fabric Pattern accepts canvas source here.
3249
3458
  repetition: "repeat"
3250
3459
  });
3251
- pattern.patternTransform = [
3252
- 1 / sceneScale,
3253
- 0,
3254
- 0,
3255
- 1 / sceneScale,
3256
- 0,
3257
- 0
3258
- ];
3259
3460
  this.cropShapeHatchPattern = pattern;
3260
3461
  this.cropShapeHatchPatternColor = color;
3261
3462
  this.cropShapeHatchPatternKey = cacheKey;
3262
3463
  return pattern;
3263
3464
  }
3264
- buildCropShapeOverlaySpecs(frame, sceneGeometry) {
3265
- var _a, _b;
3266
- if (!sceneGeometry) {
3267
- this.debug("overlay:shape:skip", { reason: "scene-geometry-missing" });
3268
- return [];
3269
- }
3270
- if (sceneGeometry.shape === "custom") {
3271
- this.debug("overlay:shape:skip", { reason: "shape-custom" });
3272
- return [];
3273
- }
3274
- const shape = sceneGeometry.shape;
3275
- const shapeStyle = sceneGeometry.shapeStyle;
3276
- const inset = 0;
3277
- const shapeWidth = Math.max(1, frame.width);
3278
- const shapeHeight = Math.max(1, frame.height);
3279
- const radius = this.resolveCutShapeRadius(sceneGeometry, frame);
3280
- this.debug("overlay:shape:geometry", {
3281
- shape,
3282
- frameWidth: frame.width,
3283
- frameHeight: frame.height,
3284
- offset: sceneGeometry.offset,
3285
- shapeStyle,
3286
- inset,
3287
- shapeWidth,
3288
- shapeHeight,
3289
- baseRadius: sceneGeometry.radius,
3290
- radius
3291
- });
3292
- const isSameAsFrame = Math.abs(shapeWidth - frame.width) <= 1e-4 && Math.abs(shapeHeight - frame.height) <= 1e-4;
3293
- if (shape === "rect" && radius <= 1e-4 && isSameAsFrame) {
3294
- this.debug("overlay:shape:skip", {
3295
- reason: "shape-rect-no-radius"
3296
- });
3297
- return [];
3298
- }
3299
- const baseOptions = {
3300
- shape,
3301
- width: shapeWidth,
3302
- height: shapeHeight,
3303
- radius,
3304
- x: frame.width / 2,
3305
- y: frame.height / 2,
3306
- features: [],
3307
- shapeStyle,
3308
- canvasWidth: frame.width,
3309
- canvasHeight: frame.height
3310
- };
3311
- try {
3312
- const shapePathData = generateDielinePath(baseOptions);
3313
- const outerRectPathData = `M 0 0 L ${frame.width} 0 L ${frame.width} ${frame.height} L 0 ${frame.height} Z`;
3314
- const hatchPathData = `${outerRectPathData} ${shapePathData}`;
3315
- if (!shapePathData || !hatchPathData) {
3316
- this.debug("overlay:shape:skip", {
3317
- reason: "path-generation-empty",
3318
- shape,
3319
- radius
3320
- });
3321
- return [];
3322
- }
3323
- const patternFill = this.getCropShapeHatchPattern();
3324
- const hatchFill = patternFill || "rgba(255, 0, 0, 0.22)";
3325
- const shapeBounds = getPathBounds(shapePathData);
3326
- const hatchBounds = getPathBounds(hatchPathData);
3327
- const frameRect = this.toLayoutSceneRect(frame);
3328
- const hatchPathLength = hatchPathData.length;
3329
- const shapePathLength = shapePathData.length;
3330
- const specs = [
3331
- {
3332
- id: "image.cropShapeHatch",
3333
- type: "path",
3334
- data: { id: "image.cropShapeHatch", zIndex: 5 },
3335
- layout: {
3336
- reference: "custom",
3337
- referenceRect: frameRect,
3338
- alignX: "start",
3339
- alignY: "start",
3340
- offsetX: hatchBounds.x,
3341
- offsetY: hatchBounds.y
3342
- },
3343
- props: {
3344
- pathData: hatchPathData,
3345
- originX: "left",
3346
- originY: "top",
3347
- fill: hatchFill,
3348
- opacity: patternFill ? 1 : 0.8,
3349
- stroke: "rgba(255, 0, 0, 0.9)",
3350
- strokeWidth: (_b = (_a = this.canvasService) == null ? void 0 : _a.toSceneLength(1)) != null ? _b : 1,
3351
- fillRule: "evenodd",
3352
- selectable: false,
3353
- evented: false,
3354
- excludeFromExport: true,
3355
- objectCaching: false
3356
- }
3357
- }
3358
- ];
3359
- this.debug("overlay:shape:built", {
3360
- shape,
3361
- radius,
3362
- inset,
3363
- shapeWidth,
3364
- shapeHeight,
3365
- fillRule: "evenodd",
3366
- shapePathLength,
3367
- hatchPathLength,
3368
- shapeBounds,
3369
- hatchBounds,
3370
- hatchFillType: hatchFill && typeof hatchFill === "object" ? "pattern" : "color",
3371
- ids: specs.map((spec) => spec.id)
3372
- });
3373
- return specs;
3374
- } catch (error) {
3375
- this.debug("overlay:shape:error", {
3376
- shape,
3377
- radius,
3378
- error: error instanceof Error ? error.message : String(error)
3379
- });
3380
- return [];
3381
- }
3382
- }
3383
3465
  resolveRenderImageState(item) {
3384
3466
  var _a;
3385
3467
  const active = this.isToolActive;
@@ -3461,172 +3543,35 @@ var ImageTool = class {
3461
3543
  }
3462
3544
  return specs;
3463
3545
  }
3464
- buildOverlaySpecs(frame, sceneGeometry) {
3546
+ buildOverlaySpecs(overlayState) {
3465
3547
  const visible = this.isImageEditingVisible();
3466
- if (!visible || frame.width <= 0 || frame.height <= 0 || !this.canvasService) {
3548
+ if (!visible || !overlayState || !this.canvasService) {
3467
3549
  this.debug("overlay:hidden", {
3468
3550
  visible,
3469
- frame,
3551
+ cutRect: overlayState == null ? void 0 : overlayState.layout.cutRect,
3470
3552
  isToolActive: this.isToolActive,
3471
3553
  isImageSelectionActive: this.isImageSelectionActive,
3472
3554
  focusedImageId: this.focusedImageId
3473
3555
  });
3474
3556
  return [];
3475
3557
  }
3476
- const viewport = this.canvasService.getSceneViewportRect();
3477
- const canvasW = viewport.width || 0;
3478
- const canvasH = viewport.height || 0;
3479
- const canvasLeft = viewport.left || 0;
3480
- const canvasTop = viewport.top || 0;
3558
+ const viewport = this.canvasService.getScreenViewportRect();
3481
3559
  const visual = this.getFrameVisualConfig();
3482
- const strokeWidthScene = this.canvasService.toSceneLength(
3483
- visual.strokeWidth
3484
- );
3485
- const dashLengthScene = this.canvasService.toSceneLength(visual.dashLength);
3486
- const frameLeft = Math.max(
3487
- canvasLeft,
3488
- Math.min(canvasLeft + canvasW, frame.left)
3489
- );
3490
- const frameTop = Math.max(
3491
- canvasTop,
3492
- Math.min(canvasTop + canvasH, frame.top)
3493
- );
3494
- const frameRight = Math.max(
3495
- frameLeft,
3496
- Math.min(canvasLeft + canvasW, frame.left + frame.width)
3497
- );
3498
- const frameBottom = Math.max(
3499
- frameTop,
3500
- Math.min(canvasTop + canvasH, frame.top + frame.height)
3501
- );
3502
- const visibleFrameH = Math.max(0, frameBottom - frameTop);
3503
- const topH = Math.max(0, frameTop - canvasTop);
3504
- const bottomH = Math.max(0, canvasTop + canvasH - frameBottom);
3505
- const leftW = Math.max(0, frameLeft - canvasLeft);
3506
- const rightW = Math.max(0, canvasLeft + canvasW - frameRight);
3507
- const viewportRect = this.toLayoutSceneRect({
3508
- left: canvasLeft,
3509
- top: canvasTop,
3510
- width: canvasW,
3511
- height: canvasH
3512
- });
3513
- const visibleFrameBandRect = this.toLayoutSceneRect({
3514
- left: canvasLeft,
3515
- top: frameTop,
3516
- width: canvasW,
3517
- height: visibleFrameH
3518
- });
3519
- const frameRect = this.toLayoutSceneRect(frame);
3520
- const shapeOverlay = this.buildCropShapeOverlaySpecs(frame, sceneGeometry);
3521
- const mask = [
3522
- {
3523
- id: "image.cropMask.top",
3524
- type: "rect",
3525
- data: { id: "image.cropMask.top", zIndex: 1 },
3526
- layout: {
3527
- reference: "custom",
3528
- referenceRect: viewportRect,
3529
- alignX: "start",
3530
- alignY: "start",
3531
- width: "100%",
3532
- height: topH
3533
- },
3534
- props: {
3535
- originX: "left",
3536
- originY: "top",
3537
- fill: visual.outerBackground,
3538
- selectable: false,
3539
- evented: false
3540
- }
3541
- },
3542
- {
3543
- id: "image.cropMask.bottom",
3544
- type: "rect",
3545
- data: { id: "image.cropMask.bottom", zIndex: 2 },
3546
- layout: {
3547
- reference: "custom",
3548
- referenceRect: viewportRect,
3549
- alignX: "start",
3550
- alignY: "end",
3551
- width: "100%",
3552
- height: bottomH
3553
- },
3554
- props: {
3555
- originX: "left",
3556
- originY: "top",
3557
- fill: visual.outerBackground,
3558
- selectable: false,
3559
- evented: false
3560
- }
3561
- },
3562
- {
3563
- id: "image.cropMask.left",
3564
- type: "rect",
3565
- data: { id: "image.cropMask.left", zIndex: 3 },
3566
- layout: {
3567
- reference: "custom",
3568
- referenceRect: visibleFrameBandRect,
3569
- alignX: "start",
3570
- alignY: "start",
3571
- width: leftW,
3572
- height: "100%"
3573
- },
3574
- props: {
3575
- originX: "left",
3576
- originY: "top",
3577
- fill: visual.outerBackground,
3578
- selectable: false,
3579
- evented: false
3580
- }
3581
- },
3582
- {
3583
- id: "image.cropMask.right",
3584
- type: "rect",
3585
- data: { id: "image.cropMask.right", zIndex: 4 },
3586
- layout: {
3587
- reference: "custom",
3588
- referenceRect: visibleFrameBandRect,
3589
- alignX: "end",
3590
- alignY: "start",
3591
- width: rightW,
3592
- height: "100%"
3593
- },
3594
- props: {
3595
- originX: "left",
3596
- originY: "top",
3597
- fill: visual.outerBackground,
3598
- selectable: false,
3599
- evented: false
3600
- }
3601
- }
3602
- ];
3603
- const frameSpec = {
3604
- id: "image.cropFrame",
3605
- type: "rect",
3606
- data: { id: "image.cropFrame", zIndex: 7 },
3607
- layout: {
3608
- reference: "custom",
3609
- referenceRect: frameRect,
3610
- alignX: "start",
3611
- alignY: "start",
3612
- width: "100%",
3613
- height: "100%"
3560
+ const specs = buildImageSessionOverlaySpecs({
3561
+ viewport: {
3562
+ left: viewport.left,
3563
+ top: viewport.top,
3564
+ width: viewport.width,
3565
+ height: viewport.height
3614
3566
  },
3615
- props: {
3616
- originX: "left",
3617
- originY: "top",
3618
- fill: visual.innerBackground,
3619
- stroke: visual.strokeStyle === "hidden" ? "rgba(0,0,0,0)" : visual.strokeColor,
3620
- strokeWidth: visual.strokeStyle === "hidden" ? 0 : strokeWidthScene,
3621
- strokeDashArray: visual.strokeStyle === "dashed" ? [dashLengthScene, dashLengthScene] : void 0,
3622
- selectable: false,
3623
- evented: false
3624
- }
3625
- };
3626
- const specs = shapeOverlay.length > 0 ? [...mask, ...shapeOverlay] : [...mask, ...shapeOverlay, frameSpec];
3567
+ layout: overlayState.layout,
3568
+ geometry: overlayState.geometry,
3569
+ visual,
3570
+ hatchPattern: this.getCropShapeHatchPattern()
3571
+ });
3627
3572
  this.debug("overlay:built", {
3628
- frame,
3629
- shape: sceneGeometry == null ? void 0 : sceneGeometry.shape,
3573
+ cutRect: overlayState.layout.cutRect,
3574
+ shape: overlayState.geometry.shape,
3630
3575
  overlayIds: specs.map((spec) => {
3631
3576
  var _a;
3632
3577
  return {
@@ -3655,10 +3600,9 @@ var ImageTool = class {
3655
3600
  }
3656
3601
  const imageSpecs = await this.buildImageSpecs(renderItems, frame);
3657
3602
  if (seq !== this.renderSeq) return;
3658
- const sceneGeometry = await this.resolveSceneGeometryForOverlay();
3659
- if (seq !== this.renderSeq) return;
3603
+ const overlayState = this.resolveSessionOverlayState();
3660
3604
  this.imageSpecs = imageSpecs;
3661
- this.overlaySpecs = this.buildOverlaySpecs(frame, sceneGeometry);
3605
+ this.overlaySpecs = this.buildOverlaySpecs(overlayState);
3662
3606
  await this.canvasService.flushRenderFromProducers();
3663
3607
  if (seq !== this.renderSeq) return;
3664
3608
  this.refreshImageObjectInteractionState();
@@ -3685,11 +3629,38 @@ var ImageTool = class {
3685
3629
  isImageSelectionActive: this.isImageSelectionActive,
3686
3630
  focusedImageId: this.focusedImageId
3687
3631
  });
3632
+ this.emitImageStateChange();
3688
3633
  this.canvasService.requestRenderAll();
3689
3634
  }
3690
3635
  clampNormalized(value) {
3691
3636
  return Math.max(-1, Math.min(2, value));
3692
3637
  }
3638
+ async setImageTransform(id, updates, options = {}) {
3639
+ const next = {};
3640
+ if (Number.isFinite(updates.scale)) {
3641
+ next.scale = Math.max(0.05, Number(updates.scale));
3642
+ }
3643
+ if (Number.isFinite(updates.angle)) {
3644
+ next.angle = Number(updates.angle);
3645
+ }
3646
+ if (Number.isFinite(updates.left)) {
3647
+ next.left = this.clampNormalized(Number(updates.left));
3648
+ }
3649
+ if (Number.isFinite(updates.top)) {
3650
+ next.top = this.clampNormalized(Number(updates.top));
3651
+ }
3652
+ if (Number.isFinite(updates.opacity)) {
3653
+ next.opacity = Math.max(0, Math.min(1, Number(updates.opacity)));
3654
+ }
3655
+ if (!Object.keys(next).length) return;
3656
+ await this.updateImage(id, next, options);
3657
+ }
3658
+ resetImageSession() {
3659
+ this.workingItems = this.cloneItems(this.items);
3660
+ this.hasWorkingChanges = false;
3661
+ this.updateImages();
3662
+ this.emitWorkingChange();
3663
+ }
3693
3664
  updateImageInWorking(id, updates) {
3694
3665
  const index = this.workingItems.findIndex((item) => item.id === id);
3695
3666
  if (index < 0) return;
@@ -3707,7 +3678,6 @@ var ImageTool = class {
3707
3678
  this.emitWorkingChange(id);
3708
3679
  }
3709
3680
  async updateImageInConfig(id, updates) {
3710
- var _a, _b, _c, _d;
3711
3681
  const index = this.items.findIndex((item) => item.id === id);
3712
3682
  if (index < 0) return;
3713
3683
  const replacingSource = typeof updates.url === "string" && updates.url.length > 0;
@@ -3720,23 +3690,12 @@ var ImageTool = class {
3720
3690
  ...replacingSource ? {
3721
3691
  url: replacingUrl,
3722
3692
  sourceUrl: replacingUrl,
3723
- committedUrl: void 0,
3724
- scale: (_a = updates.scale) != null ? _a : 1,
3725
- angle: (_b = updates.angle) != null ? _b : 0,
3726
- left: (_c = updates.left) != null ? _c : 0.5,
3727
- top: (_d = updates.top) != null ? _d : 0.5
3693
+ committedUrl: void 0
3728
3694
  } : {}
3729
3695
  });
3730
3696
  this.updateConfig(next);
3731
3697
  if (replacingSource) {
3732
- this.debug("replace:image:begin", { id, replacingUrl });
3733
3698
  this.purgeSourceSizeCacheForItem(base);
3734
- const loaded = await this.waitImageLoaded(id, true);
3735
- this.debug("replace:image:loaded", { id, loaded });
3736
- if (loaded) {
3737
- await this.refitImageToFrame(id);
3738
- this.setImageFocus(id);
3739
- }
3740
3699
  }
3741
3700
  }
3742
3701
  waitImageLoaded(id, forceWait = false) {
@@ -3754,70 +3713,43 @@ var ImageTool = class {
3754
3713
  });
3755
3714
  });
3756
3715
  }
3757
- async refitImageToFrame(id) {
3716
+ async resolveImageSourceSize(id, src) {
3758
3717
  const obj = this.getImageObject(id);
3759
- if (!obj || !this.canvasService) return;
3760
- const current = this.items.find((item) => item.id === id);
3761
- if (!current) return;
3762
- const render = this.resolveRenderImageState(current);
3763
- this.rememberSourceSize(render.src, obj);
3764
- const source = this.getSourceSize(render.src, obj);
3765
- const frame = this.getFrameRect();
3766
- const coverScale = this.getCoverScale(frame, source);
3767
- const currentScale = this.toSceneObjectScale(obj.scaleX || 1);
3768
- const zoom = Math.max(0.05, currentScale / coverScale);
3769
- const updated = {
3770
- scale: Number.isFinite(zoom) ? zoom : 1,
3771
- angle: 0,
3772
- left: 0.5,
3773
- top: 0.5
3774
- };
3775
- const index = this.items.findIndex((item) => item.id === id);
3776
- if (index < 0) return;
3777
- const next = [...this.items];
3778
- next[index] = this.normalizeItem({ ...next[index], ...updated });
3779
- this.updateConfig(next);
3780
- this.workingItems = this.cloneItems(next);
3781
- this.hasWorkingChanges = false;
3782
- this.updateImages();
3783
- this.emitWorkingChange(id);
3718
+ if (obj) {
3719
+ this.rememberSourceSize(src, obj);
3720
+ }
3721
+ const ensured = await this.ensureSourceSize(src);
3722
+ if (ensured) return ensured;
3723
+ if (!obj) return null;
3724
+ const width = Number((obj == null ? void 0 : obj.width) || 0);
3725
+ const height = Number((obj == null ? void 0 : obj.height) || 0);
3726
+ if (width <= 0 || height <= 0) return null;
3727
+ return { width, height };
3784
3728
  }
3785
- async fitImageToArea(id, area) {
3786
- var _a, _b;
3729
+ async applyImageOperation(id, operation, options = {}) {
3787
3730
  if (!this.canvasService) return;
3788
- const loaded = await this.waitImageLoaded(id, false);
3789
- if (!loaded) return;
3790
- const obj = this.getImageObject(id);
3791
- if (!obj) return;
3792
- const renderItems = this.isToolActive ? this.workingItems : this.items;
3731
+ this.syncToolActiveFromWorkbench();
3732
+ const target = options.target || "auto";
3733
+ const renderItems = target === "working" || target === "auto" && this.isToolActive ? this.workingItems : this.items;
3793
3734
  const current = renderItems.find((item) => item.id === id);
3794
3735
  if (!current) return;
3795
3736
  const render = this.resolveRenderImageState(current);
3796
- this.rememberSourceSize(render.src, obj);
3797
- const source = this.getSourceSize(render.src, obj);
3737
+ const source = await this.resolveImageSourceSize(id, render.src);
3738
+ if (!source) return;
3798
3739
  const frame = this.getFrameRect();
3799
- const baseCover = this.getCoverScale(frame, source);
3800
- const desiredScale = Math.max(
3801
- Math.max(1, area.width) / Math.max(1, source.width),
3802
- Math.max(1, area.height) / Math.max(1, source.height)
3803
- );
3804
3740
  const viewport = this.canvasService.getSceneViewportRect();
3805
- const canvasW = viewport.width || 1;
3806
- const canvasH = viewport.height || 1;
3807
- const areaLeftInput = (_a = area.left) != null ? _a : 0.5;
3808
- const areaTopInput = (_b = area.top) != null ? _b : 0.5;
3809
- const areaLeftPx = areaLeftInput <= 1.5 ? viewport.left + areaLeftInput * canvasW : areaLeftInput;
3810
- const areaTopPx = areaTopInput <= 1.5 ? viewport.top + areaTopInput * canvasH : areaTopInput;
3811
- const updates = {
3812
- scale: Math.max(0.05, desiredScale / baseCover),
3813
- left: this.clampNormalized(
3814
- (areaLeftPx - frame.left) / Math.max(1, frame.width)
3815
- ),
3816
- top: this.clampNormalized(
3817
- (areaTopPx - frame.top) / Math.max(1, frame.height)
3818
- )
3819
- };
3820
- if (this.isToolActive) {
3741
+ const area = operation.type === "resetTransform" ? resolveImageOperationArea({ frame, viewport }) : resolveImageOperationArea({
3742
+ frame,
3743
+ viewport,
3744
+ area: operation.area
3745
+ });
3746
+ const updates = computeImageOperationUpdates({
3747
+ frame,
3748
+ source,
3749
+ operation,
3750
+ area
3751
+ });
3752
+ if (target === "working" || target === "auto" && this.isToolActive) {
3821
3753
  this.updateImageInWorking(id, updates);
3822
3754
  return;
3823
3755
  }
@@ -3948,6 +3880,11 @@ var ImageTool = class {
3948
3880
  }
3949
3881
  };
3950
3882
 
3883
+ // src/extensions/image/model.ts
3884
+ function hasAnyImageInViewState(state) {
3885
+ return Boolean(state == null ? void 0 : state.hasAnyImage);
3886
+ }
3887
+
3951
3888
  // src/extensions/size/SizeTool.ts
3952
3889
  import {
3953
3890
  ContributionPointIds as ContributionPointIds3
@@ -4912,6 +4849,13 @@ function buildDielineRenderBundle(options) {
4912
4849
  canvasWidth,
4913
4850
  canvasHeight
4914
4851
  };
4852
+ const cutFrameRect = {
4853
+ left: cx - cutW / 2,
4854
+ top: cy - cutH / 2,
4855
+ width: cutW,
4856
+ height: cutH,
4857
+ space: "screen"
4858
+ };
4915
4859
  const specs = [];
4916
4860
  if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)" && !hasImages) {
4917
4861
  specs.push({
@@ -5033,9 +4977,13 @@ function buildDielineRenderBundle(options) {
5033
4977
  width: cutW,
5034
4978
  height: cutH,
5035
4979
  radius: cutR,
5036
- x: cx,
5037
- y: cy,
5038
- features: cutFeatures
4980
+ // Build the clip path in the cut frame's local coordinates so Fabric
4981
+ // does not have to infer placement from the standalone path bounds.
4982
+ x: cutW / 2,
4983
+ y: cutH / 2,
4984
+ features: cutFeatures,
4985
+ canvasWidth: cutW,
4986
+ canvasHeight: cutH
5039
4987
  });
5040
4988
  if (!clipPathData) {
5041
4989
  return { specs, effects: [] };
@@ -5052,6 +5000,12 @@ function buildDielineRenderBundle(options) {
5052
5000
  id: ids.clipSource,
5053
5001
  type: "path",
5054
5002
  space: "screen",
5003
+ layout: {
5004
+ reference: "custom",
5005
+ referenceRect: cutFrameRect,
5006
+ alignX: "start",
5007
+ alignY: "start"
5008
+ },
5055
5009
  data: {
5056
5010
  id: ids.clipSource,
5057
5011
  type: "dieline-effect",
@@ -9876,6 +9830,7 @@ export {
9876
9830
  ViewportSystem,
9877
9831
  WhiteInkTool,
9878
9832
  getCoverScale as computeImageCoverScale,
9833
+ computeImageOperationUpdates,
9879
9834
  getCoverScale as computeWhiteInkCoverScale,
9880
9835
  createDefaultDielineState,
9881
9836
  createDielineCommands,
@@ -9885,5 +9840,7 @@ export {
9885
9840
  createWhiteInkCommands,
9886
9841
  createWhiteInkConfigurations,
9887
9842
  evaluateVisibilityExpr,
9888
- readDielineState
9843
+ hasAnyImageInViewState,
9844
+ readDielineState,
9845
+ resolveImageOperationArea
9889
9846
  };