@pooder/kit 6.1.1 → 6.2.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.js CHANGED
@@ -1075,13 +1075,15 @@ __export(index_exports, {
1075
1075
  WhiteInkTool: () => WhiteInkTool,
1076
1076
  computeImageCoverScale: () => getCoverScale,
1077
1077
  computeWhiteInkCoverScale: () => getCoverScale,
1078
+ createDefaultDielineState: () => createDefaultDielineState,
1078
1079
  createDielineCommands: () => createDielineCommands,
1079
1080
  createDielineConfigurations: () => createDielineConfigurations,
1080
1081
  createImageCommands: () => createImageCommands,
1081
1082
  createImageConfigurations: () => createImageConfigurations,
1082
1083
  createWhiteInkCommands: () => createWhiteInkCommands,
1083
1084
  createWhiteInkConfigurations: () => createWhiteInkConfigurations,
1084
- evaluateVisibilityExpr: () => evaluateVisibilityExpr
1085
+ evaluateVisibilityExpr: () => evaluateVisibilityExpr,
1086
+ readDielineState: () => readDielineState
1085
1087
  });
1086
1088
  module.exports = __toCommonJS(index_exports);
1087
1089
 
@@ -1485,6 +1487,7 @@ var WHITE_INK_OBJECT_LAYER_ID = "white-ink.user";
1485
1487
  var WHITE_INK_COVER_LAYER_ID = "white-ink.cover";
1486
1488
  var WHITE_INK_OVERLAY_LAYER_ID = "white-ink.overlay";
1487
1489
  var DIELINE_LAYER_ID = "dieline-overlay";
1490
+ var FEATURE_DIELINE_LAYER_ID = "feature-dieline-overlay";
1488
1491
  var FEATURE_OVERLAY_LAYER_ID = "feature-overlay";
1489
1492
  var RULER_LAYER_ID = "ruler-overlay";
1490
1493
  var FILM_LAYER_ID = "overlay";
@@ -1626,6 +1629,18 @@ function normalizeFitMode2(value, fallback) {
1626
1629
  }
1627
1630
  return fallback;
1628
1631
  }
