dsh-codex-subscription 2.0.1 → 2.1.0-beta.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/README.en.md +13 -1
- package/README.md +13 -1
- package/THIRD_PARTY_NOTICES.md +79 -0
- package/lib/client.js +710 -33
- package/lib/index.js +261 -8
- package/lib/sketch-psd-worker.js +11 -0
- package/package.json +3 -2
package/lib/client.js
CHANGED
|
@@ -279,6 +279,66 @@ window.__ModuleLoader__.load({
|
|
|
279
279
|
};
|
|
280
280
|
}
|
|
281
281
|
};
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region node_modules/.pnpm/@heroicons+react@2.2.0_react@18.3.1/node_modules/@heroicons/react/24/outline/esm/ArrowPathIcon.js
|
|
284
|
+
function ArrowPathIcon({ title, titleId, ...props }, svgRef) {
|
|
285
|
+
return /*#__PURE__*/ react.createElement("svg", Object.assign({
|
|
286
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
287
|
+
fill: "none",
|
|
288
|
+
viewBox: "0 0 24 24",
|
|
289
|
+
strokeWidth: 1.5,
|
|
290
|
+
stroke: "currentColor",
|
|
291
|
+
"aria-hidden": "true",
|
|
292
|
+
"data-slot": "icon",
|
|
293
|
+
ref: svgRef,
|
|
294
|
+
"aria-labelledby": titleId
|
|
295
|
+
}, props), title ? /*#__PURE__*/ react.createElement("title", { id: titleId }, title) : null, /*#__PURE__*/ react.createElement("path", {
|
|
296
|
+
strokeLinecap: "round",
|
|
297
|
+
strokeLinejoin: "round",
|
|
298
|
+
d: "M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
const ForwardRef$3 = /*#__PURE__*/ react.forwardRef(ArrowPathIcon);
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region node_modules/.pnpm/@heroicons+react@2.2.0_react@18.3.1/node_modules/@heroicons/react/24/outline/esm/ExclamationCircleIcon.js
|
|
304
|
+
function ExclamationCircleIcon({ title, titleId, ...props }, svgRef) {
|
|
305
|
+
return /*#__PURE__*/ react.createElement("svg", Object.assign({
|
|
306
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
307
|
+
fill: "none",
|
|
308
|
+
viewBox: "0 0 24 24",
|
|
309
|
+
strokeWidth: 1.5,
|
|
310
|
+
stroke: "currentColor",
|
|
311
|
+
"aria-hidden": "true",
|
|
312
|
+
"data-slot": "icon",
|
|
313
|
+
ref: svgRef,
|
|
314
|
+
"aria-labelledby": titleId
|
|
315
|
+
}, props), title ? /*#__PURE__*/ react.createElement("title", { id: titleId }, title) : null, /*#__PURE__*/ react.createElement("path", {
|
|
316
|
+
strokeLinecap: "round",
|
|
317
|
+
strokeLinejoin: "round",
|
|
318
|
+
d: "M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
|
|
319
|
+
}));
|
|
320
|
+
}
|
|
321
|
+
const ForwardRef$2 = /*#__PURE__*/ react.forwardRef(ExclamationCircleIcon);
|
|
322
|
+
//#endregion
|
|
323
|
+
//#region node_modules/.pnpm/@heroicons+react@2.2.0_react@18.3.1/node_modules/@heroicons/react/24/outline/esm/PhotoIcon.js
|
|
324
|
+
function PhotoIcon({ title, titleId, ...props }, svgRef) {
|
|
325
|
+
return /*#__PURE__*/ react.createElement("svg", Object.assign({
|
|
326
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
327
|
+
fill: "none",
|
|
328
|
+
viewBox: "0 0 24 24",
|
|
329
|
+
strokeWidth: 1.5,
|
|
330
|
+
stroke: "currentColor",
|
|
331
|
+
"aria-hidden": "true",
|
|
332
|
+
"data-slot": "icon",
|
|
333
|
+
ref: svgRef,
|
|
334
|
+
"aria-labelledby": titleId
|
|
335
|
+
}, props), title ? /*#__PURE__*/ react.createElement("title", { id: titleId }, title) : null, /*#__PURE__*/ react.createElement("path", {
|
|
336
|
+
strokeLinecap: "round",
|
|
337
|
+
strokeLinejoin: "round",
|
|
338
|
+
d: "m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"
|
|
339
|
+
}));
|
|
340
|
+
}
|
|
341
|
+
const ForwardRef$1 = /*#__PURE__*/ react.forwardRef(PhotoIcon);
|
|
282
342
|
const ORIGINAL_IMAGE_ID_PATTERN = /^img_[0-9a-f]{32}$/u;
|
|
283
343
|
const positiveInteger = (value) => Number.isSafeInteger(value) && value > 0;
|
|
284
344
|
function decodeOriginalImageRef(value) {
|
|
@@ -323,7 +383,11 @@ window.__ModuleLoader__.load({
|
|
|
323
383
|
"reset-credit/inspect",
|
|
324
384
|
"reset-credit/prepare",
|
|
325
385
|
"reset-credit/consume",
|
|
326
|
-
"image/original/chunk"
|
|
386
|
+
"image/original/chunk",
|
|
387
|
+
"sketch/connect",
|
|
388
|
+
"sketch/poll",
|
|
389
|
+
"sketch/result",
|
|
390
|
+
"sketch/disconnect"
|
|
327
391
|
]);
|
|
328
392
|
function createSubscriptionRpcClient(transport) {
|
|
329
393
|
return Object.freeze({ call(channel, endpoint, payload, signal) {
|
|
@@ -511,7 +575,7 @@ window.__ModuleLoader__.load({
|
|
|
511
575
|
})
|
|
512
576
|
});
|
|
513
577
|
}
|
|
514
|
-
function CodexImageToolRow({ block, sessionId, rpc, loadImage, openSketchImage, attachForEdit, getImageViewer, getInternalImageViewer, t, preference }) {
|
|
578
|
+
function CodexImageToolRow({ presentation = "tool", block, sessionId, rpc, loadImage, openSketchImage, attachForEdit, getImageViewer, getInternalImageViewer, t, preference }) {
|
|
515
579
|
const features = (0, react.useSyncExternalStore)(preference.subscribe, preference.getSnapshot);
|
|
516
580
|
const settled = block?.kind === "tool-result";
|
|
517
581
|
const image = settled ? block.content.find((item) => item?.type === "image" && item.attachment !== void 0) : void 0;
|
|
@@ -520,14 +584,15 @@ window.__ModuleLoader__.load({
|
|
|
520
584
|
const status = !settled ? t("imageGenerating") : failed ? t("imageFailed") : t("imageGenerated");
|
|
521
585
|
const error = failed ? block.content.find((item) => item?.type === "text" && typeof item.text === "string")?.text : void 0;
|
|
522
586
|
const original = decodeImagePresentation(block?.meta)?.original;
|
|
587
|
+
const showOutput = presentation === "output";
|
|
523
588
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
524
589
|
className: "codexImageTool",
|
|
525
590
|
"data-state": state,
|
|
526
591
|
children: [
|
|
527
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
592
|
+
showOutput ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
528
593
|
className: "codexImageToolRow",
|
|
529
594
|
children: [
|
|
530
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
595
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(!settled ? ForwardRef$3 : failed ? ForwardRef$2 : ForwardRef$1, {
|
|
531
596
|
className: "codexImageToolIcon",
|
|
532
597
|
"aria-hidden": "true"
|
|
533
598
|
}),
|
|
@@ -545,7 +610,7 @@ window.__ModuleLoader__.load({
|
|
|
545
610
|
})
|
|
546
611
|
]
|
|
547
612
|
}),
|
|
548
|
-
image === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
613
|
+
!showOutput || image === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
549
614
|
className: "codexImageToolGallery",
|
|
550
615
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexGeneratedImage, {
|
|
551
616
|
features,
|
|
@@ -561,7 +626,7 @@ window.__ModuleLoader__.load({
|
|
|
561
626
|
t
|
|
562
627
|
})
|
|
563
628
|
}),
|
|
564
|
-
typeof block?.meta?.requestedModel === "string" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
629
|
+
!showOutput && typeof block?.meta?.requestedModel === "string" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
565
630
|
className: "codexImageDetails",
|
|
566
631
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("imageDetails") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
567
632
|
t("imageRequestedModel"),
|
|
@@ -595,13 +660,16 @@ window.__ModuleLoader__.load({
|
|
|
595
660
|
className: "codexImageOutput",
|
|
596
661
|
children: node.data.blocks.map((block) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexImageToolRow, {
|
|
597
662
|
block,
|
|
598
|
-
...props
|
|
663
|
+
...props,
|
|
664
|
+
presentation: "output"
|
|
599
665
|
}, block.toolCallId))
|
|
600
666
|
});
|
|
601
667
|
}
|
|
602
668
|
//#endregion
|
|
603
669
|
//#region src/sketch-document.js
|
|
604
670
|
const SKETCH_SIZE = 1024;
|
|
671
|
+
const MAX_SKETCH_STROKES = 2e3;
|
|
672
|
+
const MAX_STROKE_POINTS = 2e3;
|
|
605
673
|
function sketchPoint(clientX, clientY, rect) {
|
|
606
674
|
if (!(rect.width > 0 && rect.height > 0)) return void 0;
|
|
607
675
|
return {
|
|
@@ -635,10 +703,18 @@ window.__ModuleLoader__.load({
|
|
|
635
703
|
context.stroke();
|
|
636
704
|
} else if (stroke.shape === "rectangle") {
|
|
637
705
|
context.rect(first.x * size, first.y * height, (last.x - first.x) * size, (last.y - first.y) * height);
|
|
638
|
-
context.
|
|
706
|
+
if (stroke.fill) context.fill();
|
|
707
|
+
else context.stroke();
|
|
639
708
|
} else if (stroke.shape === "circle") {
|
|
640
709
|
context.ellipse((first.x + last.x) * size / 2, (first.y + last.y) * height / 2, Math.abs(last.x - first.x) * size / 2, Math.abs(last.y - first.y) * height / 2, 0, 0, Math.PI * 2);
|
|
641
|
-
context.
|
|
710
|
+
if (stroke.fill) context.fill();
|
|
711
|
+
else context.stroke();
|
|
712
|
+
} else if (stroke.shape === "polygon") {
|
|
713
|
+
context.moveTo(first.x * size, first.y * height);
|
|
714
|
+
for (const point of stroke.points.slice(1)) context.lineTo(point.x * size, point.y * height);
|
|
715
|
+
context.closePath();
|
|
716
|
+
if (stroke.fill) context.fill();
|
|
717
|
+
else context.stroke();
|
|
642
718
|
} else if (stroke.points.length === 1) {
|
|
643
719
|
context.arc(first.x * size, first.y * height, context.lineWidth / 2, 0, Math.PI * 2);
|
|
644
720
|
context.fill();
|
|
@@ -676,7 +752,7 @@ window.__ModuleLoader__.load({
|
|
|
676
752
|
active: id
|
|
677
753
|
};
|
|
678
754
|
if (action === "add" || action === "duplicate") {
|
|
679
|
-
if (layers.length >= 8 || action === "duplicate" && strokeCount(doc) + layer.strokes.length >
|
|
755
|
+
if (layers.length >= 8 || action === "duplicate" && strokeCount(doc) + layer.strokes.length > 2e3) return doc;
|
|
680
756
|
const next = action === "add" ? {
|
|
681
757
|
id: doc.nextId,
|
|
682
758
|
name: "",
|
|
@@ -736,6 +812,22 @@ window.__ModuleLoader__.load({
|
|
|
736
812
|
let points = stroke.points;
|
|
737
813
|
if (!points.length) return false;
|
|
738
814
|
const a = points[0], b = points.at(-1);
|
|
815
|
+
if (stroke.fill && stroke.shape === "rectangle" && point.x >= Math.min(a.x, b.x) && point.x <= Math.max(a.x, b.x) && point.y >= Math.min(a.y, b.y) && point.y <= Math.max(a.y, b.y)) return true;
|
|
816
|
+
if (stroke.fill && stroke.shape === "circle") {
|
|
817
|
+
const rx = Math.abs(b.x - a.x) / 2, ry = Math.abs(b.y - a.y) / 2;
|
|
818
|
+
if (rx && ry && ((point.x - (a.x + b.x) / 2) / rx) ** 2 + ((point.y - (a.y + b.y) / 2) / ry) ** 2 <= 1) return true;
|
|
819
|
+
}
|
|
820
|
+
if (stroke.shape === "polygon") {
|
|
821
|
+
if (stroke.fill) {
|
|
822
|
+
let inside = false;
|
|
823
|
+
for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
|
|
824
|
+
const p = points[i], q = points[j];
|
|
825
|
+
if (p.y > point.y !== q.y > point.y && point.x < (q.x - p.x) * (point.y - p.y) / (q.y - p.y) + p.x) inside = !inside;
|
|
826
|
+
}
|
|
827
|
+
if (inside) return true;
|
|
828
|
+
}
|
|
829
|
+
points = [...points, points[0]];
|
|
830
|
+
}
|
|
739
831
|
if (stroke.shape === "rectangle") points = [
|
|
740
832
|
a,
|
|
741
833
|
{
|
|
@@ -1253,10 +1345,320 @@ window.__ModuleLoader__.load({
|
|
|
1253
1345
|
]
|
|
1254
1346
|
});
|
|
1255
1347
|
}
|
|
1348
|
+
const SKETCH_COMMAND_HELP = {
|
|
1349
|
+
coordinates: "Normalized x/y in [0,1]; width is canvas pixels. Read documentId and revision before editing.",
|
|
1350
|
+
shapes: "line: exactly two endpoints; rectangle/circle: exactly two opposite bounding-box corners (circle draws an ellipse within that box); polygon: three or more vertices, closed automatically; pen: ordered path points. fill:true fills rectangle/circle/polygon. Layers and strokes paint in list order, later ones on top. All commands needed for drawing are described here; no source-code search is required.",
|
|
1351
|
+
commands: {
|
|
1352
|
+
stroke: "{op:\"stroke\",layer:1,shape:\"pen|line|rectangle|circle|polygon\",color:\"#rrggbb\",width:2,opacity:1,fill:false,points:[{x:0.1,y:0.1},...]}",
|
|
1353
|
+
layer: "{op:\"layer\",action:\"add|select|rename|visible|duplicate|up|down|delete|clear\",id:1,value:\"name\"}",
|
|
1354
|
+
resize: "{op:\"resize\",ratio:\"1:1|4:3|3:4|16:9|9:16\"}"
|
|
1355
|
+
},
|
|
1356
|
+
limits: {
|
|
1357
|
+
strokes: MAX_SKETCH_STROKES,
|
|
1358
|
+
pointsPerStroke: MAX_STROKE_POINTS,
|
|
1359
|
+
pointsTotal: 2e5,
|
|
1360
|
+
commandsPerBatch: 256
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
const finite = (value, min, max) => typeof value === "number" && Number.isFinite(value) && value >= min && value <= max;
|
|
1364
|
+
function applySketchCommands(source, commands) {
|
|
1365
|
+
if (!Array.isArray(commands) || !commands.length || commands.length > 256) throw Error("Expected 1–256 commands");
|
|
1366
|
+
let doc = source;
|
|
1367
|
+
for (const command of commands) {
|
|
1368
|
+
if (!command || typeof command !== "object") throw Error("Invalid command");
|
|
1369
|
+
if (command.op === "resize") {
|
|
1370
|
+
doc = resizeSketch(doc, command.ratio);
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
if (command.op === "layer") {
|
|
1374
|
+
if (![
|
|
1375
|
+
"add",
|
|
1376
|
+
"select",
|
|
1377
|
+
"rename",
|
|
1378
|
+
"visible",
|
|
1379
|
+
"duplicate",
|
|
1380
|
+
"up",
|
|
1381
|
+
"down",
|
|
1382
|
+
"delete",
|
|
1383
|
+
"clear"
|
|
1384
|
+
].includes(command.action)) throw Error("Unknown layer action");
|
|
1385
|
+
const next = changeSketchLayer(doc, command.action, command.id ?? doc.active, command.value);
|
|
1386
|
+
if (next === doc) throw Error("Layer action unavailable; inspect the document first");
|
|
1387
|
+
doc = next;
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
if (command.op !== "stroke") throw Error("Unknown command");
|
|
1391
|
+
const { points, color, shape = "pen", width = 2, opacity = 1, fill = false } = command;
|
|
1392
|
+
if (![
|
|
1393
|
+
"pen",
|
|
1394
|
+
"line",
|
|
1395
|
+
"rectangle",
|
|
1396
|
+
"circle",
|
|
1397
|
+
"polygon",
|
|
1398
|
+
"eraser"
|
|
1399
|
+
].includes(shape) || !/^#[0-9a-f]{6}$/i.test(color ?? "") || !finite(width, 1, 256) || !finite(opacity, 0, 1) || typeof fill !== "boolean" || !Array.isArray(points) || !points.length || points.length > 2e3 || points.some((p) => !p || !finite(p.x, 0, 1) || !finite(p.y, 0, 1))) throw Error("Invalid stroke");
|
|
1400
|
+
if ([
|
|
1401
|
+
"line",
|
|
1402
|
+
"rectangle",
|
|
1403
|
+
"circle"
|
|
1404
|
+
].includes(shape) && points.length !== 2 || shape === "polygon" && points.length < 3) throw Error("Invalid shape points");
|
|
1405
|
+
if (fill && ![
|
|
1406
|
+
"rectangle",
|
|
1407
|
+
"circle",
|
|
1408
|
+
"polygon"
|
|
1409
|
+
].includes(shape)) throw Error("Fill requires a closed shape");
|
|
1410
|
+
const layer = doc.layers.find((layer) => layer.id === (command.layer ?? doc.active));
|
|
1411
|
+
if (!layer?.visible) throw Error("Target layer is missing or hidden");
|
|
1412
|
+
const stroke = {
|
|
1413
|
+
shape,
|
|
1414
|
+
color,
|
|
1415
|
+
width,
|
|
1416
|
+
opacity,
|
|
1417
|
+
fill,
|
|
1418
|
+
brush: "pen",
|
|
1419
|
+
points: points.map((p) => ({
|
|
1420
|
+
x: p.x,
|
|
1421
|
+
y: p.y
|
|
1422
|
+
}))
|
|
1423
|
+
};
|
|
1424
|
+
doc = {
|
|
1425
|
+
...doc,
|
|
1426
|
+
layers: doc.layers.map((item) => item === layer ? {
|
|
1427
|
+
...item,
|
|
1428
|
+
strokes: [...item.strokes, stroke]
|
|
1429
|
+
} : item)
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
if (strokeCount(doc) > 2e3 || doc.layers.reduce((n, l) => n + l.strokes.reduce((m, s) => m + s.points.length, 0), 0) > 2e5) throw Error("Sketch resource budget exceeded");
|
|
1433
|
+
return doc;
|
|
1434
|
+
}
|
|
1435
|
+
function createSketchCommandSession(adapter) {
|
|
1436
|
+
const completed = /* @__PURE__ */ new Map();
|
|
1437
|
+
let pending = false;
|
|
1438
|
+
return async (request) => {
|
|
1439
|
+
if (!request || typeof request !== "object") throw Error("Invalid sketch request");
|
|
1440
|
+
if (!adapter.available()) throw Error("Open the sketch board for this session first");
|
|
1441
|
+
const current = adapter.snapshot();
|
|
1442
|
+
if (request.action === "inspect") return {
|
|
1443
|
+
...current,
|
|
1444
|
+
help: SKETCH_COMMAND_HELP
|
|
1445
|
+
};
|
|
1446
|
+
if (request.documentId !== current.documentId) throw Error("Document changed; inspect again");
|
|
1447
|
+
if (pending || adapter.busy()) throw Error("Sketch is being edited; retry after it settles");
|
|
1448
|
+
if (request.action === "preview") return {
|
|
1449
|
+
...current,
|
|
1450
|
+
png: await adapter.preview()
|
|
1451
|
+
};
|
|
1452
|
+
if (!["apply", "save"].includes(request.action)) throw Error("Unknown sketch action");
|
|
1453
|
+
if (typeof request.requestId !== "string" || !request.requestId.length || request.requestId.length > 100) throw Error("A unique requestId is required");
|
|
1454
|
+
const key = `${current.documentId}:${request.requestId}`, fingerprint = JSON.stringify(request);
|
|
1455
|
+
const cached = completed.get(key);
|
|
1456
|
+
if (cached) {
|
|
1457
|
+
if (cached.fingerprint !== fingerprint) throw Error("requestId reused with different content");
|
|
1458
|
+
return cached.result;
|
|
1459
|
+
}
|
|
1460
|
+
if (request.revision !== current.revision || adapter.busy()) throw Error("Sketch changed or is being edited; inspect again");
|
|
1461
|
+
if (request.action === "apply") {
|
|
1462
|
+
const next = applySketchCommands(adapter.document(), request.commands);
|
|
1463
|
+
adapter.commit(next);
|
|
1464
|
+
} else {
|
|
1465
|
+
if (request.name !== void 0 && (typeof request.name !== "string" || request.name.length > 60)) throw Error("Invalid draft name");
|
|
1466
|
+
pending = true;
|
|
1467
|
+
try {
|
|
1468
|
+
await adapter.save(request.name);
|
|
1469
|
+
} finally {
|
|
1470
|
+
pending = false;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
const result = adapter.snapshot();
|
|
1474
|
+
completed.set(key, {
|
|
1475
|
+
fingerprint,
|
|
1476
|
+
result
|
|
1477
|
+
});
|
|
1478
|
+
if (completed.size > 128) completed.delete(completed.keys().next().value);
|
|
1479
|
+
return result;
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
//#endregion
|
|
1483
|
+
//#region src/sketch-formats.js
|
|
1484
|
+
const SKETCH_FILE_ACCEPT = ".psd,.dsh-sketch.json,image/png,image/jpeg,image/webp";
|
|
1485
|
+
const canvas = (w, h) => {
|
|
1486
|
+
const c = document.createElement("canvas");
|
|
1487
|
+
c.width = w;
|
|
1488
|
+
c.height = h;
|
|
1489
|
+
return c;
|
|
1490
|
+
};
|
|
1491
|
+
function runPsdCodec(action, payload) {
|
|
1492
|
+
return new Promise((resolve, reject) => {
|
|
1493
|
+
const worker = new Worker("/api/codex-subscription/sketch-psd-worker", { type: "module" });
|
|
1494
|
+
const finish = (callback, value) => {
|
|
1495
|
+
clearTimeout(timer);
|
|
1496
|
+
worker.terminate();
|
|
1497
|
+
callback(value);
|
|
1498
|
+
};
|
|
1499
|
+
const timer = setTimeout(() => finish(reject, Error("PSD operation timed out")), 3e4);
|
|
1500
|
+
worker.onerror = () => finish(reject, Error("PSD codec could not be loaded"));
|
|
1501
|
+
worker.onmessage = (event) => event.data.ok ? finish(resolve, event.data.value) : finish(reject, Error(event.data.error));
|
|
1502
|
+
worker.postMessage({
|
|
1503
|
+
action,
|
|
1504
|
+
payload
|
|
1505
|
+
});
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
function encodeSketchDocument(doc) {
|
|
1509
|
+
return JSON.stringify({
|
|
1510
|
+
format: "dsh-sketch",
|
|
1511
|
+
version: 1,
|
|
1512
|
+
doc
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
function decodeSketchDocument(text) {
|
|
1516
|
+
if (text.length > 32 * 1024 * 1024) throw Error("Draft exceeds 32 MB");
|
|
1517
|
+
const file = JSON.parse(text), source = file.doc;
|
|
1518
|
+
if (file.format !== "dsh-sketch" || file.version !== 1 || !source || !Array.isArray(source.layers) || !source.layers.length || source.layers.length > 8) throw Error("Invalid sketch file");
|
|
1519
|
+
const w = source.width ?? 1024, h = source.height ?? 1024;
|
|
1520
|
+
if (!Number.isInteger(w) || !Number.isInteger(h) || w < 1 || h < 1 || w > 2048 || h > 2048) throw Error("Invalid canvas size");
|
|
1521
|
+
let doc = {
|
|
1522
|
+
...createSketchLayers(),
|
|
1523
|
+
width: w,
|
|
1524
|
+
height: h,
|
|
1525
|
+
ratio: Object.keys(SKETCH_RATIOS).find((k) => SKETCH_RATIOS[k][0] === w && SKETCH_RATIOS[k][1] === h) ?? "custom"
|
|
1526
|
+
};
|
|
1527
|
+
for (let i = 0; i < source.layers.length; i++) {
|
|
1528
|
+
const layer = source.layers[i];
|
|
1529
|
+
if (i) doc = applySketchCommands(doc, [{
|
|
1530
|
+
op: "layer",
|
|
1531
|
+
action: "add"
|
|
1532
|
+
}]);
|
|
1533
|
+
if (!Array.isArray(layer.strokes)) throw Error("Invalid strokes");
|
|
1534
|
+
for (let j = 0; j < layer.strokes.length; j += 256) {
|
|
1535
|
+
const strokes = layer.strokes.slice(j, j + 256);
|
|
1536
|
+
doc = applySketchCommands(doc, strokes.map((s) => ({
|
|
1537
|
+
...s,
|
|
1538
|
+
op: "stroke",
|
|
1539
|
+
layer: doc.active,
|
|
1540
|
+
fill: s.fill ?? false
|
|
1541
|
+
})));
|
|
1542
|
+
const added = doc.layers.at(-1).strokes;
|
|
1543
|
+
for (let k = 0; k < strokes.length; k++) {
|
|
1544
|
+
const s = strokes[k];
|
|
1545
|
+
if (s.brush !== void 0 && ![
|
|
1546
|
+
"pen",
|
|
1547
|
+
"pencil",
|
|
1548
|
+
"marker"
|
|
1549
|
+
].includes(s.brush)) throw Error("Invalid brush");
|
|
1550
|
+
if (s.pressure !== void 0 && (!Number.isFinite(s.pressure) || s.pressure < .2 || s.pressure > 1)) throw Error("Invalid pressure");
|
|
1551
|
+
Object.assign(added[added.length - strokes.length + k], {
|
|
1552
|
+
brush: s.brush ?? "pen",
|
|
1553
|
+
pressure: s.pressure ?? 1
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
const target = doc.layers.at(-1);
|
|
1558
|
+
target.name = String(layer.name ?? "").slice(0, 40);
|
|
1559
|
+
target.visible = layer.visible !== false;
|
|
1560
|
+
if (layer.image) {
|
|
1561
|
+
const image = layer.image;
|
|
1562
|
+
if (typeof image.src !== "string" || !/^data:image\/png;base64,/.test(image.src) || image.src.length > 8 * 1024 * 1024 || [
|
|
1563
|
+
"x",
|
|
1564
|
+
"y",
|
|
1565
|
+
"width",
|
|
1566
|
+
"height"
|
|
1567
|
+
].some((k) => !Number.isFinite(image[k]) || image[k] < 0 || image[k] > 1)) throw Error("Invalid draft image");
|
|
1568
|
+
const bytes = Uint8Array.from(atob(image.src.slice(image.src.indexOf(",") + 1)), (c) => c.charCodeAt(0));
|
|
1569
|
+
if (bytes.length < 24) throw Error("Invalid draft image");
|
|
1570
|
+
const header = new DataView(bytes.buffer);
|
|
1571
|
+
if (header.getUint32(0) !== 2303741511 || header.getUint32(4) !== 218765834 || header.getUint32(16) < 1 || header.getUint32(20) < 1 || header.getUint32(16) > 4096 || header.getUint32(20) > 4096) throw Error("Invalid draft image size");
|
|
1572
|
+
target.image = {
|
|
1573
|
+
src: image.src,
|
|
1574
|
+
x: image.x,
|
|
1575
|
+
y: image.y,
|
|
1576
|
+
width: image.width,
|
|
1577
|
+
height: image.height
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
const activeIndex = source.layers.findIndex((layer) => layer.id === source.active);
|
|
1582
|
+
doc.active = doc.layers[Math.max(0, activeIndex)].id;
|
|
1583
|
+
return doc;
|
|
1584
|
+
}
|
|
1585
|
+
async function exportSketchPsd(doc, images, composite) {
|
|
1586
|
+
const width = doc.width ?? 1024, height = doc.height ?? 1024;
|
|
1587
|
+
const children = doc.layers.map((layer, i) => {
|
|
1588
|
+
const ctx = canvas(width, height).getContext("2d"), ref = layer.image;
|
|
1589
|
+
if (ref) ctx.drawImage(images.get(ref.src), ref.x * width, ref.y * height, ref.width * width, ref.height * height);
|
|
1590
|
+
paintSketch(ctx, layer.strokes, width, true, height);
|
|
1591
|
+
return {
|
|
1592
|
+
name: layer.name || `Layer ${i + 1}`,
|
|
1593
|
+
hidden: !layer.visible,
|
|
1594
|
+
opacity: 1,
|
|
1595
|
+
blendMode: "normal",
|
|
1596
|
+
imageData: ctx.getImageData(0, 0, width, height)
|
|
1597
|
+
};
|
|
1598
|
+
});
|
|
1599
|
+
const context = canvas(width, height).getContext("2d");
|
|
1600
|
+
context.fillStyle = "#fff";
|
|
1601
|
+
context.fillRect(0, 0, width, height);
|
|
1602
|
+
if (children[0] && !children[0].hidden) {
|
|
1603
|
+
const bottom = canvas(width, height);
|
|
1604
|
+
bottom.getContext("2d").putImageData(children[0].imageData, 0, 0);
|
|
1605
|
+
context.drawImage(bottom, 0, 0);
|
|
1606
|
+
children[0].imageData = context.getImageData(0, 0, width, height);
|
|
1607
|
+
} else {
|
|
1608
|
+
if (children.length >= 8) throw Error("Show the bottom layer before exporting this eight-layer drawing");
|
|
1609
|
+
children.unshift({
|
|
1610
|
+
name: "Paper",
|
|
1611
|
+
opacity: 1,
|
|
1612
|
+
blendMode: "normal",
|
|
1613
|
+
imageData: context.getImageData(0, 0, width, height)
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
return runPsdCodec("write", {
|
|
1617
|
+
width,
|
|
1618
|
+
height,
|
|
1619
|
+
children,
|
|
1620
|
+
imageData: composite.getContext("2d").getImageData(0, 0, width, height)
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
async function importSketchPsd(file) {
|
|
1624
|
+
if (file.size > 32 * 1024 * 1024) throw Error("PSD exceeds 32 MB");
|
|
1625
|
+
const psd = await runPsdCodec("read", await file.arrayBuffer());
|
|
1626
|
+
const scale = Math.min(1, 1024 / Math.max(psd.width, psd.height)), width = Math.max(1, Math.round(psd.width * scale)), height = Math.max(1, Math.round(psd.height * scale));
|
|
1627
|
+
const doc = {
|
|
1628
|
+
...createSketchLayers(),
|
|
1629
|
+
width,
|
|
1630
|
+
height,
|
|
1631
|
+
ratio: Object.keys(SKETCH_RATIOS).find((k) => SKETCH_RATIOS[k][0] === width && SKETCH_RATIOS[k][1] === height) ?? "custom",
|
|
1632
|
+
layers: [],
|
|
1633
|
+
nextId: psd.layers.length + 1
|
|
1634
|
+
};
|
|
1635
|
+
for (const [i, layer] of psd.layers.entries()) {
|
|
1636
|
+
const src = canvas(layer.imageData.width, layer.imageData.height);
|
|
1637
|
+
src.getContext("2d").putImageData(new ImageData(new Uint8ClampedArray(layer.imageData.data), layer.imageData.width, layer.imageData.height), 0, 0);
|
|
1638
|
+
const out = canvas(width, height), ctx = out.getContext("2d");
|
|
1639
|
+
ctx.globalAlpha = layer.opacity;
|
|
1640
|
+
ctx.drawImage(src, layer.left * scale, layer.top * scale, src.width * scale, src.height * scale);
|
|
1641
|
+
doc.layers.push({
|
|
1642
|
+
id: i + 1,
|
|
1643
|
+
name: layer.name,
|
|
1644
|
+
visible: !layer.hidden,
|
|
1645
|
+
strokes: [],
|
|
1646
|
+
image: {
|
|
1647
|
+
src: out.toDataURL("image/png"),
|
|
1648
|
+
x: 0,
|
|
1649
|
+
y: 0,
|
|
1650
|
+
width: 1,
|
|
1651
|
+
height: 1
|
|
1652
|
+
}
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
return doc;
|
|
1656
|
+
}
|
|
1256
1657
|
//#endregion
|
|
1257
1658
|
//#region src/sketch-files.jsx
|
|
1258
|
-
function SketchFiles({ save, load, fresh, importImage, disabled, t, report, onWorking }) {
|
|
1659
|
+
function SketchFiles({ save, load, fresh, importImage, download, hasContent, disabled, t, report, onWorking }) {
|
|
1259
1660
|
const [open, setOpen] = (0, react.useState)(false), [rows, setRows] = (0, react.useState)([]), [name, setName] = (0, react.useState)(""), [remove, setRemove] = (0, react.useState)(null), [working, setWorking] = (0, react.useState)(false);
|
|
1661
|
+
const [format, setFormat] = (0, react.useState)("png");
|
|
1260
1662
|
const input = (0, react.useRef)(null), host = (0, react.useRef)(null);
|
|
1261
1663
|
(0, react.useEffect)(() => {
|
|
1262
1664
|
if (open && !working) host.current?.querySelector("input:not([type=file])")?.focus({ preventScroll: true });
|
|
@@ -1271,8 +1673,8 @@ window.__ModuleLoader__.load({
|
|
|
1271
1673
|
onWorking(true);
|
|
1272
1674
|
try {
|
|
1273
1675
|
await operation();
|
|
1274
|
-
} catch {
|
|
1275
|
-
report(t("sketchStorageFailed"));
|
|
1676
|
+
} catch (error) {
|
|
1677
|
+
report(error?.message || t("sketchStorageFailed"));
|
|
1276
1678
|
} finally {
|
|
1277
1679
|
setWorking(false);
|
|
1278
1680
|
onWorking(false);
|
|
@@ -1303,7 +1705,7 @@ window.__ModuleLoader__.load({
|
|
|
1303
1705
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1304
1706
|
ref: input,
|
|
1305
1707
|
type: "file",
|
|
1306
|
-
accept:
|
|
1708
|
+
accept: SKETCH_FILE_ACCEPT,
|
|
1307
1709
|
hidden: true,
|
|
1308
1710
|
onChange: (e) => {
|
|
1309
1711
|
const file = e.target.files?.[0];
|
|
@@ -1352,6 +1754,32 @@ window.__ModuleLoader__.load({
|
|
|
1352
1754
|
}),
|
|
1353
1755
|
children: t("sketchSave")
|
|
1354
1756
|
}),
|
|
1757
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1758
|
+
className: "codexSketchExportFormats codexSketchSegment",
|
|
1759
|
+
role: "group",
|
|
1760
|
+
"aria-label": t("sketchExportFormat"),
|
|
1761
|
+
children: [
|
|
1762
|
+
["png", "PNG"],
|
|
1763
|
+
["psd", "PSD"],
|
|
1764
|
+
["draft", t("sketchEditableFile")]
|
|
1765
|
+
].map(([value, label]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1766
|
+
type: "button",
|
|
1767
|
+
"aria-pressed": format === value,
|
|
1768
|
+
disabled: working,
|
|
1769
|
+
onClick: () => setFormat(value),
|
|
1770
|
+
children: label
|
|
1771
|
+
}, value))
|
|
1772
|
+
}),
|
|
1773
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1774
|
+
type: "button",
|
|
1775
|
+
disabled: working || !hasContent,
|
|
1776
|
+
onClick: () => void run(async () => {
|
|
1777
|
+
await download(format);
|
|
1778
|
+
setOpen(false);
|
|
1779
|
+
}),
|
|
1780
|
+
children: t("sketchDownload")
|
|
1781
|
+
}),
|
|
1782
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("sketchFormatHint") }),
|
|
1355
1783
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("sketchLocalDrafts") }),
|
|
1356
1784
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1357
1785
|
className: "codexSketchDraftList",
|
|
@@ -1430,6 +1858,62 @@ window.__ModuleLoader__.load({
|
|
|
1430
1858
|
});
|
|
1431
1859
|
}
|
|
1432
1860
|
//#endregion
|
|
1861
|
+
//#region src/sketch-agent-client.js
|
|
1862
|
+
function connectSketchAgent(rpc, sessionId, execute, report, pollDelay = () => 350) {
|
|
1863
|
+
let stopped = false, token, timer;
|
|
1864
|
+
const call = (endpoint, payload) => rpc.call(CHANNEL, `sketch/${endpoint}`, {
|
|
1865
|
+
sessionId,
|
|
1866
|
+
token,
|
|
1867
|
+
...payload
|
|
1868
|
+
}).then(unwrap);
|
|
1869
|
+
const poll = async () => {
|
|
1870
|
+
try {
|
|
1871
|
+
const tasks = await call("poll");
|
|
1872
|
+
for (const task of tasks) {
|
|
1873
|
+
if (stopped) break;
|
|
1874
|
+
let value, error;
|
|
1875
|
+
try {
|
|
1876
|
+
if (task.expiresAt < Date.now()) throw Error("Sketch command expired; inspect before retrying");
|
|
1877
|
+
value = await execute(task.request);
|
|
1878
|
+
} catch (cause) {
|
|
1879
|
+
error = cause.message;
|
|
1880
|
+
}
|
|
1881
|
+
await call("result", {
|
|
1882
|
+
id: task.id,
|
|
1883
|
+
value,
|
|
1884
|
+
error
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
} catch (error) {
|
|
1888
|
+
if (!stopped) report(error.message);
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
if (!stopped) timer = setTimeout(poll, pollDelay());
|
|
1892
|
+
};
|
|
1893
|
+
call("connect").then((value) => {
|
|
1894
|
+
token = value.token;
|
|
1895
|
+
if (stopped) call("disconnect").catch(() => {});
|
|
1896
|
+
else poll();
|
|
1897
|
+
}, (error) => {
|
|
1898
|
+
if (!stopped) report(error.message);
|
|
1899
|
+
});
|
|
1900
|
+
return () => {
|
|
1901
|
+
stopped = true;
|
|
1902
|
+
clearTimeout(timer);
|
|
1903
|
+
if (token) call("disconnect").catch(() => {});
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
async function executeSketchFromAgent(request, { available, open, execute, live = () => true, wait = () => new Promise((resolve) => setTimeout(resolve, 20)) }) {
|
|
1907
|
+
if (!available()) {
|
|
1908
|
+
if (request.action !== "inspect") throw Error("Sketch board closed; inspect before editing");
|
|
1909
|
+
if (!live()) throw Error("Sketch session disconnected");
|
|
1910
|
+
open();
|
|
1911
|
+
for (let i = 0; i < 100 && live() && !available(); i++) await wait();
|
|
1912
|
+
}
|
|
1913
|
+
if (!live() || !available()) throw Error("Sketch board could not open in the current session");
|
|
1914
|
+
return execute(request);
|
|
1915
|
+
}
|
|
1916
|
+
//#endregion
|
|
1433
1917
|
//#region src/sketch-studio.jsx
|
|
1434
1918
|
const PALETTE = [
|
|
1435
1919
|
"#18181b",
|
|
@@ -1440,15 +1924,17 @@ window.__ModuleLoader__.load({
|
|
|
1440
1924
|
"#34c759",
|
|
1441
1925
|
"#0088ff"
|
|
1442
1926
|
];
|
|
1443
|
-
function SketchStudio({ open, onClose, attachSketch, enabled, t, incoming }) {
|
|
1927
|
+
function SketchStudio({ open, agentEnabled, onOpen, onClose, attachSketch, enabled, t, incoming, sessionId, rpc }) {
|
|
1444
1928
|
const dialog = (0, react.useRef)(null), canvas = (0, react.useRef)(null), doc = (0, react.useRef)(createSketchLayers()), cache = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
1445
1929
|
const undo = (0, react.useRef)([]), redo = (0, react.useRef)([]), active = (0, react.useRef)(null), frame = (0, react.useRef)(null);
|
|
1446
1930
|
const images = (0, react.useRef)(/* @__PURE__ */ new Map()), saved = (0, react.useRef)(null), dirty = (0, react.useRef)(false), updateUi = (0, react.useRef)(false);
|
|
1931
|
+
const documentId = (0, react.useRef)(crypto.randomUUID()), documentRevision = (0, react.useRef)(0), agentAdapter = (0, react.useRef)({}), agentSession = (0, react.useRef)(null);
|
|
1447
1932
|
const [stability, setStability] = (0, react.useState)(0), [flow, setFlow] = (0, react.useState)(100), [picturesOpen, setPicturesOpen] = (0, react.useState)(false);
|
|
1448
1933
|
const pictureInput = (0, react.useRef)(null), received = (0, react.useRef)(null);
|
|
1449
1934
|
const navigation = useSketchView(canvas, open);
|
|
1450
1935
|
const [revision, redraw] = (0, react.useState)(0), [tool, setTool] = (0, react.useState)("pen"), [brush, setBrush] = (0, react.useState)("pen");
|
|
1451
1936
|
const [eraser, setEraser] = (0, react.useState)("pixel"), [color, setColor] = (0, react.useState)("#0088ff"), [width, setWidth] = (0, react.useState)(12);
|
|
1937
|
+
const [fillShape, setFillShape] = (0, react.useState)(false);
|
|
1452
1938
|
const [layersOpen, setLayersOpen] = (0, react.useState)(false), [busy, setBusy] = (0, react.useState)(false), [error, setError] = (0, react.useState)("");
|
|
1453
1939
|
const cursorRing = (0, react.useRef)(null);
|
|
1454
1940
|
const cursor = useSketchCursor(canvas, cursorRing, width, tool === "pen" ? brush : "pen", navigation.view.scale, !open || navigation.space || busy);
|
|
@@ -1474,6 +1960,7 @@ window.__ModuleLoader__.load({
|
|
|
1474
1960
|
});
|
|
1475
1961
|
};
|
|
1476
1962
|
const checkpoint = () => {
|
|
1963
|
+
documentRevision.current++;
|
|
1477
1964
|
dirty.current = true;
|
|
1478
1965
|
undo.current.push(doc.current);
|
|
1479
1966
|
if (undo.current.length > 30) undo.current.shift();
|
|
@@ -1484,6 +1971,7 @@ window.__ModuleLoader__.load({
|
|
|
1484
1971
|
const next = changeSketchLayer(doc.current, action, id, value);
|
|
1485
1972
|
if (next === doc.current) return;
|
|
1486
1973
|
if (action !== "select") checkpoint();
|
|
1974
|
+
else documentRevision.current++;
|
|
1487
1975
|
doc.current = next;
|
|
1488
1976
|
setError("");
|
|
1489
1977
|
schedule();
|
|
@@ -1517,6 +2005,8 @@ window.__ModuleLoader__.load({
|
|
|
1517
2005
|
if (dirty.current && (hasContent() || saved.current)) await save();
|
|
1518
2006
|
};
|
|
1519
2007
|
const replace = (next, decoded, identity) => {
|
|
2008
|
+
documentId.current = crypto.randomUUID();
|
|
2009
|
+
documentRevision.current++;
|
|
1520
2010
|
doc.current = structuredClone(next);
|
|
1521
2011
|
images.current = decoded;
|
|
1522
2012
|
cache.current.clear();
|
|
@@ -1541,6 +2031,16 @@ window.__ModuleLoader__.load({
|
|
|
1541
2031
|
});
|
|
1542
2032
|
};
|
|
1543
2033
|
const importImage = async (file) => {
|
|
2034
|
+
if (file.name?.toLowerCase().endsWith(".psd") || file.name?.toLowerCase().endsWith(".dsh-sketch.json")) {
|
|
2035
|
+
if (file.size > 32 * 1024 * 1024) throw Error("File exceeds 32 MB");
|
|
2036
|
+
const next = file.name.toLowerCase().endsWith(".psd") ? await importSketchPsd(file) : decodeSketchDocument(await file.text());
|
|
2037
|
+
const decoded = /* @__PURE__ */ new Map();
|
|
2038
|
+
await decodeSketchImages(next, decoded);
|
|
2039
|
+
await saveChanges();
|
|
2040
|
+
replace(next, decoded, null);
|
|
2041
|
+
dirty.current = true;
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
1544
2044
|
if (doc.current.layers.length >= 8) throw Error("Layer limit");
|
|
1545
2045
|
const image = await importSketchImage(file), w = doc.current.width ?? 1024, h = doc.current.height ?? 1024;
|
|
1546
2046
|
const scale = Math.min(w / image.width, h / image.height), width = image.width * scale / w, height = image.height * scale / h;
|
|
@@ -1703,11 +2203,101 @@ window.__ModuleLoader__.load({
|
|
|
1703
2203
|
if (busy || active.current) return;
|
|
1704
2204
|
const source = direction === "undo" ? undo : redo, target = direction === "undo" ? redo : undo;
|
|
1705
2205
|
if (!source.current.length) return;
|
|
2206
|
+
documentRevision.current++;
|
|
1706
2207
|
dirty.current = true;
|
|
1707
2208
|
target.current.push(doc.current);
|
|
1708
2209
|
doc.current = source.current.pop();
|
|
1709
2210
|
schedule();
|
|
1710
2211
|
};
|
|
2212
|
+
Object.assign(agentAdapter.current, {
|
|
2213
|
+
available: () => open && enabled,
|
|
2214
|
+
open: () => onOpen(),
|
|
2215
|
+
busy: () => busy || Boolean(active.current),
|
|
2216
|
+
document: () => doc.current,
|
|
2217
|
+
snapshot: () => ({
|
|
2218
|
+
documentId: documentId.current,
|
|
2219
|
+
revision: documentRevision.current,
|
|
2220
|
+
width: doc.current.width ?? 1024,
|
|
2221
|
+
height: doc.current.height ?? 1024,
|
|
2222
|
+
active: doc.current.active,
|
|
2223
|
+
layers: doc.current.layers.map((layer) => ({
|
|
2224
|
+
id: layer.id,
|
|
2225
|
+
name: layer.name,
|
|
2226
|
+
visible: layer.visible,
|
|
2227
|
+
strokes: layer.strokes.length,
|
|
2228
|
+
image: Boolean(layer.image)
|
|
2229
|
+
})),
|
|
2230
|
+
strokeCount: strokeCount(doc.current)
|
|
2231
|
+
}),
|
|
2232
|
+
commit: (next) => {
|
|
2233
|
+
checkpoint();
|
|
2234
|
+
doc.current = next;
|
|
2235
|
+
setError("");
|
|
2236
|
+
schedule();
|
|
2237
|
+
},
|
|
2238
|
+
preview: async () => {
|
|
2239
|
+
paint();
|
|
2240
|
+
return canvas.current.toDataURL("image/png");
|
|
2241
|
+
},
|
|
2242
|
+
save: async (name) => {
|
|
2243
|
+
setBusy(true);
|
|
2244
|
+
try {
|
|
2245
|
+
await save(name);
|
|
2246
|
+
} finally {
|
|
2247
|
+
setBusy(false);
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2251
|
+
agentSession.current ??= createSketchCommandSession(agentAdapter.current);
|
|
2252
|
+
(0, react.useEffect)(() => {
|
|
2253
|
+
if (!open || !enabled || !agentEnabled) return;
|
|
2254
|
+
const api = Object.freeze({
|
|
2255
|
+
version: 1,
|
|
2256
|
+
sessionId,
|
|
2257
|
+
execute: (request) => agentSession.current(request),
|
|
2258
|
+
export: async (format) => {
|
|
2259
|
+
if (agentAdapter.current.busy()) throw Error("Sketch is being edited");
|
|
2260
|
+
const { blob, extension } = await agentAdapter.current.export(format);
|
|
2261
|
+
const data = new Uint8Array(await blob.arrayBuffer());
|
|
2262
|
+
let raw = "";
|
|
2263
|
+
for (let i = 0; i < data.length; i += 8192) raw += String.fromCharCode(...data.subarray(i, i + 8192));
|
|
2264
|
+
return {
|
|
2265
|
+
extension,
|
|
2266
|
+
mediaType: blob.type,
|
|
2267
|
+
base64: btoa(raw)
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
});
|
|
2271
|
+
window.dshSketchAgent = api;
|
|
2272
|
+
return () => {
|
|
2273
|
+
if (window.dshSketchAgent === api) delete window.dshSketchAgent;
|
|
2274
|
+
};
|
|
2275
|
+
}, [
|
|
2276
|
+
open,
|
|
2277
|
+
enabled,
|
|
2278
|
+
agentEnabled,
|
|
2279
|
+
rpc,
|
|
2280
|
+
sessionId
|
|
2281
|
+
]);
|
|
2282
|
+
(0, react.useEffect)(() => {
|
|
2283
|
+
if (!enabled || !agentEnabled || !rpc || !sessionId) return;
|
|
2284
|
+
let live = true;
|
|
2285
|
+
const disconnect = connectSketchAgent(rpc, sessionId, (request) => executeSketchFromAgent(request, {
|
|
2286
|
+
available: () => agentAdapter.current.available(),
|
|
2287
|
+
open: () => agentAdapter.current.open(),
|
|
2288
|
+
execute: (request) => agentSession.current(request),
|
|
2289
|
+
live: () => live
|
|
2290
|
+
}), (message) => setError(message), () => agentAdapter.current.available() ? 350 : 1500);
|
|
2291
|
+
return () => {
|
|
2292
|
+
live = false;
|
|
2293
|
+
disconnect();
|
|
2294
|
+
};
|
|
2295
|
+
}, [
|
|
2296
|
+
enabled,
|
|
2297
|
+
agentEnabled,
|
|
2298
|
+
rpc,
|
|
2299
|
+
sessionId
|
|
2300
|
+
]);
|
|
1711
2301
|
const attach = async () => {
|
|
1712
2302
|
if (!enabled || busy) return;
|
|
1713
2303
|
setBusy(true);
|
|
@@ -1724,6 +2314,34 @@ window.__ModuleLoader__.load({
|
|
|
1724
2314
|
setBusy(false);
|
|
1725
2315
|
}
|
|
1726
2316
|
};
|
|
2317
|
+
const exportFile = async (format = "png") => {
|
|
2318
|
+
paint();
|
|
2319
|
+
if (format === "draft") return {
|
|
2320
|
+
blob: new Blob([encodeSketchDocument(doc.current)], { type: "application/json" }),
|
|
2321
|
+
extension: "dsh-sketch.json"
|
|
2322
|
+
};
|
|
2323
|
+
if (format === "psd") return {
|
|
2324
|
+
blob: new Blob([await exportSketchPsd(doc.current, images.current, canvas.current)], { type: "image/vnd.adobe.photoshop" }),
|
|
2325
|
+
extension: "psd"
|
|
2326
|
+
};
|
|
2327
|
+
if (format !== "png") throw Error("Unsupported format");
|
|
2328
|
+
return {
|
|
2329
|
+
blob: await new Promise((resolve, reject) => canvas.current.toBlob((blob) => blob ? resolve(blob) : reject(Error("PNG")), "image/png")),
|
|
2330
|
+
extension: "png"
|
|
2331
|
+
};
|
|
2332
|
+
};
|
|
2333
|
+
agentAdapter.current.export = exportFile;
|
|
2334
|
+
const download = async (format) => {
|
|
2335
|
+
const { blob, extension } = await exportFile(format);
|
|
2336
|
+
await saveChanges();
|
|
2337
|
+
const url = URL.createObjectURL(blob), link = document.createElement("a");
|
|
2338
|
+
link.href = url;
|
|
2339
|
+
link.download = `${(saved.current?.name || "sketch").replace(/[\\/:*?"<>|\u0000-\u001f]/g, "-").slice(0, 80)}.${extension}`;
|
|
2340
|
+
document.body.append(link);
|
|
2341
|
+
link.click();
|
|
2342
|
+
link.remove();
|
|
2343
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e4);
|
|
2344
|
+
};
|
|
1727
2345
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dialog", {
|
|
1728
2346
|
ref: dialog,
|
|
1729
2347
|
className: "codexSketchDialog codexSketchStudio codexLayerStudio",
|
|
@@ -1774,6 +2392,8 @@ window.__ModuleLoader__.load({
|
|
|
1774
2392
|
load,
|
|
1775
2393
|
fresh,
|
|
1776
2394
|
importImage,
|
|
2395
|
+
download,
|
|
2396
|
+
hasContent: doc.current.layers.some((l) => l.visible && (l.image || l.strokes.length)),
|
|
1777
2397
|
disabled: busy,
|
|
1778
2398
|
t,
|
|
1779
2399
|
report: setError,
|
|
@@ -1801,7 +2421,7 @@ window.__ModuleLoader__.load({
|
|
|
1801
2421
|
})
|
|
1802
2422
|
}, name))
|
|
1803
2423
|
}),
|
|
1804
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2424
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1805
2425
|
className: "codexSketchRatio",
|
|
1806
2426
|
"aria-label": t("sketchRatio"),
|
|
1807
2427
|
title: t("sketchRatioHint"),
|
|
@@ -1815,10 +2435,18 @@ window.__ModuleLoader__.load({
|
|
|
1815
2435
|
doc.current = next;
|
|
1816
2436
|
schedule();
|
|
1817
2437
|
},
|
|
1818
|
-
children:
|
|
2438
|
+
children: [doc.current.ratio === "custom" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
2439
|
+
value: "custom",
|
|
2440
|
+
disabled: true,
|
|
2441
|
+
children: [
|
|
2442
|
+
doc.current.width,
|
|
2443
|
+
"×",
|
|
2444
|
+
doc.current.height
|
|
2445
|
+
]
|
|
2446
|
+
}) : null, Object.keys(SKETCH_RATIOS).map((ratio) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1819
2447
|
value: ratio,
|
|
1820
2448
|
children: ratio
|
|
1821
|
-
}, ratio))
|
|
2449
|
+
}, ratio))]
|
|
1822
2450
|
}),
|
|
1823
2451
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1824
2452
|
type: "button",
|
|
@@ -1873,7 +2501,11 @@ window.__ModuleLoader__.load({
|
|
|
1873
2501
|
setError(t("sketchHiddenLayer"));
|
|
1874
2502
|
return;
|
|
1875
2503
|
}
|
|
1876
|
-
if (!(tool === "eraser" && eraser === "stroke") && strokeCount(doc.current) >=
|
|
2504
|
+
if (!(tool === "eraser" && eraser === "stroke") && strokeCount(doc.current) >= 2e3) {
|
|
2505
|
+
setError(t("sketchLimit"));
|
|
2506
|
+
return;
|
|
2507
|
+
}
|
|
2508
|
+
if (tool !== "eraser" && doc.current.layers.reduce((n, l) => n + l.strokes.reduce((m, s) => m + s.points.length, 0), 0) >= 2e5) {
|
|
1877
2509
|
setError(t("sketchLimit"));
|
|
1878
2510
|
return;
|
|
1879
2511
|
}
|
|
@@ -1895,6 +2527,7 @@ window.__ModuleLoader__.load({
|
|
|
1895
2527
|
opacity: flow / 100,
|
|
1896
2528
|
shape: tool,
|
|
1897
2529
|
width,
|
|
2530
|
+
fill: fillShape && ["rectangle", "circle"].includes(tool),
|
|
1898
2531
|
brush: tool === "pen" ? brush : "pen",
|
|
1899
2532
|
pressure: event.pointerType === "pen" ? Math.max(.2, event.pressure) : 1,
|
|
1900
2533
|
points: [start]
|
|
@@ -2021,7 +2654,7 @@ window.__ModuleLoader__.load({
|
|
|
2021
2654
|
type: "button",
|
|
2022
2655
|
title: t(`sketchLayer_${action}`),
|
|
2023
2656
|
"aria-label": t(`sketchLayer_${action}`),
|
|
2024
|
-
disabled: busy || action === "delete" && doc.current.layers.length === 1 || action === "duplicate" && (doc.current.layers.length >= 8 || strokeCount(doc.current) + current.strokes.length >
|
|
2657
|
+
disabled: busy || action === "delete" && doc.current.layers.length === 1 || action === "duplicate" && (doc.current.layers.length >= 8 || strokeCount(doc.current) + current.strokes.length > 2e3) || action === "up" && current === doc.current.layers.at(-1) || action === "down" && current === doc.current.layers[0],
|
|
2025
2658
|
onClick: () => change(action),
|
|
2026
2659
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
|
|
2027
2660
|
name: action === "delete" ? "clear" : action,
|
|
@@ -2097,6 +2730,11 @@ window.__ModuleLoader__.load({
|
|
|
2097
2730
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2098
2731
|
className: "codexLayerBrush",
|
|
2099
2732
|
children: [
|
|
2733
|
+
["rectangle", "circle"].includes(tool) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2734
|
+
type: "checkbox",
|
|
2735
|
+
checked: fillShape,
|
|
2736
|
+
onChange: (e) => setFillShape(e.target.checked)
|
|
2737
|
+
}), t("sketchFill")] }) : null,
|
|
2100
2738
|
tool === "pen" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2101
2739
|
className: "codexSketchStability",
|
|
2102
2740
|
children: [t("sketchStability"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
@@ -2241,6 +2879,7 @@ window.__ModuleLoader__.load({
|
|
|
2241
2879
|
.codexSketchFilePanel button{min-height:36px;border-radius:8px;padding:4px 10px;background:color-mix(in srgb,var(--sketch-fg) 6%,transparent)}
|
|
2242
2880
|
.codexSketchFilePanel input{min-width:0;min-height:36px;border:1px solid var(--sketch-line);border-radius:8px;padding:6px 10px;background:var(--sketch-bg);color:inherit;font:inherit}
|
|
2243
2881
|
.codexSketchFilePanel small{color:var(--sketch-muted);line-height:1.5}.codexSketchFileActions{display:flex;gap:8px}.codexSketchFileActions>button{flex:1}
|
|
2882
|
+
.codexSketchFilePanel .codexSketchExportFormats{gap:2px;border-radius:10px}.codexSketchFilePanel .codexSketchExportFormats button{flex:1;min-width:0;min-height:34px;padding:4px 8px;background:transparent;font-size:12px}.codexSketchFilePanel .codexSketchExportFormats button[aria-pressed=true]{background:var(--sketch-bg);box-shadow:0 1px 4px #0002,inset 0 0 0 1px var(--sketch-line)}
|
|
2244
2883
|
.codexSketchDraftList{max-height:220px;overflow:auto}.codexSketchDraftList>div{display:flex;gap:6px;margin-bottom:6px}.codexSketchDraftList button:first-child{flex:1;min-width:0;justify-content:flex-start;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
2245
2884
|
.codexSketchStability{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--sketch-muted)}.codexSketchStability select{height:32px;border:1px solid var(--sketch-line);border-radius:8px;background:var(--sketch-bg);color:var(--sketch-fg);font:inherit}
|
|
2246
2885
|
@media(max-width:760px){.codexSketchTop{gap:2px}.codexSketchUtility{gap:2px}.codexSketchRound{width:32px}.codexSketchLayersToggle{padding:0 6px}.codexSketchFiles>button{padding:0 6px}.codexSketchRatio{max-width:65px}.codexSketchPill button{min-width:42px}.codexSketchDialog .codexSketchConfirm{padding:0 8px}.codexSketchTop{flex-shrink:0}}
|
|
@@ -2254,7 +2893,7 @@ window.__ModuleLoader__.load({
|
|
|
2254
2893
|
`;
|
|
2255
2894
|
//#endregion
|
|
2256
2895
|
//#region src/sketch-workspace.jsx
|
|
2257
|
-
function SketchWorkspace({ preference, attachSketch, registerOpen, t }) {
|
|
2896
|
+
function SketchWorkspace({ preference, attachSketch, registerOpen, t, sessionId, rpc }) {
|
|
2258
2897
|
const settings = (0, react.useSyncExternalStore)(preference.subscribe, preference.getSnapshot);
|
|
2259
2898
|
const [open, setOpen] = (0, react.useState)(false);
|
|
2260
2899
|
const [incoming, setIncoming] = (0, react.useState)(null);
|
|
@@ -2267,6 +2906,13 @@ window.__ModuleLoader__.load({
|
|
|
2267
2906
|
}
|
|
2268
2907
|
}), [registerOpen]);
|
|
2269
2908
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SketchStudio, {
|
|
2909
|
+
agentEnabled: settings.imageSketchAgent,
|
|
2910
|
+
onOpen: () => {
|
|
2911
|
+
opener.current = document.activeElement;
|
|
2912
|
+
setOpen(true);
|
|
2913
|
+
},
|
|
2914
|
+
sessionId,
|
|
2915
|
+
rpc,
|
|
2270
2916
|
incoming,
|
|
2271
2917
|
open,
|
|
2272
2918
|
onClose: () => {
|
|
@@ -2367,6 +3013,11 @@ window.__ModuleLoader__.load({
|
|
|
2367
3013
|
//#endregion
|
|
2368
3014
|
//#region src/client-locales.js
|
|
2369
3015
|
const zh = {
|
|
3016
|
+
sketchFill: "填色",
|
|
3017
|
+
sketchDownload: "下载",
|
|
3018
|
+
sketchExportFormat: "导出格式",
|
|
3019
|
+
sketchEditableFile: "可编辑草稿",
|
|
3020
|
+
sketchFormatHint: "PNG 为合并图片;PSD 交换像素图层;可编辑草稿保留原生笔画。可导入 PSD 或草稿文件(32 MB 内);PSD 仅支持普通像素图层,长边适配至 1024。",
|
|
2370
3021
|
imageToSketch: "进入草图",
|
|
2371
3022
|
sketchPictures: "图片",
|
|
2372
3023
|
sketchPictureAdd: "添加",
|
|
@@ -2391,7 +3042,7 @@ window.__ModuleLoader__.load({
|
|
|
2391
3042
|
sketchKey_fit: "重置视图",
|
|
2392
3043
|
sketchFiles: "草稿",
|
|
2393
3044
|
sketchNew: "新建草图",
|
|
2394
|
-
sketchImport: "
|
|
3045
|
+
sketchImport: "导入文件",
|
|
2395
3046
|
sketchDraftName: "草稿名称",
|
|
2396
3047
|
sketchSave: "保存草稿",
|
|
2397
3048
|
sketchDeleteDraft: "删除",
|
|
@@ -2430,7 +3081,7 @@ window.__ModuleLoader__.load({
|
|
|
2430
3081
|
imageCapability_on: "启用",
|
|
2431
3082
|
imageCapability_off: "关闭",
|
|
2432
3083
|
imageEntryPoints: "创作入口",
|
|
2433
|
-
imageEntryPointsHint: "
|
|
3084
|
+
imageEntryPointsHint: "@生图入口;隐藏后仍可用自然语言生图。",
|
|
2434
3085
|
imageEntryPoints_on: "显示",
|
|
2435
3086
|
imageEntryPoints_off: "隐藏",
|
|
2436
3087
|
imageBrowsing: "图片浏览",
|
|
@@ -2466,6 +3117,14 @@ window.__ModuleLoader__.load({
|
|
|
2466
3117
|
sketchTool_rectangle: "矩形",
|
|
2467
3118
|
sketchTool_circle: "椭圆",
|
|
2468
3119
|
sketchTool_eraser: "橡皮",
|
|
3120
|
+
sketchCanvas: "草图画板 · Beta",
|
|
3121
|
+
sketchCanvasHint: "默认关闭;开启后显示手动画板入口。",
|
|
3122
|
+
sketchCanvas_on: "开启",
|
|
3123
|
+
sketchCanvas_off: "关闭",
|
|
3124
|
+
sketchAgent: "Agent 绘图 · Beta",
|
|
3125
|
+
sketchAgentHint: "默认关闭;开启后向模型提供绘图工具,需要同时开启草图画板。",
|
|
3126
|
+
sketchAgent_on: "开启",
|
|
3127
|
+
sketchAgent_off: "关闭",
|
|
2469
3128
|
imageSettings: "图片",
|
|
2470
3129
|
imageGeneration: "生成新图片",
|
|
2471
3130
|
imageEditing: "参考图编辑",
|
|
@@ -2739,6 +3398,11 @@ window.__ModuleLoader__.load({
|
|
|
2739
3398
|
imageRemoveAnnotation: "删除标注"
|
|
2740
3399
|
};
|
|
2741
3400
|
const en = {
|
|
3401
|
+
sketchFill: "Fill",
|
|
3402
|
+
sketchDownload: "Download",
|
|
3403
|
+
sketchExportFormat: "Export format",
|
|
3404
|
+
sketchEditableFile: "Editable draft",
|
|
3405
|
+
sketchFormatHint: "PNG is flattened; PSD exchanges pixel layers; editable drafts retain native strokes. Import PSD or draft files up to 32 MB. PSD supports normal pixel layers, fitted to a 1024 px long edge.",
|
|
2742
3406
|
imageToSketch: "Open in sketch",
|
|
2743
3407
|
sketchPictures: "Images",
|
|
2744
3408
|
sketchPictureAdd: "Add",
|
|
@@ -2763,7 +3427,7 @@ window.__ModuleLoader__.load({
|
|
|
2763
3427
|
sketchKey_fit: "Reset view",
|
|
2764
3428
|
sketchFiles: "Drafts",
|
|
2765
3429
|
sketchNew: "New sketch",
|
|
2766
|
-
sketchImport: "Import
|
|
3430
|
+
sketchImport: "Import file",
|
|
2767
3431
|
sketchDraftName: "Draft name",
|
|
2768
3432
|
sketchSave: "Save draft",
|
|
2769
3433
|
sketchDeleteDraft: "Delete",
|
|
@@ -2802,7 +3466,7 @@ window.__ModuleLoader__.load({
|
|
|
2802
3466
|
imageCapability_on: "Enabled",
|
|
2803
3467
|
imageCapability_off: "Off",
|
|
2804
3468
|
imageEntryPoints: "Creative shortcuts",
|
|
2805
|
-
imageEntryPointsHint: "@Image
|
|
3469
|
+
imageEntryPointsHint: "@Image shortcut. Natural-language image requests still work when hidden.",
|
|
2806
3470
|
imageEntryPoints_on: "Show",
|
|
2807
3471
|
imageEntryPoints_off: "Hide",
|
|
2808
3472
|
imageBrowsing: "Image browsing",
|
|
@@ -2838,6 +3502,14 @@ window.__ModuleLoader__.load({
|
|
|
2838
3502
|
sketchTool_rectangle: "Rectangle",
|
|
2839
3503
|
sketchTool_circle: "Ellipse",
|
|
2840
3504
|
sketchTool_eraser: "Eraser",
|
|
3505
|
+
sketchCanvas: "Sketch canvas · Beta",
|
|
3506
|
+
sketchCanvasHint: "Off by default. Enables the manual sketch board.",
|
|
3507
|
+
sketchCanvas_on: "On",
|
|
3508
|
+
sketchCanvas_off: "Off",
|
|
3509
|
+
sketchAgent: "Agent drawing · Beta",
|
|
3510
|
+
sketchAgentHint: "Off by default. Exposes drawing tools to the model; requires the sketch canvas.",
|
|
3511
|
+
sketchAgent_on: "On",
|
|
3512
|
+
sketchAgent_off: "Off",
|
|
2841
3513
|
imageSettings: "Images",
|
|
2842
3514
|
imageGeneration: "Generate new images",
|
|
2843
3515
|
imageEditing: "Reference image editing",
|
|
@@ -3313,14 +3985,14 @@ window.__ModuleLoader__.load({
|
|
|
3313
3985
|
.codexModelSelectMenu{overflow:visible}
|
|
3314
3986
|
.codexImageTool{display:flex;flex-direction:column;gap:8px;margin:4px 0;color:var(--dsw-alias-label-primary)}
|
|
3315
3987
|
.codexImageToolRow{display:flex;align-items:center;min-height:24px;gap:8px;font-size:13px;line-height:20px}
|
|
3316
|
-
.codexImageToolIcon{display:
|
|
3317
|
-
.codexImageToolIcon
|
|
3318
|
-
.codexImageTool[data-state=
|
|
3319
|
-
.codexImageTool[data-state=error] .codexImageToolIcon::before{border-color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-state-error-primary)}
|
|
3988
|
+
.codexImageToolIcon{display:block;flex-shrink:0;width:16px;height:16px;color:var(--dsw-alias-label-secondary)}
|
|
3989
|
+
.codexImageTool[data-state=running] .codexImageToolIcon{animation:codexImageSpin 800ms linear infinite}
|
|
3990
|
+
.codexImageTool[data-state=error] .codexImageToolIcon{color:var(--dsw-alias-state-error-primary)}
|
|
3320
3991
|
.codexImageToolTitle{font-weight:500}
|
|
3321
3992
|
.codexImageToolState{color:var(--dsw-alias-label-tertiary)}
|
|
3322
3993
|
.codexImageToolError{margin:0 0 0 24px;font-size:12px;line-height:18px;color:var(--dsw-alias-state-error-primary)}
|
|
3323
|
-
.codexImageToolGallery{margin-left:
|
|
3994
|
+
.codexImageToolGallery{margin-left:0}
|
|
3995
|
+
.codexImageDetails{margin-left:24px;font-size:12px;color:var(--dsw-alias-label-secondary)}.codexImageDetails summary{cursor:pointer}.codexImageDetails p{margin:6px 0;line-height:1.6;overflow-wrap:anywhere}
|
|
3324
3996
|
.codexGeneratedImageFrame{display:flex;align-items:center;justify-content:center;width:min(240px,100%);height:240px;padding:0;overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:16px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-tertiary);cursor:pointer}
|
|
3325
3997
|
.codexGeneratedImageFrame img{display:block;width:100%;height:100%;object-fit:cover}
|
|
3326
3998
|
.codexGeneratedImageRetry{min-height:36px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);cursor:pointer}
|
|
@@ -4291,7 +4963,8 @@ window.__ModuleLoader__.load({
|
|
|
4291
4963
|
imageEditing: true,
|
|
4292
4964
|
imageViewer: true,
|
|
4293
4965
|
imageAnnotations: true,
|
|
4294
|
-
imageSketch:
|
|
4966
|
+
imageSketch: false,
|
|
4967
|
+
imageSketchAgent: false
|
|
4295
4968
|
});
|
|
4296
4969
|
function readImageFeatures(value = {}) {
|
|
4297
4970
|
return Object.fromEntries(Object.entries(IMAGE_FEATURE_DEFAULTS).map(([key, fallback]) => [key, typeof value?.[key] === "boolean" ? value[key] : fallback]));
|
|
@@ -4929,7 +5602,7 @@ window.__ModuleLoader__.load({
|
|
|
4929
5602
|
}
|
|
4930
5603
|
//#endregion
|
|
4931
5604
|
//#region src/version.js
|
|
4932
|
-
const PACKAGE_VERSION = "2.0.1";
|
|
5605
|
+
const PACKAGE_VERSION = "2.1.0-beta.1";
|
|
4933
5606
|
//#endregion
|
|
4934
5607
|
//#region src/client-recovery.js
|
|
4935
5608
|
async function recoveryCall(rpc, endpoint, payload = {}, timeoutMs = 1e4) {
|
|
@@ -5496,7 +6169,9 @@ window.__ModuleLoader__.load({
|
|
|
5496
6169
|
//#region src/image-setting-groups.js
|
|
5497
6170
|
const IMAGE_SETTING_GROUPS = Object.freeze({
|
|
5498
6171
|
imageCapability: ["imageGeneration", "imageEditing"],
|
|
5499
|
-
imageEntryPoints: ["imageShortcut"
|
|
6172
|
+
imageEntryPoints: ["imageShortcut"],
|
|
6173
|
+
sketchCanvas: ["imageSketch"],
|
|
6174
|
+
sketchAgent: ["imageSketchAgent"],
|
|
5500
6175
|
imageBrowsing: ["imageViewer", "imageAnnotations"]
|
|
5501
6176
|
});
|
|
5502
6177
|
function imageGroupValue(snapshot, group) {
|
|
@@ -7350,6 +8025,8 @@ window.__ModuleLoader__.load({
|
|
|
7350
8025
|
inject: (sessionId) => ({
|
|
7351
8026
|
preference,
|
|
7352
8027
|
t,
|
|
8028
|
+
sessionId,
|
|
8029
|
+
rpc,
|
|
7353
8030
|
registerOpen: (callback) => {
|
|
7354
8031
|
sketchOpeners.set(sessionId, callback);
|
|
7355
8032
|
return () => {
|