@pooder/kit 6.3.0 → 6.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.test-dist/src/extensions/image/ImageTool.js +116 -0
- package/.test-dist/src/extensions/image/commands.js +9 -1
- package/.test-dist/src/extensions/image/config.js +7 -0
- package/.test-dist/src/extensions/image/imagePlacement.js +44 -0
- package/.test-dist/tests/run.js +12 -4
- package/CHANGELOG.md +8 -0
- package/dist/index.d.mts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +195 -4
- package/dist/index.mjs +195 -4
- package/package.json +2 -2
- package/src/extensions/image/ImageTool.ts +180 -1
- package/src/extensions/image/commands.ts +9 -1
- package/src/extensions/image/config.ts +7 -0
- package/src/extensions/image/imagePlacement.ts +78 -0
- package/tests/run.ts +48 -15
package/dist/index.js
CHANGED
|
@@ -2383,12 +2383,20 @@ function createImageCommands(tool) {
|
|
|
2383
2383
|
tool.resetImageSession();
|
|
2384
2384
|
}
|
|
2385
2385
|
},
|
|
2386
|
+
{
|
|
2387
|
+
command: "validateImageSession",
|
|
2388
|
+
id: "validateImageSession",
|
|
2389
|
+
title: "Validate Image Session",
|
|
2390
|
+
handler: async () => {
|
|
2391
|
+
return await tool.validateImageSession();
|
|
2392
|
+
}
|
|
2393
|
+
},
|
|
2386
2394
|
{
|
|
2387
2395
|
command: "completeImages",
|
|
2388
2396
|
id: "completeImages",
|
|
2389
2397
|
title: "Complete Images",
|
|
2390
2398
|
handler: async () => {
|
|
2391
|
-
return await tool.
|
|
2399
|
+
return await tool.completeImageSession();
|
|
2392
2400
|
}
|
|
2393
2401
|
},
|
|
2394
2402
|
{
|
|
@@ -2633,6 +2641,13 @@ function createImageConfigurations() {
|
|
|
2633
2641
|
type: "color",
|
|
2634
2642
|
label: "Image Frame Outer Background",
|
|
2635
2643
|
default: "#f5f5f5"
|
|
2644
|
+
},
|
|
2645
|
+
{
|
|
2646
|
+
id: "image.session.placementPolicy",
|
|
2647
|
+
type: "select",
|
|
2648
|
+
label: "Image Session Placement Policy",
|
|
2649
|
+
options: ["free", "warn", "strict"],
|
|
2650
|
+
default: "free"
|
|
2636
2651
|
}
|
|
2637
2652
|
];
|
|
2638
2653
|
}
|
|
@@ -2709,6 +2724,44 @@ function computeImageOperationUpdates(args) {
|
|
|
2709
2724
|
};
|
|
2710
2725
|
}
|
|
2711
2726
|
|
|
2727
|
+
// src/extensions/image/imagePlacement.ts
|
|
2728
|
+
function toRadians(angle) {
|
|
2729
|
+
return angle * Math.PI / 180;
|
|
2730
|
+
}
|
|
2731
|
+
function validateImagePlacement(args) {
|
|
2732
|
+
const { frame, source, placement } = args;
|
|
2733
|
+
if (frame.width <= 0 || frame.height <= 0 || source.width <= 0 || source.height <= 0) {
|
|
2734
|
+
return { ok: true };
|
|
2735
|
+
}
|
|
2736
|
+
const coverScale = getCoverScale(frame, source);
|
|
2737
|
+
const imageWidth = source.width * coverScale * Math.max(0.05, Number(placement.scale || 1));
|
|
2738
|
+
const imageHeight = source.height * coverScale * Math.max(0.05, Number(placement.scale || 1));
|
|
2739
|
+
if (imageWidth <= 0 || imageHeight <= 0) {
|
|
2740
|
+
return { ok: true };
|
|
2741
|
+
}
|
|
2742
|
+
const centerX = frame.left + placement.left * frame.width;
|
|
2743
|
+
const centerY = frame.top + placement.top * frame.height;
|
|
2744
|
+
const halfWidth = imageWidth / 2;
|
|
2745
|
+
const halfHeight = imageHeight / 2;
|
|
2746
|
+
const radians = toRadians(placement.angle || 0);
|
|
2747
|
+
const cos = Math.cos(radians);
|
|
2748
|
+
const sin = Math.sin(radians);
|
|
2749
|
+
const frameCorners = [
|
|
2750
|
+
{ x: frame.left, y: frame.top },
|
|
2751
|
+
{ x: frame.left + frame.width, y: frame.top },
|
|
2752
|
+
{ x: frame.left + frame.width, y: frame.top + frame.height },
|
|
2753
|
+
{ x: frame.left, y: frame.top + frame.height }
|
|
2754
|
+
];
|
|
2755
|
+
const coversFrame = frameCorners.every((corner) => {
|
|
2756
|
+
const dx = corner.x - centerX;
|
|
2757
|
+
const dy = corner.y - centerY;
|
|
2758
|
+
const localX = dx * cos + dy * sin;
|
|
2759
|
+
const localY = -dx * sin + dy * cos;
|
|
2760
|
+
return Math.abs(localX) <= halfWidth + 1e-6 && Math.abs(localY) <= halfHeight + 1e-6;
|
|
2761
|
+
});
|
|
2762
|
+
return { ok: coversFrame };
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2712
2765
|
// src/extensions/geometry.ts
|
|
2713
2766
|
var import_paper = __toESM(require("paper"));
|
|
2714
2767
|
|
|
@@ -3511,6 +3564,7 @@ var ImageTool = class {
|
|
|
3511
3564
|
this.activeSnapX = null;
|
|
3512
3565
|
this.activeSnapY = null;
|
|
3513
3566
|
this.movingImageId = null;
|
|
3567
|
+
this.sessionNotice = null;
|
|
3514
3568
|
this.hasRenderedSnapGuides = false;
|
|
3515
3569
|
this.subscriptions = new SubscriptionBag();
|
|
3516
3570
|
this.imageControlsByCapabilityKey = /* @__PURE__ */ new Map();
|
|
@@ -3710,7 +3764,10 @@ var ImageTool = class {
|
|
|
3710
3764
|
this.updateImages();
|
|
3711
3765
|
return;
|
|
3712
3766
|
}
|
|
3713
|
-
if (e.key.startsWith("size.") || e.key.startsWith("image.frame.") || e.key.startsWith("image.control.")) {
|
|
3767
|
+
if (e.key.startsWith("size.") || e.key.startsWith("image.frame.") || e.key.startsWith("image.session.") || e.key.startsWith("image.control.")) {
|
|
3768
|
+
if (e.key === "image.session.placementPolicy") {
|
|
3769
|
+
this.clearSessionNotice();
|
|
3770
|
+
}
|
|
3714
3771
|
if (e.key.startsWith("image.control.")) {
|
|
3715
3772
|
this.imageControlsByCapabilityKey.clear();
|
|
3716
3773
|
}
|
|
@@ -4154,6 +4211,7 @@ var ImageTool = class {
|
|
|
4154
4211
|
interaction: "session",
|
|
4155
4212
|
commands: {
|
|
4156
4213
|
begin: "imageSessionReset",
|
|
4214
|
+
validate: "validateImageSession",
|
|
4157
4215
|
commit: "completeImages",
|
|
4158
4216
|
rollback: "imageSessionReset"
|
|
4159
4217
|
},
|
|
@@ -4192,6 +4250,32 @@ var ImageTool = class {
|
|
|
4192
4250
|
getViewItems() {
|
|
4193
4251
|
return this.isToolActive ? this.workingItems : this.items;
|
|
4194
4252
|
}
|
|
4253
|
+
getPlacementPolicy() {
|
|
4254
|
+
const policy = this.getConfig(
|
|
4255
|
+
"image.session.placementPolicy",
|
|
4256
|
+
"free"
|
|
4257
|
+
);
|
|
4258
|
+
return policy === "warn" || policy === "strict" ? policy : "free";
|
|
4259
|
+
}
|
|
4260
|
+
areSessionNoticesEqual(a, b) {
|
|
4261
|
+
if (!a && !b) return true;
|
|
4262
|
+
if (!a || !b) return false;
|
|
4263
|
+
return a.code === b.code && a.level === b.level && a.message === b.message && a.policy === b.policy && JSON.stringify(a.imageIds) === JSON.stringify(b.imageIds);
|
|
4264
|
+
}
|
|
4265
|
+
setSessionNotice(notice, options = {}) {
|
|
4266
|
+
var _a;
|
|
4267
|
+
if (this.areSessionNoticesEqual(this.sessionNotice, notice)) {
|
|
4268
|
+
return;
|
|
4269
|
+
}
|
|
4270
|
+
this.sessionNotice = notice;
|
|
4271
|
+
if (options.emit !== false) {
|
|
4272
|
+
(_a = this.context) == null ? void 0 : _a.eventBus.emit("image:session:notice", this.sessionNotice);
|
|
4273
|
+
this.emitImageStateChange();
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
clearSessionNotice(options = {}) {
|
|
4277
|
+
this.setSessionNotice(null, options);
|
|
4278
|
+
}
|
|
4195
4279
|
getImageViewState() {
|
|
4196
4280
|
this.syncToolActiveFromWorkbench();
|
|
4197
4281
|
const items = this.cloneItems(this.getViewItems());
|
|
@@ -4204,7 +4288,9 @@ var ImageTool = class {
|
|
|
4204
4288
|
isToolActive: this.isToolActive,
|
|
4205
4289
|
isImageSelectionActive: this.isImageSelectionActive,
|
|
4206
4290
|
hasWorkingChanges: this.hasWorkingChanges,
|
|
4207
|
-
source: this.isToolActive ? "working" : "committed"
|
|
4291
|
+
source: this.isToolActive ? "working" : "committed",
|
|
4292
|
+
placementPolicy: this.getPlacementPolicy(),
|
|
4293
|
+
sessionNotice: this.sessionNotice
|
|
4208
4294
|
};
|
|
4209
4295
|
}
|
|
4210
4296
|
emitImageStateChange() {
|
|
@@ -4253,6 +4339,7 @@ var ImageTool = class {
|
|
|
4253
4339
|
}
|
|
4254
4340
|
async addImageEntry(url, options, operation) {
|
|
4255
4341
|
this.syncToolActiveFromWorkbench();
|
|
4342
|
+
this.clearSessionNotice({ emit: false });
|
|
4256
4343
|
const id = this.generateId();
|
|
4257
4344
|
const newItem = this.normalizeItem({
|
|
4258
4345
|
id,
|
|
@@ -4313,7 +4400,11 @@ var ImageTool = class {
|
|
|
4313
4400
|
}
|
|
4314
4401
|
return { id: targetId, mode: "replace" };
|
|
4315
4402
|
}
|
|
4316
|
-
const id = await this.addImageEntry(
|
|
4403
|
+
const id = await this.addImageEntry(
|
|
4404
|
+
url,
|
|
4405
|
+
options.addOptions,
|
|
4406
|
+
options.operation
|
|
4407
|
+
);
|
|
4317
4408
|
return { id, mode: "add" };
|
|
4318
4409
|
}
|
|
4319
4410
|
async updateImage(id, updates, options = {}) {
|
|
@@ -4351,6 +4442,7 @@ var ImageTool = class {
|
|
|
4351
4442
|
}
|
|
4352
4443
|
updateConfig(newItems, skipCanvasUpdate = false) {
|
|
4353
4444
|
if (!this.context) return;
|
|
4445
|
+
this.clearSessionNotice({ emit: false });
|
|
4354
4446
|
this.applyCommittedItems(newItems);
|
|
4355
4447
|
runDeferredConfigUpdate(
|
|
4356
4448
|
this,
|
|
@@ -4453,6 +4545,71 @@ var ImageTool = class {
|
|
|
4453
4545
|
getCoverScale(frame, size) {
|
|
4454
4546
|
return getCoverScale(frame, size);
|
|
4455
4547
|
}
|
|
4548
|
+
resolvePlacementState(item) {
|
|
4549
|
+
var _a;
|
|
4550
|
+
return {
|
|
4551
|
+
left: Number.isFinite(item.left) ? item.left : 0.5,
|
|
4552
|
+
top: Number.isFinite(item.top) ? item.top : 0.5,
|
|
4553
|
+
scale: Math.max(0.05, (_a = item.scale) != null ? _a : 1),
|
|
4554
|
+
angle: Number.isFinite(item.angle) ? item.angle : 0
|
|
4555
|
+
};
|
|
4556
|
+
}
|
|
4557
|
+
async validatePlacementForItem(item) {
|
|
4558
|
+
const frame = this.getFrameRect();
|
|
4559
|
+
if (!frame.width || !frame.height) {
|
|
4560
|
+
return true;
|
|
4561
|
+
}
|
|
4562
|
+
const src = item.sourceUrl || item.url;
|
|
4563
|
+
if (!src) {
|
|
4564
|
+
return true;
|
|
4565
|
+
}
|
|
4566
|
+
const source = await this.resolveImageSourceSize(item.id, src);
|
|
4567
|
+
if (!source) {
|
|
4568
|
+
return true;
|
|
4569
|
+
}
|
|
4570
|
+
return validateImagePlacement({
|
|
4571
|
+
frame,
|
|
4572
|
+
source,
|
|
4573
|
+
placement: this.resolvePlacementState(item)
|
|
4574
|
+
}).ok;
|
|
4575
|
+
}
|
|
4576
|
+
async validateImageSession() {
|
|
4577
|
+
const policy = this.getPlacementPolicy();
|
|
4578
|
+
if (policy === "free") {
|
|
4579
|
+
this.clearSessionNotice();
|
|
4580
|
+
return { ok: true, policy };
|
|
4581
|
+
}
|
|
4582
|
+
const invalidImageIds = [];
|
|
4583
|
+
for (const item of this.workingItems) {
|
|
4584
|
+
const valid = await this.validatePlacementForItem(item);
|
|
4585
|
+
if (!valid) {
|
|
4586
|
+
invalidImageIds.push(item.id);
|
|
4587
|
+
}
|
|
4588
|
+
}
|
|
4589
|
+
if (!invalidImageIds.length) {
|
|
4590
|
+
this.clearSessionNotice();
|
|
4591
|
+
return { ok: true, policy };
|
|
4592
|
+
}
|
|
4593
|
+
const notice = {
|
|
4594
|
+
code: "image-outside-frame",
|
|
4595
|
+
level: policy === "strict" ? "error" : "warning",
|
|
4596
|
+
message: policy === "strict" ? "\u56FE\u7247\u4F4D\u7F6E\u4E0D\u80FD\u8D85\u51FA frame\uFF0C\u8BF7\u8C03\u6574\u540E\u518D\u63D0\u4EA4\u3002" : "\u56FE\u7247\u4F4D\u7F6E\u5DF2\u8D85\u51FA frame\uFF0C\u5EFA\u8BAE\u8C03\u6574\u540E\u518D\u63D0\u4EA4\u3002",
|
|
4597
|
+
imageIds: invalidImageIds,
|
|
4598
|
+
policy
|
|
4599
|
+
};
|
|
4600
|
+
this.setSessionNotice(notice);
|
|
4601
|
+
this.setImageFocus(invalidImageIds[0], {
|
|
4602
|
+
syncCanvasSelection: true,
|
|
4603
|
+
skipRender: true
|
|
4604
|
+
});
|
|
4605
|
+
return {
|
|
4606
|
+
ok: policy !== "strict",
|
|
4607
|
+
reason: notice.code,
|
|
4608
|
+
message: notice.message,
|
|
4609
|
+
imageIds: notice.imageIds,
|
|
4610
|
+
policy: notice.policy
|
|
4611
|
+
};
|
|
4612
|
+
}
|
|
4456
4613
|
getFrameVisualConfig() {
|
|
4457
4614
|
var _a, _b;
|
|
4458
4615
|
const strokeStyleRaw = this.getConfig(
|
|
@@ -4737,6 +4894,7 @@ var ImageTool = class {
|
|
|
4737
4894
|
await this.updateImage(id, next, options);
|
|
4738
4895
|
}
|
|
4739
4896
|
resetImageSession() {
|
|
4897
|
+
this.clearSessionNotice({ emit: false });
|
|
4740
4898
|
this.workingItems = this.cloneItems(this.items);
|
|
4741
4899
|
this.hasWorkingChanges = false;
|
|
4742
4900
|
this.updateImages();
|
|
@@ -4745,6 +4903,7 @@ var ImageTool = class {
|
|
|
4745
4903
|
updateImageInWorking(id, updates) {
|
|
4746
4904
|
const index = this.workingItems.findIndex((item) => item.id === id);
|
|
4747
4905
|
if (index < 0) return;
|
|
4906
|
+
this.clearSessionNotice({ emit: false });
|
|
4748
4907
|
const next = [...this.workingItems];
|
|
4749
4908
|
next[index] = this.normalizeItem({ ...next[index], ...updates });
|
|
4750
4909
|
this.workingItems = next;
|
|
@@ -4761,6 +4920,7 @@ var ImageTool = class {
|
|
|
4761
4920
|
async updateImageInConfig(id, updates) {
|
|
4762
4921
|
const index = this.items.findIndex((item) => item.id === id);
|
|
4763
4922
|
if (index < 0) return;
|
|
4923
|
+
this.clearSessionNotice({ emit: false });
|
|
4764
4924
|
const replacingSource = typeof updates.url === "string" && updates.url.length > 0;
|
|
4765
4925
|
const next = [...this.items];
|
|
4766
4926
|
const base = next[index];
|
|
@@ -4869,11 +5029,42 @@ var ImageTool = class {
|
|
|
4869
5029
|
}
|
|
4870
5030
|
}
|
|
4871
5031
|
this.hasWorkingChanges = false;
|
|
5032
|
+
this.clearSessionNotice({ emit: false });
|
|
4872
5033
|
this.workingItems = this.cloneItems(next);
|
|
4873
5034
|
this.updateConfig(next);
|
|
4874
5035
|
this.emitWorkingChange(this.focusedImageId);
|
|
4875
5036
|
return { ok: true };
|
|
4876
5037
|
}
|
|
5038
|
+
async completeImageSession() {
|
|
5039
|
+
var _a, _b, _c;
|
|
5040
|
+
const sessionState = (_a = this.context) == null ? void 0 : _a.services.get("ToolSessionService");
|
|
5041
|
+
const workbench = (_b = this.context) == null ? void 0 : _b.services.get("WorkbenchService");
|
|
5042
|
+
console.info("[ImageTool] completeImageSession:start", {
|
|
5043
|
+
activeToolId: (_c = workbench == null ? void 0 : workbench.activeToolId) != null ? _c : null,
|
|
5044
|
+
isToolActive: this.isToolActive,
|
|
5045
|
+
dirtyBeforeComplete: this.hasWorkingChanges,
|
|
5046
|
+
workingCount: this.workingItems.length,
|
|
5047
|
+
committedCount: this.items.length,
|
|
5048
|
+
sessionDirty: sessionState == null ? void 0 : sessionState.isDirty(this.id)
|
|
5049
|
+
});
|
|
5050
|
+
const validation = await this.validateImageSession();
|
|
5051
|
+
if (!validation.ok) {
|
|
5052
|
+
console.warn("[ImageTool] completeImageSession:validation-failed", {
|
|
5053
|
+
validation,
|
|
5054
|
+
dirtyAfterValidation: this.hasWorkingChanges
|
|
5055
|
+
});
|
|
5056
|
+
return validation;
|
|
5057
|
+
}
|
|
5058
|
+
const result = await this.commitWorkingImagesAsCropped();
|
|
5059
|
+
console.info("[ImageTool] completeImageSession:done", {
|
|
5060
|
+
result,
|
|
5061
|
+
dirtyAfterComplete: this.hasWorkingChanges,
|
|
5062
|
+
workingCount: this.workingItems.length,
|
|
5063
|
+
committedCount: this.items.length,
|
|
5064
|
+
sessionDirty: sessionState == null ? void 0 : sessionState.isDirty(this.id)
|
|
5065
|
+
});
|
|
5066
|
+
return result;
|
|
5067
|
+
}
|
|
4877
5068
|
async exportCroppedImageByIds(imageIds, options) {
|
|
4878
5069
|
var _a, _b, _c;
|
|
4879
5070
|
if (!this.canvasService) {
|
package/dist/index.mjs
CHANGED
|
@@ -1302,12 +1302,20 @@ function createImageCommands(tool) {
|
|
|
1302
1302
|
tool.resetImageSession();
|
|
1303
1303
|
}
|
|
1304
1304
|
},
|
|
1305
|
+
{
|
|
1306
|
+
command: "validateImageSession",
|
|
1307
|
+
id: "validateImageSession",
|
|
1308
|
+
title: "Validate Image Session",
|
|
1309
|
+
handler: async () => {
|
|
1310
|
+
return await tool.validateImageSession();
|
|
1311
|
+
}
|
|
1312
|
+
},
|
|
1305
1313
|
{
|
|
1306
1314
|
command: "completeImages",
|
|
1307
1315
|
id: "completeImages",
|
|
1308
1316
|
title: "Complete Images",
|
|
1309
1317
|
handler: async () => {
|
|
1310
|
-
return await tool.
|
|
1318
|
+
return await tool.completeImageSession();
|
|
1311
1319
|
}
|
|
1312
1320
|
},
|
|
1313
1321
|
{
|
|
@@ -1552,6 +1560,13 @@ function createImageConfigurations() {
|
|
|
1552
1560
|
type: "color",
|
|
1553
1561
|
label: "Image Frame Outer Background",
|
|
1554
1562
|
default: "#f5f5f5"
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
id: "image.session.placementPolicy",
|
|
1566
|
+
type: "select",
|
|
1567
|
+
label: "Image Session Placement Policy",
|
|
1568
|
+
options: ["free", "warn", "strict"],
|
|
1569
|
+
default: "free"
|
|
1555
1570
|
}
|
|
1556
1571
|
];
|
|
1557
1572
|
}
|
|
@@ -1628,6 +1643,44 @@ function computeImageOperationUpdates(args) {
|
|
|
1628
1643
|
};
|
|
1629
1644
|
}
|
|
1630
1645
|
|
|
1646
|
+
// src/extensions/image/imagePlacement.ts
|
|
1647
|
+
function toRadians(angle) {
|
|
1648
|
+
return angle * Math.PI / 180;
|
|
1649
|
+
}
|
|
1650
|
+
function validateImagePlacement(args) {
|
|
1651
|
+
const { frame, source, placement } = args;
|
|
1652
|
+
if (frame.width <= 0 || frame.height <= 0 || source.width <= 0 || source.height <= 0) {
|
|
1653
|
+
return { ok: true };
|
|
1654
|
+
}
|
|
1655
|
+
const coverScale = getCoverScale(frame, source);
|
|
1656
|
+
const imageWidth = source.width * coverScale * Math.max(0.05, Number(placement.scale || 1));
|
|
1657
|
+
const imageHeight = source.height * coverScale * Math.max(0.05, Number(placement.scale || 1));
|
|
1658
|
+
if (imageWidth <= 0 || imageHeight <= 0) {
|
|
1659
|
+
return { ok: true };
|
|
1660
|
+
}
|
|
1661
|
+
const centerX = frame.left + placement.left * frame.width;
|
|
1662
|
+
const centerY = frame.top + placement.top * frame.height;
|
|
1663
|
+
const halfWidth = imageWidth / 2;
|
|
1664
|
+
const halfHeight = imageHeight / 2;
|
|
1665
|
+
const radians = toRadians(placement.angle || 0);
|
|
1666
|
+
const cos = Math.cos(radians);
|
|
1667
|
+
const sin = Math.sin(radians);
|
|
1668
|
+
const frameCorners = [
|
|
1669
|
+
{ x: frame.left, y: frame.top },
|
|
1670
|
+
{ x: frame.left + frame.width, y: frame.top },
|
|
1671
|
+
{ x: frame.left + frame.width, y: frame.top + frame.height },
|
|
1672
|
+
{ x: frame.left, y: frame.top + frame.height }
|
|
1673
|
+
];
|
|
1674
|
+
const coversFrame = frameCorners.every((corner) => {
|
|
1675
|
+
const dx = corner.x - centerX;
|
|
1676
|
+
const dy = corner.y - centerY;
|
|
1677
|
+
const localX = dx * cos + dy * sin;
|
|
1678
|
+
const localY = -dx * sin + dy * cos;
|
|
1679
|
+
return Math.abs(localX) <= halfWidth + 1e-6 && Math.abs(localY) <= halfHeight + 1e-6;
|
|
1680
|
+
});
|
|
1681
|
+
return { ok: coversFrame };
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1631
1684
|
// src/extensions/geometry.ts
|
|
1632
1685
|
import paper from "paper";
|
|
1633
1686
|
|
|
@@ -2430,6 +2483,7 @@ var ImageTool = class {
|
|
|
2430
2483
|
this.activeSnapX = null;
|
|
2431
2484
|
this.activeSnapY = null;
|
|
2432
2485
|
this.movingImageId = null;
|
|
2486
|
+
this.sessionNotice = null;
|
|
2433
2487
|
this.hasRenderedSnapGuides = false;
|
|
2434
2488
|
this.subscriptions = new SubscriptionBag();
|
|
2435
2489
|
this.imageControlsByCapabilityKey = /* @__PURE__ */ new Map();
|
|
@@ -2629,7 +2683,10 @@ var ImageTool = class {
|
|
|
2629
2683
|
this.updateImages();
|
|
2630
2684
|
return;
|
|
2631
2685
|
}
|
|
2632
|
-
if (e.key.startsWith("size.") || e.key.startsWith("image.frame.") || e.key.startsWith("image.control.")) {
|
|
2686
|
+
if (e.key.startsWith("size.") || e.key.startsWith("image.frame.") || e.key.startsWith("image.session.") || e.key.startsWith("image.control.")) {
|
|
2687
|
+
if (e.key === "image.session.placementPolicy") {
|
|
2688
|
+
this.clearSessionNotice();
|
|
2689
|
+
}
|
|
2633
2690
|
if (e.key.startsWith("image.control.")) {
|
|
2634
2691
|
this.imageControlsByCapabilityKey.clear();
|
|
2635
2692
|
}
|
|
@@ -3073,6 +3130,7 @@ var ImageTool = class {
|
|
|
3073
3130
|
interaction: "session",
|
|
3074
3131
|
commands: {
|
|
3075
3132
|
begin: "imageSessionReset",
|
|
3133
|
+
validate: "validateImageSession",
|
|
3076
3134
|
commit: "completeImages",
|
|
3077
3135
|
rollback: "imageSessionReset"
|
|
3078
3136
|
},
|
|
@@ -3111,6 +3169,32 @@ var ImageTool = class {
|
|
|
3111
3169
|
getViewItems() {
|
|
3112
3170
|
return this.isToolActive ? this.workingItems : this.items;
|
|
3113
3171
|
}
|
|
3172
|
+
getPlacementPolicy() {
|
|
3173
|
+
const policy = this.getConfig(
|
|
3174
|
+
"image.session.placementPolicy",
|
|
3175
|
+
"free"
|
|
3176
|
+
);
|
|
3177
|
+
return policy === "warn" || policy === "strict" ? policy : "free";
|
|
3178
|
+
}
|
|
3179
|
+
areSessionNoticesEqual(a, b) {
|
|
3180
|
+
if (!a && !b) return true;
|
|
3181
|
+
if (!a || !b) return false;
|
|
3182
|
+
return a.code === b.code && a.level === b.level && a.message === b.message && a.policy === b.policy && JSON.stringify(a.imageIds) === JSON.stringify(b.imageIds);
|
|
3183
|
+
}
|
|
3184
|
+
setSessionNotice(notice, options = {}) {
|
|
3185
|
+
var _a;
|
|
3186
|
+
if (this.areSessionNoticesEqual(this.sessionNotice, notice)) {
|
|
3187
|
+
return;
|
|
3188
|
+
}
|
|
3189
|
+
this.sessionNotice = notice;
|
|
3190
|
+
if (options.emit !== false) {
|
|
3191
|
+
(_a = this.context) == null ? void 0 : _a.eventBus.emit("image:session:notice", this.sessionNotice);
|
|
3192
|
+
this.emitImageStateChange();
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
clearSessionNotice(options = {}) {
|
|
3196
|
+
this.setSessionNotice(null, options);
|
|
3197
|
+
}
|
|
3114
3198
|
getImageViewState() {
|
|
3115
3199
|
this.syncToolActiveFromWorkbench();
|
|
3116
3200
|
const items = this.cloneItems(this.getViewItems());
|
|
@@ -3123,7 +3207,9 @@ var ImageTool = class {
|
|
|
3123
3207
|
isToolActive: this.isToolActive,
|
|
3124
3208
|
isImageSelectionActive: this.isImageSelectionActive,
|
|
3125
3209
|
hasWorkingChanges: this.hasWorkingChanges,
|
|
3126
|
-
source: this.isToolActive ? "working" : "committed"
|
|
3210
|
+
source: this.isToolActive ? "working" : "committed",
|
|
3211
|
+
placementPolicy: this.getPlacementPolicy(),
|
|
3212
|
+
sessionNotice: this.sessionNotice
|
|
3127
3213
|
};
|
|
3128
3214
|
}
|
|
3129
3215
|
emitImageStateChange() {
|
|
@@ -3172,6 +3258,7 @@ var ImageTool = class {
|
|
|
3172
3258
|
}
|
|
3173
3259
|
async addImageEntry(url, options, operation) {
|
|
3174
3260
|
this.syncToolActiveFromWorkbench();
|
|
3261
|
+
this.clearSessionNotice({ emit: false });
|
|
3175
3262
|
const id = this.generateId();
|
|
3176
3263
|
const newItem = this.normalizeItem({
|
|
3177
3264
|
id,
|
|
@@ -3232,7 +3319,11 @@ var ImageTool = class {
|
|
|
3232
3319
|
}
|
|
3233
3320
|
return { id: targetId, mode: "replace" };
|
|
3234
3321
|
}
|
|
3235
|
-
const id = await this.addImageEntry(
|
|
3322
|
+
const id = await this.addImageEntry(
|
|
3323
|
+
url,
|
|
3324
|
+
options.addOptions,
|
|
3325
|
+
options.operation
|
|
3326
|
+
);
|
|
3236
3327
|
return { id, mode: "add" };
|
|
3237
3328
|
}
|
|
3238
3329
|
async updateImage(id, updates, options = {}) {
|
|
@@ -3270,6 +3361,7 @@ var ImageTool = class {
|
|
|
3270
3361
|
}
|
|
3271
3362
|
updateConfig(newItems, skipCanvasUpdate = false) {
|
|
3272
3363
|
if (!this.context) return;
|
|
3364
|
+
this.clearSessionNotice({ emit: false });
|
|
3273
3365
|
this.applyCommittedItems(newItems);
|
|
3274
3366
|
runDeferredConfigUpdate(
|
|
3275
3367
|
this,
|
|
@@ -3372,6 +3464,71 @@ var ImageTool = class {
|
|
|
3372
3464
|
getCoverScale(frame, size) {
|
|
3373
3465
|
return getCoverScale(frame, size);
|
|
3374
3466
|
}
|
|
3467
|
+
resolvePlacementState(item) {
|
|
3468
|
+
var _a;
|
|
3469
|
+
return {
|
|
3470
|
+
left: Number.isFinite(item.left) ? item.left : 0.5,
|
|
3471
|
+
top: Number.isFinite(item.top) ? item.top : 0.5,
|
|
3472
|
+
scale: Math.max(0.05, (_a = item.scale) != null ? _a : 1),
|
|
3473
|
+
angle: Number.isFinite(item.angle) ? item.angle : 0
|
|
3474
|
+
};
|
|
3475
|
+
}
|
|
3476
|
+
async validatePlacementForItem(item) {
|
|
3477
|
+
const frame = this.getFrameRect();
|
|
3478
|
+
if (!frame.width || !frame.height) {
|
|
3479
|
+
return true;
|
|
3480
|
+
}
|
|
3481
|
+
const src = item.sourceUrl || item.url;
|
|
3482
|
+
if (!src) {
|
|
3483
|
+
return true;
|
|
3484
|
+
}
|
|
3485
|
+
const source = await this.resolveImageSourceSize(item.id, src);
|
|
3486
|
+
if (!source) {
|
|
3487
|
+
return true;
|
|
3488
|
+
}
|
|
3489
|
+
return validateImagePlacement({
|
|
3490
|
+
frame,
|
|
3491
|
+
source,
|
|
3492
|
+
placement: this.resolvePlacementState(item)
|
|
3493
|
+
}).ok;
|
|
3494
|
+
}
|
|
3495
|
+
async validateImageSession() {
|
|
3496
|
+
const policy = this.getPlacementPolicy();
|
|
3497
|
+
if (policy === "free") {
|
|
3498
|
+
this.clearSessionNotice();
|
|
3499
|
+
return { ok: true, policy };
|
|
3500
|
+
}
|
|
3501
|
+
const invalidImageIds = [];
|
|
3502
|
+
for (const item of this.workingItems) {
|
|
3503
|
+
const valid = await this.validatePlacementForItem(item);
|
|
3504
|
+
if (!valid) {
|
|
3505
|
+
invalidImageIds.push(item.id);
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
if (!invalidImageIds.length) {
|
|
3509
|
+
this.clearSessionNotice();
|
|
3510
|
+
return { ok: true, policy };
|
|
3511
|
+
}
|
|
3512
|
+
const notice = {
|
|
3513
|
+
code: "image-outside-frame",
|
|
3514
|
+
level: policy === "strict" ? "error" : "warning",
|
|
3515
|
+
message: policy === "strict" ? "\u56FE\u7247\u4F4D\u7F6E\u4E0D\u80FD\u8D85\u51FA frame\uFF0C\u8BF7\u8C03\u6574\u540E\u518D\u63D0\u4EA4\u3002" : "\u56FE\u7247\u4F4D\u7F6E\u5DF2\u8D85\u51FA frame\uFF0C\u5EFA\u8BAE\u8C03\u6574\u540E\u518D\u63D0\u4EA4\u3002",
|
|
3516
|
+
imageIds: invalidImageIds,
|
|
3517
|
+
policy
|
|
3518
|
+
};
|
|
3519
|
+
this.setSessionNotice(notice);
|
|
3520
|
+
this.setImageFocus(invalidImageIds[0], {
|
|
3521
|
+
syncCanvasSelection: true,
|
|
3522
|
+
skipRender: true
|
|
3523
|
+
});
|
|
3524
|
+
return {
|
|
3525
|
+
ok: policy !== "strict",
|
|
3526
|
+
reason: notice.code,
|
|
3527
|
+
message: notice.message,
|
|
3528
|
+
imageIds: notice.imageIds,
|
|
3529
|
+
policy: notice.policy
|
|
3530
|
+
};
|
|
3531
|
+
}
|
|
3375
3532
|
getFrameVisualConfig() {
|
|
3376
3533
|
var _a, _b;
|
|
3377
3534
|
const strokeStyleRaw = this.getConfig(
|
|
@@ -3656,6 +3813,7 @@ var ImageTool = class {
|
|
|
3656
3813
|
await this.updateImage(id, next, options);
|
|
3657
3814
|
}
|
|
3658
3815
|
resetImageSession() {
|
|
3816
|
+
this.clearSessionNotice({ emit: false });
|
|
3659
3817
|
this.workingItems = this.cloneItems(this.items);
|
|
3660
3818
|
this.hasWorkingChanges = false;
|
|
3661
3819
|
this.updateImages();
|
|
@@ -3664,6 +3822,7 @@ var ImageTool = class {
|
|
|
3664
3822
|
updateImageInWorking(id, updates) {
|
|
3665
3823
|
const index = this.workingItems.findIndex((item) => item.id === id);
|
|
3666
3824
|
if (index < 0) return;
|
|
3825
|
+
this.clearSessionNotice({ emit: false });
|
|
3667
3826
|
const next = [...this.workingItems];
|
|
3668
3827
|
next[index] = this.normalizeItem({ ...next[index], ...updates });
|
|
3669
3828
|
this.workingItems = next;
|
|
@@ -3680,6 +3839,7 @@ var ImageTool = class {
|
|
|
3680
3839
|
async updateImageInConfig(id, updates) {
|
|
3681
3840
|
const index = this.items.findIndex((item) => item.id === id);
|
|
3682
3841
|
if (index < 0) return;
|
|
3842
|
+
this.clearSessionNotice({ emit: false });
|
|
3683
3843
|
const replacingSource = typeof updates.url === "string" && updates.url.length > 0;
|
|
3684
3844
|
const next = [...this.items];
|
|
3685
3845
|
const base = next[index];
|
|
@@ -3788,11 +3948,42 @@ var ImageTool = class {
|
|
|
3788
3948
|
}
|
|
3789
3949
|
}
|
|
3790
3950
|
this.hasWorkingChanges = false;
|
|
3951
|
+
this.clearSessionNotice({ emit: false });
|
|
3791
3952
|
this.workingItems = this.cloneItems(next);
|
|
3792
3953
|
this.updateConfig(next);
|
|
3793
3954
|
this.emitWorkingChange(this.focusedImageId);
|
|
3794
3955
|
return { ok: true };
|
|
3795
3956
|
}
|
|
3957
|
+
async completeImageSession() {
|
|
3958
|
+
var _a, _b, _c;
|
|
3959
|
+
const sessionState = (_a = this.context) == null ? void 0 : _a.services.get("ToolSessionService");
|
|
3960
|
+
const workbench = (_b = this.context) == null ? void 0 : _b.services.get("WorkbenchService");
|
|
3961
|
+
console.info("[ImageTool] completeImageSession:start", {
|
|
3962
|
+
activeToolId: (_c = workbench == null ? void 0 : workbench.activeToolId) != null ? _c : null,
|
|
3963
|
+
isToolActive: this.isToolActive,
|
|
3964
|
+
dirtyBeforeComplete: this.hasWorkingChanges,
|
|
3965
|
+
workingCount: this.workingItems.length,
|
|
3966
|
+
committedCount: this.items.length,
|
|
3967
|
+
sessionDirty: sessionState == null ? void 0 : sessionState.isDirty(this.id)
|
|
3968
|
+
});
|
|
3969
|
+
const validation = await this.validateImageSession();
|
|
3970
|
+
if (!validation.ok) {
|
|
3971
|
+
console.warn("[ImageTool] completeImageSession:validation-failed", {
|
|
3972
|
+
validation,
|
|
3973
|
+
dirtyAfterValidation: this.hasWorkingChanges
|
|
3974
|
+
});
|
|
3975
|
+
return validation;
|
|
3976
|
+
}
|
|
3977
|
+
const result = await this.commitWorkingImagesAsCropped();
|
|
3978
|
+
console.info("[ImageTool] completeImageSession:done", {
|
|
3979
|
+
result,
|
|
3980
|
+
dirtyAfterComplete: this.hasWorkingChanges,
|
|
3981
|
+
workingCount: this.workingItems.length,
|
|
3982
|
+
committedCount: this.items.length,
|
|
3983
|
+
sessionDirty: sessionState == null ? void 0 : sessionState.isDirty(this.id)
|
|
3984
|
+
});
|
|
3985
|
+
return result;
|
|
3986
|
+
}
|
|
3796
3987
|
async exportCroppedImageByIds(imageIds, options) {
|
|
3797
3988
|
var _a, _b, _c;
|
|
3798
3989
|
if (!this.canvasService) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pooder/kit",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.1",
|
|
4
4
|
"description": "Standard plugins for Pooder editor",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"paper": "^0.12.18",
|
|
21
21
|
"fabric": "^7.0.0",
|
|
22
|
-
"@pooder/core": "2.2.
|
|
22
|
+
"@pooder/core": "2.2.2"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|