1632
+ function normalizeRegionUnit(value, fallback) {
1633
+ if (value === "px" || value === "normalized") {
1634
+ return value;
1635
+ }
1636
+ return fallback;
1637
+ }
1638
+ function normalizeRegistrationFrame(value, fallback) {
1639
+ if (value === "trim" || value === "cut" || value === "bleed" || value === "focus" || value === "viewport") {
1640
+ return value;
1641
+ }
1642
+ return fallback;
1643
+ }
1629
1644
  function normalizeAnchor(value, fallback) {
1630
1645
  if (typeof value !== "string") return fallback;
1631
1646
  const trimmed = value.trim();
@@ -1636,6 +1651,63 @@ function normalizeOrder(value, fallback) {
1636
1651
  if (!Number.isFinite(numeric)) return fallback;
1637
1652
  return numeric;
1638
1653
  }
1654
+ function normalizeRegionValue(value, fallback) {
1655
+ const numeric = Number(value);
1656
+ return Number.isFinite(numeric) ? numeric : fallback;
1657
+ }
1658
+ function normalizeRegistrationRegion(raw, fallback) {
1659
+ if (!raw || typeof raw !== "object") {
1660
+ return fallback ? { ...fallback } : void 0;
1661
+ }
1662
+ const input = raw;
1663
+ const base = fallback || {
1664
+ left: 0,
1665
+ top: 0,
1666
+ width: 1,
1667
+ height: 1,
1668
+ unit: "normalized"
1669
+ };
1670
+ return {
1671
+ left: normalizeRegionValue(input.left, base.left),
1672
+ top: normalizeRegionValue(input.top, base.top),
1673
+ width: normalizeRegionValue(input.width, base.width),
1674
+ height: normalizeRegionValue(input.height, base.height),
1675
+ unit: normalizeRegionUnit(input.unit, base.unit)
1676
+ };
1677
+ }
1678
+ function normalizeRegistration(raw, fallback) {
1679
+ if (!raw || typeof raw !== "object") {
1680
+ return fallback ? {
1681
+ sourceRegion: fallback.sourceRegion ? { ...fallback.sourceRegion } : void 0,
1682
+ targetFrame: fallback.targetFrame,
1683
+ fit: fallback.fit
1684
+ } : void 0;
1685
+ }
1686
+ const input = raw;
1687
+ const normalized = {
1688
+ sourceRegion: normalizeRegistrationRegion(
1689
+ input.sourceRegion,
1690
+ fallback == null ? void 0 : fallback.sourceRegion
1691
+ ),
1692
+ targetFrame: normalizeRegistrationFrame(
1693
+ input.targetFrame,
1694
+ (fallback == null ? void 0 : fallback.targetFrame) || "trim"
1695
+ ),
1696
+ fit: normalizeFitMode2(input.fit, (fallback == null ? void 0 : fallback.fit) || "stretch")
1697
+ };
1698
+ if (!normalized.sourceRegion) {
1699
+ return void 0;
1700
+ }
1701
+ return normalized;
1702
+ }
1703
+ function cloneRegistration(registration) {
1704
+ if (!registration) return void 0;
1705
+ return {
1706
+ sourceRegion: registration.sourceRegion ? { ...registration.sourceRegion } : void 0,
1707
+ targetFrame: registration.targetFrame,
1708
+ fit: registration.fit
1709
+ };
1710
+ }
1639
1711
  function normalizeLayer(raw, index, fallback) {
1640
1712
  const fallbackLayer = fallback || {
1641
1713
  id: `layer-${index + 1}`,
@@ -1649,7 +1721,10 @@ function normalizeLayer(raw, index, fallback) {
1649
1721
  src: ""
1650
1722
  };
1651
1723
  if (!raw || typeof raw !== "object") {
1652
- return { ...fallbackLayer };
1724
+ return {
1725
+ ...fallbackLayer,
1726
+ registration: cloneRegistration(fallbackLayer.registration)
1727
+ };
1653
1728
  }
1654
1729
  const input = raw;
1655
1730
  const kind = normalizeLayerKind(input.kind, fallbackLayer.kind);
@@ -1663,7 +1738,8 @@ function normalizeLayer(raw, index, fallback) {
1663
1738
  enabled: typeof input.enabled === "boolean" ? input.enabled : fallbackLayer.enabled,
1664
1739
  exportable: typeof input.exportable === "boolean" ? input.exportable : fallbackLayer.exportable,
1665
1740
  color: kind === "color" ? typeof input.color === "string" ? input.color : typeof fallbackLayer.color === "string" ? fallbackLayer.color : "#ffffff" : void 0,
1666
- src: kind === "image" ? typeof input.src === "string" ? input.src.trim() : typeof fallbackLayer.src === "string" ? fallbackLayer.src : "" : void 0
1741
+ src: kind === "image" ? typeof input.src === "string" ? input.src.trim() : typeof fallbackLayer.src === "string" ? fallbackLayer.src : "" : void 0,
1742
+ registration: kind === "image" ? normalizeRegistration(input.registration, fallbackLayer.registration) : void 0
1667
1743
  };
1668
1744
  }
1669
1745
  function normalizeConfig(raw) {
@@ -1693,7 +1769,10 @@ function normalizeConfig(raw) {
1693
1769
  function cloneConfig(config) {
1694
1770
  return {
1695
1771
  version: config.version,
1696
- layers: (config.layers || []).map((layer) => ({ ...layer }))
1772
+ layers: (config.layers || []).map((layer) => ({
1773
+ ...layer,
1774
+ registration: cloneRegistration(layer.registration)
1775
+ }))
1697
1776
  };
1698
1777
  }
1699
1778
  function mergeConfig(base, patch) {
@@ -1944,6 +2023,41 @@ var BackgroundTool = class {
1944
2023
  height: layout.trimRect.height
1945
2024
  };
1946
2025
  }
2026
+ resolveTargetFrameRect(frame) {
2027
+ if (frame === "viewport") {
2028
+ return this.getViewportRect();
2029
+ }
2030
+ const layout = this.resolveSceneLayout();
2031
+ if (!layout) {
2032
+ return frame === "focus" ? this.getViewportRect() : null;
2033
+ }
2034
+ switch (frame) {
2035
+ case "trim":
2036
+ case "focus":
2037
+ return {
2038
+ left: layout.trimRect.left,
2039
+ top: layout.trimRect.top,
2040
+ width: layout.trimRect.width,
2041
+ height: layout.trimRect.height
2042
+ };
2043
+ case "cut":
2044
+ return {
2045
+ left: layout.cutRect.left,
2046
+ top: layout.cutRect.top,
2047
+ width: layout.cutRect.width,
2048
+ height: layout.cutRect.height
2049
+ };
2050
+ case "bleed":
2051
+ return {
2052
+ left: layout.bleedRect.left,
2053
+ top: layout.bleedRect.top,
2054
+ width: layout.bleedRect.width,
2055
+ height: layout.bleedRect.height
2056
+ };
2057
+ default:
2058
+ return null;
2059
+ }
2060
+ }
1947
2061
  resolveAnchorRect(anchor) {
1948
2062
  if (anchor === "focus") {
1949
2063
  return this.resolveFocusRect() || this.getViewportRect();
@@ -1976,6 +2090,53 @@ var BackgroundTool = class {
1976
2090
  scaleY: scale
1977
2091
  };
1978
2092
  }
2093
+ resolveRegistrationRegion(region, sourceSize) {
2094
+ const sourceWidth = Math.max(1, Number(sourceSize.width || 0));
2095
+ const sourceHeight = Math.max(1, Number(sourceSize.height || 0));
2096
+ const width = region.unit === "normalized" ? region.width * sourceWidth : region.width;
2097
+ const height = region.unit === "normalized" ? region.height * sourceHeight : region.height;
2098
+ const left = region.unit === "normalized" ? region.left * sourceWidth : region.left;
2099
+ const top = region.unit === "normalized" ? region.top * sourceHeight : region.top;
2100
+ if (!Number.isFinite(left) || !Number.isFinite(top) || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
2101
+ return null;
2102
+ }
2103
+ return { left, top, width, height };
2104
+ }
2105
+ resolveRegistrationPlacement(layer, sourceSize) {
2106
+ const registration = layer.registration;
2107
+ if (!(registration == null ? void 0 : registration.sourceRegion)) return null;
2108
+ const targetRect = this.resolveTargetFrameRect(
2109
+ registration.targetFrame || "trim"
2110
+ );
2111
+ if (!targetRect) return null;
2112
+ const sourceRegion = this.resolveRegistrationRegion(
2113
+ registration.sourceRegion,
2114
+ sourceSize
2115
+ );
2116
+ if (!sourceRegion) return null;
2117
+ const fit = registration.fit || "stretch";
2118
+ const baseScaleX = targetRect.width / sourceRegion.width;
2119
+ const baseScaleY = targetRect.height / sourceRegion.height;
2120
+ if (fit === "stretch") {
2121
+ return {
2122
+ left: targetRect.left - sourceRegion.left * baseScaleX,
2123
+ top: targetRect.top - sourceRegion.top * baseScaleY,
2124
+ scaleX: baseScaleX,
2125
+ scaleY: baseScaleY
2126
+ };
2127
+ }
2128
+ const uniformScale = fit === "contain" ? Math.min(baseScaleX, baseScaleY) : Math.max(baseScaleX, baseScaleY);
2129
+ const alignedWidth = sourceRegion.width * uniformScale;
2130
+ const alignedHeight = sourceRegion.height * uniformScale;
2131
+ const offsetLeft = targetRect.left + (targetRect.width - alignedWidth) / 2;
2132
+ const offsetTop = targetRect.top + (targetRect.height - alignedHeight) / 2;
2133
+ return {
2134
+ left: offsetLeft - sourceRegion.left * uniformScale,
2135
+ top: offsetTop - sourceRegion.top * uniformScale,
2136
+ scaleX: uniformScale,
2137
+ scaleY: uniformScale
2138
+ };
2139
+ }
1979
2140
  buildColorLayerSpec(layer) {
1980
2141
  const rect = this.resolveAnchorRect(layer.anchor);
1981
2142
  return {
@@ -2009,8 +2170,11 @@ var BackgroundTool = class {
2009
2170
  if (!src) return [];
2010
2171
  const sourceSize = this.sourceSizeCache.getSourceSize(src);
2011
2172
  if (!sourceSize) return [];
2012
- const rect = this.resolveAnchorRect(layer.anchor);
2013
- const placement = this.resolveImagePlacement(rect, sourceSize, layer.fit);
2173
+ const placement = this.resolveRegistrationPlacement(layer, sourceSize) || this.resolveImagePlacement(
2174
+ this.resolveAnchorRect(layer.anchor),
2175
+ sourceSize,
2176
+ layer.fit
2177
+ );
2014
2178
  return [
2015
2179
  {
2016
2180
  id: `background.layer.${layer.id}.image`,
@@ -3074,6 +3238,7 @@ var IMAGE_DEFAULT_CONTROL_CAPABILITIES = [
3074
3238
  "rotate",
3075
3239
  "scale"
3076
3240
  ];
3241
+ var IMAGE_MOVE_SNAP_THRESHOLD_PX = 6;
3077
3242
  var IMAGE_CONTROL_DESCRIPTORS = [
3078
3243
  {
3079
3244
  key: "tl",
@@ -3118,12 +3283,17 @@ var ImageTool = class {
3118
3283
  this.renderSeq = 0;
3119
3284
  this.imageSpecs = [];
3120
3285
  this.overlaySpecs = [];
3286
+ this.activeSnapX = null;
3287
+ this.activeSnapY = null;
3288
+ this.movingImageId = null;
3289
+ this.hasRenderedSnapGuides = false;
3121
3290
  this.subscriptions = new SubscriptionBag();
3122
3291
  this.imageControlsByCapabilityKey = /* @__PURE__ */ new Map();
3123
3292
  this.onToolActivated = (event) => {
3124
3293
  const before = this.isToolActive;
3125
3294
  this.syncToolActiveFromWorkbench(event.id);
3126
3295
  if (!this.isToolActive) {
3296
+ this.endMoveSnapInteraction();
3127
3297
  this.setImageFocus(null, {
3128
3298
  syncCanvasSelection: true,
3129
3299
  skipRender: true
@@ -3174,6 +3344,7 @@ var ImageTool = class {
3174
3344
  this.updateImages();
3175
3345
  };
3176
3346
  this.onSelectionCleared = () => {
3347
+ this.endMoveSnapInteraction();
3177
3348
  this.setImageFocus(null, {
3178
3349
  syncCanvasSelection: false,
3179
3350
  skipRender: true
@@ -3182,6 +3353,8 @@ var ImageTool = class {
3182
3353
  this.updateImages();
3183
3354
  };
3184
3355
  this.onSceneLayoutChanged = () => {
3356
+ var _a;
3357
+ (_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
3185
3358
  this.updateImages();
3186
3359
  };
3187
3360
  this.onSceneGeometryChanged = () => {
@@ -3194,7 +3367,11 @@ var ImageTool = class {
3194
3367
  const id = (_a = target == null ? void 0 : target.data) == null ? void 0 : _a.id;
3195
3368
  const layerId = (_b = target == null ? void 0 : target.data) == null ? void 0 : _b.layerId;
3196
3369
  if (typeof id !== "string" || layerId !== IMAGE_OBJECT_LAYER_ID) return;
3370
+ if (this.movingImageId === id) {
3371
+ this.applyMoveSnapToTarget(target);
3372
+ }
3197
3373
  const frame = this.getFrameRect();
3374
+ this.endMoveSnapInteraction();
3198
3375
  if (!frame.width || !frame.height) return;
3199
3376
  const center = target.getCenterPoint ? target.getCenterPoint() : new import_fabric2.Point((_c = target.left) != null ? _c : 0, (_d = target.top) != null ? _d : 0);
3200
3377
  const centerScene = this.canvasService ? this.canvasService.toScenePoint({ x: center.x, y: center.y }) : { x: center.x, y: center.y };
@@ -3258,8 +3435,17 @@ var ImageTool = class {
3258
3435
  }),
3259
3436
  { priority: 300 }
3260
3437
  );
3261
- this.subscriptions.on(context.eventBus, "tool:activated", this.onToolActivated);
3262
- this.subscriptions.on(context.eventBus, "object:modified", this.onObjectModified);
3438
+ this.bindCanvasInteractionHandlers();
3439
+ this.subscriptions.on(
3440
+ context.eventBus,
3441
+ "tool:activated",
3442
+ this.onToolActivated
3443
+ );
3444
+ this.subscriptions.on(
3445
+ context.eventBus,
3446
+ "object:modified",
3447
+ this.onObjectModified
3448
+ );
3263
3449
  this.subscriptions.on(
3264
3450
  context.eventBus,
3265
3451
  "selection:created",
@@ -3327,6 +3513,8 @@ var ImageTool = class {
3327
3513
  this.imageSpecs = [];
3328
3514
  this.overlaySpecs = [];
3329
3515
  this.imageControlsByCapabilityKey.clear();
3516
+ this.endMoveSnapInteraction();
3517
+ this.unbindCanvasInteractionHandlers();
3330
3518
  this.clearRenderedImages();
3331
3519
  (_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
3332
3520
  this.renderProducerDisposable = void 0;
@@ -3336,6 +3524,283 @@ var ImageTool = class {
3336
3524
  }
3337
3525
  this.context = void 0;
3338
3526
  }
3527
+ bindCanvasInteractionHandlers() {
3528
+ if (!this.canvasService || this.canvasObjectMovingHandler) return;
3529
+ this.canvasMouseUpHandler = (e) => {
3530
+ var _a;
3531
+ const target = this.getActiveImageTarget(e == null ? void 0 : e.target);
3532
+ if (target && typeof ((_a = target == null ? void 0 : target.data) == null ? void 0 : _a.id) === "string" && target.data.id === this.movingImageId) {
3533
+ this.applyMoveSnapToTarget(target);
3534
+ }
3535
+ this.endMoveSnapInteraction();
3536
+ };
3537
+ this.canvasObjectMovingHandler = (e) => {
3538
+ this.handleCanvasObjectMoving(e);
3539
+ };
3540
+ this.canvasBeforeRenderHandler = () => {
3541
+ this.handleCanvasBeforeRender();
3542
+ };
3543
+ this.canvasAfterRenderHandler = () => {
3544
+ this.handleCanvasAfterRender();
3545
+ };
3546
+ this.canvasService.canvas.on("mouse:up", this.canvasMouseUpHandler);
3547
+ this.canvasService.canvas.on(
3548
+ "object:moving",
3549
+ this.canvasObjectMovingHandler
3550
+ );
3551
+ this.canvasService.canvas.on(
3552
+ "before:render",
3553
+ this.canvasBeforeRenderHandler
3554
+ );
3555
+ this.canvasService.canvas.on("after:render", this.canvasAfterRenderHandler);
3556
+ }
3557
+ unbindCanvasInteractionHandlers() {
3558
+ if (!this.canvasService) return;
3559
+ if (this.canvasMouseUpHandler) {
3560
+ this.canvasService.canvas.off("mouse:up", this.canvasMouseUpHandler);
3561
+ }
3562
+ if (this.canvasObjectMovingHandler) {
3563
+ this.canvasService.canvas.off(
3564
+ "object:moving",
3565
+ this.canvasObjectMovingHandler
3566
+ );
3567
+ }
3568
+ if (this.canvasBeforeRenderHandler) {
3569
+ this.canvasService.canvas.off(
3570
+ "before:render",
3571
+ this.canvasBeforeRenderHandler
3572
+ );
3573
+ }
3574
+ if (this.canvasAfterRenderHandler) {
3575
+ this.canvasService.canvas.off(
3576
+ "after:render",
3577
+ this.canvasAfterRenderHandler
3578
+ );
3579
+ }
3580
+ this.canvasMouseUpHandler = void 0;
3581
+ this.canvasObjectMovingHandler = void 0;
3582
+ this.canvasBeforeRenderHandler = void 0;
3583
+ this.canvasAfterRenderHandler = void 0;
3584
+ }
3585
+ getActiveImageTarget(target) {
3586
+ var _a, _b;
3587
+ if (!this.isToolActive) return null;
3588
+ if (!target) return null;
3589
+ if (((_a = target == null ? void 0 : target.data) == null ? void 0 : _a.layerId) !== IMAGE_OBJECT_LAYER_ID) return null;
3590
+ if (typeof ((_b = target == null ? void 0 : target.data) == null ? void 0 : _b.id) !== "string") return null;
3591
+ return target;
3592
+ }
3593
+ getTargetBoundsScene(target) {
3594
+ if (!this.canvasService || !target) return null;
3595
+ const rawBounds = typeof target.getBoundingRect === "function" ? target.getBoundingRect() : {
3596
+ left: Number(target.left || 0),
3597
+ top: Number(target.top || 0),
3598
+ width: Number(target.width || 0),
3599
+ height: Number(target.height || 0)
3600
+ };
3601
+ return this.canvasService.toSceneRect({
3602
+ left: Number(rawBounds.left || 0),
3603
+ top: Number(rawBounds.top || 0),
3604
+ width: Number(rawBounds.width || 0),
3605
+ height: Number(rawBounds.height || 0)
3606
+ });
3607
+ }
3608
+ getSnapThresholdScene(px) {
3609
+ if (!this.canvasService) return px;
3610
+ return this.canvasService.toSceneLength(px);
3611
+ }
3612
+ pickSnapMatch(candidates) {
3613
+ if (!candidates.length) return null;
3614
+ const snapThreshold = this.getSnapThresholdScene(
3615
+ IMAGE_MOVE_SNAP_THRESHOLD_PX
3616
+ );
3617
+ let best = null;
3618
+ candidates.forEach((candidate) => {
3619
+ if (Math.abs(candidate.deltaScene) > snapThreshold) return;
3620
+ if (!best || Math.abs(candidate.deltaScene) < Math.abs(best.deltaScene)) {
3621
+ best = candidate;
3622
+ }
3623
+ });
3624
+ return best;
3625
+ }
3626
+ computeMoveSnapMatches(bounds, frame) {
3627
+ if (!bounds || frame.width <= 0 || frame.height <= 0) {
3628
+ return { x: null, y: null };
3629
+ }
3630
+ const xCandidates = [
3631
+ {
3632
+ axis: "x",
3633
+ lineId: "frame-left",
3634
+ kind: "edge",
3635
+ lineScene: frame.left,
3636
+ deltaScene: frame.left - bounds.left
3637
+ },
3638
+ {
3639
+ axis: "x",
3640
+ lineId: "frame-center-x",
3641
+ kind: "center",
3642
+ lineScene: frame.left + frame.width / 2,
3643
+ deltaScene: frame.left + frame.width / 2 - (bounds.left + bounds.width / 2)
3644
+ },
3645
+ {
3646
+ axis: "x",
3647
+ lineId: "frame-right",
3648
+ kind: "edge",
3649
+ lineScene: frame.left + frame.width,
3650
+ deltaScene: frame.left + frame.width - (bounds.left + bounds.width)
3651
+ }
3652
+ ];
3653
+ const yCandidates = [
3654
+ {
3655
+ axis: "y",
3656
+ lineId: "frame-top",
3657
+ kind: "edge",
3658
+ lineScene: frame.top,
3659
+ deltaScene: frame.top - bounds.top
3660
+ },
3661
+ {
3662
+ axis: "y",
3663
+ lineId: "frame-center-y",
3664
+ kind: "center",
3665
+ lineScene: frame.top + frame.height / 2,
3666
+ deltaScene: frame.top + frame.height / 2 - (bounds.top + bounds.height / 2)
3667
+ },
3668
+ {
3669
+ axis: "y",
3670
+ lineId: "frame-bottom",
3671
+ kind: "edge",
3672
+ lineScene: frame.top + frame.height,
3673
+ deltaScene: frame.top + frame.height - (bounds.top + bounds.height)
3674
+ }
3675
+ ];
3676
+ return {
3677
+ x: this.pickSnapMatch(xCandidates),
3678
+ y: this.pickSnapMatch(yCandidates)
3679
+ };
3680
+ }
3681
+ areSnapMatchesEqual(a, b) {
3682
+ if (!a && !b) return true;
3683
+ if (!a || !b) return false;
3684
+ return a.lineId === b.lineId && a.axis === b.axis && a.kind === b.kind;
3685
+ }
3686
+ updateSnapMatchState(nextX, nextY) {
3687
+ var _a;
3688
+ const changed = !this.areSnapMatchesEqual(this.activeSnapX, nextX) || !this.areSnapMatchesEqual(this.activeSnapY, nextY);
3689
+ this.activeSnapX = nextX;
3690
+ this.activeSnapY = nextY;
3691
+ if (changed) {
3692
+ (_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
3693
+ }
3694
+ }
3695
+ clearSnapPreview() {
3696
+ var _a;
3697
+ this.activeSnapX = null;
3698
+ this.activeSnapY = null;
3699
+ this.hasRenderedSnapGuides = false;
3700
+ (_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
3701
+ }
3702
+ endMoveSnapInteraction() {
3703
+ this.movingImageId = null;
3704
+ this.clearSnapPreview();
3705
+ }
3706
+ applyMoveSnapToTarget(target) {
3707
+ var _a, _b, _c, _d;
3708
+ if (!this.canvasService) {
3709
+ return { x: null, y: null };
3710
+ }
3711
+ const frame = this.getFrameRect();
3712
+ if (frame.width <= 0 || frame.height <= 0) {
3713
+ return { x: null, y: null };
3714
+ }
3715
+ const bounds = this.getTargetBoundsScene(target);
3716
+ const matches = this.computeMoveSnapMatches(bounds, frame);
3717
+ const deltaScreenX = this.canvasService.toScreenLength(
3718
+ (_b = (_a = matches.x) == null ? void 0 : _a.deltaScene) != null ? _b : 0
3719
+ );
3720
+ const deltaScreenY = this.canvasService.toScreenLength(
3721
+ (_d = (_c = matches.y) == null ? void 0 : _c.deltaScene) != null ? _d : 0
3722
+ );
3723
+ if (deltaScreenX || deltaScreenY) {
3724
+ target.set({
3725
+ left: Number(target.left || 0) + deltaScreenX,
3726
+ top: Number(target.top || 0) + deltaScreenY
3727
+ });
3728
+ target.setCoords();
3729
+ }
3730
+ return matches;
3731
+ }
3732
+ handleCanvasBeforeRender() {
3733
+ if (!this.canvasService) return;
3734
+ if (!this.hasRenderedSnapGuides && !this.activeSnapX && !this.activeSnapY) {
3735
+ return;
3736
+ }
3737
+ this.canvasService.canvas.clearContext(
3738
+ this.canvasService.canvas.contextTop
3739
+ );
3740
+ this.hasRenderedSnapGuides = false;
3741
+ }
3742
+ drawSnapGuideLine(from, to) {
3743
+ if (!this.canvasService) return;
3744
+ const ctx = this.canvasService.canvas.contextTop;
3745
+ if (!ctx) return;
3746
+ const color = this.getConfig("image.control.borderColor", "#1677ff") || "#1677ff";
3747
+ ctx.save();
3748
+ ctx.strokeStyle = color;
3749
+ ctx.lineWidth = 1;
3750
+ ctx.beginPath();
3751
+ ctx.moveTo(from.x, from.y);
3752
+ ctx.lineTo(to.x, to.y);
3753
+ ctx.stroke();
3754
+ ctx.restore();
3755
+ }
3756
+ handleCanvasAfterRender() {
3757
+ if (!this.canvasService || !this.isImageEditingVisible()) {
3758
+ return;
3759
+ }
3760
+ const frame = this.getFrameRect();
3761
+ if (frame.width <= 0 || frame.height <= 0) {
3762
+ return;
3763
+ }
3764
+ const frameScreen = this.getFrameRectScreen(frame);
3765
+ let drew = false;
3766
+ if (this.activeSnapX) {
3767
+ const x = this.canvasService.toScreenPoint({
3768
+ x: this.activeSnapX.lineScene,
3769
+ y: frame.top
3770
+ }).x;
3771
+ this.drawSnapGuideLine(
3772
+ { x, y: frameScreen.top },
3773
+ { x, y: frameScreen.top + frameScreen.height }
3774
+ );
3775
+ drew = true;
3776
+ }
3777
+ if (this.activeSnapY) {
3778
+ const y = this.canvasService.toScreenPoint({
3779
+ x: frame.left,
3780
+ y: this.activeSnapY.lineScene
3781
+ }).y;
3782
+ this.drawSnapGuideLine(
3783
+ { x: frameScreen.left, y },
3784
+ { x: frameScreen.left + frameScreen.width, y }
3785
+ );
3786
+ drew = true;
3787
+ }
3788
+ this.hasRenderedSnapGuides = drew;
3789
+ }
3790
+ handleCanvasObjectMoving(e) {
3791
+ var _a;
3792
+ const target = this.getActiveImageTarget(e == null ? void 0 : e.target);
3793
+ if (!target || !this.canvasService) return;
3794
+ this.movingImageId = typeof ((_a = target == null ? void 0 : target.data) == null ? void 0 : _a.id) === "string" ? target.data.id : null;
3795
+ const frame = this.getFrameRect();
3796
+ if (frame.width <= 0 || frame.height <= 0) {
3797
+ this.endMoveSnapInteraction();
3798
+ return;
3799
+ }
3800
+ const rawBounds = this.getTargetBoundsScene(target);
3801
+ const matches = this.computeMoveSnapMatches(rawBounds, frame);
3802
+ this.updateSnapMatchState(matches.x, matches.y);
3803
+ }
3339
3804
  syncToolActiveFromWorkbench(fallbackId) {
3340
3805
  var _a;
3341
3806
  const wb = (_a = this.context) == null ? void 0 : _a.services.get("WorkbenchService");
@@ -3959,33 +4424,9 @@ var ImageTool = class {
3959
4424
  originY: "top",
3960
4425
  fill: hatchFill,
3961
4426
  opacity: patternFill ? 1 : 0.8,
3962
- stroke: null,
3963
- fillRule: "evenodd",
3964
- selectable: false,
3965
- evented: false,
3966
- excludeFromExport: true,
3967
- objectCaching: false
3968
- }
3969
- },
3970
- {
3971
- id: "image.cropShapePath",
3972
- type: "path",
3973
- data: { id: "image.cropShapePath", zIndex: 6 },
3974
- layout: {
3975
- reference: "custom",
3976
- referenceRect: frameRect,
3977
- alignX: "start",
3978
- alignY: "start",
3979
- offsetX: shapeBounds.x,
3980
- offsetY: shapeBounds.y
3981
- },
3982
- props: {
3983
- pathData: shapePathData,
3984
- originX: "left",
3985
- originY: "top",
3986
- fill: "rgba(0,0,0,0)",
3987
4427
  stroke: "rgba(255, 0, 0, 0.9)",
3988
4428
  strokeWidth: (_b = (_a = this.canvasService) == null ? void 0 : _a.toSceneLength(1)) != null ? _b : 1,
4429
+ fillRule: "evenodd",
3989
4430
  selectable: false,
3990
4431
  evented: false,
3991
4432
  excludeFromExport: true,
@@ -5118,6 +5559,318 @@ function createDielineConfigurations(state) {
5118
5559
  ];
5119
5560
  }
5120
5561
 
5562
+ // src/extensions/dieline/model.ts
5563
+ function createDefaultDielineState() {
5564
+ return {
5565
+ shape: DEFAULT_DIELINE_SHAPE,
5566
+ shapeStyle: { ...DEFAULT_DIELINE_SHAPE_STYLE },
5567
+ width: 500,
5568
+ height: 500,
5569
+ radius: 0,
5570
+ offset: 0,
5571
+ padding: 140,
5572
+ mainLine: {
5573
+ width: 2.7,
5574
+ color: "#FF0000",
5575
+ dashLength: 5,
5576
+ style: "solid"
5577
+ },
5578
+ offsetLine: {
5579
+ width: 2.7,
5580
+ color: "#FF0000",
5581
+ dashLength: 5,
5582
+ style: "solid"
5583
+ },
5584
+ insideColor: "rgba(0,0,0,0)",
5585
+ showBleedLines: true,
5586
+ features: []
5587
+ };
5588
+ }
5589
+ function readDielineState(configService, fallback) {
5590
+ const base = createDefaultDielineState();
5591
+ if (fallback) {
5592
+ Object.assign(base, fallback);
5593
+ if (fallback.mainLine) {
5594
+ base.mainLine = { ...base.mainLine, ...fallback.mainLine };
5595
+ }
5596
+ if (fallback.offsetLine) {
5597
+ base.offsetLine = { ...base.offsetLine, ...fallback.offsetLine };
5598
+ }
5599
+ if (fallback.shapeStyle) {
5600
+ base.shapeStyle = normalizeShapeStyle(fallback.shapeStyle, base.shapeStyle);
5601
+ }
5602
+ }
5603
+ const sizeState = readSizeState(configService);
5604
+ const sourceWidth = Number(configService.get("dieline.customSourceWidthPx", 0));
5605
+ const sourceHeight = Number(
5606
+ configService.get("dieline.customSourceHeightPx", 0)
5607
+ );
5608
+ return {
5609
+ ...base,
5610
+ shape: normalizeDielineShape(
5611
+ configService.get("dieline.shape", base.shape),
5612
+ base.shape
5613
+ ),
5614
+ shapeStyle: normalizeShapeStyle(
5615
+ configService.get("dieline.shapeStyle", base.shapeStyle),
5616
+ base.shapeStyle
5617
+ ),
5618
+ width: sizeState.actualWidthMm,
5619
+ height: sizeState.actualHeightMm,
5620
+ radius: parseLengthToMm(configService.get("dieline.radius", base.radius), "mm"),
5621
+ padding: sizeState.viewPadding,
5622
+ offset: sizeState.cutMode === "outset" ? sizeState.cutMarginMm : sizeState.cutMode === "inset" ? -sizeState.cutMarginMm : 0,
5623
+ mainLine: {
5624
+ width: configService.get("dieline.strokeWidth", base.mainLine.width),
5625
+ color: configService.get("dieline.strokeColor", base.mainLine.color),
5626
+ dashLength: configService.get(
5627
+ "dieline.dashLength",
5628
+ base.mainLine.dashLength
5629
+ ),
5630
+ style: configService.get("dieline.style", base.mainLine.style)
5631
+ },
5632
+ offsetLine: {
5633
+ width: configService.get(
5634
+ "dieline.offsetStrokeWidth",
5635
+ base.offsetLine.width
5636
+ ),
5637
+ color: configService.get(
5638
+ "dieline.offsetStrokeColor",
5639
+ base.offsetLine.color
5640
+ ),
5641
+ dashLength: configService.get(
5642
+ "dieline.offsetDashLength",
5643
+ base.offsetLine.dashLength
5644
+ ),
5645
+ style: configService.get("dieline.offsetStyle", base.offsetLine.style)
5646
+ },
5647
+ insideColor: configService.get("dieline.insideColor", base.insideColor),
5648
+ showBleedLines: configService.get(
5649
+ "dieline.showBleedLines",
5650
+ base.showBleedLines
5651
+ ),
5652
+ features: configService.get("dieline.features", base.features),
5653
+ pathData: configService.get("dieline.pathData", base.pathData),
5654
+ customSourceWidthPx: Number.isFinite(sourceWidth) && sourceWidth > 0 ? sourceWidth : void 0,
5655
+ customSourceHeightPx: Number.isFinite(sourceHeight) && sourceHeight > 0 ? sourceHeight : void 0
5656
+ };
5657
+ }
5658
+
5659
+ // src/extensions/dieline/renderBuilder.ts
5660
+ var DEFAULT_IDS = {
5661
+ inside: "dieline.inside",
5662
+ bleedZone: "dieline.bleed-zone",
5663
+ offsetBorder: "dieline.offset-border",
5664
+ border: "dieline.border",
5665
+ clip: "dieline.clip.image",
5666
+ clipSource: "dieline.effect.clip-path"
5667
+ };
5668
+ function scaleFeatures(state, scale) {
5669
+ return (state.features || []).map((feature) => ({
5670
+ ...feature,
5671
+ x: feature.x,
5672
+ y: feature.y,
5673
+ width: (feature.width || 0) * scale,
5674
+ height: (feature.height || 0) * scale,
5675
+ radius: (feature.radius || 0) * scale
5676
+ }));
5677
+ }
5678
+ function buildDielineRenderBundle(options) {
5679
+ const ids = { ...DEFAULT_IDS, ...options.ids || {} };
5680
+ const {
5681
+ state,
5682
+ sceneLayout,
5683
+ canvasWidth,
5684
+ canvasHeight,
5685
+ hasImages,
5686
+ createHatchPattern,
5687
+ includeImageClipEffect = true,
5688
+ clipTargetPassIds = [IMAGE_OBJECT_LAYER_ID],
5689
+ clipVisibility
5690
+ } = options;
5691
+ const { shape, shapeStyle, radius, mainLine, offsetLine, insideColor } = state;
5692
+ const scale = sceneLayout.scale;
5693
+ const cx = sceneLayout.trimRect.centerX;
5694
+ const cy = sceneLayout.trimRect.centerY;
5695
+ const visualWidth = sceneLayout.trimRect.width;
5696
+ const visualHeight = sceneLayout.trimRect.height;
5697
+ const visualRadius = radius * scale;
5698
+ const cutW = sceneLayout.cutRect.width;
5699
+ const cutH = sceneLayout.cutRect.height;
5700
+ const visualOffset = (cutW - visualWidth) / 2;
5701
+ const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
5702
+ const absoluteFeatures = scaleFeatures(state, scale);
5703
+ const cutFeatures = absoluteFeatures.filter((feature) => !feature.skipCut);
5704
+ const common = {
5705
+ shape,
5706
+ shapeStyle,
5707
+ pathData: state.pathData,
5708
+ customSourceWidthPx: state.customSourceWidthPx,
5709
+ customSourceHeightPx: state.customSourceHeightPx,
5710
+ canvasWidth,
5711
+ canvasHeight
5712
+ };
5713
+ const specs = [];
5714
+ if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)" && !hasImages) {
5715
+ specs.push({
5716
+ id: ids.inside,
5717
+ type: "path",
5718
+ space: "screen",
5719
+ data: { id: ids.inside, type: "dieline" },
5720
+ props: {
5721
+ pathData: generateDielinePath({
5722
+ ...common,
5723
+ width: cutW,
5724
+ height: cutH,
5725
+ radius: cutR,
5726
+ x: cx,
5727
+ y: cy,
5728
+ features: cutFeatures
5729
+ }),
5730
+ fill: insideColor,
5731
+ stroke: null,
5732
+ selectable: false,
5733
+ evented: false,
5734
+ originX: "left",
5735
+ originY: "top"
5736
+ }
5737
+ });
5738
+ }
5739
+ if (Math.abs(visualOffset) > 1e-4) {
5740
+ const trimPathInput = {
5741
+ ...common,
5742
+ width: visualWidth,
5743
+ height: visualHeight,
5744
+ radius: visualRadius,
5745
+ x: cx,
5746
+ y: cy,
5747
+ features: cutFeatures
5748
+ };
5749
+ const cutPathInput = {
5750
+ ...common,
5751
+ width: cutW,
5752
+ height: cutH,
5753
+ radius: cutR,
5754
+ x: cx,
5755
+ y: cy,
5756
+ features: cutFeatures
5757
+ };
5758
+ if (state.showBleedLines !== false) {
5759
+ const pattern = createHatchPattern == null ? void 0 : createHatchPattern(mainLine.color);
5760
+ if (pattern) {
5761
+ specs.push({
5762
+ id: ids.bleedZone,
5763
+ type: "path",
5764
+ space: "screen",
5765
+ data: { id: ids.bleedZone, type: "dieline" },
5766
+ props: {
5767
+ pathData: generateBleedZonePath(
5768
+ trimPathInput,
5769
+ cutPathInput,
5770
+ visualOffset
5771
+ ),
5772
+ fill: pattern,
5773
+ stroke: null,
5774
+ selectable: false,
5775
+ evented: false,
5776
+ objectCaching: false,
5777
+ originX: "left",
5778
+ originY: "top"
5779
+ }
5780
+ });
5781
+ }
5782
+ }
5783
+ specs.push({
5784
+ id: ids.offsetBorder,
5785
+ type: "path",
5786
+ space: "screen",
5787
+ data: { id: ids.offsetBorder, type: "dieline" },
5788
+ props: {
5789
+ pathData: generateDielinePath(cutPathInput),
5790
+ fill: null,
5791
+ stroke: offsetLine.style === "hidden" ? null : offsetLine.color,
5792
+ strokeWidth: offsetLine.width,
5793
+ strokeDashArray: offsetLine.style === "dashed" ? [offsetLine.dashLength, offsetLine.dashLength] : void 0,
5794
+ selectable: false,
5795
+ evented: false,
5796
+ originX: "left",
5797
+ originY: "top"
5798
+ }
5799
+ });
5800
+ }
5801
+ specs.push({
5802
+ id: ids.border,
5803
+ type: "path",
5804
+ space: "screen",
5805
+ data: { id: ids.border, type: "dieline" },
5806
+ props: {
5807
+ pathData: generateDielinePath({
5808
+ ...common,
5809
+ width: visualWidth,
5810
+ height: visualHeight,
5811
+ radius: visualRadius,
5812
+ x: cx,
5813
+ y: cy,
5814
+ features: absoluteFeatures
5815
+ }),
5816
+ fill: "transparent",
5817
+ stroke: mainLine.style === "hidden" ? null : mainLine.color,
5818
+ strokeWidth: mainLine.width,
5819
+ strokeDashArray: mainLine.style === "dashed" ? [mainLine.dashLength, mainLine.dashLength] : void 0,
5820
+ selectable: false,
5821
+ evented: false,
5822
+ originX: "left",
5823
+ originY: "top"
5824
+ }
5825
+ });
5826
+ if (!includeImageClipEffect) {
5827
+ return { specs, effects: [] };
5828
+ }
5829
+ const clipPathData = generateDielinePath({
5830
+ ...common,
5831
+ width: cutW,
5832
+ height: cutH,
5833
+ radius: cutR,
5834
+ x: cx,
5835
+ y: cy,
5836
+ features: cutFeatures
5837
+ });
5838
+ if (!clipPathData) {
5839
+ return { specs, effects: [] };
5840
+ }
5841
+ return {
5842
+ specs,
5843
+ effects: [
5844
+ {
5845
+ type: "clipPath",
5846
+ id: ids.clip,
5847
+ visibility: clipVisibility,
5848
+ targetPassIds: clipTargetPassIds,
5849
+ source: {
5850
+ id: ids.clipSource,
5851
+ type: "path",
5852
+ space: "screen",
5853
+ data: {
5854
+ id: ids.clipSource,
5855
+ type: "dieline-effect",
5856
+ effect: "clipPath"
5857
+ },
5858
+ props: {
5859
+ pathData: clipPathData,
5860
+ fill: "#000000",
5861
+ stroke: null,
5862
+ originX: "left",
5863
+ originY: "top",
5864
+ selectable: false,
5865
+ evented: false,
5866
+ excludeFromExport: true
5867
+ }
5868
+ }
5869
+ }
5870
+ ]
5871
+ };
5872
+ }
5873
+
5121
5874
  // src/extensions/dieline/DielineTool.ts
5122
5875
  var DielineTool = class {
5123
5876
  constructor(options) {
@@ -5125,30 +5878,7 @@ var DielineTool = class {
5125
5878
  this.metadata = {
5126
5879
  name: "DielineTool"
5127
5880
  };
5128
- this.state = {
5129
- shape: DEFAULT_DIELINE_SHAPE,
5130
- shapeStyle: { ...DEFAULT_DIELINE_SHAPE_STYLE },
5131
- width: 500,
5132
- height: 500,
5133
- radius: 0,
5134
- offset: 0,
5135
- padding: 140,
5136
- mainLine: {
5137
- width: 2.7,
5138
- color: "#FF0000",
5139
- dashLength: 5,
5140
- style: "solid"
5141
- },
5142
- offsetLine: {
5143
- width: 2.7,
5144
- color: "#FF0000",
5145
- dashLength: 5,
5146
- style: "solid"
5147
- },
5148
- insideColor: "rgba(0,0,0,0)",
5149
- showBleedLines: true,
5150
- features: []
5151
- };
5881
+ this.state = createDefaultDielineState();
5152
5882
  this.specs = [];
5153
5883
  this.effects = [];
5154
5884
  this.renderSeq = 0;
@@ -5187,156 +5917,34 @@ var DielineTool = class {
5187
5917
  this.renderProducerDisposable = this.canvasService.registerRenderProducer(
5188
5918
  this.id,
5189
5919
  () => ({
5190
- passes: [
5191
- {
5192
- id: DIELINE_LAYER_ID,
5193
- stack: 700,
5194
- order: 0,
5195
- replace: true,
5196
- visibility: {
5197
- op: "not",
5198
- expr: {
5199
- op: "activeToolIn",
5200
- ids: ["pooder.kit.image", "pooder.kit.white-ink"]
5201
- }
5202
- },
5203
- effects: this.effects,
5204
- objects: this.specs
5205
- }
5206
- ]
5207
- }),
5208
- { priority: 250 }
5209
- );
5210
- const configService = context.services.get(
5211
- "ConfigurationService"
5212
- );
5213
- if (configService) {
5214
- const s = this.state;
5215
- const sizeState = readSizeState(configService);
5216
- s.shape = normalizeDielineShape(
5217
- configService.get("dieline.shape", s.shape),
5218
- s.shape
5219
- );
5220
- s.shapeStyle = normalizeShapeStyle(
5221
- configService.get("dieline.shapeStyle", s.shapeStyle),
5222
- s.shapeStyle
5223
- );
5224
- s.width = sizeState.actualWidthMm;
5225
- s.height = sizeState.actualHeightMm;
5226
- s.radius = parseLengthToMm(
5227
- configService.get("dieline.radius", s.radius),
5228
- "mm"
5229
- );
5230
- s.padding = sizeState.viewPadding;
5231
- s.offset = sizeState.cutMode === "outset" ? sizeState.cutMarginMm : sizeState.cutMode === "inset" ? -sizeState.cutMarginMm : 0;
5232
- s.mainLine.width = configService.get(
5233
- "dieline.strokeWidth",
5234
- s.mainLine.width
5235
- );
5236
- s.mainLine.color = configService.get(
5237
- "dieline.strokeColor",
5238
- s.mainLine.color
5239
- );
5240
- s.mainLine.dashLength = configService.get(
5241
- "dieline.dashLength",
5242
- s.mainLine.dashLength
5243
- );
5244
- s.mainLine.style = configService.get("dieline.style", s.mainLine.style);
5245
- s.offsetLine.width = configService.get(
5246
- "dieline.offsetStrokeWidth",
5247
- s.offsetLine.width
5248
- );
5249
- s.offsetLine.color = configService.get(
5250
- "dieline.offsetStrokeColor",
5251
- s.offsetLine.color
5252
- );
5253
- s.offsetLine.dashLength = configService.get(
5254
- "dieline.offsetDashLength",
5255
- s.offsetLine.dashLength
5256
- );
5257
- s.offsetLine.style = configService.get(
5258
- "dieline.offsetStyle",
5259
- s.offsetLine.style
5260
- );
5261
- s.insideColor = configService.get("dieline.insideColor", s.insideColor);
5262
- s.showBleedLines = configService.get(
5263
- "dieline.showBleedLines",
5264
- s.showBleedLines
5265
- );
5266
- s.features = configService.get("dieline.features", s.features);
5267
- s.pathData = configService.get("dieline.pathData", s.pathData);
5268
- const sourceWidth = Number(
5269
- configService.get("dieline.customSourceWidthPx", 0)
5270
- );
5271
- const sourceHeight = Number(
5272
- configService.get("dieline.customSourceHeightPx", 0)
5273
- );
5274
- s.customSourceWidthPx = Number.isFinite(sourceWidth) && sourceWidth > 0 ? sourceWidth : void 0;
5275
- s.customSourceHeightPx = Number.isFinite(sourceHeight) && sourceHeight > 0 ? sourceHeight : void 0;
5276
- configService.onAnyChange((e) => {
5277
- if (e.key.startsWith("size.")) {
5278
- const nextSize = readSizeState(configService);
5279
- s.width = nextSize.actualWidthMm;
5280
- s.height = nextSize.actualHeightMm;
5281
- s.padding = nextSize.viewPadding;
5282
- s.offset = nextSize.cutMode === "outset" ? nextSize.cutMarginMm : nextSize.cutMode === "inset" ? -nextSize.cutMarginMm : 0;
5283
- this.updateDieline();
5284
- return;
5285
- }
5286
- if (e.key.startsWith("dieline.")) {
5287
- switch (e.key) {
5288
- case "dieline.shape":
5289
- s.shape = normalizeDielineShape(e.value, s.shape);
5290
- break;
5291
- case "dieline.shapeStyle":
5292
- s.shapeStyle = normalizeShapeStyle(e.value, s.shapeStyle);
5293
- break;
5294
- case "dieline.radius":
5295
- s.radius = parseLengthToMm(e.value, "mm");
5296
- break;
5297
- case "dieline.strokeWidth":
5298
- s.mainLine.width = e.value;
5299
- break;
5300
- case "dieline.strokeColor":
5301
- s.mainLine.color = e.value;
5302
- break;
5303
- case "dieline.dashLength":
5304
- s.mainLine.dashLength = e.value;
5305
- break;
5306
- case "dieline.style":
5307
- s.mainLine.style = e.value;
5308
- break;
5309
- case "dieline.offsetStrokeWidth":
5310
- s.offsetLine.width = e.value;
5311
- break;
5312
- case "dieline.offsetStrokeColor":
5313
- s.offsetLine.color = e.value;
5314
- break;
5315
- case "dieline.offsetDashLength":
5316
- s.offsetLine.dashLength = e.value;
5317
- break;
5318
- case "dieline.offsetStyle":
5319
- s.offsetLine.style = e.value;
5320
- break;
5321
- case "dieline.insideColor":
5322
- s.insideColor = e.value;
5323
- break;
5324
- case "dieline.showBleedLines":
5325
- s.showBleedLines = e.value;
5326
- break;
5327
- case "dieline.features":
5328
- s.features = e.value;
5329
- break;
5330
- case "dieline.pathData":
5331
- s.pathData = e.value;
5332
- break;
5333
- case "dieline.customSourceWidthPx":
5334
- s.customSourceWidthPx = Number.isFinite(Number(e.value)) && Number(e.value) > 0 ? Number(e.value) : void 0;
5335
- break;
5336
- case "dieline.customSourceHeightPx":
5337
- s.customSourceHeightPx = Number.isFinite(Number(e.value)) && Number(e.value) > 0 ? Number(e.value) : void 0;
5338
- break;
5920
+ passes: [
5921
+ {
5922
+ id: DIELINE_LAYER_ID,
5923
+ stack: 700,
5924
+ order: 0,
5925
+ replace: true,
5926
+ visibility: {
5927
+ op: "not",
5928
+ expr: {
5929
+ op: "activeToolIn",
5930
+ ids: ["pooder.kit.image", "pooder.kit.white-ink"]
5931
+ }
5932
+ },
5933
+ effects: this.effects,
5934
+ objects: this.specs
5339
5935
  }
5936
+ ]
5937
+ }),
5938
+ { priority: 250 }
5939
+ );
5940
+ const configService = context.services.get(
5941
+ "ConfigurationService"
5942
+ );
5943
+ if (configService) {
5944
+ Object.assign(this.state, readDielineState(configService, this.state));
5945
+ configService.onAnyChange((e) => {
5946
+ if (e.key.startsWith("size.") || e.key.startsWith("dieline.")) {
5947
+ Object.assign(this.state, readDielineState(configService, this.state));
5340
5948
  this.updateDieline();
5341
5949
  }
5342
5950
  });
@@ -5407,272 +6015,34 @@ var DielineTool = class {
5407
6015
  const items = configService.get("image.items", []);
5408
6016
  return Array.isArray(items) && items.length > 0;
5409
6017
  }
5410
- syncSizeState(configService) {
5411
- const sizeState = readSizeState(configService);
5412
- this.state.width = sizeState.actualWidthMm;
5413
- this.state.height = sizeState.actualHeightMm;
5414
- this.state.padding = sizeState.viewPadding;
5415
- this.state.offset = sizeState.cutMode === "outset" ? sizeState.cutMarginMm : sizeState.cutMode === "inset" ? -sizeState.cutMarginMm : 0;
5416
- }
5417
6018
  buildDielineSpecs(sceneLayout) {
5418
6019
  var _a, _b;
5419
- const {
5420
- shape,
5421
- shapeStyle,
5422
- radius,
5423
- mainLine,
5424
- offsetLine,
5425
- insideColor,
5426
- showBleedLines,
5427
- features
5428
- } = this.state;
5429
6020
  const hasImages = this.hasImageItems();
5430
- const canvasW = sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
5431
- const canvasH = sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
5432
- const scale = sceneLayout.scale;
5433
- const cx = sceneLayout.trimRect.centerX;
5434
- const cy = sceneLayout.trimRect.centerY;
5435
- const visualWidth = sceneLayout.trimRect.width;
5436
- const visualHeight = sceneLayout.trimRect.height;
5437
- const visualRadius = radius * scale;
5438
- const cutW = sceneLayout.cutRect.width;
5439
- const cutH = sceneLayout.cutRect.height;
5440
- const visualOffset = (cutW - visualWidth) / 2;
5441
- const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
5442
- const absoluteFeatures = (features || []).map((f) => ({
5443
- ...f,
5444
- x: f.x,
5445
- y: f.y,
5446
- width: (f.width || 0) * scale,
5447
- height: (f.height || 0) * scale,
5448
- radius: (f.radius || 0) * scale
5449
- }));
5450
- const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
5451
- const specs = [];
5452
- if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)" && !hasImages) {
5453
- const productPathData = generateDielinePath({
5454
- shape,
5455
- width: cutW,
5456
- height: cutH,
5457
- radius: cutR,
5458
- x: cx,
5459
- y: cy,
5460
- features: cutFeatures,
5461
- shapeStyle,
5462
- pathData: this.state.pathData,
5463
- customSourceWidthPx: this.state.customSourceWidthPx,
5464
- customSourceHeightPx: this.state.customSourceHeightPx,
5465
- canvasWidth: canvasW,
5466
- canvasHeight: canvasH
5467
- });
5468
- specs.push({
5469
- id: "dieline.inside",
5470
- type: "path",
5471
- space: "screen",
5472
- data: { id: "dieline.inside", type: "dieline" },
5473
- props: {
5474
- pathData: productPathData,
5475
- fill: insideColor,
5476
- stroke: null,
5477
- selectable: false,
5478
- evented: false,
5479
- originX: "left",
5480
- originY: "top"
5481
- }
5482
- });
5483
- }
5484
- if (Math.abs(visualOffset) > 1e-4) {
5485
- const bleedPathData = generateBleedZonePath(
5486
- {
5487
- shape,
5488
- width: visualWidth,
5489
- height: visualHeight,
5490
- radius: visualRadius,
5491
- x: cx,
5492
- y: cy,
5493
- features: cutFeatures,
5494
- shapeStyle,
5495
- pathData: this.state.pathData,
5496
- customSourceWidthPx: this.state.customSourceWidthPx,
5497
- customSourceHeightPx: this.state.customSourceHeightPx,
5498
- canvasWidth: canvasW,
5499
- canvasHeight: canvasH
5500
- },
5501
- {
5502
- shape,
5503
- width: cutW,
5504
- height: cutH,
5505
- radius: cutR,
5506
- x: cx,
5507
- y: cy,
5508
- features: cutFeatures,
5509
- shapeStyle,
5510
- pathData: this.state.pathData,
5511
- customSourceWidthPx: this.state.customSourceWidthPx,
5512
- customSourceHeightPx: this.state.customSourceHeightPx,
5513
- canvasWidth: canvasW,
5514
- canvasHeight: canvasH
5515
- },
5516
- visualOffset
5517
- );
5518
- if (showBleedLines !== false) {
5519
- const pattern = this.createHatchPattern(mainLine.color);
5520
- if (pattern) {
5521
- specs.push({
5522
- id: "dieline.bleed-zone",
5523
- type: "path",
5524
- space: "screen",
5525
- data: { id: "dieline.bleed-zone", type: "dieline" },
5526
- props: {
5527
- pathData: bleedPathData,
5528
- fill: pattern,
5529
- stroke: null,
5530
- selectable: false,
5531
- evented: false,
5532
- objectCaching: false,
5533
- originX: "left",
5534
- originY: "top"
5535
- }
5536
- });
5537
- }
5538
- }
5539
- const offsetPathData = generateDielinePath({
5540
- shape,
5541
- width: cutW,
5542
- height: cutH,
5543
- radius: cutR,
5544
- x: cx,
5545
- y: cy,
5546
- features: cutFeatures,
5547
- shapeStyle,
5548
- pathData: this.state.pathData,
5549
- customSourceWidthPx: this.state.customSourceWidthPx,
5550
- customSourceHeightPx: this.state.customSourceHeightPx,
5551
- canvasWidth: canvasW,
5552
- canvasHeight: canvasH
5553
- });
5554
- specs.push({
5555
- id: "dieline.offset-border",
5556
- type: "path",
5557
- space: "screen",
5558
- data: { id: "dieline.offset-border", type: "dieline" },
5559
- props: {
5560
- pathData: offsetPathData,
5561
- fill: null,
5562
- stroke: offsetLine.style === "hidden" ? null : offsetLine.color,
5563
- strokeWidth: offsetLine.width,
5564
- strokeDashArray: offsetLine.style === "dashed" ? [offsetLine.dashLength, offsetLine.dashLength] : void 0,
5565
- selectable: false,
5566
- evented: false,
5567
- originX: "left",
5568
- originY: "top"
5569
- }
5570
- });
5571
- }
5572
- const borderPathData = generateDielinePath({
5573
- shape,
5574
- width: visualWidth,
5575
- height: visualHeight,
5576
- radius: visualRadius,
5577
- x: cx,
5578
- y: cy,
5579
- features: absoluteFeatures,
5580
- shapeStyle,
5581
- pathData: this.state.pathData,
5582
- customSourceWidthPx: this.state.customSourceWidthPx,
5583
- customSourceHeightPx: this.state.customSourceHeightPx,
5584
- canvasWidth: canvasW,
5585
- canvasHeight: canvasH
5586
- });
5587
- specs.push({
5588
- id: "dieline.border",
5589
- type: "path",
5590
- space: "screen",
5591
- data: { id: "dieline.border", type: "dieline" },
5592
- props: {
5593
- pathData: borderPathData,
5594
- fill: "transparent",
5595
- stroke: mainLine.style === "hidden" ? null : mainLine.color,
5596
- strokeWidth: mainLine.width,
5597
- strokeDashArray: mainLine.style === "dashed" ? [mainLine.dashLength, mainLine.dashLength] : void 0,
5598
- selectable: false,
5599
- evented: false,
5600
- originX: "left",
5601
- originY: "top"
5602
- }
5603
- });
5604
- return specs;
6021
+ return buildDielineRenderBundle({
6022
+ state: this.state,
6023
+ sceneLayout,
6024
+ canvasWidth: sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800,
6025
+ canvasHeight: sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600,
6026
+ hasImages,
6027
+ createHatchPattern: (color) => this.createHatchPattern(color),
6028
+ includeImageClipEffect: false
6029
+ }).specs;
5605
6030
  }
5606
6031
  buildImageClipEffects(sceneLayout) {
5607
6032
  var _a, _b;
5608
- const { shape, shapeStyle, radius, features } = this.state;
5609
- const canvasW = sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
5610
- const canvasH = sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
5611
- const scale = sceneLayout.scale;
5612
- const cx = sceneLayout.trimRect.centerX;
5613
- const cy = sceneLayout.trimRect.centerY;
5614
- const visualWidth = sceneLayout.trimRect.width;
5615
- const visualRadius = radius * scale;
5616
- const cutW = sceneLayout.cutRect.width;
5617
- const cutH = sceneLayout.cutRect.height;
5618
- const visualOffset = (cutW - visualWidth) / 2;
5619
- const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
5620
- const absoluteFeatures = (features || []).map((f) => ({
5621
- ...f,
5622
- x: f.x,
5623
- y: f.y,
5624
- width: (f.width || 0) * scale,
5625
- height: (f.height || 0) * scale,
5626
- radius: (f.radius || 0) * scale
5627
- }));
5628
- const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
5629
- const clipPathData = generateDielinePath({
5630
- shape,
5631
- width: cutW,
5632
- height: cutH,
5633
- radius: cutR,
5634
- x: cx,
5635
- y: cy,
5636
- features: cutFeatures,
5637
- shapeStyle,
5638
- pathData: this.state.pathData,
5639
- customSourceWidthPx: this.state.customSourceWidthPx,
5640
- customSourceHeightPx: this.state.customSourceHeightPx,
5641
- canvasWidth: canvasW,
5642
- canvasHeight: canvasH
5643
- });
5644
- if (!clipPathData) return [];
5645
- return [
5646
- {
5647
- type: "clipPath",
5648
- id: "dieline.clip.image",
5649
- visibility: {
5650
- op: "not",
5651
- expr: { op: "anySessionActive" }
5652
- },
5653
- targetPassIds: [IMAGE_OBJECT_LAYER_ID],
5654
- source: {
5655
- id: "dieline.effect.clip-path",
5656
- type: "path",
5657
- space: "screen",
5658
- data: {
5659
- id: "dieline.effect.clip-path",
5660
- type: "dieline-effect",
5661
- effect: "clipPath"
5662
- },
5663
- props: {
5664
- pathData: clipPathData,
5665
- fill: "#000000",
5666
- stroke: null,
5667
- originX: "left",
5668
- originY: "top",
5669
- selectable: false,
5670
- evented: false,
5671
- excludeFromExport: true
5672
- }
5673
- }
5674
- }
5675
- ];
6033
+ return buildDielineRenderBundle({
6034
+ state: this.state,
6035
+ sceneLayout,
6036
+ canvasWidth: sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800,
6037
+ canvasHeight: sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600,
6038
+ hasImages: this.hasImageItems(),
6039
+ includeImageClipEffect: true,
6040
+ clipTargetPassIds: [IMAGE_OBJECT_LAYER_ID],
6041
+ clipVisibility: {
6042
+ op: "not",
6043
+ expr: { op: "anySessionActive" }
6044
+ }
6045
+ }).effects;
5676
6046
  }
5677
6047
  updateDieline(_emitEvent = true) {
5678
6048
  void this.updateDielineAsync();
@@ -5682,7 +6052,7 @@ var DielineTool = class {
5682
6052
  const configService = this.getConfigService();
5683
6053
  if (!configService) return;
5684
6054
  const seq = ++this.renderSeq;
5685
- this.syncSizeState(configService);
6055
+ Object.assign(this.state, readDielineState(configService, this.state));
5686
6056
  const sceneLayout = computeSceneLayout(
5687
6057
  this.canvasService,
5688
6058
  readSizeState(configService)
@@ -5737,7 +6107,7 @@ var DielineTool = class {
5737
6107
  );
5738
6108
  return null;
5739
6109
  }
5740
- this.syncSizeState(configService);
6110
+ this.state = readDielineState(configService, this.state);
5741
6111
  const sceneLayout = computeSceneLayout(
5742
6112
  this.canvasService,
5743
6113
  readSizeState(configService)
@@ -5889,6 +6259,7 @@ var DielineTool = class {
5889
6259
 
5890
6260
  // src/extensions/feature/FeatureTool.ts
5891
6261
  var import_core5 = require("@pooder/core");
6262
+ var import_fabric4 = require("fabric");
5892
6263
 
5893
6264
  // src/extensions/constraints.ts
5894
6265
  var ConstraintRegistry = class {
@@ -6103,7 +6474,9 @@ var FeatureTool = class {
6103
6474
  this.isFeatureSessionActive = false;
6104
6475
  this.sessionOriginalFeatures = null;
6105
6476
  this.hasWorkingChanges = false;
6106
- this.specs = [];
6477
+ this.markerSpecs = [];
6478
+ this.sessionDielineSpecs = [];
6479
+ this.sessionDielineEffects = [];
6107
6480
  this.renderSeq = 0;
6108
6481
  this.subscriptions = new SubscriptionBag();
6109
6482
  this.handleMoving = null;
@@ -6113,7 +6486,7 @@ var FeatureTool = class {
6113
6486
  this.onToolActivated = (event) => {
6114
6487
  this.isToolActive = event.id === this.id;
6115
6488
  if (!this.isToolActive) {
6116
- this.restoreSessionFeaturesToConfig();
6489
+ this.suspendFeatureSession();
6117
6490
  }
6118
6491
  this.updateVisibility();
6119
6492
  };
@@ -6133,16 +6506,38 @@ var FeatureTool = class {
6133
6506
  (_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
6134
6507
  this.renderProducerDisposable = this.canvasService.registerRenderProducer(
6135
6508
  this.id,
6136
- () => ({
6137
- passes: [
6509
+ () => {
6510
+ const passes = [
6138
6511
  {
6139
6512
  id: FEATURE_OVERLAY_LAYER_ID,
6140
6513
  stack: 880,
6141
6514
  order: 0,
6142
- objects: this.specs
6515
+ replace: true,
6516
+ objects: this.markerSpecs
6143
6517
  }
6144
- ]
6145
- }),
6518
+ ];
6519
+ if (this.isSessionVisible()) {
6520
+ passes.push(
6521
+ {
6522
+ id: DIELINE_LAYER_ID,
6523
+ stack: 700,
6524
+ order: 0,
6525
+ replace: false,
6526
+ visibility: { op: "const", value: false },
6527
+ objects: []
6528
+ },
6529
+ {
6530
+ id: FEATURE_DIELINE_LAYER_ID,
6531
+ stack: 705,
6532
+ order: 0,
6533
+ replace: true,
6534
+ effects: this.sessionDielineEffects,
6535
+ objects: this.sessionDielineSpecs
6536
+ }
6537
+ );
6538
+ }
6539
+ return { passes };
6540
+ },
6146
6541
  { priority: 350 }
6147
6542
  );
6148
6543
  const configService = context.services.get(
@@ -6157,12 +6552,22 @@ var FeatureTool = class {
6157
6552
  (e) => {
6158
6553
  if (this.isUpdatingConfig) return;
6159
6554
  if (e.key === "dieline.features") {
6160
- if (this.isFeatureSessionActive) return;
6555
+ if (this.isFeatureSessionActive && this.hasFeatureSessionDraft()) {
6556
+ return;
6557
+ }
6558
+ if (this.hasFeatureSessionDraft()) {
6559
+ this.clearFeatureSessionState();
6560
+ }
6161
6561
  const next = e.value || [];
6162
6562
  this.workingFeatures = this.cloneFeatures(next);
6163
6563
  this.hasWorkingChanges = false;
6164
6564
  this.redraw();
6165
6565
  this.emitWorkingChange();
6566
+ return;
6567
+ }
6568
+ if (e.key.startsWith("size.") || e.key.startsWith("dieline.")) {
6569
+ void this.refreshGeometry();
6570
+ this.redraw({ enforceConstraints: true });
6166
6571
  }
6167
6572
  }
6168
6573
  );
@@ -6178,7 +6583,8 @@ var FeatureTool = class {
6178
6583
  deactivate(context) {
6179
6584
  var _a;
6180
6585
  this.subscriptions.disposeAll();
6181
- this.restoreSessionFeaturesToConfig();
6586
+ this.restoreCommittedFeaturesToConfig();
6587
+ this.clearFeatureSessionState();
6182
6588
  (_a = this.dirtyTrackerDisposable) == null ? void 0 : _a.dispose();
6183
6589
  this.dirtyTrackerDisposable = void 0;
6184
6590
  this.teardown();
@@ -6188,6 +6594,9 @@ var FeatureTool = class {
6188
6594
  updateVisibility() {
6189
6595
  this.redraw();
6190
6596
  }
6597
+ isSessionVisible() {
6598
+ return this.isToolActive && this.isFeatureSessionActive;
6599
+ }
6191
6600
  contribute() {
6192
6601
  return {
6193
6602
  [import_core5.ContributionPointIds.TOOLS]: [
@@ -6214,15 +6623,16 @@ var FeatureTool = class {
6214
6623
  if (this.isFeatureSessionActive) {
6215
6624
  return { ok: true };
6216
6625
  }
6217
- const original = this.getCommittedFeatures();
6218
- this.sessionOriginalFeatures = this.cloneFeatures(original);
6626
+ if (!this.hasFeatureSessionDraft()) {
6627
+ const original = this.getCommittedFeatures();
6628
+ this.sessionOriginalFeatures = this.cloneFeatures(original);
6629
+ this.setWorkingFeatures(this.cloneFeatures(original));
6630
+ this.hasWorkingChanges = false;
6631
+ }
6219
6632
  this.isFeatureSessionActive = true;
6220
6633
  await this.refreshGeometry();
6221
- this.setWorkingFeatures(this.cloneFeatures(original));
6222
- this.hasWorkingChanges = false;
6223
6634
  this.redraw();
6224
6635
  this.emitWorkingChange();
6225
- this.updateCommittedFeatures([]);
6226
6636
  return { ok: true };
6227
6637
  }
6228
6638
  },
@@ -6258,25 +6668,6 @@ var FeatureTool = class {
6258
6668
  return true;
6259
6669
  }
6260
6670
  },
6261
- {
6262
- command: "getWorkingFeatures",
6263
- title: "Get Working Features",
6264
- handler: () => {
6265
- return this.cloneFeatures(this.workingFeatures);
6266
- }
6267
- },
6268
- {
6269
- command: "setWorkingFeatures",
6270
- title: "Set Working Features",
6271
- handler: async (features) => {
6272
- await this.refreshGeometry();
6273
- this.setWorkingFeatures(this.cloneFeatures(features || []));
6274
- this.hasWorkingChanges = true;
6275
- this.redraw();
6276
- this.emitWorkingChange();
6277
- return { ok: true };
6278
- }
6279
- },
6280
6671
  {
6281
6672
  command: "rollbackFeatureSession",
6282
6673
  title: "Rollback Feature Session",
@@ -6343,17 +6734,24 @@ var FeatureTool = class {
6343
6734
  this.isUpdatingConfig = false;
6344
6735
  }
6345
6736
  }
6737
+ hasFeatureSessionDraft() {
6738
+ return Array.isArray(this.sessionOriginalFeatures);
6739
+ }
6346
6740
  clearFeatureSessionState() {
6347
6741
  this.isFeatureSessionActive = false;
6348
6742
  this.sessionOriginalFeatures = null;
6349
6743
  }
6350
- restoreSessionFeaturesToConfig() {
6351
- if (!this.isFeatureSessionActive) return;
6744
+ restoreCommittedFeaturesToConfig() {
6745
+ if (!this.hasFeatureSessionDraft()) return;
6352
6746
  const original = this.cloneFeatures(
6353
6747
  this.sessionOriginalFeatures || this.getCommittedFeatures()
6354
6748
  );
6355
6749
  this.updateCommittedFeatures(original);
6356
- this.clearFeatureSessionState();
6750
+ }
6751
+ suspendFeatureSession() {
6752
+ if (!this.isFeatureSessionActive) return;
6753
+ this.restoreCommittedFeaturesToConfig();
6754
+ this.isFeatureSessionActive = false;
6357
6755
  }
6358
6756
  emitWorkingChange() {
6359
6757
  var _a;
@@ -6375,7 +6773,7 @@ var FeatureTool = class {
6375
6773
  }
6376
6774
  async resetWorkingFeaturesFromSource() {
6377
6775
  const next = this.cloneFeatures(
6378
- this.isFeatureSessionActive && this.sessionOriginalFeatures ? this.sessionOriginalFeatures : this.getCommittedFeatures()
6776
+ this.sessionOriginalFeatures || this.getCommittedFeatures()
6379
6777
  );
6380
6778
  await this.refreshGeometry();
6381
6779
  this.setWorkingFeatures(next);
@@ -6599,11 +6997,35 @@ var FeatureTool = class {
6599
6997
  this.handleSceneGeometryChange = null;
6600
6998
  }
6601
6999
  this.renderSeq += 1;
6602
- this.specs = [];
7000
+ this.markerSpecs = [];
7001
+ this.sessionDielineSpecs = [];
7002
+ this.sessionDielineEffects = [];
6603
7003
  (_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
6604
7004
  this.renderProducerDisposable = void 0;
6605
7005
  void this.canvasService.flushRenderFromProducers();
6606
7006
  }
7007
+ createHatchPattern(color = "rgba(0, 0, 0, 0.3)") {
7008
+ if (typeof document === "undefined") {
7009
+ return void 0;
7010
+ }
7011
+ const size = 20;
7012
+ const canvas = document.createElement("canvas");
7013
+ canvas.width = size;
7014
+ canvas.height = size;
7015
+ const ctx = canvas.getContext("2d");
7016
+ if (ctx) {
7017
+ ctx.clearRect(0, 0, size, size);
7018
+ ctx.strokeStyle = color;
7019
+ ctx.lineWidth = 1;
7020
+ ctx.beginPath();
7021
+ ctx.moveTo(0, size);
7022
+ ctx.lineTo(size, 0);
7023
+ ctx.stroke();
7024
+ }
7025
+ return new import_fabric4.Pattern({
7026
+ source: canvas
7027
+ });
7028
+ }
6607
7029
  getDraggableMarkerTarget(target) {
6608
7030
  var _a, _b;
6609
7031
  if (!this.isFeatureSessionActive || !this.isToolActive) return null;
@@ -6679,6 +7101,7 @@ var FeatureTool = class {
6679
7101
  next[index] = updatedFeature;
6680
7102
  this.setWorkingFeatures(next);
6681
7103
  this.hasWorkingChanges = true;
7104
+ this.redraw();
6682
7105
  this.emitWorkingChange();
6683
7106
  }
6684
7107
  syncGroupFromCanvas(target) {
@@ -6717,6 +7140,7 @@ var FeatureTool = class {
6717
7140
  if (!changed) return;
6718
7141
  this.setWorkingFeatures(next);
6719
7142
  this.hasWorkingChanges = true;
7143
+ this.redraw();
6720
7144
  this.emitWorkingChange();
6721
7145
  }
6722
7146
  redraw(options = {}) {
@@ -6725,7 +7149,10 @@ var FeatureTool = class {
6725
7149
  async redrawAsync(options = {}) {
6726
7150
  if (!this.canvasService) return;
6727
7151
  const seq = ++this.renderSeq;
6728
- this.specs = this.buildFeatureSpecs();
7152
+ this.markerSpecs = this.buildMarkerSpecs();
7153
+ const sessionRender = this.buildSessionDielineRender();
7154
+ this.sessionDielineSpecs = sessionRender.specs;
7155
+ this.sessionDielineEffects = sessionRender.effects;
6729
7156
  if (seq !== this.renderSeq) return;
6730
7157
  await this.canvasService.flushRenderFromProducers();
6731
7158
  if (seq !== this.renderSeq) return;
@@ -6733,7 +7160,49 @@ var FeatureTool = class {
6733
7160
  this.enforceConstraints();
6734
7161
  }
6735
7162
  }
6736
- buildFeatureSpecs() {
7163
+ buildSessionDielineRender() {
7164
+ if (!this.isSessionVisible() || !this.canvasService) {
7165
+ return { specs: [], effects: [] };
7166
+ }
7167
+ const configService = this.getConfigService();
7168
+ if (!configService) {
7169
+ return { specs: [], effects: [] };
7170
+ }
7171
+ const sceneLayout = computeSceneLayout(
7172
+ this.canvasService,
7173
+ readSizeState(configService)
7174
+ );
7175
+ if (!sceneLayout) {
7176
+ return { specs: [], effects: [] };
7177
+ }
7178
+ const state = readDielineState(configService);
7179
+ state.features = this.cloneFeatures(this.workingFeatures);
7180
+ return buildDielineRenderBundle({
7181
+ state,
7182
+ sceneLayout,
7183
+ canvasWidth: sceneLayout.canvasWidth || this.canvasService.canvas.width || 800,
7184
+ canvasHeight: sceneLayout.canvasHeight || this.canvasService.canvas.height || 600,
7185
+ hasImages: this.hasImageItems(),
7186
+ createHatchPattern: (color) => this.createHatchPattern(color),
7187
+ clipTargetPassIds: [IMAGE_OBJECT_LAYER_ID],
7188
+ clipVisibility: { op: "const", value: true },
7189
+ ids: {
7190
+ inside: "feature.session.dieline.inside",
7191
+ bleedZone: "feature.session.dieline.bleed-zone",
7192
+ offsetBorder: "feature.session.dieline.offset-border",
7193
+ border: "feature.session.dieline.border",
7194
+ clip: "feature.session.dieline.clip.image",
7195
+ clipSource: "feature.session.dieline.effect.clip-path"
7196
+ }
7197
+ });
7198
+ }
7199
+ hasImageItems() {
7200
+ const configService = this.getConfigService();
7201
+ if (!configService) return false;
7202
+ const items = configService.get("image.items", []);
7203
+ return Array.isArray(items) && items.length > 0;
7204
+ }
7205
+ buildMarkerSpecs() {
6737
7206
  if (!this.isFeatureSessionActive || !this.currentGeometry || this.workingFeatures.length === 0) {
6738
7207
  return [];
6739
7208
  }
@@ -7006,7 +7475,7 @@ var FeatureTool = class {
7006
7475
 
7007
7476
  // src/extensions/film/FilmTool.ts
7008
7477
  var import_core6 = require("@pooder/core");
7009
- var import_fabric4 = require("fabric");
7478
+ var import_fabric5 = require("fabric");
7010
7479
  var FILM_IMAGE_ID = "film-image";
7011
7480
  var DEFAULT_WIDTH2 = 800;
7012
7481
  var DEFAULT_HEIGHT2 = 600;
@@ -7180,7 +7649,7 @@ var FilmTool = class {
7180
7649
  }
7181
7650
  async loadImageSize(src) {
7182
7651
  try {
7183
- const image = await import_fabric4.FabricImage.fromURL(src, {
7652
+ const image = await import_fabric5.FabricImage.fromURL(src, {
7184
7653
  crossOrigin: "anonymous"
7185
7654
  });
7186
7655
  const width = Number((image == null ? void 0 : image.width) || 0);
@@ -9175,7 +9644,7 @@ var SceneLayoutService = class {
9175
9644
  };
9176
9645
 
9177
9646
  // src/services/CanvasService.ts
9178
- var import_fabric5 = require("fabric");
9647
+ var import_fabric6 = require("fabric");
9179
9648
  var import_core11 = require("@pooder/core");
9180
9649
 
9181
9650
  // src/services/ViewportSystem.ts
@@ -9351,10 +9820,10 @@ var CanvasService = class {
9351
9820
  if (this.producerApplyInProgress) return;
9352
9821
  this.scheduleManagedPassVisibilityRefresh();
9353
9822
  };
9354
- if (el instanceof import_fabric5.Canvas) {
9823
+ if (el instanceof import_fabric6.Canvas) {
9355
9824
  this.canvas = el;
9356
9825
  } else {
9357
- this.canvas = new import_fabric5.Canvas(el, {
9826
+ this.canvas = new import_fabric6.Canvas(el, {
9358
9827
  preserveObjectStacking: true,
9359
9828
  ...options
9360
9829
  });
@@ -10249,7 +10718,7 @@ var CanvasService = class {
10249
10718
  var _a, _b;
10250
10719
  if (spec.type === "rect") {
10251
10720
  const props = this.resolveFabricProps(spec, spec.props || {});
10252
- const rect = new import_fabric5.Rect({
10721
+ const rect = new import_fabric6.Rect({
10253
10722
  ...props,
10254
10723
  data: { ...spec.data || {}, id: spec.id }
10255
10724
  });
@@ -10260,7 +10729,7 @@ var CanvasService = class {
10260
10729
  const pathData = this.readPathDataFromSpec(spec);
10261
10730
  if (!pathData) return void 0;
10262
10731
  const props = this.resolveFabricProps(spec, spec.props || {});
10263
- const path = new import_fabric5.Path(pathData, {
10732
+ const path = new import_fabric6.Path(pathData, {
10264
10733
  ...props,
10265
10734
  data: { ...spec.data || {}, id: spec.id }
10266
10735
  });
@@ -10269,7 +10738,7 @@ var CanvasService = class {
10269
10738
  }
10270
10739
  if (spec.type === "image") {
10271
10740
  if (!spec.src) return void 0;
10272
- const image = await import_fabric5.Image.fromURL(spec.src, { crossOrigin: "anonymous" });
10741
+ const image = await import_fabric6.Image.fromURL(spec.src, { crossOrigin: "anonymous" });
10273
10742
  const props = this.resolveFabricProps(spec, spec.props || {});
10274
10743
  image.set({
10275
10744
  ...props,
@@ -10281,7 +10750,7 @@ var CanvasService = class {
10281
10750
  if (spec.type === "text") {
10282
10751
  const content = String((_b = (_a = spec.props) == null ? void 0 : _a.text) != null ? _b : "");
10283
10752
  const props = this.resolveFabricProps(spec, spec.props || {});
10284
- const text = new import_fabric5.Text(content, {
10753
+ const text = new import_fabric6.Text(content, {
10285
10754
  ...props,
10286
10755
  data: { ...spec.data || {}, id: spec.id }
10287
10756
  });
@@ -10307,11 +10776,13 @@ var CanvasService = class {
10307
10776
  WhiteInkTool,
10308
10777
  computeImageCoverScale,
10309
10778
  computeWhiteInkCoverScale,
10779
+ createDefaultDielineState,
10310
10780
  createDielineCommands,
10311
10781
  createDielineConfigurations,
10312
10782
  createImageCommands,
10313
10783
  createImageConfigurations,
10314
10784
  createWhiteInkCommands,
10315
10785
  createWhiteInkConfigurations,
10316
- evaluateVisibilityExpr
10786
+ evaluateVisibilityExpr,
10787
+ readDielineState
10317
10788
  });