dsh-codex-subscription 2.1.0-beta.2 → 2.1.0-beta.4
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 +16 -11
- package/README.md +16 -11
- package/lib/client.js +1042 -183
- package/lib/index.js +160 -24
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -29,8 +29,8 @@ window.__ModuleLoader__.load({
|
|
|
29
29
|
let react = require("react");
|
|
30
30
|
react = __toESM(react, 1);
|
|
31
31
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
32
|
-
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
33
32
|
let react_dom = require("react-dom");
|
|
33
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
34
34
|
//#region src/image-edit.js
|
|
35
35
|
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
36
36
|
const validCoordinate = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
|
|
@@ -386,6 +386,7 @@ window.__ModuleLoader__.load({
|
|
|
386
386
|
"image/original/chunk",
|
|
387
387
|
"sketch/connect",
|
|
388
388
|
"sketch/poll",
|
|
389
|
+
"sketch/claim",
|
|
389
390
|
"sketch/result",
|
|
390
391
|
"sketch/disconnect"
|
|
391
392
|
]);
|
|
@@ -697,7 +698,34 @@ window.__ModuleLoader__.load({
|
|
|
697
698
|
context.lineWidth = stroke.width * (stroke.brush === "pencil" ? .55 : 1) * (stroke.pressure ?? 1);
|
|
698
699
|
context.beginPath();
|
|
699
700
|
const last = stroke.points.at(-1);
|
|
700
|
-
if (stroke.shape === "
|
|
701
|
+
if (stroke.shape === "text") {
|
|
702
|
+
const x = Math.min(first.x, last.x) * size, y = Math.min(first.y, last.y) * height, w = Math.abs(last.x - first.x) * size, h = Math.abs(last.y - first.y) * height;
|
|
703
|
+
const lines = stroke.text.split("\n"), fontSize = Math.min(stroke.width, h / Math.max(1, lines.length) / 1.2);
|
|
704
|
+
context.font = `${fontSize}px system-ui, sans-serif`;
|
|
705
|
+
context.textBaseline = "top";
|
|
706
|
+
lines.forEach((line, i) => context.fillText(line, x, y + i * fontSize * 1.2, w));
|
|
707
|
+
} else if (stroke.shape === "arrow") {
|
|
708
|
+
const x = last.x * size, y = last.y * height, a = Math.atan2(y - first.y * height, x - first.x * size), head = Math.min(Math.hypot(x - first.x * size, y - first.y * height) * .4, Math.max(12, stroke.width * 3));
|
|
709
|
+
context.moveTo(first.x * size, first.y * height);
|
|
710
|
+
context.lineTo(x, y);
|
|
711
|
+
context.stroke();
|
|
712
|
+
context.beginPath();
|
|
713
|
+
context.moveTo(x, y);
|
|
714
|
+
context.lineTo(x - head * Math.cos(a - .5), y - head * Math.sin(a - .5));
|
|
715
|
+
context.lineTo(x - head * Math.cos(a + .5), y - head * Math.sin(a + .5));
|
|
716
|
+
context.closePath();
|
|
717
|
+
context.fill();
|
|
718
|
+
} else if (stroke.shape === "bezier") {
|
|
719
|
+
context.moveTo(first.x * size, first.y * height);
|
|
720
|
+
for (let i = 1; i < stroke.points.length; i += 3) {
|
|
721
|
+
const [a, b, c] = stroke.points.slice(i, i + 3);
|
|
722
|
+
context.bezierCurveTo(a.x * size, a.y * height, b.x * size, b.y * height, c.x * size, c.y * height);
|
|
723
|
+
}
|
|
724
|
+
if (stroke.fill) {
|
|
725
|
+
context.closePath();
|
|
726
|
+
context.fill();
|
|
727
|
+
} else context.stroke();
|
|
728
|
+
} else if (stroke.shape === "line") {
|
|
701
729
|
context.moveTo(first.x * size, first.y * height);
|
|
702
730
|
context.lineTo(last.x * size, last.y * height);
|
|
703
731
|
context.stroke();
|
|
@@ -732,6 +760,46 @@ window.__ModuleLoader__.load({
|
|
|
732
760
|
context.globalCompositeOperation = "source-over";
|
|
733
761
|
context.globalAlpha = 1;
|
|
734
762
|
}
|
|
763
|
+
//#endregion
|
|
764
|
+
//#region src/sketch-curves.js
|
|
765
|
+
const cached = /* @__PURE__ */ new WeakMap();
|
|
766
|
+
const midpoint = (a, b) => ({
|
|
767
|
+
x: (a.x + b.x) / 2,
|
|
768
|
+
y: (a.y + b.y) / 2
|
|
769
|
+
});
|
|
770
|
+
function flattenSketchCurve(stroke, width, height) {
|
|
771
|
+
const previous = cached.get(stroke);
|
|
772
|
+
if (previous?.width === width && previous.height === height) return previous.points;
|
|
773
|
+
const controls = stroke.points.map((p) => ({
|
|
774
|
+
x: p.x * width,
|
|
775
|
+
y: p.y * height
|
|
776
|
+
})), points = [controls[0]];
|
|
777
|
+
const distance = (p, a, b) => {
|
|
778
|
+
const dx = b.x - a.x, dy = b.y - a.y, d = dx * dx + dy * dy;
|
|
779
|
+
const t = d ? Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / d)) : 0;
|
|
780
|
+
return Math.hypot(p.x - a.x - t * dx, p.y - a.y - t * dy);
|
|
781
|
+
};
|
|
782
|
+
const split = (a, b, c, d, depth) => {
|
|
783
|
+
if (depth === 10 || Math.max(distance(b, a, d), distance(c, a, d)) <= .5) {
|
|
784
|
+
points.push(d);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const ab = midpoint(a, b), bc = midpoint(b, c), cd = midpoint(c, d), abc = midpoint(ab, bc), bcd = midpoint(bc, cd), m = midpoint(abc, bcd);
|
|
788
|
+
split(a, ab, abc, m, depth + 1);
|
|
789
|
+
split(m, bcd, cd, d, depth + 1);
|
|
790
|
+
};
|
|
791
|
+
for (let i = 1; i < controls.length; i += 3) split(controls[i - 1], controls[i], controls[i + 1], controls[i + 2], 0);
|
|
792
|
+
const normalized = points.map((p) => ({
|
|
793
|
+
x: p.x / width,
|
|
794
|
+
y: p.y / height
|
|
795
|
+
}));
|
|
796
|
+
cached.set(stroke, {
|
|
797
|
+
width,
|
|
798
|
+
height,
|
|
799
|
+
points: normalized
|
|
800
|
+
});
|
|
801
|
+
return normalized;
|
|
802
|
+
}
|
|
735
803
|
const createSketchLayers = () => ({
|
|
736
804
|
active: 1,
|
|
737
805
|
nextId: 2,
|
|
@@ -809,15 +877,15 @@ window.__ModuleLoader__.load({
|
|
|
809
877
|
return Math.hypot(p.x - a.x - k * dx, p.y - a.y - k * dy);
|
|
810
878
|
};
|
|
811
879
|
function strokeHit(stroke, point, radius, width = SKETCH_SIZE, height = width) {
|
|
812
|
-
let points = stroke.points;
|
|
880
|
+
let points = stroke.shape === "bezier" ? flattenSketchCurve(stroke, width, height) : stroke.points;
|
|
813
881
|
if (!points.length) return false;
|
|
814
882
|
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;
|
|
883
|
+
if ((stroke.shape === "text" || 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
884
|
if (stroke.fill && stroke.shape === "circle") {
|
|
817
885
|
const rx = Math.abs(b.x - a.x) / 2, ry = Math.abs(b.y - a.y) / 2;
|
|
818
886
|
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
887
|
}
|
|
820
|
-
if (stroke.shape === "polygon") {
|
|
888
|
+
if (stroke.shape === "polygon" || stroke.shape === "bezier" && stroke.fill) {
|
|
821
889
|
if (stroke.fill) {
|
|
822
890
|
let inside = false;
|
|
823
891
|
for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
|
|
@@ -1283,8 +1351,39 @@ window.__ModuleLoader__.load({
|
|
|
1283
1351
|
};
|
|
1284
1352
|
}
|
|
1285
1353
|
function SketchViewControls({ navigation, t }) {
|
|
1286
|
-
const [open, setOpen] = (0, react.useState)(false),
|
|
1287
|
-
|
|
1354
|
+
const [open, setOpen] = (0, react.useState)(false), [placement, setPlacement] = (0, react.useState)(null);
|
|
1355
|
+
const host = (0, react.useRef)(null), trigger = (0, react.useRef)(null);
|
|
1356
|
+
const close = () => {
|
|
1357
|
+
setOpen(false);
|
|
1358
|
+
trigger.current?.focus({ preventScroll: true });
|
|
1359
|
+
};
|
|
1360
|
+
useSketchDismiss(open, setOpen, host, [".codexSketchViewControls", ".codexSketchKeyPanel"]);
|
|
1361
|
+
(0, react.useLayoutEffect)(() => {
|
|
1362
|
+
if (!open) return;
|
|
1363
|
+
const dialog = host.current.closest("dialog");
|
|
1364
|
+
const place = () => {
|
|
1365
|
+
const box = dialog.getBoundingClientRect(), anchor = trigger.current.getBoundingClientRect();
|
|
1366
|
+
const width = Math.min(360, box.width - 24);
|
|
1367
|
+
setPlacement({
|
|
1368
|
+
dialog,
|
|
1369
|
+
style: {
|
|
1370
|
+
width,
|
|
1371
|
+
left: Math.max(12, Math.min(anchor.left - box.left, box.width - width - 12)),
|
|
1372
|
+
bottom: box.bottom - anchor.top + 8,
|
|
1373
|
+
maxHeight: Math.max(80, anchor.top - box.top - 24)
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
};
|
|
1377
|
+
place();
|
|
1378
|
+
const observer = new ResizeObserver(place);
|
|
1379
|
+
observer.observe(dialog);
|
|
1380
|
+
observer.observe(host.current);
|
|
1381
|
+
window.addEventListener("resize", place);
|
|
1382
|
+
return () => {
|
|
1383
|
+
observer.disconnect();
|
|
1384
|
+
window.removeEventListener("resize", place);
|
|
1385
|
+
};
|
|
1386
|
+
}, [open]);
|
|
1288
1387
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1289
1388
|
ref: host,
|
|
1290
1389
|
className: "codexSketchViewControls",
|
|
@@ -1308,49 +1407,116 @@ window.__ModuleLoader__.load({
|
|
|
1308
1407
|
children: "+"
|
|
1309
1408
|
}),
|
|
1310
1409
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1410
|
+
ref: trigger,
|
|
1311
1411
|
type: "button",
|
|
1412
|
+
"aria-expanded": open,
|
|
1312
1413
|
onClick: () => setOpen(!open),
|
|
1313
1414
|
children: t("sketchKeys")
|
|
1314
1415
|
}),
|
|
1315
|
-
open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1416
|
+
open && placement ? (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1316
1417
|
className: "codexSketchKeyPanel",
|
|
1418
|
+
"aria-label": t("sketchKeys"),
|
|
1419
|
+
style: placement.style,
|
|
1317
1420
|
onKeyDown: (e) => {
|
|
1318
1421
|
if (e.key === "Escape") {
|
|
1319
1422
|
e.preventDefault();
|
|
1320
1423
|
e.stopPropagation();
|
|
1321
|
-
|
|
1424
|
+
close();
|
|
1322
1425
|
}
|
|
1323
1426
|
},
|
|
1324
1427
|
children: [
|
|
1325
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
1326
|
-
type: "
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1428
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("sketchKeys") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1429
|
+
type: "button",
|
|
1430
|
+
"aria-label": t("sketchFileClose"),
|
|
1431
|
+
onClick: close,
|
|
1432
|
+
children: "×"
|
|
1433
|
+
})] }),
|
|
1434
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1435
|
+
className: "codexSketchKeysEnabled",
|
|
1436
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("sketchKeysEnabled") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1437
|
+
type: "checkbox",
|
|
1438
|
+
checked: navigation.shortcuts,
|
|
1439
|
+
onChange: navigation.toggle
|
|
1440
|
+
})]
|
|
1441
|
+
}),
|
|
1330
1442
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("sketchNavigationHint") }),
|
|
1331
|
-
|
|
1332
|
-
"
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
e
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1443
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1444
|
+
className: "codexSketchKeyGrid",
|
|
1445
|
+
children: Object.entries(navigation.keys).map(([action, key]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t(`sketchKey_${action}`), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1446
|
+
"aria-label": t(`sketchKey_${action}`),
|
|
1447
|
+
value: key === " " ? "Space" : key,
|
|
1448
|
+
readOnly: true,
|
|
1449
|
+
onKeyDown: (e) => {
|
|
1450
|
+
if (e.key === "Tab" || e.key === "Escape") return;
|
|
1451
|
+
e.preventDefault();
|
|
1452
|
+
e.stopPropagation();
|
|
1453
|
+
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) navigation.setKey(action, e.key);
|
|
1454
|
+
}
|
|
1455
|
+
})] }, action))
|
|
1456
|
+
}),
|
|
1342
1457
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("sketchKeyHint") })
|
|
1343
1458
|
]
|
|
1344
|
-
}) : null
|
|
1459
|
+
}), placement.dialog) : null
|
|
1345
1460
|
]
|
|
1346
1461
|
});
|
|
1347
1462
|
}
|
|
1463
|
+
//#endregion
|
|
1464
|
+
//#region src/sketch-objects.js
|
|
1465
|
+
const objectId = (stroke, index) => stroke.id ?? `legacy-${index}`;
|
|
1466
|
+
const identifyObjects = (doc) => ({
|
|
1467
|
+
...doc,
|
|
1468
|
+
layers: doc.layers.map((layer) => ({
|
|
1469
|
+
...layer,
|
|
1470
|
+
strokes: layer.strokes.map((s, i) => s.id ? s : {
|
|
1471
|
+
...s,
|
|
1472
|
+
id: objectId(s, i)
|
|
1473
|
+
})
|
|
1474
|
+
}))
|
|
1475
|
+
});
|
|
1476
|
+
function objectBounds(stroke) {
|
|
1477
|
+
const xs = stroke.points.map((p) => p.x), ys = stroke.points.map((p) => p.y);
|
|
1478
|
+
return {
|
|
1479
|
+
x: Math.min(...xs),
|
|
1480
|
+
y: Math.min(...ys),
|
|
1481
|
+
width: Math.max(...xs) - Math.min(...xs),
|
|
1482
|
+
height: Math.max(...ys) - Math.min(...ys)
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
function transformObject(stroke, { dx = 0, dy = 0, scaleX = 1, scaleY = 1 }) {
|
|
1486
|
+
if (![
|
|
1487
|
+
dx,
|
|
1488
|
+
dy,
|
|
1489
|
+
scaleX,
|
|
1490
|
+
scaleY
|
|
1491
|
+
].every(Number.isFinite) || scaleX <= 0 || scaleY <= 0) throw Error("Invalid object transform");
|
|
1492
|
+
const box = objectBounds(stroke);
|
|
1493
|
+
const points = stroke.points.map((p) => ({
|
|
1494
|
+
x: box.x + (p.x - box.x) * scaleX + dx,
|
|
1495
|
+
y: box.y + (p.y - box.y) * scaleY + dy
|
|
1496
|
+
}));
|
|
1497
|
+
if (points.some((p) => p.x < 0 || p.x > 1 || p.y < 0 || p.y > 1)) throw Error("Object would leave the canvas");
|
|
1498
|
+
return {
|
|
1499
|
+
...stroke,
|
|
1500
|
+
points
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
function sketchObjectSummary(doc) {
|
|
1504
|
+
return doc.layers.flatMap((layer) => layer.strokes.map((stroke, i) => ({
|
|
1505
|
+
layer: layer.id,
|
|
1506
|
+
id: objectId(stroke, i),
|
|
1507
|
+
shape: stroke.shape,
|
|
1508
|
+
color: stroke.color,
|
|
1509
|
+
bounds: objectBounds(stroke),
|
|
1510
|
+
...stroke.text ? { text: stroke.text } : {}
|
|
1511
|
+
})));
|
|
1512
|
+
}
|
|
1348
1513
|
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.",
|
|
1514
|
+
coordinates: "Assign short meaningful stroke id values for later edits. Text uses two opposite box corners and text content; width is font size in pixels, automatic fitting within the box. Arrow uses two endpoints. Normalized x/y in [0,1]; width is canvas pixels. Read documentId and revision before editing.",
|
|
1515
|
+
shapes: "line: exactly two endpoints; rectangle/circle (ellipse alias accepted): exactly two opposite bounding-box corners (circle draws an ellipse within that box); polygon: three or more vertices, closed automatically; pen: ordered path points. bezier: start point, then groups of control1/control2/end; use 4 points for one cubic curve, max 64 segments. Prefer bezier for smooth designed curves instead of many pen samples. fill:true closes and fills the curve. 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
1516
|
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},...]}",
|
|
1517
|
+
stroke: "{op:\"stroke\",layer:1,shape:\"pen|line|arrow|text|rectangle|circle|polygon|bezier\",color:\"#rrggbb\",width:2,opacity:1,fill:false,points:[{x:0.1,y:0.1},...]}",
|
|
1353
1518
|
layer: "{op:\"layer\",action:\"add|select|rename|visible|duplicate|up|down|delete|clear\",id:1,value:\"name\"}",
|
|
1519
|
+
object: "{op:\"object\",layer:1,id:\"title\",action:\"update|duplicate|delete\",patch:{color:\"#0088ff\",text:\"Title\"},transform:{dx:0.05,dy:0,scaleX:1,scaleY:1}}. All patch and transform fields optional. Inspect returns object IDs and bounds. Prefer targeted edits over redrawing layers.",
|
|
1354
1520
|
resize: "{op:\"resize\",ratio:\"1:1|4:3|3:4|16:9|9:16\"}"
|
|
1355
1521
|
},
|
|
1356
1522
|
limits: {
|
|
@@ -1363,7 +1529,7 @@ window.__ModuleLoader__.load({
|
|
|
1363
1529
|
const finite = (value, min, max) => typeof value === "number" && Number.isFinite(value) && value >= min && value <= max;
|
|
1364
1530
|
function applySketchCommands(source, commands) {
|
|
1365
1531
|
if (!Array.isArray(commands) || !commands.length || commands.length > 256) throw Error("Expected 1–256 commands");
|
|
1366
|
-
let doc = source;
|
|
1532
|
+
let doc = identifyObjects(source);
|
|
1367
1533
|
for (const command of commands) {
|
|
1368
1534
|
if (!command || typeof command !== "object") throw Error("Invalid command");
|
|
1369
1535
|
if (command.op === "resize") {
|
|
@@ -1387,29 +1553,94 @@ window.__ModuleLoader__.load({
|
|
|
1387
1553
|
doc = next;
|
|
1388
1554
|
continue;
|
|
1389
1555
|
}
|
|
1556
|
+
if (command.op === "object") {
|
|
1557
|
+
const layer = doc.layers.find((l) => l.id === (command.layer ?? doc.active)), index = layer?.strokes.findIndex((s) => s.id === command.id);
|
|
1558
|
+
if (!layer?.visible || index < 0 || index === void 0) throw Error("Object missing or hidden; inspect again");
|
|
1559
|
+
const strokes = layer.strokes.slice(), original = strokes[index];
|
|
1560
|
+
if (command.action === "delete") strokes.splice(index, 1);
|
|
1561
|
+
else if (command.action === "duplicate") strokes.splice(index + 1, 0, {
|
|
1562
|
+
...original,
|
|
1563
|
+
id: crypto.randomUUID(),
|
|
1564
|
+
points: original.points.map((p) => ({ ...p }))
|
|
1565
|
+
});
|
|
1566
|
+
else if (command.action === "update") {
|
|
1567
|
+
const patch = command.patch ?? {};
|
|
1568
|
+
if (Object.keys(patch).some((k) => ![
|
|
1569
|
+
"color",
|
|
1570
|
+
"width",
|
|
1571
|
+
"opacity",
|
|
1572
|
+
"fill",
|
|
1573
|
+
"text",
|
|
1574
|
+
"points"
|
|
1575
|
+
].includes(k))) throw Error("Unsupported object property");
|
|
1576
|
+
const changed = command.transform ? transformObject({
|
|
1577
|
+
...original,
|
|
1578
|
+
...patch
|
|
1579
|
+
}, command.transform) : {
|
|
1580
|
+
...original,
|
|
1581
|
+
...patch
|
|
1582
|
+
};
|
|
1583
|
+
strokes[index] = {
|
|
1584
|
+
...applySketchCommands({
|
|
1585
|
+
...doc,
|
|
1586
|
+
layers: [{
|
|
1587
|
+
...layer,
|
|
1588
|
+
strokes: []
|
|
1589
|
+
}]
|
|
1590
|
+
}, [{
|
|
1591
|
+
...changed,
|
|
1592
|
+
op: "stroke",
|
|
1593
|
+
layer: layer.id
|
|
1594
|
+
}]).layers[0].strokes[0],
|
|
1595
|
+
brush: original.brush ?? "pen",
|
|
1596
|
+
...original.pressure !== void 0 ? { pressure: original.pressure } : {}
|
|
1597
|
+
};
|
|
1598
|
+
} else throw Error("Unknown object action");
|
|
1599
|
+
doc = {
|
|
1600
|
+
...doc,
|
|
1601
|
+
layers: doc.layers.map((l) => l === layer ? {
|
|
1602
|
+
...l,
|
|
1603
|
+
strokes
|
|
1604
|
+
} : l)
|
|
1605
|
+
};
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
1390
1608
|
if (command.op !== "stroke") throw Error("Unknown command");
|
|
1391
|
-
const
|
|
1609
|
+
const shape = command.shape === "ellipse" ? "circle" : command.shape ?? "pen";
|
|
1610
|
+
const { points, color, width = shape === "text" ? 24 : 2, opacity = 1, fill = false } = command;
|
|
1392
1611
|
if (![
|
|
1393
1612
|
"pen",
|
|
1394
1613
|
"line",
|
|
1395
1614
|
"rectangle",
|
|
1396
1615
|
"circle",
|
|
1397
1616
|
"polygon",
|
|
1617
|
+
"bezier",
|
|
1618
|
+
"arrow",
|
|
1619
|
+
"text",
|
|
1398
1620
|
"eraser"
|
|
1399
1621
|
].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
1622
|
if ([
|
|
1401
1623
|
"line",
|
|
1624
|
+
"arrow",
|
|
1625
|
+
"text",
|
|
1402
1626
|
"rectangle",
|
|
1403
1627
|
"circle"
|
|
1404
1628
|
].includes(shape) && points.length !== 2 || shape === "polygon" && points.length < 3) throw Error("Invalid shape points");
|
|
1629
|
+
if (shape === "bezier" && (points.length < 4 || points.length > 193 || (points.length - 1) % 3 !== 0)) throw Error(`commands[${commands.indexOf(command)}]: Bezier has ${points.length} points; expected 4, 7, 10, ... 193 (start + control1/control2/end per segment). No commands in this batch were applied.`);
|
|
1405
1630
|
if (fill && ![
|
|
1406
1631
|
"rectangle",
|
|
1407
1632
|
"circle",
|
|
1408
|
-
"polygon"
|
|
1633
|
+
"polygon",
|
|
1634
|
+
"bezier"
|
|
1409
1635
|
].includes(shape)) throw Error("Fill requires a closed shape");
|
|
1410
1636
|
const layer = doc.layers.find((layer) => layer.id === (command.layer ?? doc.active));
|
|
1411
1637
|
if (!layer?.visible) throw Error("Target layer is missing or hidden");
|
|
1638
|
+
if (shape === "text" && (typeof command.text !== "string" || !command.text.trim() || command.text.length > 500 || points[0].x === points[1].x || points[0].y === points[1].y)) throw Error("Text requires 1–500 characters and a non-empty bounding box");
|
|
1639
|
+
const id = command.id ?? crypto.randomUUID();
|
|
1640
|
+
if (typeof id !== "string" || !id.length || id.length > 100 || layer.strokes.some((s) => s.id === id)) throw Error("Invalid or duplicate object id");
|
|
1412
1641
|
const stroke = {
|
|
1642
|
+
id,
|
|
1643
|
+
...shape === "text" ? { text: command.text } : {},
|
|
1413
1644
|
shape,
|
|
1414
1645
|
color,
|
|
1415
1646
|
width,
|
|
@@ -1434,15 +1665,24 @@ window.__ModuleLoader__.load({
|
|
|
1434
1665
|
}
|
|
1435
1666
|
function createSketchCommandSession(adapter) {
|
|
1436
1667
|
const completed = /* @__PURE__ */ new Map();
|
|
1437
|
-
let pending = false;
|
|
1668
|
+
let pending = false, cachedCharacters = 0;
|
|
1438
1669
|
return async (request) => {
|
|
1439
1670
|
if (!request || typeof request !== "object") throw Error("Invalid sketch request");
|
|
1440
1671
|
if (!adapter.available()) throw Error("Open the sketch board for this session first");
|
|
1441
1672
|
const current = adapter.snapshot();
|
|
1442
|
-
if (request.action === "inspect")
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1673
|
+
if (request.action === "inspect") {
|
|
1674
|
+
const offset = request.offset ?? 0, objects = adapter.objects?.() ?? [];
|
|
1675
|
+
if (!Number.isInteger(offset) || offset < 0) throw Error("offset must be a non-negative integer");
|
|
1676
|
+
return {
|
|
1677
|
+
...current,
|
|
1678
|
+
objects: objects.slice(offset, offset + 50),
|
|
1679
|
+
objectCount: objects.length,
|
|
1680
|
+
...offset + 50 < objects.length ? { nextOffset: offset + 50 } : {},
|
|
1681
|
+
...request.objectId ? { object: adapter.object?.(request.objectId, request.layer) } : {},
|
|
1682
|
+
recentRequests: [...completed.values()].slice(-8).map((entry) => entry.receipt),
|
|
1683
|
+
help: SKETCH_COMMAND_HELP
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1446
1686
|
if (request.documentId !== current.documentId) throw Error("Document changed; inspect again");
|
|
1447
1687
|
if (pending || adapter.busy()) throw Error("Sketch is being edited; retry after it settles");
|
|
1448
1688
|
if (request.action === "preview") return {
|
|
@@ -1451,16 +1691,25 @@ window.__ModuleLoader__.load({
|
|
|
1451
1691
|
};
|
|
1452
1692
|
if (!["apply", "save"].includes(request.action)) throw Error("Unknown sketch action");
|
|
1453
1693
|
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(
|
|
1694
|
+
const key = `${current.documentId}:${request.requestId}`, fingerprint = JSON.stringify({
|
|
1695
|
+
...request,
|
|
1696
|
+
runId: void 0
|
|
1697
|
+
});
|
|
1455
1698
|
const cached = completed.get(key);
|
|
1456
1699
|
if (cached) {
|
|
1457
1700
|
if (cached.fingerprint !== fingerprint) throw Error("requestId reused with different content");
|
|
1458
1701
|
return cached.result;
|
|
1459
1702
|
}
|
|
1460
1703
|
if (request.revision !== current.revision || adapter.busy()) throw Error("Sketch changed or is being edited; inspect again");
|
|
1704
|
+
let changedObjects;
|
|
1461
1705
|
if (request.action === "apply") {
|
|
1462
|
-
const
|
|
1706
|
+
const before = adapter.document(), next = applySketchCommands(before, request.commands);
|
|
1463
1707
|
adapter.commit(next);
|
|
1708
|
+
const previous = new Map(before.layers.flatMap((l) => l.strokes.map((s) => [`${l.id}:${s.id}`, s])));
|
|
1709
|
+
changedObjects = next.layers.flatMap((l) => l.strokes.filter((s) => previous.get(`${l.id}:${s.id}`) !== s).map((s) => ({
|
|
1710
|
+
layer: l.id,
|
|
1711
|
+
id: s.id
|
|
1712
|
+
})));
|
|
1464
1713
|
} else {
|
|
1465
1714
|
if (request.name !== void 0 && (typeof request.name !== "string" || request.name.length > 60)) throw Error("Invalid draft name");
|
|
1466
1715
|
pending = true;
|
|
@@ -1470,12 +1719,28 @@ window.__ModuleLoader__.load({
|
|
|
1470
1719
|
pending = false;
|
|
1471
1720
|
}
|
|
1472
1721
|
}
|
|
1473
|
-
const result =
|
|
1722
|
+
const result = {
|
|
1723
|
+
...adapter.snapshot(),
|
|
1724
|
+
...changedObjects ? {
|
|
1725
|
+
changedObjects: changedObjects.slice(0, 100),
|
|
1726
|
+
changedObjectCount: changedObjects.length
|
|
1727
|
+
} : {}
|
|
1728
|
+
};
|
|
1474
1729
|
completed.set(key, {
|
|
1475
1730
|
fingerprint,
|
|
1476
|
-
result
|
|
1731
|
+
result,
|
|
1732
|
+
receipt: {
|
|
1733
|
+
requestId: request.requestId,
|
|
1734
|
+
action: request.action,
|
|
1735
|
+
revision: result.revision
|
|
1736
|
+
}
|
|
1477
1737
|
});
|
|
1478
|
-
|
|
1738
|
+
cachedCharacters += fingerprint.length;
|
|
1739
|
+
while (completed.size > 1 && (completed.size > 128 || cachedCharacters > 4e6)) {
|
|
1740
|
+
const oldest = completed.keys().next().value;
|
|
1741
|
+
cachedCharacters -= completed.get(oldest).fingerprint.length;
|
|
1742
|
+
completed.delete(oldest);
|
|
1743
|
+
}
|
|
1479
1744
|
return result;
|
|
1480
1745
|
};
|
|
1481
1746
|
}
|
|
@@ -1669,6 +1934,7 @@ window.__ModuleLoader__.load({
|
|
|
1669
1934
|
}, [open, working]);
|
|
1670
1935
|
useSketchDismiss(open, setOpen, host, [".codexSketchFiles"]);
|
|
1671
1936
|
const run = async (operation) => {
|
|
1937
|
+
if (disabled || working) return;
|
|
1672
1938
|
setWorking(true);
|
|
1673
1939
|
onWorking(true);
|
|
1674
1940
|
try {
|
|
@@ -1724,7 +1990,7 @@ window.__ModuleLoader__.load({
|
|
|
1724
1990
|
className: "codexSketchFileActions",
|
|
1725
1991
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1726
1992
|
type: "button",
|
|
1727
|
-
disabled: working,
|
|
1993
|
+
disabled: disabled || working,
|
|
1728
1994
|
onClick: () => void run(async () => {
|
|
1729
1995
|
await fresh();
|
|
1730
1996
|
setName("");
|
|
@@ -1733,7 +1999,7 @@ window.__ModuleLoader__.load({
|
|
|
1733
1999
|
children: t("sketchNew")
|
|
1734
2000
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1735
2001
|
type: "button",
|
|
1736
|
-
disabled: working,
|
|
2002
|
+
disabled: disabled || working,
|
|
1737
2003
|
onClick: () => input.current.click(),
|
|
1738
2004
|
children: t("sketchImport")
|
|
1739
2005
|
})]
|
|
@@ -1747,7 +2013,7 @@ window.__ModuleLoader__.load({
|
|
|
1747
2013
|
}),
|
|
1748
2014
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1749
2015
|
type: "button",
|
|
1750
|
-
disabled: working,
|
|
2016
|
+
disabled: disabled || working,
|
|
1751
2017
|
onClick: () => void run(async () => {
|
|
1752
2018
|
await save(name);
|
|
1753
2019
|
await refresh();
|
|
@@ -1765,14 +2031,14 @@ window.__ModuleLoader__.load({
|
|
|
1765
2031
|
].map(([value, label]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1766
2032
|
type: "button",
|
|
1767
2033
|
"aria-pressed": format === value,
|
|
1768
|
-
disabled: working,
|
|
2034
|
+
disabled: disabled || working,
|
|
1769
2035
|
onClick: () => setFormat(value),
|
|
1770
2036
|
children: label
|
|
1771
2037
|
}, value))
|
|
1772
2038
|
}),
|
|
1773
2039
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1774
2040
|
type: "button",
|
|
1775
|
-
disabled: working || !hasContent,
|
|
2041
|
+
disabled: disabled || working || !hasContent,
|
|
1776
2042
|
onClick: () => void run(async () => {
|
|
1777
2043
|
await download(format);
|
|
1778
2044
|
setOpen(false);
|
|
@@ -1785,7 +2051,7 @@ window.__ModuleLoader__.load({
|
|
|
1785
2051
|
className: "codexSketchDraftList",
|
|
1786
2052
|
children: rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1787
2053
|
type: "button",
|
|
1788
|
-
disabled: working,
|
|
2054
|
+
disabled: disabled || working,
|
|
1789
2055
|
onClick: () => void run(async () => {
|
|
1790
2056
|
await load(row);
|
|
1791
2057
|
setName(row.name);
|
|
@@ -1794,7 +2060,7 @@ window.__ModuleLoader__.load({
|
|
|
1794
2060
|
children: row.name
|
|
1795
2061
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1796
2062
|
type: "button",
|
|
1797
|
-
disabled: working,
|
|
2063
|
+
disabled: disabled || working,
|
|
1798
2064
|
"aria-label": `${t("sketchDeleteDraft")} ${row.name}`,
|
|
1799
2065
|
onClick: () => {
|
|
1800
2066
|
if (remove !== row.id) {
|
|
@@ -1824,6 +2090,9 @@ window.__ModuleLoader__.load({
|
|
|
1824
2090
|
//#region src/workspace-icons.jsx
|
|
1825
2091
|
function WorkspaceIcon({ name, size = 24 }) {
|
|
1826
2092
|
const paths = {
|
|
2093
|
+
select: "M5 3l14 9-7 2-3 7-4-18z",
|
|
2094
|
+
text: "M4 5h16M12 5v15M8 20h8M4 5v3M20 5v3",
|
|
2095
|
+
arrow: "M4 20L20 4M10 4h10v10",
|
|
1827
2096
|
line: "M4 20L20 4",
|
|
1828
2097
|
layers: "M12 3L2 8l10 5 10-5-10-5zM2 12l10 5 10-5M2 16l10 5 10-5",
|
|
1829
2098
|
eye: "M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12zM15 12a3 3 0 11-6 0 3 3 0 016 0",
|
|
@@ -1858,9 +2127,161 @@ window.__ModuleLoader__.load({
|
|
|
1858
2127
|
});
|
|
1859
2128
|
}
|
|
1860
2129
|
//#endregion
|
|
2130
|
+
//#region src/sketch-size-control.jsx
|
|
2131
|
+
function SketchSizeControl({ value, onChange, onStart, onEnd, label, disabled, min = 2, max = 128, mode, modes, onModeChange, suffix = "" }) {
|
|
2132
|
+
const active = (0, react.useRef)(false);
|
|
2133
|
+
const start = () => {
|
|
2134
|
+
if (!active.current) {
|
|
2135
|
+
active.current = true;
|
|
2136
|
+
onStart?.();
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
const end = () => {
|
|
2140
|
+
if (active.current) {
|
|
2141
|
+
active.current = false;
|
|
2142
|
+
onEnd?.();
|
|
2143
|
+
}
|
|
2144
|
+
};
|
|
2145
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2146
|
+
className: "codexSketchSizeControl",
|
|
2147
|
+
title: label,
|
|
2148
|
+
children: [
|
|
2149
|
+
modes ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2150
|
+
className: "codexSketchSizeModes",
|
|
2151
|
+
children: modes.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2152
|
+
type: "button",
|
|
2153
|
+
"aria-pressed": mode === item.value,
|
|
2154
|
+
disabled,
|
|
2155
|
+
onClick: () => {
|
|
2156
|
+
end();
|
|
2157
|
+
onModeChange(item.value);
|
|
2158
|
+
},
|
|
2159
|
+
children: item.label
|
|
2160
|
+
}, item.value))
|
|
2161
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }),
|
|
2162
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2163
|
+
type: "range",
|
|
2164
|
+
"aria-label": label,
|
|
2165
|
+
"aria-orientation": "vertical",
|
|
2166
|
+
min,
|
|
2167
|
+
max,
|
|
2168
|
+
value,
|
|
2169
|
+
disabled,
|
|
2170
|
+
onPointerDown: start,
|
|
2171
|
+
onPointerUp: end,
|
|
2172
|
+
onPointerCancel: end,
|
|
2173
|
+
onBlur: end,
|
|
2174
|
+
onKeyDown: start,
|
|
2175
|
+
onKeyUp: end,
|
|
2176
|
+
onChange: (event) => {
|
|
2177
|
+
start();
|
|
2178
|
+
onChange(Number(event.target.value));
|
|
2179
|
+
}
|
|
2180
|
+
}),
|
|
2181
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("output", { children: [Math.round(value), suffix] })
|
|
2182
|
+
]
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
//#endregion
|
|
2186
|
+
//#region src/sketch-agent-run.js
|
|
2187
|
+
function createSketchAgentRun({ execute, open, changed, busy = () => false, previewEnabled = () => false, idleMs = 18e4 }) {
|
|
2188
|
+
let state = "idle", generation = 0, runNumber = 0, pending = false, runId, timer, completed;
|
|
2189
|
+
const update = (next) => {
|
|
2190
|
+
state = next;
|
|
2191
|
+
changed(next);
|
|
2192
|
+
};
|
|
2193
|
+
const clear = () => {
|
|
2194
|
+
clearTimeout(timer);
|
|
2195
|
+
timer = void 0;
|
|
2196
|
+
};
|
|
2197
|
+
const expire = () => {
|
|
2198
|
+
clear();
|
|
2199
|
+
generation++;
|
|
2200
|
+
if (state !== "stopped") update("failed");
|
|
2201
|
+
};
|
|
2202
|
+
return {
|
|
2203
|
+
get state() {
|
|
2204
|
+
return state;
|
|
2205
|
+
},
|
|
2206
|
+
get locked() {
|
|
2207
|
+
return state === "drawing";
|
|
2208
|
+
},
|
|
2209
|
+
stop() {
|
|
2210
|
+
clear();
|
|
2211
|
+
generation++;
|
|
2212
|
+
update("stopped");
|
|
2213
|
+
},
|
|
2214
|
+
resume() {
|
|
2215
|
+
clear();
|
|
2216
|
+
generation++;
|
|
2217
|
+
update("idle");
|
|
2218
|
+
},
|
|
2219
|
+
fail: expire,
|
|
2220
|
+
dispose() {
|
|
2221
|
+
clear();
|
|
2222
|
+
generation++;
|
|
2223
|
+
state = "idle";
|
|
2224
|
+
},
|
|
2225
|
+
async execute(request) {
|
|
2226
|
+
if (!request || typeof request !== "object") throw Error("Invalid sketch request");
|
|
2227
|
+
if (state === "stopped") throw Error("Drawing stopped by the user. Do not retry until they enable drawing again.");
|
|
2228
|
+
if (pending || busy()) throw Error("Sketch is being edited; retry after it settles");
|
|
2229
|
+
if (request.action !== "inspect" && request.runId !== runId) throw Error("Drawing run changed; inspect again");
|
|
2230
|
+
if (state === "finished" && completed?.key === JSON.stringify(request)) return completed.value;
|
|
2231
|
+
if (state !== "drawing" && request.action !== "inspect") throw Error("Start drawing with inspect");
|
|
2232
|
+
clear();
|
|
2233
|
+
const version = generation;
|
|
2234
|
+
pending = true;
|
|
2235
|
+
try {
|
|
2236
|
+
if (state !== "drawing") {
|
|
2237
|
+
runId = `run-${++runNumber}`;
|
|
2238
|
+
completed = void 0;
|
|
2239
|
+
update("drawing");
|
|
2240
|
+
open();
|
|
2241
|
+
}
|
|
2242
|
+
const value = await execute(request.action === "finish" ? {
|
|
2243
|
+
...request,
|
|
2244
|
+
action: "save"
|
|
2245
|
+
} : request);
|
|
2246
|
+
if (version !== generation) throw Error("Drawing interrupted");
|
|
2247
|
+
if (request.action !== "finish") return {
|
|
2248
|
+
...value,
|
|
2249
|
+
runId
|
|
2250
|
+
};
|
|
2251
|
+
const result = previewEnabled() ? await execute({
|
|
2252
|
+
action: "preview",
|
|
2253
|
+
documentId: value.documentId
|
|
2254
|
+
}) : value;
|
|
2255
|
+
if (version !== generation) throw Error("Drawing interrupted");
|
|
2256
|
+
completed = {
|
|
2257
|
+
key: JSON.stringify(request),
|
|
2258
|
+
value: {
|
|
2259
|
+
...result,
|
|
2260
|
+
runId
|
|
2261
|
+
}
|
|
2262
|
+
};
|
|
2263
|
+
update("finished");
|
|
2264
|
+
return completed.value;
|
|
2265
|
+
} catch (error) {
|
|
2266
|
+
if (version === generation) {
|
|
2267
|
+
update("failed");
|
|
2268
|
+
throw new Error(`${error.message} Call inspect to obtain the current runId and revision before retrying.`, { cause: error });
|
|
2269
|
+
}
|
|
2270
|
+
throw error;
|
|
2271
|
+
} finally {
|
|
2272
|
+
pending = false;
|
|
2273
|
+
if (state === "drawing") {
|
|
2274
|
+
timer = setTimeout(expire, idleMs);
|
|
2275
|
+
timer.unref?.();
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
//#endregion
|
|
1861
2282
|
//#region src/sketch-agent-client.js
|
|
1862
2283
|
function connectSketchAgent(rpc, sessionId, execute, report, pollDelay = () => 350) {
|
|
1863
|
-
let stopped = false, token, timer;
|
|
2284
|
+
let stopped = false, token, timer, attempts = 0;
|
|
1864
2285
|
const call = (endpoint, payload) => rpc.call(CHANNEL, `sketch/${endpoint}`, {
|
|
1865
2286
|
sessionId,
|
|
1866
2287
|
token,
|
|
@@ -1871,9 +2292,14 @@ window.__ModuleLoader__.load({
|
|
|
1871
2292
|
const tasks = await call("poll");
|
|
1872
2293
|
for (const task of tasks) {
|
|
1873
2294
|
if (stopped) break;
|
|
2295
|
+
if (task.cancelled) {
|
|
2296
|
+
report("Sketch operation interrupted; completed strokes are preserved.");
|
|
2297
|
+
continue;
|
|
2298
|
+
}
|
|
1874
2299
|
let value, error;
|
|
1875
2300
|
try {
|
|
1876
|
-
if (task.expiresAt < Date.now()) throw Error("Sketch command expired; inspect before retrying");
|
|
2301
|
+
if (task.expiresAt < Date.now() || !await call("claim", { id: task.id })) throw Error("Sketch command expired or cancelled; inspect before retrying");
|
|
2302
|
+
if (stopped) break;
|
|
1877
2303
|
value = await execute(task.request);
|
|
1878
2304
|
} catch (cause) {
|
|
1879
2305
|
error = cause.message;
|
|
@@ -1890,29 +2316,23 @@ window.__ModuleLoader__.load({
|
|
|
1890
2316
|
}
|
|
1891
2317
|
if (!stopped) timer = setTimeout(poll, pollDelay());
|
|
1892
2318
|
};
|
|
1893
|
-
call("connect").then((value) => {
|
|
2319
|
+
const connect = () => void call("connect").then((value) => {
|
|
1894
2320
|
token = value.token;
|
|
1895
2321
|
if (stopped) call("disconnect").catch(() => {});
|
|
1896
2322
|
else poll();
|
|
1897
2323
|
}, (error) => {
|
|
1898
|
-
if (
|
|
2324
|
+
if (stopped) return;
|
|
2325
|
+
const leaseConflict = /Another board is connected/.test(error.message);
|
|
2326
|
+
if (++attempts < (leaseConflict ? 8 : 3)) timer = setTimeout(connect, Math.min(3e3, 500 * attempts));
|
|
2327
|
+
else report(error.message);
|
|
1899
2328
|
});
|
|
2329
|
+
connect();
|
|
1900
2330
|
return () => {
|
|
1901
2331
|
stopped = true;
|
|
1902
2332
|
clearTimeout(timer);
|
|
1903
2333
|
if (token) call("disconnect").catch(() => {});
|
|
1904
2334
|
};
|
|
1905
2335
|
}
|
|
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
2336
|
//#endregion
|
|
1917
2337
|
//#region src/sketch-studio.jsx
|
|
1918
2338
|
const PALETTE = [
|
|
@@ -1924,20 +2344,51 @@ window.__ModuleLoader__.load({
|
|
|
1924
2344
|
"#34c759",
|
|
1925
2345
|
"#0088ff"
|
|
1926
2346
|
];
|
|
1927
|
-
function SketchStudio({ open, agentEnabled, onOpen, onClose, attachSketch, enabled, t, incoming, sessionId, rpc }) {
|
|
2347
|
+
function SketchStudio({ open, agentEnabled, agentPreview, onOpen, onClose, attachSketch, enabled, t, incoming, sessionId, rpc }) {
|
|
1928
2348
|
const dialog = (0, react.useRef)(null), canvas = (0, react.useRef)(null), doc = (0, react.useRef)(createSketchLayers()), cache = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
1929
2349
|
const undo = (0, react.useRef)([]), redo = (0, react.useRef)([]), active = (0, react.useRef)(null), frame = (0, react.useRef)(null);
|
|
1930
2350
|
const images = (0, react.useRef)(/* @__PURE__ */ new Map()), saved = (0, react.useRef)(null), dirty = (0, react.useRef)(false), updateUi = (0, react.useRef)(false);
|
|
1931
2351
|
const documentId = (0, react.useRef)(crypto.randomUUID()), documentRevision = (0, react.useRef)(0), agentAdapter = (0, react.useRef)({}), agentSession = (0, react.useRef)(null);
|
|
2352
|
+
const [agentState, setAgentState] = (0, react.useState)("idle"), agentRun = (0, react.useRef)(null);
|
|
2353
|
+
const agentLocked = agentState === "drawing";
|
|
2354
|
+
const [noticeHidden, setNoticeHidden] = (0, react.useState)(false);
|
|
1932
2355
|
const [stability, setStability] = (0, react.useState)(0), [flow, setFlow] = (0, react.useState)(100), [picturesOpen, setPicturesOpen] = (0, react.useState)(false);
|
|
1933
2356
|
const pictureInput = (0, react.useRef)(null), received = (0, react.useRef)(null);
|
|
1934
2357
|
const navigation = useSketchView(canvas, open);
|
|
1935
2358
|
const [revision, redraw] = (0, react.useState)(0), [tool, setTool] = (0, react.useState)("pen"), [brush, setBrush] = (0, react.useState)("pen");
|
|
1936
2359
|
const [eraser, setEraser] = (0, react.useState)("pixel"), [color, setColor] = (0, react.useState)("#0088ff"), [width, setWidth] = (0, react.useState)(12);
|
|
2360
|
+
const [selection, setSelection] = (0, react.useState)(null), [textEdit, setTextEdit] = (0, react.useState)(null), [shapesOpen, setShapesOpen] = (0, react.useState)(false);
|
|
2361
|
+
const sizeGesture = (0, react.useRef)(false);
|
|
2362
|
+
const [sizeMode, setSizeMode] = (0, react.useState)("size");
|
|
2363
|
+
const showOpacity = tool !== "eraser";
|
|
2364
|
+
const opacityMode = showOpacity && sizeMode === "opacity";
|
|
2365
|
+
const selected = doc.current.layers.find((l) => l.id === selection?.layer)?.strokes.find((s, i) => objectId(s, i) === selection?.id);
|
|
2366
|
+
const editObject = (patch, action = "update") => {
|
|
2367
|
+
if (agentRun.current?.locked || busy || !selected) return;
|
|
2368
|
+
try {
|
|
2369
|
+
const next = applySketchCommands(doc.current, [{
|
|
2370
|
+
op: "object",
|
|
2371
|
+
...selection,
|
|
2372
|
+
action,
|
|
2373
|
+
patch
|
|
2374
|
+
}]);
|
|
2375
|
+
if (!sizeGesture.current) checkpoint();
|
|
2376
|
+
doc.current = next;
|
|
2377
|
+
schedule();
|
|
2378
|
+
if (action === "delete") setSelection(null);
|
|
2379
|
+
} catch (e) {
|
|
2380
|
+
setError(e.message);
|
|
2381
|
+
}
|
|
2382
|
+
};
|
|
2383
|
+
const pickColor = (value) => {
|
|
2384
|
+
setColor(value);
|
|
2385
|
+
if (selected) editObject({ color: value });
|
|
2386
|
+
};
|
|
1937
2387
|
const [fillShape, setFillShape] = (0, react.useState)(false);
|
|
1938
2388
|
const [layersOpen, setLayersOpen] = (0, react.useState)(false), [busy, setBusy] = (0, react.useState)(false), [error, setError] = (0, react.useState)("");
|
|
1939
2389
|
const cursorRing = (0, react.useRef)(null);
|
|
1940
|
-
const cursor = useSketchCursor(canvas, cursorRing, width, tool === "pen" ? brush : "pen", navigation.view.scale, !open || navigation.space || busy);
|
|
2390
|
+
const cursor = useSketchCursor(canvas, cursorRing, width, tool === "pen" ? brush : "pen", navigation.view.scale, !open || navigation.space || busy || agentLocked || tool === "select" || tool === "text");
|
|
2391
|
+
useSketchDismiss(shapesOpen, setShapesOpen, dialog, [".codexSketchShapeMenu", ".codexSketchShapeToggle"]);
|
|
1941
2392
|
useSketchDismiss(layersOpen, setLayersOpen, dialog, [".codexSketchLayers", ".codexSketchLayersToggle"]);
|
|
1942
2393
|
useSketchDismiss(picturesOpen, setPicturesOpen, dialog, [".codexSketchPictures", ".codexSketchPicturesToggle"]);
|
|
1943
2394
|
const paint = () => {
|
|
@@ -1967,7 +2418,7 @@ window.__ModuleLoader__.load({
|
|
|
1967
2418
|
redo.current = [];
|
|
1968
2419
|
};
|
|
1969
2420
|
const change = (action, id, value) => {
|
|
1970
|
-
if (busy || active.current) return;
|
|
2421
|
+
if (busy || agentRun.current?.locked || active.current) return;
|
|
1971
2422
|
const next = changeSketchLayer(doc.current, action, id, value);
|
|
1972
2423
|
if (next === doc.current) return;
|
|
1973
2424
|
if (action !== "select") checkpoint();
|
|
@@ -1979,12 +2430,14 @@ window.__ModuleLoader__.load({
|
|
|
1979
2430
|
(0, react.useEffect)(() => {
|
|
1980
2431
|
if (open) {
|
|
1981
2432
|
dialog.current.showModal();
|
|
2433
|
+
canvas.current.width = doc.current.width ?? 1024;
|
|
1982
2434
|
paint();
|
|
1983
2435
|
} else dialog.current?.close();
|
|
1984
2436
|
}, [open]);
|
|
1985
2437
|
(0, react.useEffect)(() => () => {
|
|
1986
2438
|
cancelAnimationFrame(frame.current);
|
|
1987
2439
|
cache.current.clear();
|
|
2440
|
+
agentRun.current?.dispose();
|
|
1988
2441
|
}, []);
|
|
1989
2442
|
const hasContent = () => doc.current.layers.some((layer) => layer.image || layer.strokes.length);
|
|
1990
2443
|
const save = async (name) => {
|
|
@@ -2007,7 +2460,9 @@ window.__ModuleLoader__.load({
|
|
|
2007
2460
|
const replace = (next, decoded, identity) => {
|
|
2008
2461
|
documentId.current = crypto.randomUUID();
|
|
2009
2462
|
documentRevision.current++;
|
|
2010
|
-
doc.current = structuredClone(next);
|
|
2463
|
+
doc.current = identifyObjects(structuredClone(next));
|
|
2464
|
+
setSelection(null);
|
|
2465
|
+
setTextEdit(null);
|
|
2011
2466
|
images.current = decoded;
|
|
2012
2467
|
cache.current.clear();
|
|
2013
2468
|
undo.current = [];
|
|
@@ -2068,6 +2523,10 @@ window.__ModuleLoader__.load({
|
|
|
2068
2523
|
schedule();
|
|
2069
2524
|
};
|
|
2070
2525
|
const close = async () => {
|
|
2526
|
+
if (agentRun.current?.locked) {
|
|
2527
|
+
onClose();
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2071
2530
|
if (busy || active.current) return;
|
|
2072
2531
|
setBusy(true);
|
|
2073
2532
|
try {
|
|
@@ -2080,7 +2539,7 @@ window.__ModuleLoader__.load({
|
|
|
2080
2539
|
}
|
|
2081
2540
|
};
|
|
2082
2541
|
const runFile = async (operation) => {
|
|
2083
|
-
if (busy || active.current) return;
|
|
2542
|
+
if (busy || agentRun.current?.locked || active.current) return;
|
|
2084
2543
|
setBusy(true);
|
|
2085
2544
|
setError("");
|
|
2086
2545
|
try {
|
|
@@ -2092,15 +2551,33 @@ window.__ModuleLoader__.load({
|
|
|
2092
2551
|
}
|
|
2093
2552
|
};
|
|
2094
2553
|
(0, react.useEffect)(() => {
|
|
2095
|
-
if (open && incoming && incoming !== received.current) {
|
|
2554
|
+
if (open && !agentLocked && incoming && incoming !== received.current) {
|
|
2096
2555
|
received.current = incoming;
|
|
2097
2556
|
runFile(() => importImage(incoming.file));
|
|
2098
2557
|
}
|
|
2099
|
-
}, [
|
|
2558
|
+
}, [
|
|
2559
|
+
open,
|
|
2560
|
+
incoming,
|
|
2561
|
+
agentLocked
|
|
2562
|
+
]);
|
|
2100
2563
|
const keyDown = (event) => {
|
|
2101
2564
|
if (event.target.closest("input,textarea,select,[contenteditable=true]") || event.isComposing || busy || active.current) return;
|
|
2102
2565
|
if (!navigation.shortcuts) return;
|
|
2103
2566
|
if (navigation.keyDown(event)) return;
|
|
2567
|
+
if (agentRun.current?.locked) return;
|
|
2568
|
+
if (selection && ["Delete", "Backspace"].includes(event.key)) {
|
|
2569
|
+
event.preventDefault();
|
|
2570
|
+
editObject({}, "delete");
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
if (event.key.toLowerCase() === "v") {
|
|
2574
|
+
setTool("select");
|
|
2575
|
+
return;
|
|
2576
|
+
}
|
|
2577
|
+
if (event.key.toLowerCase() === "t") {
|
|
2578
|
+
setTool("text");
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2104
2581
|
const key = event.key.toLowerCase(), command = event.ctrlKey || event.metaKey;
|
|
2105
2582
|
if (command && [
|
|
2106
2583
|
"z",
|
|
@@ -2137,6 +2614,32 @@ window.__ModuleLoader__.load({
|
|
|
2137
2614
|
const gesture = active.current;
|
|
2138
2615
|
if (!gesture || gesture.id !== event.pointerId || busy) return;
|
|
2139
2616
|
const rect = bounds ?? canvas.current.getBoundingClientRect();
|
|
2617
|
+
if (gesture.object) {
|
|
2618
|
+
const point = sketchPoint(event.clientX, event.clientY, rect);
|
|
2619
|
+
if (!point) return;
|
|
2620
|
+
try {
|
|
2621
|
+
const box = objectBounds(gesture.object);
|
|
2622
|
+
const stroke = gesture.handle === "end" ? {
|
|
2623
|
+
...gesture.object,
|
|
2624
|
+
points: [gesture.object.points[0], point]
|
|
2625
|
+
} : gesture.handle === "size" ? transformObject(gesture.object, {
|
|
2626
|
+
scaleX: Math.max(.001, point.x - box.x) / Math.max(.001, box.width),
|
|
2627
|
+
scaleY: Math.max(.001, point.y - box.y) / Math.max(.001, box.height)
|
|
2628
|
+
}) : transformObject(gesture.object, {
|
|
2629
|
+
dx: point.x - gesture.start.x,
|
|
2630
|
+
dy: point.y - gesture.start.y
|
|
2631
|
+
});
|
|
2632
|
+
doc.current = {
|
|
2633
|
+
...doc.current,
|
|
2634
|
+
layers: doc.current.layers.map((l) => l.id === gesture.layer ? {
|
|
2635
|
+
...l,
|
|
2636
|
+
strokes: l.strokes.map((s) => s.id === gesture.object.id ? stroke : s)
|
|
2637
|
+
} : l)
|
|
2638
|
+
};
|
|
2639
|
+
schedule();
|
|
2640
|
+
} catch {}
|
|
2641
|
+
return;
|
|
2642
|
+
}
|
|
2140
2643
|
const native = event.nativeEvent ?? event;
|
|
2141
2644
|
const events = native.getCoalescedEvents?.() ?? [];
|
|
2142
2645
|
for (const sample of events.length ? [...events, native] : [native]) {
|
|
@@ -2157,6 +2660,7 @@ window.__ModuleLoader__.load({
|
|
|
2157
2660
|
const stroke = layer.strokes.at(-1);
|
|
2158
2661
|
if ([
|
|
2159
2662
|
"line",
|
|
2663
|
+
"arrow",
|
|
2160
2664
|
"rectangle",
|
|
2161
2665
|
"circle"
|
|
2162
2666
|
].includes(stroke.shape)) {
|
|
@@ -2194,13 +2698,29 @@ window.__ModuleLoader__.load({
|
|
|
2194
2698
|
const gesture = active.current, stroke = doc.current.layers.find((layer) => layer.id === gesture.layer)?.strokes.at(-1);
|
|
2195
2699
|
if (gesture.smoothing && stroke) stroke.points = smoothStrokePoints(stroke.points, gesture.smoothing);
|
|
2196
2700
|
}
|
|
2701
|
+
const drawn = active.current;
|
|
2702
|
+
if (!cancel && !drawn.object && [
|
|
2703
|
+
"line",
|
|
2704
|
+
"arrow",
|
|
2705
|
+
"rectangle",
|
|
2706
|
+
"circle"
|
|
2707
|
+
].includes(tool)) {
|
|
2708
|
+
const stroke = doc.current.layers.find((l) => l.id === drawn.layer)?.strokes.at(-1);
|
|
2709
|
+
if (stroke) {
|
|
2710
|
+
setSelection({
|
|
2711
|
+
layer: drawn.layer,
|
|
2712
|
+
id: stroke.id
|
|
2713
|
+
});
|
|
2714
|
+
setTool("select");
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2197
2717
|
active.current = null;
|
|
2198
2718
|
if (canvas.current.hasPointerCapture(event.pointerId)) canvas.current.releasePointerCapture(event.pointerId);
|
|
2199
2719
|
if (cancel) doc.current = undo.current.pop() ?? doc.current;
|
|
2200
2720
|
schedule();
|
|
2201
2721
|
};
|
|
2202
2722
|
const history = (direction) => {
|
|
2203
|
-
if (busy || active.current) return;
|
|
2723
|
+
if (busy || agentRun.current?.locked || active.current) return;
|
|
2204
2724
|
const source = direction === "undo" ? undo : redo, target = direction === "undo" ? redo : undo;
|
|
2205
2725
|
if (!source.current.length) return;
|
|
2206
2726
|
documentRevision.current++;
|
|
@@ -2210,9 +2730,10 @@ window.__ModuleLoader__.load({
|
|
|
2210
2730
|
schedule();
|
|
2211
2731
|
};
|
|
2212
2732
|
Object.assign(agentAdapter.current, {
|
|
2213
|
-
available: () =>
|
|
2733
|
+
available: () => enabled && agentEnabled,
|
|
2734
|
+
previewEnabled: () => agentPreview,
|
|
2214
2735
|
open: () => onOpen(),
|
|
2215
|
-
busy: () => busy || Boolean(active.current),
|
|
2736
|
+
busy: () => busy || Boolean(active.current) || Boolean(textEdit) || sizeGesture.current,
|
|
2216
2737
|
document: () => doc.current,
|
|
2217
2738
|
snapshot: () => ({
|
|
2218
2739
|
documentId: documentId.current,
|
|
@@ -2229,6 +2750,15 @@ window.__ModuleLoader__.load({
|
|
|
2229
2750
|
})),
|
|
2230
2751
|
strokeCount: strokeCount(doc.current)
|
|
2231
2752
|
}),
|
|
2753
|
+
objects: () => sketchObjectSummary(doc.current),
|
|
2754
|
+
object: (id, layer = doc.current.active) => {
|
|
2755
|
+
const target = doc.current.layers.find((l) => l.id === layer)?.strokes.find((s, i) => objectId(s, i) === id);
|
|
2756
|
+
if (!target) throw Error("Object not found");
|
|
2757
|
+
return {
|
|
2758
|
+
...target,
|
|
2759
|
+
id
|
|
2760
|
+
};
|
|
2761
|
+
},
|
|
2232
2762
|
commit: (next) => {
|
|
2233
2763
|
checkpoint();
|
|
2234
2764
|
doc.current = next;
|
|
@@ -2239,24 +2769,27 @@ window.__ModuleLoader__.load({
|
|
|
2239
2769
|
paint();
|
|
2240
2770
|
return canvas.current.toDataURL("image/png");
|
|
2241
2771
|
},
|
|
2242
|
-
save
|
|
2243
|
-
setBusy(true);
|
|
2244
|
-
try {
|
|
2245
|
-
await save(name);
|
|
2246
|
-
} finally {
|
|
2247
|
-
setBusy(false);
|
|
2248
|
-
}
|
|
2249
|
-
}
|
|
2772
|
+
save
|
|
2250
2773
|
});
|
|
2251
2774
|
agentSession.current ??= createSketchCommandSession(agentAdapter.current);
|
|
2775
|
+
agentRun.current ??= createSketchAgentRun({
|
|
2776
|
+
execute: (request) => agentSession.current(request),
|
|
2777
|
+
open: () => agentAdapter.current.open(),
|
|
2778
|
+
changed: (state) => {
|
|
2779
|
+
setAgentState(state);
|
|
2780
|
+
setNoticeHidden(false);
|
|
2781
|
+
},
|
|
2782
|
+
busy: () => agentAdapter.current.busy(),
|
|
2783
|
+
previewEnabled: () => agentAdapter.current.previewEnabled()
|
|
2784
|
+
});
|
|
2252
2785
|
(0, react.useEffect)(() => {
|
|
2253
|
-
if (!
|
|
2786
|
+
if (!enabled || !agentEnabled) return;
|
|
2254
2787
|
const api = Object.freeze({
|
|
2255
2788
|
version: 1,
|
|
2256
2789
|
sessionId,
|
|
2257
|
-
execute: (request) =>
|
|
2790
|
+
execute: (request) => agentRun.current.execute(request),
|
|
2258
2791
|
export: async (format) => {
|
|
2259
|
-
if (agentAdapter.current.busy()) throw Error("Sketch is being edited");
|
|
2792
|
+
if (agentAdapter.current.busy() || agentRun.current.locked) throw Error("Sketch is being edited");
|
|
2260
2793
|
const { blob, extension } = await agentAdapter.current.export(format);
|
|
2261
2794
|
const data = new Uint8Array(await blob.arrayBuffer());
|
|
2262
2795
|
let raw = "";
|
|
@@ -2273,7 +2806,6 @@ window.__ModuleLoader__.load({
|
|
|
2273
2806
|
if (window.dshSketchAgent === api) delete window.dshSketchAgent;
|
|
2274
2807
|
};
|
|
2275
2808
|
}, [
|
|
2276
|
-
open,
|
|
2277
2809
|
enabled,
|
|
2278
2810
|
agentEnabled,
|
|
2279
2811
|
rpc,
|
|
@@ -2282,14 +2814,17 @@ window.__ModuleLoader__.load({
|
|
|
2282
2814
|
(0, react.useEffect)(() => {
|
|
2283
2815
|
if (!enabled || !agentEnabled || !rpc || !sessionId) return;
|
|
2284
2816
|
let live = true;
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2817
|
+
agentRun.current.resume();
|
|
2818
|
+
const disconnect = connectSketchAgent(rpc, sessionId, (request) => {
|
|
2819
|
+
if (!live) throw Error("Sketch session disconnected");
|
|
2820
|
+
return agentRun.current.execute(request);
|
|
2821
|
+
}, (message) => {
|
|
2822
|
+
agentRun.current.fail();
|
|
2823
|
+
setError(message);
|
|
2824
|
+
}, () => 350);
|
|
2291
2825
|
return () => {
|
|
2292
2826
|
live = false;
|
|
2827
|
+
agentRun.current.stop();
|
|
2293
2828
|
disconnect();
|
|
2294
2829
|
};
|
|
2295
2830
|
}, [
|
|
@@ -2299,7 +2834,7 @@ window.__ModuleLoader__.load({
|
|
|
2299
2834
|
sessionId
|
|
2300
2835
|
]);
|
|
2301
2836
|
const attach = async () => {
|
|
2302
|
-
if (!enabled || busy) return;
|
|
2837
|
+
if (!enabled || busy || agentRun.current?.locked) return;
|
|
2303
2838
|
setBusy(true);
|
|
2304
2839
|
setError("");
|
|
2305
2840
|
try {
|
|
@@ -2342,13 +2877,36 @@ window.__ModuleLoader__.load({
|
|
|
2342
2877
|
link.remove();
|
|
2343
2878
|
setTimeout(() => URL.revokeObjectURL(url), 1e4);
|
|
2344
2879
|
};
|
|
2345
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
2880
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [enabled && agentEnabled && !open && !noticeHidden && agentState !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2881
|
+
className: "codexSketchBackgroundStatus",
|
|
2882
|
+
role: "status",
|
|
2883
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2884
|
+
type: "button",
|
|
2885
|
+
onClick: onOpen,
|
|
2886
|
+
children: t(`sketchRun_${agentState}`)
|
|
2887
|
+
}), agentLocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2888
|
+
type: "button",
|
|
2889
|
+
"aria-label": t("sketchRunStop"),
|
|
2890
|
+
onClick: () => agentRun.current.stop(),
|
|
2891
|
+
children: "×"
|
|
2892
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2893
|
+
type: "button",
|
|
2894
|
+
"aria-label": t("sketchCancel"),
|
|
2895
|
+
onClick: () => setNoticeHidden(true),
|
|
2896
|
+
children: "×"
|
|
2897
|
+
})]
|
|
2898
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dialog", {
|
|
2346
2899
|
ref: dialog,
|
|
2347
2900
|
className: "codexSketchDialog codexSketchStudio codexLayerStudio",
|
|
2348
2901
|
"aria-label": t("sketchTitle"),
|
|
2349
2902
|
onKeyDown: keyDown,
|
|
2350
2903
|
onKeyUp: navigation.keyUp,
|
|
2351
2904
|
onPaste: (event) => {
|
|
2905
|
+
if (agentRun.current?.locked) {
|
|
2906
|
+
event.preventDefault();
|
|
2907
|
+
event.stopPropagation();
|
|
2908
|
+
return;
|
|
2909
|
+
}
|
|
2352
2910
|
if (event.target.closest("input,textarea")) return;
|
|
2353
2911
|
const file = Array.from(event.clipboardData.items).find((item) => item.type.startsWith("image/"))?.getAsFile();
|
|
2354
2912
|
if (file) {
|
|
@@ -2383,7 +2941,7 @@ window.__ModuleLoader__.load({
|
|
|
2383
2941
|
type: "button",
|
|
2384
2942
|
"aria-label": t("sketchCancel"),
|
|
2385
2943
|
title: t("sketchCancel"),
|
|
2386
|
-
disabled: busy,
|
|
2944
|
+
disabled: busy && !agentLocked,
|
|
2387
2945
|
onClick: close,
|
|
2388
2946
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, { name: "close" })
|
|
2389
2947
|
}),
|
|
@@ -2394,7 +2952,7 @@ window.__ModuleLoader__.load({
|
|
|
2394
2952
|
importImage,
|
|
2395
2953
|
download,
|
|
2396
2954
|
hasContent: doc.current.layers.some((l) => l.visible && (l.image || l.strokes.length)),
|
|
2397
|
-
disabled: busy,
|
|
2955
|
+
disabled: agentLocked || busy,
|
|
2398
2956
|
t,
|
|
2399
2957
|
report: setError,
|
|
2400
2958
|
onWorking: setBusy
|
|
@@ -2413,7 +2971,7 @@ window.__ModuleLoader__.load({
|
|
|
2413
2971
|
className: "codexSketchRound",
|
|
2414
2972
|
"aria-label": t(name === "undo" ? "sketchUndo" : "sketchRedo"),
|
|
2415
2973
|
title: t(name === "undo" ? "sketchUndo" : "sketchRedo"),
|
|
2416
|
-
disabled: busy || !(name === "undo" ? undo : redo).current.length,
|
|
2974
|
+
disabled: agentLocked || busy || !(name === "undo" ? undo : redo).current.length,
|
|
2417
2975
|
onClick: () => history(name),
|
|
2418
2976
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
|
|
2419
2977
|
name,
|
|
@@ -2426,9 +2984,9 @@ window.__ModuleLoader__.load({
|
|
|
2426
2984
|
"aria-label": t("sketchRatio"),
|
|
2427
2985
|
title: t("sketchRatioHint"),
|
|
2428
2986
|
value: doc.current.ratio ?? "1:1",
|
|
2429
|
-
disabled: busy,
|
|
2987
|
+
disabled: agentLocked || busy,
|
|
2430
2988
|
onChange: (event) => {
|
|
2431
|
-
if (active.current) return;
|
|
2989
|
+
if (active.current || agentRun.current?.locked) return;
|
|
2432
2990
|
const next = resizeSketch(doc.current, event.target.value);
|
|
2433
2991
|
if (next === doc.current) return;
|
|
2434
2992
|
checkpoint();
|
|
@@ -2469,7 +3027,7 @@ window.__ModuleLoader__.load({
|
|
|
2469
3027
|
type: "button",
|
|
2470
3028
|
className: "codexSketchConfirm",
|
|
2471
3029
|
"aria-label": t("sketchAttach"),
|
|
2472
|
-
disabled: busy || !enabled || !doc.current.layers.some((l) => l.visible && (l.strokes.length || l.image)),
|
|
3030
|
+
disabled: agentLocked || busy || !enabled || !doc.current.layers.some((l) => l.visible && (l.strokes.length || l.image)),
|
|
2473
3031
|
onClick: () => void attach(),
|
|
2474
3032
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
|
|
2475
3033
|
name: "check",
|
|
@@ -2478,6 +3036,25 @@ window.__ModuleLoader__.load({
|
|
|
2478
3036
|
})
|
|
2479
3037
|
]
|
|
2480
3038
|
}),
|
|
3039
|
+
enabled && agentEnabled && agentState !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3040
|
+
className: "codexSketchAgentStatus",
|
|
3041
|
+
role: "status",
|
|
3042
|
+
"aria-live": "polite",
|
|
3043
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`sketchRun_${agentState}`) }), agentLocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3044
|
+
type: "button",
|
|
3045
|
+
"aria-label": t("sketchRunStop"),
|
|
3046
|
+
title: t("sketchRunStop"),
|
|
3047
|
+
onClick: () => agentRun.current.stop(),
|
|
3048
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
|
|
3049
|
+
name: "close",
|
|
3050
|
+
size: 16
|
|
3051
|
+
})
|
|
3052
|
+
}) : agentState === "stopped" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3053
|
+
type: "button",
|
|
3054
|
+
onClick: () => agentRun.current.resume(),
|
|
3055
|
+
children: t("sketchRunResume")
|
|
3056
|
+
}) : null]
|
|
3057
|
+
}) : null,
|
|
2481
3058
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2482
3059
|
className: `codexLayerBody ${layersOpen ? "withLayers" : ""}`,
|
|
2483
3060
|
children: [
|
|
@@ -2485,7 +3062,7 @@ window.__ModuleLoader__.load({
|
|
|
2485
3062
|
tabIndex: 0,
|
|
2486
3063
|
style: {
|
|
2487
3064
|
transform: `translate(${navigation.view.x}px,${navigation.view.y}px) scale(${navigation.view.scale})`,
|
|
2488
|
-
cursor: navigation.space ? "grab" : "none",
|
|
3065
|
+
cursor: navigation.space ? "grab" : agentLocked ? "default" : tool === "select" ? "default" : tool === "text" ? "text" : "none",
|
|
2489
3066
|
"--sketch-ratio": (doc.current.width ?? 1024) / (doc.current.height ?? 1024)
|
|
2490
3067
|
},
|
|
2491
3068
|
ref: canvas,
|
|
@@ -2495,7 +3072,69 @@ window.__ModuleLoader__.load({
|
|
|
2495
3072
|
onPointerDown: (event) => {
|
|
2496
3073
|
if (busy || !enabled || active.current) return;
|
|
2497
3074
|
if (navigation.down(event)) return;
|
|
3075
|
+
if (agentRun.current?.locked) return;
|
|
2498
3076
|
if (event.button !== 0) return;
|
|
3077
|
+
const point = sketchPoint(event.clientX, event.clientY, canvas.current.getBoundingClientRect());
|
|
3078
|
+
if (!point) return;
|
|
3079
|
+
if (tool === "text") {
|
|
3080
|
+
setTextEdit({
|
|
3081
|
+
point,
|
|
3082
|
+
value: ""
|
|
3083
|
+
});
|
|
3084
|
+
return;
|
|
3085
|
+
}
|
|
3086
|
+
if (tool === "select") {
|
|
3087
|
+
doc.current = identifyObjects(doc.current);
|
|
3088
|
+
if (selected) {
|
|
3089
|
+
const b = objectBounds(selected), end = ["line", "arrow"].includes(selected.shape) ? selected.points.at(-1) : {
|
|
3090
|
+
x: b.x + b.width,
|
|
3091
|
+
y: b.y + b.height
|
|
3092
|
+
}, rect = canvas.current.getBoundingClientRect();
|
|
3093
|
+
if (Math.hypot((end.x - point.x) * rect.width, (end.y - point.y) * rect.height) < 12) {
|
|
3094
|
+
checkpoint();
|
|
3095
|
+
active.current = {
|
|
3096
|
+
id: event.pointerId,
|
|
3097
|
+
layer: selection.layer,
|
|
3098
|
+
object: {
|
|
3099
|
+
...selected,
|
|
3100
|
+
id: selection.id
|
|
3101
|
+
},
|
|
3102
|
+
start: point,
|
|
3103
|
+
handle: ["line", "arrow"].includes(selected.shape) ? "end" : "size"
|
|
3104
|
+
};
|
|
3105
|
+
canvas.current.setPointerCapture(event.pointerId);
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
let hit;
|
|
3110
|
+
for (const l of doc.current.layers.slice().reverse()) {
|
|
3111
|
+
if (!l.visible) continue;
|
|
3112
|
+
const stroke = l.strokes.slice().reverse().find((s) => s.shape !== "eraser" && strokeHit(s, point, 6, doc.current.width, doc.current.height));
|
|
3113
|
+
if (stroke) {
|
|
3114
|
+
hit = {
|
|
3115
|
+
layer: l.id,
|
|
3116
|
+
stroke
|
|
3117
|
+
};
|
|
3118
|
+
break;
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
setSelection(hit ? {
|
|
3122
|
+
layer: hit.layer,
|
|
3123
|
+
id: hit.stroke.id
|
|
3124
|
+
} : null);
|
|
3125
|
+
if (hit) {
|
|
3126
|
+
checkpoint();
|
|
3127
|
+
active.current = {
|
|
3128
|
+
id: event.pointerId,
|
|
3129
|
+
layer: hit.layer,
|
|
3130
|
+
object: hit.stroke,
|
|
3131
|
+
start: point
|
|
3132
|
+
};
|
|
3133
|
+
canvas.current.setPointerCapture(event.pointerId);
|
|
3134
|
+
}
|
|
3135
|
+
return;
|
|
3136
|
+
}
|
|
3137
|
+
setSelection(null);
|
|
2499
3138
|
cursor.down(event);
|
|
2500
3139
|
if (!current.visible) {
|
|
2501
3140
|
setError(t("sketchHiddenLayer"));
|
|
@@ -2523,6 +3162,7 @@ window.__ModuleLoader__.load({
|
|
|
2523
3162
|
const layer = layers.find((layer) => layer.id === doc.current.active);
|
|
2524
3163
|
const eraseStroke = tool === "eraser" && eraser === "stroke";
|
|
2525
3164
|
if (!eraseStroke) layer.strokes.push({
|
|
3165
|
+
id: crypto.randomUUID(),
|
|
2526
3166
|
color,
|
|
2527
3167
|
opacity: flow / 100,
|
|
2528
3168
|
shape: tool,
|
|
@@ -2554,11 +3194,131 @@ window.__ModuleLoader__.load({
|
|
|
2554
3194
|
onPointerUp: (event) => end(event),
|
|
2555
3195
|
onPointerCancel: (event) => end(event, true)
|
|
2556
3196
|
}),
|
|
3197
|
+
selected && tool === "select" && !agentLocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
3198
|
+
className: "codexSketchSelection",
|
|
3199
|
+
viewBox: "0 0 1 1",
|
|
3200
|
+
preserveAspectRatio: "none",
|
|
3201
|
+
style: {
|
|
3202
|
+
"--sketch-ratio": (doc.current.width ?? 1024) / (doc.current.height ?? 1024),
|
|
3203
|
+
transform: `translate(${navigation.view.x}px,${navigation.view.y}px) scale(${navigation.view.scale})`
|
|
3204
|
+
},
|
|
3205
|
+
"aria-hidden": "true",
|
|
3206
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
3207
|
+
...objectBounds(selected),
|
|
3208
|
+
fill: "none",
|
|
3209
|
+
stroke: "#0088ff",
|
|
3210
|
+
strokeWidth: ".002",
|
|
3211
|
+
strokeDasharray: ".008 .005"
|
|
3212
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
3213
|
+
cx: ["line", "arrow"].includes(selected.shape) ? selected.points.at(-1).x : objectBounds(selected).x + objectBounds(selected).width,
|
|
3214
|
+
cy: ["line", "arrow"].includes(selected.shape) ? selected.points.at(-1).y : objectBounds(selected).y + objectBounds(selected).height,
|
|
3215
|
+
r: ".007",
|
|
3216
|
+
fill: "white",
|
|
3217
|
+
stroke: "#0088ff",
|
|
3218
|
+
strokeWidth: ".002"
|
|
3219
|
+
})]
|
|
3220
|
+
}) : null,
|
|
3221
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SketchSizeControl, {
|
|
3222
|
+
label: t(opacityMode ? "sketchFlow" : selected?.shape === "text" || tool === "text" ? "sketchTextSize" : "sketchWidth"),
|
|
3223
|
+
mode: opacityMode ? "opacity" : "size",
|
|
3224
|
+
modes: showOpacity ? [{
|
|
3225
|
+
value: "size",
|
|
3226
|
+
label: t(selected?.shape === "text" || tool === "text" ? "sketchTextSize" : "sketchSizeShort")
|
|
3227
|
+
}, {
|
|
3228
|
+
value: "opacity",
|
|
3229
|
+
label: t("sketchFlow")
|
|
3230
|
+
}] : void 0,
|
|
3231
|
+
onModeChange: setSizeMode,
|
|
3232
|
+
min: opacityMode ? 5 : 1,
|
|
3233
|
+
max: opacityMode ? 100 : 256,
|
|
3234
|
+
suffix: opacityMode ? "%" : "",
|
|
3235
|
+
value: opacityMode ? (selected?.opacity ?? flow / 100) * 100 : selected?.width ?? width,
|
|
3236
|
+
disabled: agentLocked || busy,
|
|
3237
|
+
onStart: () => {
|
|
3238
|
+
if (selected) {
|
|
3239
|
+
checkpoint();
|
|
3240
|
+
sizeGesture.current = true;
|
|
3241
|
+
}
|
|
3242
|
+
},
|
|
3243
|
+
onEnd: () => {
|
|
3244
|
+
sizeGesture.current = false;
|
|
3245
|
+
},
|
|
3246
|
+
onChange: (value) => {
|
|
3247
|
+
if (opacityMode) {
|
|
3248
|
+
setFlow(value);
|
|
3249
|
+
if (selected) editObject({ opacity: value / 100 });
|
|
3250
|
+
} else {
|
|
3251
|
+
setWidth(value);
|
|
3252
|
+
if (selected) editObject({ width: value });
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
}),
|
|
3256
|
+
textEdit ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
3257
|
+
className: "codexSketchTextEditor",
|
|
3258
|
+
onSubmit: (event) => {
|
|
3259
|
+
event.preventDefault();
|
|
3260
|
+
if (!textEdit.value.trim()) {
|
|
3261
|
+
setTextEdit(null);
|
|
3262
|
+
return;
|
|
3263
|
+
}
|
|
3264
|
+
try {
|
|
3265
|
+
if (textEdit.selection) editObject({ text: textEdit.value });
|
|
3266
|
+
else {
|
|
3267
|
+
const a = textEdit.point, b = {
|
|
3268
|
+
x: Math.min(1, a.x + .35),
|
|
3269
|
+
y: Math.min(1, a.y + .15)
|
|
3270
|
+
}, id = crypto.randomUUID();
|
|
3271
|
+
const next = applySketchCommands(doc.current, [{
|
|
3272
|
+
op: "stroke",
|
|
3273
|
+
id,
|
|
3274
|
+
shape: "text",
|
|
3275
|
+
text: textEdit.value,
|
|
3276
|
+
color,
|
|
3277
|
+
width: Math.max(24, width),
|
|
3278
|
+
points: [a, b]
|
|
3279
|
+
}]);
|
|
3280
|
+
checkpoint();
|
|
3281
|
+
doc.current = next;
|
|
3282
|
+
setSelection({
|
|
3283
|
+
layer: doc.current.active,
|
|
3284
|
+
id
|
|
3285
|
+
});
|
|
3286
|
+
schedule();
|
|
3287
|
+
}
|
|
3288
|
+
setTextEdit(null);
|
|
3289
|
+
setTool("select");
|
|
3290
|
+
} catch (e) {
|
|
3291
|
+
setError(e.message);
|
|
3292
|
+
}
|
|
3293
|
+
},
|
|
3294
|
+
children: [
|
|
3295
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
3296
|
+
autoFocus: true,
|
|
3297
|
+
"aria-label": t("sketchText"),
|
|
3298
|
+
maxLength: 500,
|
|
3299
|
+
value: textEdit.value,
|
|
3300
|
+
onChange: (e) => setTextEdit({
|
|
3301
|
+
...textEdit,
|
|
3302
|
+
value: e.target.value
|
|
3303
|
+
})
|
|
3304
|
+
}),
|
|
3305
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3306
|
+
type: "submit",
|
|
3307
|
+
children: t("sketchTextDone")
|
|
3308
|
+
}),
|
|
3309
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3310
|
+
type: "button",
|
|
3311
|
+
onClick: () => setTextEdit(null),
|
|
3312
|
+
children: t("sketchCancel")
|
|
3313
|
+
})
|
|
3314
|
+
]
|
|
3315
|
+
}) : null,
|
|
2557
3316
|
picturesOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
|
|
2558
3317
|
className: "codexSketchPictures",
|
|
2559
3318
|
children: [
|
|
2560
3319
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("sketchPictures") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2561
3320
|
type: "button",
|
|
3321
|
+
disabled: agentLocked || busy,
|
|
2562
3322
|
onClick: () => pictureInput.current.click(),
|
|
2563
3323
|
children: t("sketchPictureAdd")
|
|
2564
3324
|
})] }),
|
|
@@ -2577,6 +3337,7 @@ window.__ModuleLoader__.load({
|
|
|
2577
3337
|
"data-active": layer.id === doc.current.active,
|
|
2578
3338
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2579
3339
|
type: "button",
|
|
3340
|
+
disabled: agentLocked || busy,
|
|
2580
3341
|
"aria-label": `${t("sketchPictureSelect")} ${layer.name}`,
|
|
2581
3342
|
onClick: () => change("select", layer.id),
|
|
2582
3343
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
@@ -2585,6 +3346,7 @@ window.__ModuleLoader__.load({
|
|
|
2585
3346
|
})
|
|
2586
3347
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2587
3348
|
type: "button",
|
|
3349
|
+
disabled: agentLocked || busy,
|
|
2588
3350
|
"aria-label": `${t("sketchDeleteDraft")} ${layer.name}`,
|
|
2589
3351
|
onClick: () => change(doc.current.layers.length === 1 ? "clear" : "delete", layer.id),
|
|
2590
3352
|
children: "×"
|
|
@@ -2601,7 +3363,7 @@ window.__ModuleLoader__.load({
|
|
|
2601
3363
|
type: "button",
|
|
2602
3364
|
title: t("sketchLayerAdd"),
|
|
2603
3365
|
"aria-label": t("sketchLayerAdd"),
|
|
2604
|
-
disabled: busy || doc.current.layers.length >= 8,
|
|
3366
|
+
disabled: agentLocked || busy || doc.current.layers.length >= 8,
|
|
2605
3367
|
onClick: () => change("add"),
|
|
2606
3368
|
children: "+"
|
|
2607
3369
|
})] }),
|
|
@@ -2612,6 +3374,7 @@ window.__ModuleLoader__.load({
|
|
|
2612
3374
|
"data-active": layer.id === doc.current.active,
|
|
2613
3375
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2614
3376
|
type: "button",
|
|
3377
|
+
disabled: agentLocked || busy,
|
|
2615
3378
|
"aria-label": `${t("sketchLayerVisible")} ${layer.id}`,
|
|
2616
3379
|
"aria-pressed": layer.visible,
|
|
2617
3380
|
onClick: () => change("visible", layer.id),
|
|
@@ -2621,6 +3384,7 @@ window.__ModuleLoader__.load({
|
|
|
2621
3384
|
})
|
|
2622
3385
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2623
3386
|
type: "button",
|
|
3387
|
+
disabled: agentLocked || busy,
|
|
2624
3388
|
"aria-pressed": layer.id === doc.current.active,
|
|
2625
3389
|
onClick: () => change("select", layer.id),
|
|
2626
3390
|
children: layer.name || `${t("sketchLayer")} ${layer.id}`
|
|
@@ -2632,6 +3396,7 @@ window.__ModuleLoader__.load({
|
|
|
2632
3396
|
children: t("sketchLayerName")
|
|
2633
3397
|
}),
|
|
2634
3398
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3399
|
+
disabled: agentLocked || busy,
|
|
2635
3400
|
"aria-label": t("sketchLayerName"),
|
|
2636
3401
|
defaultValue: current.name,
|
|
2637
3402
|
placeholder: `${t("sketchLayer")} ${current.id}`,
|
|
@@ -2654,7 +3419,7 @@ window.__ModuleLoader__.load({
|
|
|
2654
3419
|
type: "button",
|
|
2655
3420
|
title: t(`sketchLayer_${action}`),
|
|
2656
3421
|
"aria-label": t(`sketchLayer_${action}`),
|
|
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],
|
|
3422
|
+
disabled: agentLocked || 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],
|
|
2658
3423
|
onClick: () => change(action),
|
|
2659
3424
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
|
|
2660
3425
|
name: action === "delete" ? "clear" : action,
|
|
@@ -2665,7 +3430,7 @@ window.__ModuleLoader__.load({
|
|
|
2665
3430
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2666
3431
|
type: "button",
|
|
2667
3432
|
className: "codexSketchClearLayer",
|
|
2668
|
-
disabled: busy || !current.strokes.length && !current.image,
|
|
3433
|
+
disabled: agentLocked || busy || !current.strokes.length && !current.image,
|
|
2669
3434
|
onClick: () => change("clear"),
|
|
2670
3435
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
|
|
2671
3436
|
name: "clear",
|
|
@@ -2690,57 +3455,123 @@ window.__ModuleLoader__.load({
|
|
|
2690
3455
|
navigation,
|
|
2691
3456
|
t
|
|
2692
3457
|
}),
|
|
2693
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3458
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2694
3459
|
className: "codexSketchPill",
|
|
2695
3460
|
role: "toolbar",
|
|
2696
3461
|
"aria-label": t("sketchTitle"),
|
|
2697
3462
|
title: t("sketchShortcuts"),
|
|
2698
3463
|
children: [
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
"marker",
|
|
2702
|
-
"eraser",
|
|
2703
|
-
"line",
|
|
2704
|
-
"rectangle",
|
|
2705
|
-
"circle"
|
|
2706
|
-
].map((name) => {
|
|
2707
|
-
const drawing = [
|
|
3464
|
+
[
|
|
3465
|
+
"select",
|
|
2708
3466
|
"pen",
|
|
2709
|
-
"
|
|
2710
|
-
"
|
|
2711
|
-
].
|
|
2712
|
-
|
|
2713
|
-
|
|
3467
|
+
"text",
|
|
3468
|
+
"eraser"
|
|
3469
|
+
].map((name) => {
|
|
3470
|
+
const drawing = [
|
|
3471
|
+
"pen",
|
|
3472
|
+
"pencil",
|
|
3473
|
+
"marker"
|
|
3474
|
+
].includes(name);
|
|
3475
|
+
const label = t(drawing ? `sketchBrush_${name}` : `sketchTool_${name}`);
|
|
3476
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3477
|
+
type: "button",
|
|
3478
|
+
"aria-label": label,
|
|
3479
|
+
title: label,
|
|
3480
|
+
"aria-pressed": drawing ? tool === "pen" && brush === name : tool === name,
|
|
3481
|
+
disabled: agentLocked || busy,
|
|
3482
|
+
onClick: () => {
|
|
3483
|
+
setTool(drawing ? "pen" : name);
|
|
3484
|
+
if (name !== "select") setSelection(null);
|
|
3485
|
+
if (drawing) setBrush(name);
|
|
3486
|
+
},
|
|
3487
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
|
|
3488
|
+
name,
|
|
3489
|
+
size: 23
|
|
3490
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
|
|
3491
|
+
}, name);
|
|
3492
|
+
}),
|
|
3493
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3494
|
+
className: "codexSketchShapeToggle",
|
|
2714
3495
|
type: "button",
|
|
2715
|
-
"aria-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
disabled: busy,
|
|
2719
|
-
onClick: () => {
|
|
2720
|
-
setTool(drawing ? "pen" : name);
|
|
2721
|
-
if (drawing) setBrush(name);
|
|
2722
|
-
},
|
|
3496
|
+
"aria-expanded": shapesOpen,
|
|
3497
|
+
disabled: agentLocked || busy,
|
|
3498
|
+
onClick: () => setShapesOpen((v) => !v),
|
|
2723
3499
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
|
|
2724
|
-
name,
|
|
3500
|
+
name: "rectangle",
|
|
2725
3501
|
size: 23
|
|
2726
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children:
|
|
2727
|
-
},
|
|
2728
|
-
|
|
3502
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("sketchShapes") })]
|
|
3503
|
+
}),
|
|
3504
|
+
shapesOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3505
|
+
className: "codexSketchShapeMenu",
|
|
3506
|
+
children: [
|
|
3507
|
+
"line",
|
|
3508
|
+
"arrow",
|
|
3509
|
+
"rectangle",
|
|
3510
|
+
"circle"
|
|
3511
|
+
].map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3512
|
+
type: "button",
|
|
3513
|
+
onClick: () => {
|
|
3514
|
+
setTool(name);
|
|
3515
|
+
setSelection(null);
|
|
3516
|
+
setShapesOpen(false);
|
|
3517
|
+
},
|
|
3518
|
+
children: t(`sketchTool_${name}`)
|
|
3519
|
+
}, name))
|
|
3520
|
+
}) : null
|
|
3521
|
+
]
|
|
2729
3522
|
}),
|
|
2730
3523
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2731
3524
|
className: "codexLayerBrush",
|
|
2732
3525
|
children: [
|
|
2733
3526
|
["rectangle", "circle"].includes(tool) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2734
3527
|
type: "checkbox",
|
|
3528
|
+
disabled: agentLocked || busy,
|
|
2735
3529
|
checked: fillShape,
|
|
2736
3530
|
onChange: (e) => setFillShape(e.target.checked)
|
|
2737
3531
|
}), t("sketchFill")] }) : null,
|
|
3532
|
+
tool === "pen" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
3533
|
+
"aria-label": t("sketchBrush"),
|
|
3534
|
+
value: brush,
|
|
3535
|
+
onChange: (e) => setBrush(e.target.value),
|
|
3536
|
+
disabled: agentLocked || busy,
|
|
3537
|
+
children: [
|
|
3538
|
+
"pen",
|
|
3539
|
+
"pencil",
|
|
3540
|
+
"marker"
|
|
3541
|
+
].map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3542
|
+
value: b,
|
|
3543
|
+
children: t(`sketchBrush_${b}`)
|
|
3544
|
+
}, b))
|
|
3545
|
+
}) : null,
|
|
3546
|
+
selected ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3547
|
+
className: "codexSketchObjectActions",
|
|
3548
|
+
children: [
|
|
3549
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3550
|
+
disabled: agentLocked || busy,
|
|
3551
|
+
onClick: () => editObject({}, "duplicate"),
|
|
3552
|
+
children: t("sketchObjectDuplicate")
|
|
3553
|
+
}),
|
|
3554
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3555
|
+
disabled: agentLocked || busy,
|
|
3556
|
+
onClick: () => editObject({}, "delete"),
|
|
3557
|
+
children: t("sketchObjectDelete")
|
|
3558
|
+
}),
|
|
3559
|
+
selected.shape === "text" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3560
|
+
disabled: agentLocked || busy,
|
|
3561
|
+
onClick: () => setTextEdit({
|
|
3562
|
+
selection,
|
|
3563
|
+
value: selected.text
|
|
3564
|
+
}),
|
|
3565
|
+
children: t("sketchText")
|
|
3566
|
+
}) : null
|
|
3567
|
+
]
|
|
3568
|
+
}) : null,
|
|
2738
3569
|
tool === "pen" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2739
3570
|
className: "codexSketchStability",
|
|
2740
3571
|
children: [t("sketchStability"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
2741
3572
|
"aria-label": t("sketchStability"),
|
|
2742
3573
|
value: stability,
|
|
2743
|
-
disabled: busy,
|
|
3574
|
+
disabled: agentLocked || busy,
|
|
2744
3575
|
onChange: (e) => setStability(Number(e.target.value)),
|
|
2745
3576
|
children: [
|
|
2746
3577
|
0,
|
|
@@ -2760,43 +3591,11 @@ window.__ModuleLoader__.load({
|
|
|
2760
3591
|
children: ["pixel", "stroke"].map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2761
3592
|
type: "button",
|
|
2762
3593
|
"aria-pressed": eraser === value,
|
|
2763
|
-
disabled: busy,
|
|
3594
|
+
disabled: agentLocked || busy,
|
|
2764
3595
|
onClick: () => setEraser(value),
|
|
2765
3596
|
children: t(`sketchErase_${value}`)
|
|
2766
3597
|
}, value))
|
|
2767
|
-
}) : null
|
|
2768
|
-
tool !== "eraser" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2769
|
-
className: "codexSketchWidth",
|
|
2770
|
-
children: [
|
|
2771
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("sketchFlow") }),
|
|
2772
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2773
|
-
type: "range",
|
|
2774
|
-
min: 5,
|
|
2775
|
-
max: 100,
|
|
2776
|
-
step: 5,
|
|
2777
|
-
value: flow,
|
|
2778
|
-
"aria-label": t("sketchFlow"),
|
|
2779
|
-
onChange: (e) => setFlow(Number(e.target.value))
|
|
2780
|
-
}),
|
|
2781
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("output", { children: flow })
|
|
2782
|
-
]
|
|
2783
|
-
}) : null,
|
|
2784
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2785
|
-
className: "codexSketchWidth",
|
|
2786
|
-
children: [
|
|
2787
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("sketchWidth") }),
|
|
2788
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2789
|
-
type: "range",
|
|
2790
|
-
min: 2,
|
|
2791
|
-
max: 64,
|
|
2792
|
-
value: width,
|
|
2793
|
-
"aria-label": t("sketchWidth"),
|
|
2794
|
-
onChange: (e) => setWidth(Number(e.target.value)),
|
|
2795
|
-
disabled: busy
|
|
2796
|
-
}),
|
|
2797
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("output", { children: width })
|
|
2798
|
-
]
|
|
2799
|
-
})
|
|
3598
|
+
}) : null
|
|
2800
3599
|
]
|
|
2801
3600
|
}),
|
|
2802
3601
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2808,9 +3607,9 @@ window.__ModuleLoader__.load({
|
|
|
2808
3607
|
className: "codexSketchSwatch",
|
|
2809
3608
|
style: { "--swatch": value },
|
|
2810
3609
|
"aria-label": `${t("sketchColor")} ${value}`,
|
|
2811
|
-
"aria-pressed": color === value,
|
|
2812
|
-
disabled: busy,
|
|
2813
|
-
onClick: () =>
|
|
3610
|
+
"aria-pressed": (selected?.color ?? color) === value,
|
|
3611
|
+
disabled: agentLocked || busy,
|
|
3612
|
+
onClick: () => pickColor(value)
|
|
2814
3613
|
}, value)), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2815
3614
|
className: "codexSketchCustom",
|
|
2816
3615
|
title: t("sketchColor"),
|
|
@@ -2818,8 +3617,8 @@ window.__ModuleLoader__.load({
|
|
|
2818
3617
|
type: "color",
|
|
2819
3618
|
"aria-label": t("sketchColor"),
|
|
2820
3619
|
value: color,
|
|
2821
|
-
disabled: busy,
|
|
2822
|
-
onChange: (e) =>
|
|
3620
|
+
disabled: agentLocked || busy,
|
|
3621
|
+
onChange: (e) => pickColor(e.target.value)
|
|
2823
3622
|
})]
|
|
2824
3623
|
})]
|
|
2825
3624
|
})
|
|
@@ -2831,11 +3630,26 @@ window.__ModuleLoader__.load({
|
|
|
2831
3630
|
children: error
|
|
2832
3631
|
}) : null
|
|
2833
3632
|
]
|
|
2834
|
-
});
|
|
3633
|
+
})] });
|
|
2835
3634
|
}
|
|
2836
3635
|
//#endregion
|
|
2837
3636
|
//#region src/sketch-styles.js
|
|
2838
3637
|
const SKETCH_CSS = `
|
|
3638
|
+
.codexSketchSizeModes{display:flex;flex-direction:column;gap:3px}.codexSketchSizeModes button{font-size:11px;padding:4px 6px;border-radius:10px;color:var(--sketch-muted)}.codexSketchSizeModes button[aria-pressed=true]{background:var(--sketch-line);color:var(--sketch-fg)}
|
|
3639
|
+
|
|
3640
|
+
.codexSketchSizeControl{position:absolute;left:10px;top:50%;transform:translateY(-50%);z-index:2;display:flex;flex-direction:column;align-items:center;gap:10px;padding:12px 6px;border:1px solid var(--sketch-line);border-radius:24px;background:var(--sketch-glass);backdrop-filter:blur(18px);box-shadow:0 3px 14px #0001}
|
|
3641
|
+
.codexSketchSizeControl>span{font-size:10px;color:var(--sketch-muted);max-width:42px;text-align:center}.codexSketchSizeControl output{font-size:11px;font-variant-numeric:tabular-nums;color:var(--sketch-muted)}
|
|
3642
|
+
.codexSketchSizeControl input{writing-mode:vertical-lr;direction:rtl;width:28px;height:150px;appearance:none;background:transparent;cursor:ns-resize;touch-action:none}
|
|
3643
|
+
.codexSketchSizeControl input::-webkit-slider-runnable-track{width:4px;border-radius:4px;background:var(--sketch-line)}
|
|
3644
|
+
.codexSketchSizeControl input::-webkit-slider-thumb{appearance:none;width:20px;height:20px;margin-left:-8px;border-radius:50%;background:var(--sketch-fg);border:2px solid var(--sketch-bg);box-shadow:0 1px 5px #0004}
|
|
3645
|
+
.codexSketchSelection{position:absolute;width:min(100cqw,calc(100cqh * var(--sketch-ratio,1)));height:auto;aspect-ratio:var(--sketch-ratio);pointer-events:none;overflow:visible}
|
|
3646
|
+
.codexSketchShapeMenu{position:absolute;bottom:65px;display:grid;grid-template-columns:1fr 1fr;padding:8px;border:1px solid var(--sketch-line);border-radius:14px;background:var(--sketch-bg);box-shadow:0 8px 24px #0003;z-index:5}.codexSketchShapeMenu button{min-height:40px;padding:8px 14px}
|
|
3647
|
+
.codexSketchTextEditor{position:absolute;z-index:4;left:50%;top:50%;transform:translate(-50%,-50%);display:flex;flex-wrap:wrap;gap:8px;width:min(320px,80%);padding:12px;background:var(--sketch-bg);border:1px solid var(--sketch-line);border-radius:12px;box-shadow:0 6px 24px #0003}.codexSketchTextEditor textarea{width:100%;min-height:72px;resize:vertical;background:transparent;color:inherit;border:1px solid var(--sketch-line);border-radius:8px;padding:8px;font:inherit}.codexSketchTextEditor button{padding:6px 10px;border-radius:8px;background:var(--sketch-line)}
|
|
3648
|
+
.codexSketchObjectActions{display:flex;gap:8px}.codexSketchObjectActions button{padding:6px 8px;border-radius:8px;background:var(--sketch-line)}
|
|
3649
|
+
.codexLayerBrush>select{background:var(--sketch-bg);color:inherit;border:1px solid var(--sketch-line);border-radius:8px;padding:6px}
|
|
3650
|
+
|
|
3651
|
+
.codexSketchBackgroundStatus{position:fixed;top:16px;left:50%;transform:translateX(-50%);z-index:1000;display:flex;gap:8px;align-items:center;padding:6px 10px;border-radius:12px;background:var(--dsw-alias-bg-layer-1,#f5f5f7);color:var(--dsw-alias-label-primary,#202124);border:1px solid #8883;box-shadow:0 4px 16px #0002;font:12px system-ui}.codexSketchBackgroundStatus button{color:inherit;background:none;border:0;cursor:pointer}
|
|
3652
|
+
.codexSketchAgentStatus{display:flex;align-items:center;justify-content:center;gap:8px;min-height:24px;color:var(--sketch-muted);font-size:12px}.codexSketchAgentStatus button{padding:3px 6px;border-radius:6px;background:var(--sketch-line)}
|
|
2839
3653
|
.codexSketchDialog{--sketch-bg:var(--dsw-alias-bg-layer-1,#f5f5f7);--sketch-fg:var(--dsw-alias-label-primary,#202124);--sketch-muted:var(--dsw-alias-label-secondary,#727279);--sketch-line:var(--dsw-alias-border-l2,#8883);--sketch-glass:color-mix(in srgb,var(--sketch-bg) 90%,transparent);box-sizing:border-box;width:min(1280px,calc(100vw - 24px));height:94dvh;max-height:94dvh;margin:auto;padding:10px;border:1px solid var(--sketch-line);border-radius:18px;background:var(--sketch-bg);color:var(--sketch-fg);box-shadow:0 24px 90px #0004;overflow:hidden;font:13px/1.4 system-ui}
|
|
2840
3654
|
.codexSketchDialog[open]{display:flex;flex-direction:column;gap:8px}
|
|
2841
3655
|
.codexSketchDialog::backdrop{background:#0005;backdrop-filter:blur(12px)}
|
|
@@ -2887,7 +3701,7 @@ window.__ModuleLoader__.load({
|
|
|
2887
3701
|
.codexLayerBody{overflow:hidden}.codexLayerStudio canvas{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Ccircle cx='12' cy='12' r='7' fill='none' stroke='white' stroke-width='3'/%3E%3Ccircle cx='12' cy='12' r='7' fill='none' stroke='%23333' stroke-width='1'/%3E%3C/svg%3E") 12 12,crosshair}
|
|
2888
3702
|
.codexSketchDialog .codexSketchSwatch{width:32px;height:32px;padding:0;border-radius:50%;corner-shape:round}.codexSketchSwatch::before{width:22px;height:22px;flex-shrink:0;border-radius:50%;corner-shape:round;clip-path:circle(50%)}.codexSketchSwatch[aria-pressed=true]::after{inset:1px;border:1.5px solid var(--sketch-fg);border-radius:50%;corner-shape:round}.codexSketchCustom{width:30px;height:30px;margin:1px 4px;corner-shape:round;clip-path:circle(50%)}.codexSketchCustom span{width:22px;height:22px;border-width:2px;corner-shape:round}
|
|
2889
3703
|
.codexSketchPictures{position:absolute;left:0;top:0;z-index:3;background:var(--sketch-bg);border:1px solid var(--sketch-line);border-radius:12px;padding:10px;width:160px;max-height:100%;overflow:auto}.codexSketchPictures header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.codexSketchPictures>div{display:flex;gap:5px;align-items:center;padding:4px;border:1px solid transparent;border-radius:8px}.codexSketchPictures>div[data-active=true]{border-color:#0a84ff}.codexSketchPictures img{width:90px;height:65px;object-fit:contain}.codexSketchPictures small{color:var(--sketch-muted)}
|
|
2890
|
-
.codexSketchViewControls{display:flex;gap:2px;align-items:center}.codexSketchViewControls button{min-height:32px;padding:0 7px;border-radius:8px}.codexSketchKeyPanel{position:absolute;
|
|
3704
|
+
.codexSketchViewControls{display:flex;gap:2px;align-items:center}.codexSketchViewControls button{min-height:32px;padding:0 7px;border-radius:8px}.codexSketchKeyPanel{position:absolute;overflow:auto;overscroll-behavior:contain;padding:12px;border:1px solid var(--sketch-line);border-radius:14px;background:var(--sketch-bg);z-index:5;box-shadow:0 8px 30px #0003;scrollbar-width:thin}.codexSketchKeyPanel header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.codexSketchKeyPanel header button{width:28px;height:28px;border-radius:8px;font-size:20px}.codexSketchKeyPanel label{display:flex;align-items:center;justify-content:space-between;min-height:32px;gap:8px}.codexSketchKeysEnabled{padding-bottom:8px;border-bottom:1px solid var(--sketch-line)}.codexSketchKeysEnabled input{margin:0;accent-color:#0a84ff}.codexSketchKeyGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px 16px;margin:10px 0}.codexSketchKeyPanel input:not([type=checkbox]){width:52px;min-width:0;border:1px solid var(--sketch-line);border-radius:6px;color:inherit;background:color-mix(in srgb,var(--sketch-fg) 4%,transparent);padding:4px;text-align:center;font:inherit}.codexSketchKeyPanel p,.codexSketchKeyPanel small{display:block;color:var(--sketch-muted);font-size:11px;line-height:1.5;margin:0}.codexSketchKeyPanel small{border-top:1px solid var(--sketch-line);padding-top:8px}@media(max-width:380px){.codexSketchKeyGrid{grid-template-columns:1fr}}.codexSketchControls{position:static}
|
|
2891
3705
|
|
|
2892
3706
|
.codexSketchCursor{position:fixed;left:0;top:0;z-index:100;pointer-events:none;border:1px solid #222;border-radius:50%;corner-shape:round;box-shadow:0 0 0 1px #fff;box-sizing:border-box}.codexSketchCursor[hidden]{display:none}
|
|
2893
3707
|
`;
|
|
@@ -2906,6 +3720,7 @@ window.__ModuleLoader__.load({
|
|
|
2906
3720
|
}
|
|
2907
3721
|
}), [registerOpen]);
|
|
2908
3722
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SketchStudio, {
|
|
3723
|
+
agentPreview: settings.imageSketchAgentPreview,
|
|
2909
3724
|
agentEnabled: settings.imageSketchAgent,
|
|
2910
3725
|
onOpen: () => {
|
|
2911
3726
|
opener.current = document.activeElement;
|
|
@@ -2953,7 +3768,7 @@ window.__ModuleLoader__.load({
|
|
|
2953
3768
|
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SketchWorkspace, {
|
|
2954
3769
|
...props,
|
|
2955
3770
|
registerOpen: registerWorkspace
|
|
2956
|
-
})] });
|
|
3771
|
+
}, props.sessionId)] });
|
|
2957
3772
|
}
|
|
2958
3773
|
//#endregion
|
|
2959
3774
|
//#region src/image-composer.js
|
|
@@ -3000,9 +3815,10 @@ window.__ModuleLoader__.load({
|
|
|
3000
3815
|
}
|
|
3001
3816
|
const createSketchTrigger = (options) => createWorkspaceTrigger({
|
|
3002
3817
|
...options,
|
|
3818
|
+
open: () => {},
|
|
3003
3819
|
name: "Sketch · Beta",
|
|
3004
3820
|
aliases: ["sketch", "草图"],
|
|
3005
|
-
description: "
|
|
3821
|
+
description: "Ask the Agent to draw or edit a sketch"
|
|
3006
3822
|
});
|
|
3007
3823
|
const createImageTrigger = (options) => createWorkspaceTrigger({
|
|
3008
3824
|
...options,
|
|
@@ -3013,6 +3829,17 @@ window.__ModuleLoader__.load({
|
|
|
3013
3829
|
//#endregion
|
|
3014
3830
|
//#region src/client-locales.js
|
|
3015
3831
|
const zh = {
|
|
3832
|
+
sketchSizeShort: "粗细",
|
|
3833
|
+
sketchObjectDuplicate: "复制对象",
|
|
3834
|
+
sketchObjectDelete: "删除对象",
|
|
3835
|
+
sketchTool_select: "选择",
|
|
3836
|
+
sketchTool_text: "文字",
|
|
3837
|
+
sketchTool_arrow: "箭头",
|
|
3838
|
+
sketchShapes: "图形",
|
|
3839
|
+
sketchText: "编辑文字",
|
|
3840
|
+
sketchTextDone: "完成",
|
|
3841
|
+
sketchTextSize: "字号",
|
|
3842
|
+
sketchBrush: "笔型",
|
|
3016
3843
|
sketchFill: "填色",
|
|
3017
3844
|
sketchDownload: "下载",
|
|
3018
3845
|
sketchExportFormat: "导出格式",
|
|
@@ -3122,7 +3949,17 @@ window.__ModuleLoader__.load({
|
|
|
3122
3949
|
sketchCanvas_on: "开启",
|
|
3123
3950
|
sketchCanvas_off: "关闭",
|
|
3124
3951
|
sketchAgent: "Agent 绘图 · Beta",
|
|
3125
|
-
sketchAgentHint: "
|
|
3952
|
+
sketchAgentHint: "默认关闭;开启后可用 @sketch 请求 Agent 绘图,需要同时开启草图画板。",
|
|
3953
|
+
sketchRun_drawing: "Agent 正在绘制…",
|
|
3954
|
+
sketchRun_finished: "绘制完成",
|
|
3955
|
+
sketchRun_stopped: "绘制已停止",
|
|
3956
|
+
sketchRun_failed: "绘制失败",
|
|
3957
|
+
sketchRunStop: "停止绘制",
|
|
3958
|
+
sketchRunResume: "允许继续绘制",
|
|
3959
|
+
sketchAgentPreview: "完成后返回预览 · Beta",
|
|
3960
|
+
sketchAgentPreviewHint: "默认关闭;完成绘制后向模型返回画布图片,会增加图片输入用量。",
|
|
3961
|
+
sketchAgentPreview_on: "开启",
|
|
3962
|
+
sketchAgentPreview_off: "关闭",
|
|
3126
3963
|
sketchAgent_on: "开启",
|
|
3127
3964
|
sketchAgent_off: "关闭",
|
|
3128
3965
|
imageSettings: "图片",
|
|
@@ -3398,6 +4235,17 @@ window.__ModuleLoader__.load({
|
|
|
3398
4235
|
imageRemoveAnnotation: "删除标注"
|
|
3399
4236
|
};
|
|
3400
4237
|
const en = {
|
|
4238
|
+
sketchSizeShort: "Size",
|
|
4239
|
+
sketchObjectDuplicate: "Duplicate object",
|
|
4240
|
+
sketchObjectDelete: "Delete object",
|
|
4241
|
+
sketchTool_select: "Select",
|
|
4242
|
+
sketchTool_text: "Text",
|
|
4243
|
+
sketchTool_arrow: "Arrow",
|
|
4244
|
+
sketchShapes: "Shapes",
|
|
4245
|
+
sketchText: "Edit text",
|
|
4246
|
+
sketchTextDone: "Done",
|
|
4247
|
+
sketchTextSize: "Text size",
|
|
4248
|
+
sketchBrush: "Brush",
|
|
3401
4249
|
sketchFill: "Fill",
|
|
3402
4250
|
sketchDownload: "Download",
|
|
3403
4251
|
sketchExportFormat: "Export format",
|
|
@@ -3507,7 +4355,17 @@ window.__ModuleLoader__.load({
|
|
|
3507
4355
|
sketchCanvas_on: "On",
|
|
3508
4356
|
sketchCanvas_off: "Off",
|
|
3509
4357
|
sketchAgent: "Agent drawing · Beta",
|
|
3510
|
-
sketchAgentHint: "Off by default.
|
|
4358
|
+
sketchAgentHint: "Off by default. Use @sketch to ask the Agent to draw; requires the sketch canvas.",
|
|
4359
|
+
sketchRun_drawing: "Agent is drawing…",
|
|
4360
|
+
sketchRun_finished: "Drawing complete",
|
|
4361
|
+
sketchRun_stopped: "Drawing stopped",
|
|
4362
|
+
sketchRun_failed: "Drawing failed",
|
|
4363
|
+
sketchRunStop: "Stop drawing",
|
|
4364
|
+
sketchRunResume: "Allow drawing again",
|
|
4365
|
+
sketchAgentPreview: "Preview on completion · Beta",
|
|
4366
|
+
sketchAgentPreviewHint: "Off by default. Returns the canvas to the model on completion, adding image input usage.",
|
|
4367
|
+
sketchAgentPreview_on: "On",
|
|
4368
|
+
sketchAgentPreview_off: "Off",
|
|
3511
4369
|
sketchAgent_on: "On",
|
|
3512
4370
|
sketchAgent_off: "Off",
|
|
3513
4371
|
imageSettings: "Images",
|
|
@@ -4964,7 +5822,8 @@ window.__ModuleLoader__.load({
|
|
|
4964
5822
|
imageViewer: true,
|
|
4965
5823
|
imageAnnotations: true,
|
|
4966
5824
|
imageSketch: false,
|
|
4967
|
-
imageSketchAgent: false
|
|
5825
|
+
imageSketchAgent: false,
|
|
5826
|
+
imageSketchAgentPreview: false
|
|
4968
5827
|
});
|
|
4969
5828
|
function readImageFeatures(value = {}) {
|
|
4970
5829
|
return Object.fromEntries(Object.entries(IMAGE_FEATURE_DEFAULTS).map(([key, fallback]) => [key, typeof value?.[key] === "boolean" ? value[key] : fallback]));
|
|
@@ -5602,7 +6461,7 @@ window.__ModuleLoader__.load({
|
|
|
5602
6461
|
}
|
|
5603
6462
|
//#endregion
|
|
5604
6463
|
//#region src/version.js
|
|
5605
|
-
const PACKAGE_VERSION = "2.1.0-beta.
|
|
6464
|
+
const PACKAGE_VERSION = "2.1.0-beta.4";
|
|
5606
6465
|
//#endregion
|
|
5607
6466
|
//#region src/client-recovery.js
|
|
5608
6467
|
async function recoveryCall(rpc, endpoint, payload = {}, timeoutMs = 1e4) {
|
|
@@ -6172,6 +7031,7 @@ window.__ModuleLoader__.load({
|
|
|
6172
7031
|
imageEntryPoints: ["imageShortcut"],
|
|
6173
7032
|
sketchCanvas: ["imageSketch"],
|
|
6174
7033
|
sketchAgent: ["imageSketchAgent"],
|
|
7034
|
+
sketchAgentPreview: ["imageSketchAgentPreview"],
|
|
6175
7035
|
imageBrowsing: ["imageViewer", "imageAnnotations"]
|
|
6176
7036
|
});
|
|
6177
7037
|
function imageGroupValue(snapshot, group) {
|
|
@@ -7921,15 +8781,14 @@ window.__ModuleLoader__.load({
|
|
|
7921
8781
|
ctx.inject(["inputTriggers"], (triggerContext) => triggerContext.effect(() => triggerContext.get("inputTriggers").registerSource(createSketchTrigger({
|
|
7922
8782
|
enabled: () => {
|
|
7923
8783
|
const value = preference.getSnapshot();
|
|
7924
|
-
return value.imageSketch && value.imageEditing;
|
|
8784
|
+
return value.imageSketchAgent && value.imageSketch && value.imageEditing;
|
|
7925
8785
|
},
|
|
7926
|
-
open: (sessionId) => sketchOpeners.get(sessionId)?.(),
|
|
7927
8786
|
consume: (sessionId, span) => {
|
|
7928
8787
|
const actx = sessions.scope(sessionId);
|
|
7929
|
-
return sketchOpeners.has(sessionId) && actx?.bail(actx, "slash/input-
|
|
7930
|
-
|
|
8788
|
+
return sketchOpeners.has(sessionId) && actx?.bail(actx, "slash/input-insert-text", {
|
|
8789
|
+
text: "@sketch ",
|
|
7931
8790
|
span
|
|
7932
|
-
}
|
|
8791
|
+
}) === true;
|
|
7933
8792
|
}
|
|
7934
8793
|
})), "codex-subscription: Sketch trigger"));
|
|
7935
8794
|
ctx.inject(["inputTriggers"], (triggerContext) => triggerContext.effect(() => triggerContext.get("inputTriggers").registerSource(createImageTrigger({
|