dsh-codex-subscription 2.1.0-beta.4 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -31,6 +31,366 @@ window.__ModuleLoader__.load({
31
31
  let react_jsx_runtime = require("react/jsx-runtime");
32
32
  let react_dom = require("react-dom");
33
33
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
34
+ //#region src/sketch-curves.js
35
+ const cached = /* @__PURE__ */ new WeakMap();
36
+ const midpoint = (a, b) => ({
37
+ x: (a.x + b.x) / 2,
38
+ y: (a.y + b.y) / 2
39
+ });
40
+ function flattenSketchCurve(stroke, width, height) {
41
+ const previous = cached.get(stroke);
42
+ if (previous?.width === width && previous.height === height) return previous.points;
43
+ const controls = stroke.points.map((p) => ({
44
+ x: p.x * width,
45
+ y: p.y * height
46
+ })), points = [controls[0]];
47
+ const distance = (p, a, b) => {
48
+ const dx = b.x - a.x, dy = b.y - a.y, d = dx * dx + dy * dy;
49
+ const t = d ? Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / d)) : 0;
50
+ return Math.hypot(p.x - a.x - t * dx, p.y - a.y - t * dy);
51
+ };
52
+ const split = (a, b, c, d, depth) => {
53
+ if (depth === 10 || Math.max(distance(b, a, d), distance(c, a, d)) <= .5) {
54
+ points.push(d);
55
+ return;
56
+ }
57
+ 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);
58
+ split(a, ab, abc, m, depth + 1);
59
+ split(m, bcd, cd, d, depth + 1);
60
+ };
61
+ for (let i = 1; i < controls.length; i += 3) split(controls[i - 1], controls[i], controls[i + 1], controls[i + 2], 0);
62
+ const normalized = points.map((p) => ({
63
+ x: p.x / width,
64
+ y: p.y / height
65
+ }));
66
+ cached.set(stroke, {
67
+ width,
68
+ height,
69
+ points: normalized
70
+ });
71
+ return normalized;
72
+ }
73
+ //#endregion
74
+ //#region src/sketch-brushes.js
75
+ const grains = /* @__PURE__ */ new Map();
76
+ function pencilGrain(context, color) {
77
+ if (!context.createPattern || typeof document === "undefined") return color;
78
+ if (!grains.has(color)) {
79
+ const canvas = document.createElement("canvas");
80
+ canvas.width = 64;
81
+ canvas.height = 64;
82
+ const ctx = canvas.getContext("2d");
83
+ ctx.fillStyle = color;
84
+ let seed = 173;
85
+ for (let y = 0; y < 64; y++) for (let x = 0; x < 64; x++) {
86
+ seed = Math.imul(seed, 1664525) + 1013904223 >>> 0;
87
+ const value = seed / 4294967296;
88
+ if (value < .28) continue;
89
+ ctx.globalAlpha = .2 + value * .8;
90
+ ctx.fillRect(x, y, 1, 1);
91
+ }
92
+ if (grains.size >= 16) grains.delete(grains.keys().next().value);
93
+ grains.set(color, canvas);
94
+ }
95
+ return context.createPattern(grains.get(color), "repeat") ?? color;
96
+ }
97
+ function configureSketchBrush(context, stroke) {
98
+ const modern = stroke.brushVersion === 2 && stroke.shape === "pen";
99
+ const brush = stroke.brush ?? "pen";
100
+ context.lineCap = modern && brush === "marker" ? "butt" : "round";
101
+ context.lineJoin = "round";
102
+ context.globalAlpha = (stroke.opacity ?? 1) * (brush === "marker" ? .28 : brush === "pencil" ? modern ? .85 : .65 : 1);
103
+ context.lineWidth = stroke.width * (brush === "pencil" && !modern ? .55 : 1) * (stroke.pressure ?? 1);
104
+ context.strokeStyle = modern && brush === "pencil" ? pencilGrain(context, stroke.color) : stroke.color;
105
+ context.fillStyle = context.strokeStyle;
106
+ }
107
+ //#endregion
108
+ //#region src/sketch-document.js
109
+ const SKETCH_SIZE = 1024;
110
+ const MAX_SKETCH_STROKES = 2e3;
111
+ const MAX_STROKE_POINTS = 2e3;
112
+ function sketchPoint(clientX, clientY, rect) {
113
+ if (!(rect.width > 0 && rect.height > 0)) return void 0;
114
+ return {
115
+ x: Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)),
116
+ y: Math.max(0, Math.min(1, (clientY - rect.top) / rect.height))
117
+ };
118
+ }
119
+ function paintSketch(context, strokes, size = SKETCH_SIZE, transparent = false, height = size, start = 0, end = strokes.length) {
120
+ context.globalCompositeOperation = "source-over";
121
+ context.globalAlpha = 1;
122
+ if (!transparent) {
123
+ context.fillStyle = "#ffffff";
124
+ context.fillRect(0, 0, size, height);
125
+ }
126
+ context.lineCap = "round";
127
+ context.lineJoin = "round";
128
+ for (let index = start; index < end; index++) {
129
+ const stroke = strokes[index];
130
+ const first = stroke.points[0];
131
+ if (!first) continue;
132
+ context.globalCompositeOperation = stroke.shape === "eraser" ? "destination-out" : "source-over";
133
+ configureSketchBrush(context, stroke);
134
+ context.beginPath();
135
+ const last = stroke.points.at(-1);
136
+ if (stroke.shape === "text") {
137
+ 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;
138
+ const lines = stroke.text.split("\n"), fontSize = Math.min(stroke.width, h / Math.max(1, lines.length) / 1.2);
139
+ context.font = `${fontSize}px system-ui, sans-serif`;
140
+ context.textBaseline = "top";
141
+ lines.forEach((line, i) => context.fillText(line, x, y + i * fontSize * 1.2, w));
142
+ } else if (stroke.shape === "arrow") {
143
+ 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));
144
+ context.moveTo(first.x * size, first.y * height);
145
+ context.lineTo(x, y);
146
+ context.stroke();
147
+ context.beginPath();
148
+ context.moveTo(x, y);
149
+ context.lineTo(x - head * Math.cos(a - .5), y - head * Math.sin(a - .5));
150
+ context.lineTo(x - head * Math.cos(a + .5), y - head * Math.sin(a + .5));
151
+ context.closePath();
152
+ context.fill();
153
+ } else if (stroke.shape === "bezier") {
154
+ context.moveTo(first.x * size, first.y * height);
155
+ for (let i = 1; i < stroke.points.length; i += 3) {
156
+ const [a, b, c] = stroke.points.slice(i, i + 3);
157
+ context.bezierCurveTo(a.x * size, a.y * height, b.x * size, b.y * height, c.x * size, c.y * height);
158
+ }
159
+ if (stroke.fill) {
160
+ context.closePath();
161
+ context.fill();
162
+ } else context.stroke();
163
+ } else if (stroke.shape === "line") {
164
+ context.moveTo(first.x * size, first.y * height);
165
+ context.lineTo(last.x * size, last.y * height);
166
+ context.stroke();
167
+ } else if (stroke.shape === "rectangle") {
168
+ context.rect(first.x * size, first.y * height, (last.x - first.x) * size, (last.y - first.y) * height);
169
+ if (stroke.fill) context.fill();
170
+ else context.stroke();
171
+ } else if (stroke.shape === "circle") {
172
+ context.ellipse((first.x + last.x) * size / 2, (first.y + last.y) * height / 2, Math.abs(last.x - first.x) * size / 2, Math.abs(last.y - first.y) * height / 2, 0, 0, Math.PI * 2);
173
+ if (stroke.fill) context.fill();
174
+ else context.stroke();
175
+ } else if (stroke.shape === "polygon") {
176
+ context.moveTo(first.x * size, first.y * height);
177
+ for (const point of stroke.points.slice(1)) context.lineTo(point.x * size, point.y * height);
178
+ context.closePath();
179
+ if (stroke.fill) context.fill();
180
+ else context.stroke();
181
+ } else if (stroke.points.length === 1) {
182
+ if (stroke.brushVersion === 2 && stroke.brush === "marker") context.rect(first.x * size - context.lineWidth / 2, first.y * height - context.lineWidth / 4, context.lineWidth, context.lineWidth / 2);
183
+ else context.arc(first.x * size, first.y * height, context.lineWidth / 2, 0, Math.PI * 2);
184
+ context.fill();
185
+ } else {
186
+ context.moveTo(first.x * size, first.y * height);
187
+ for (let i = 1; i < stroke.points.length - 1; i++) {
188
+ const point = stroke.points[i], next = stroke.points[i + 1];
189
+ if (context.quadraticCurveTo) context.quadraticCurveTo(point.x * size, point.y * height, (point.x + next.x) * size / 2, (point.y + next.y) * height / 2);
190
+ else context.lineTo(point.x * size, point.y * height);
191
+ }
192
+ context.lineTo(last.x * size, last.y * height);
193
+ context.stroke();
194
+ }
195
+ }
196
+ context.globalCompositeOperation = "source-over";
197
+ context.globalAlpha = 1;
198
+ }
199
+ const createSketchLayers = () => ({
200
+ active: 1,
201
+ nextId: 2,
202
+ layers: [{
203
+ id: 1,
204
+ name: "",
205
+ visible: true,
206
+ strokes: []
207
+ }]
208
+ });
209
+ const strokeCount = (doc) => doc.layers.reduce((n, layer) => n + layer.strokes.length, 0);
210
+ function changeSketchLayer(doc, action, id = doc.active, value) {
211
+ const index = doc.layers.findIndex((layer) => layer.id === id);
212
+ if (index < 0) return doc;
213
+ const layers = doc.layers.slice(), layer = layers[index];
214
+ if (action === "select") return {
215
+ ...doc,
216
+ active: id
217
+ };
218
+ if (action === "add" || action === "duplicate") {
219
+ if (layers.length >= 8 || action === "duplicate" && strokeCount(doc) + layer.strokes.length > 2e3) return doc;
220
+ const next = action === "add" ? {
221
+ id: doc.nextId,
222
+ name: "",
223
+ visible: true,
224
+ strokes: []
225
+ } : {
226
+ ...layer,
227
+ id: doc.nextId,
228
+ strokes: layer.strokes.slice()
229
+ };
230
+ layers.splice(index + 1, 0, next);
231
+ return {
232
+ ...doc,
233
+ layers,
234
+ active: next.id,
235
+ nextId: doc.nextId + 1
236
+ };
237
+ }
238
+ if (action === "delete") {
239
+ if (layers.length === 1) return doc;
240
+ layers.splice(index, 1);
241
+ return {
242
+ ...doc,
243
+ layers,
244
+ active: doc.active === id ? layers[Math.min(index, layers.length - 1)].id : doc.active
245
+ };
246
+ }
247
+ if (action === "up" || action === "down") {
248
+ const target = index + (action === "up" ? 1 : -1);
249
+ if (!layers[target]) return doc;
250
+ [layers[index], layers[target]] = [layers[target], layer];
251
+ } else if (action === "visible") layers[index] = {
252
+ ...layer,
253
+ visible: !layer.visible
254
+ };
255
+ else if (action === "rename") layers[index] = {
256
+ ...layer,
257
+ name: String(value).trim().slice(0, 40)
258
+ };
259
+ else if (action === "clear") layers[index] = {
260
+ ...layer,
261
+ strokes: [],
262
+ image: void 0
263
+ };
264
+ else return doc;
265
+ return {
266
+ ...doc,
267
+ layers
268
+ };
269
+ }
270
+ const distanceToSegment = (p, a, b) => {
271
+ const dx = b.x - a.x, dy = b.y - a.y, length = dx * dx + dy * dy;
272
+ const k = length ? Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / length)) : 0;
273
+ return Math.hypot(p.x - a.x - k * dx, p.y - a.y - k * dy);
274
+ };
275
+ function strokeHit(stroke, point, radius, width = SKETCH_SIZE, height = width) {
276
+ let points = stroke.shape === "bezier" ? flattenSketchCurve(stroke, width, height) : stroke.points;
277
+ if (!points.length) return false;
278
+ const a = points[0], b = points.at(-1);
279
+ 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;
280
+ if (stroke.fill && stroke.shape === "circle") {
281
+ const rx = Math.abs(b.x - a.x) / 2, ry = Math.abs(b.y - a.y) / 2;
282
+ if (rx && ry && ((point.x - (a.x + b.x) / 2) / rx) ** 2 + ((point.y - (a.y + b.y) / 2) / ry) ** 2 <= 1) return true;
283
+ }
284
+ if (stroke.shape === "polygon" || stroke.shape === "bezier" && stroke.fill) {
285
+ if (stroke.fill) {
286
+ let inside = false;
287
+ for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
288
+ const p = points[i], q = points[j];
289
+ if (p.y > point.y !== q.y > point.y && point.x < (q.x - p.x) * (point.y - p.y) / (q.y - p.y) + p.x) inside = !inside;
290
+ }
291
+ if (inside) return true;
292
+ }
293
+ points = [...points, points[0]];
294
+ }
295
+ if (stroke.shape === "rectangle") points = [
296
+ a,
297
+ {
298
+ x: b.x,
299
+ y: a.y
300
+ },
301
+ b,
302
+ {
303
+ x: a.x,
304
+ y: b.y
305
+ },
306
+ a
307
+ ];
308
+ if (stroke.shape === "circle") points = Array.from({ length: 65 }, (_, i) => ({
309
+ x: (a.x + b.x) / 2 + Math.abs(b.x - a.x) / 2 * Math.cos(i * Math.PI / 32),
310
+ y: (a.y + b.y) / 2 + Math.abs(b.y - a.y) / 2 * Math.sin(i * Math.PI / 32)
311
+ }));
312
+ points = points.map((p) => ({
313
+ x: p.x * width,
314
+ y: p.y * height
315
+ }));
316
+ point = {
317
+ x: point.x * width,
318
+ y: point.y * height
319
+ };
320
+ const tolerance = radius + stroke.width / 2;
321
+ return points.some((p, i) => distanceToSegment(point, i ? points[i - 1] : p, p) <= tolerance);
322
+ }
323
+ const SKETCH_RATIOS = Object.freeze({
324
+ "1:1": [1024, 1024],
325
+ "4:3": [1024, 768],
326
+ "3:4": [768, 1024],
327
+ "16:9": [1024, 576],
328
+ "9:16": [576, 1024]
329
+ });
330
+ function resizeSketch(doc, ratio) {
331
+ if (!Object.hasOwn(SKETCH_RATIOS, ratio)) throw new Error("Invalid sketch ratio");
332
+ const [width, height] = SKETCH_RATIOS[ratio];
333
+ const oldWidth = doc.width ?? 1024, oldHeight = doc.height ?? 1024;
334
+ if (width === oldWidth && height === oldHeight) return doc;
335
+ const scale = Math.min(width / oldWidth, height / oldHeight);
336
+ const dx = (width - oldWidth * scale) / 2, dy = (height - oldHeight * scale) / 2;
337
+ return {
338
+ ...doc,
339
+ width,
340
+ height,
341
+ ratio,
342
+ layers: doc.layers.map((layer) => ({
343
+ ...layer,
344
+ ...layer.image ? { image: {
345
+ ...layer.image,
346
+ x: (layer.image.x * oldWidth * scale + dx) / width,
347
+ y: (layer.image.y * oldHeight * scale + dy) / height,
348
+ width: layer.image.width * oldWidth * scale / width,
349
+ height: layer.image.height * oldHeight * scale / height
350
+ } } : {},
351
+ strokes: layer.strokes.map((stroke) => ({
352
+ ...stroke,
353
+ width: stroke.width * scale,
354
+ points: stroke.points.map((p) => ({
355
+ x: (p.x * oldWidth * scale + dx) / width,
356
+ y: (p.y * oldHeight * scale + dy) / height
357
+ }))
358
+ }))
359
+ }))
360
+ };
361
+ }
362
+ //#endregion
363
+ //#region src/sketch-session-state.js
364
+ function createSketchSessionState() {
365
+ const ref = (current) => ({ current });
366
+ return {
367
+ doc: ref(createSketchLayers()),
368
+ undo: ref([]),
369
+ redo: ref([]),
370
+ images: ref(/* @__PURE__ */ new Map()),
371
+ saved: ref(null),
372
+ dirty: ref(false),
373
+ documentId: ref(crypto.randomUUID()),
374
+ documentRevision: ref(0),
375
+ agentAdapter: ref({}),
376
+ agentSession: ref(null),
377
+ agentRun: ref(null)
378
+ };
379
+ }
380
+ function createSketchSessionRegistry() {
381
+ const sessions = /* @__PURE__ */ new Map();
382
+ return {
383
+ get(id) {
384
+ if (!sessions.has(id)) sessions.set(id, createSketchSessionState());
385
+ return sessions.get(id);
386
+ },
387
+ dispose() {
388
+ for (const value of sessions.values()) value.agentRun.current?.dispose();
389
+ sessions.clear();
390
+ }
391
+ };
392
+ }
393
+ //#endregion
34
394
  //#region src/image-edit.js
35
395
  const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
36
396
  const validCoordinate = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
@@ -650,318 +1010,107 @@ window.__ModuleLoader__.load({
650
1010
  ] })]
651
1011
  }) : null,
652
1012
  error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
653
- className: "codexImageToolError",
654
- children: error
655
- })
656
- ]
657
- });
658
- }
659
- function CodexImageOutput({ node, ...props }) {
660
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
661
- className: "codexImageOutput",
662
- children: node.data.blocks.map((block) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexImageToolRow, {
663
- block,
664
- ...props,
665
- presentation: "output"
666
- }, block.toolCallId))
667
- });
668
- }
669
- //#endregion
670
- //#region src/sketch-document.js
671
- const SKETCH_SIZE = 1024;
672
- const MAX_SKETCH_STROKES = 2e3;
673
- const MAX_STROKE_POINTS = 2e3;
674
- function sketchPoint(clientX, clientY, rect) {
675
- if (!(rect.width > 0 && rect.height > 0)) return void 0;
676
- return {
677
- x: Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)),
678
- y: Math.max(0, Math.min(1, (clientY - rect.top) / rect.height))
679
- };
680
- }
681
- function paintSketch(context, strokes, size = SKETCH_SIZE, transparent = false, height = size, start = 0, end = strokes.length) {
682
- context.globalCompositeOperation = "source-over";
683
- context.globalAlpha = 1;
684
- if (!transparent) {
685
- context.fillStyle = "#ffffff";
686
- context.fillRect(0, 0, size, height);
687
- }
688
- context.lineCap = "round";
689
- context.lineJoin = "round";
690
- for (let index = start; index < end; index++) {
691
- const stroke = strokes[index];
692
- const first = stroke.points[0];
693
- if (!first) continue;
694
- context.globalCompositeOperation = stroke.shape === "eraser" ? "destination-out" : "source-over";
695
- context.globalAlpha = (stroke.opacity ?? 1) * (stroke.brush === "marker" ? .28 : stroke.brush === "pencil" ? .65 : 1);
696
- context.strokeStyle = stroke.color;
697
- context.fillStyle = stroke.color;
698
- context.lineWidth = stroke.width * (stroke.brush === "pencil" ? .55 : 1) * (stroke.pressure ?? 1);
699
- context.beginPath();
700
- const last = stroke.points.at(-1);
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") {
729
- context.moveTo(first.x * size, first.y * height);
730
- context.lineTo(last.x * size, last.y * height);
731
- context.stroke();
732
- } else if (stroke.shape === "rectangle") {
733
- context.rect(first.x * size, first.y * height, (last.x - first.x) * size, (last.y - first.y) * height);
734
- if (stroke.fill) context.fill();
735
- else context.stroke();
736
- } else if (stroke.shape === "circle") {
737
- context.ellipse((first.x + last.x) * size / 2, (first.y + last.y) * height / 2, Math.abs(last.x - first.x) * size / 2, Math.abs(last.y - first.y) * height / 2, 0, 0, Math.PI * 2);
738
- if (stroke.fill) context.fill();
739
- else context.stroke();
740
- } else if (stroke.shape === "polygon") {
741
- context.moveTo(first.x * size, first.y * height);
742
- for (const point of stroke.points.slice(1)) context.lineTo(point.x * size, point.y * height);
743
- context.closePath();
744
- if (stroke.fill) context.fill();
745
- else context.stroke();
746
- } else if (stroke.points.length === 1) {
747
- context.arc(first.x * size, first.y * height, context.lineWidth / 2, 0, Math.PI * 2);
748
- context.fill();
749
- } else {
750
- context.moveTo(first.x * size, first.y * height);
751
- for (let i = 1; i < stroke.points.length - 1; i++) {
752
- const point = stroke.points[i], next = stroke.points[i + 1];
753
- if (context.quadraticCurveTo) context.quadraticCurveTo(point.x * size, point.y * height, (point.x + next.x) * size / 2, (point.y + next.y) * height / 2);
754
- else context.lineTo(point.x * size, point.y * height);
755
- }
756
- context.lineTo(last.x * size, last.y * height);
757
- context.stroke();
758
- }
759
- }
760
- context.globalCompositeOperation = "source-over";
761
- context.globalAlpha = 1;
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
- }
803
- const createSketchLayers = () => ({
804
- active: 1,
805
- nextId: 2,
806
- layers: [{
807
- id: 1,
808
- name: "",
809
- visible: true,
810
- strokes: []
811
- }]
812
- });
813
- const strokeCount = (doc) => doc.layers.reduce((n, layer) => n + layer.strokes.length, 0);
814
- function changeSketchLayer(doc, action, id = doc.active, value) {
815
- const index = doc.layers.findIndex((layer) => layer.id === id);
816
- if (index < 0) return doc;
817
- const layers = doc.layers.slice(), layer = layers[index];
818
- if (action === "select") return {
819
- ...doc,
820
- active: id
821
- };
822
- if (action === "add" || action === "duplicate") {
823
- if (layers.length >= 8 || action === "duplicate" && strokeCount(doc) + layer.strokes.length > 2e3) return doc;
824
- const next = action === "add" ? {
825
- id: doc.nextId,
826
- name: "",
827
- visible: true,
828
- strokes: []
829
- } : {
830
- ...layer,
831
- id: doc.nextId,
832
- strokes: layer.strokes.slice()
833
- };
834
- layers.splice(index + 1, 0, next);
835
- return {
836
- ...doc,
837
- layers,
838
- active: next.id,
839
- nextId: doc.nextId + 1
840
- };
841
- }
842
- if (action === "delete") {
843
- if (layers.length === 1) return doc;
844
- layers.splice(index, 1);
845
- return {
846
- ...doc,
847
- layers,
848
- active: doc.active === id ? layers[Math.min(index, layers.length - 1)].id : doc.active
849
- };
850
- }
851
- if (action === "up" || action === "down") {
852
- const target = index + (action === "up" ? 1 : -1);
853
- if (!layers[target]) return doc;
854
- [layers[index], layers[target]] = [layers[target], layer];
855
- } else if (action === "visible") layers[index] = {
856
- ...layer,
857
- visible: !layer.visible
858
- };
859
- else if (action === "rename") layers[index] = {
860
- ...layer,
861
- name: String(value).trim().slice(0, 40)
862
- };
863
- else if (action === "clear") layers[index] = {
864
- ...layer,
865
- strokes: [],
866
- image: void 0
867
- };
868
- else return doc;
869
- return {
870
- ...doc,
871
- layers
872
- };
1013
+ className: "codexImageToolError",
1014
+ children: error
1015
+ })
1016
+ ]
1017
+ });
873
1018
  }
874
- const distanceToSegment = (p, a, b) => {
875
- const dx = b.x - a.x, dy = b.y - a.y, length = dx * dx + dy * dy;
876
- const k = length ? Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / length)) : 0;
877
- return Math.hypot(p.x - a.x - k * dx, p.y - a.y - k * dy);
878
- };
879
- function strokeHit(stroke, point, radius, width = SKETCH_SIZE, height = width) {
880
- let points = stroke.shape === "bezier" ? flattenSketchCurve(stroke, width, height) : stroke.points;
881
- if (!points.length) return false;
882
- const a = points[0], b = points.at(-1);
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;
884
- if (stroke.fill && stroke.shape === "circle") {
885
- const rx = Math.abs(b.x - a.x) / 2, ry = Math.abs(b.y - a.y) / 2;
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;
887
- }
888
- if (stroke.shape === "polygon" || stroke.shape === "bezier" && stroke.fill) {
889
- if (stroke.fill) {
890
- let inside = false;
891
- for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
892
- const p = points[i], q = points[j];
893
- if (p.y > point.y !== q.y > point.y && point.x < (q.x - p.x) * (point.y - p.y) / (q.y - p.y) + p.x) inside = !inside;
894
- }
895
- if (inside) return true;
896
- }
897
- points = [...points, points[0]];
898
- }
899
- if (stroke.shape === "rectangle") points = [
900
- a,
901
- {
902
- x: b.x,
903
- y: a.y
904
- },
905
- b,
906
- {
907
- x: a.x,
908
- y: b.y
909
- },
910
- a
911
- ];
912
- if (stroke.shape === "circle") points = Array.from({ length: 65 }, (_, i) => ({
913
- x: (a.x + b.x) / 2 + Math.abs(b.x - a.x) / 2 * Math.cos(i * Math.PI / 32),
914
- y: (a.y + b.y) / 2 + Math.abs(b.y - a.y) / 2 * Math.sin(i * Math.PI / 32)
915
- }));
916
- points = points.map((p) => ({
917
- x: p.x * width,
918
- y: p.y * height
919
- }));
920
- point = {
921
- x: point.x * width,
922
- y: point.y * height
923
- };
924
- const tolerance = radius + stroke.width / 2;
925
- return points.some((p, i) => distanceToSegment(point, i ? points[i - 1] : p, p) <= tolerance);
1019
+ function CodexImageOutput({ node, ...props }) {
1020
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1021
+ className: "codexImageOutput",
1022
+ children: node.data.blocks.map((block) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexImageToolRow, {
1023
+ block,
1024
+ ...props,
1025
+ presentation: "output"
1026
+ }, block.toolCallId))
1027
+ });
926
1028
  }
927
- const SKETCH_RATIOS = Object.freeze({
928
- "1:1": [1024, 1024],
929
- "4:3": [1024, 768],
930
- "3:4": [768, 1024],
931
- "16:9": [1024, 576],
932
- "9:16": [576, 1024]
933
- });
934
- function resizeSketch(doc, ratio) {
935
- if (!Object.hasOwn(SKETCH_RATIOS, ratio)) throw new Error("Invalid sketch ratio");
936
- const [width, height] = SKETCH_RATIOS[ratio];
937
- const oldWidth = doc.width ?? 1024, oldHeight = doc.height ?? 1024;
938
- if (width === oldWidth && height === oldHeight) return doc;
939
- const scale = Math.min(width / oldWidth, height / oldHeight);
940
- const dx = (width - oldWidth * scale) / 2, dy = (height - oldHeight * scale) / 2;
941
- return {
942
- ...doc,
943
- width,
944
- height,
945
- ratio,
946
- layers: doc.layers.map((layer) => ({
947
- ...layer,
948
- ...layer.image ? { image: {
949
- ...layer.image,
950
- x: (layer.image.x * oldWidth * scale + dx) / width,
951
- y: (layer.image.y * oldHeight * scale + dy) / height,
952
- width: layer.image.width * oldWidth * scale / width,
953
- height: layer.image.height * oldHeight * scale / height
954
- } } : {},
955
- strokes: layer.strokes.map((stroke) => ({
956
- ...stroke,
957
- width: stroke.width * scale,
958
- points: stroke.points.map((p) => ({
959
- x: (p.x * oldWidth * scale + dx) / width,
960
- y: (p.y * oldHeight * scale + dy) / height
961
- }))
962
- }))
963
- }))
1029
+ //#endregion
1030
+ //#region src/workspace-icons.jsx
1031
+ function WorkspaceIcon({ name, size = 24 }) {
1032
+ const paths = {
1033
+ select: "M5 3l14 9-7 2-3 7-4-18z",
1034
+ text: "M4 5h16M12 5v15M8 20h8M4 5v3M20 5v3",
1035
+ arrow: "M4 20L20 4M10 4h10v10",
1036
+ line: "M4 20L20 4",
1037
+ layers: "M12 3L2 8l10 5 10-5-10-5zM2 12l10 5 10-5M2 16l10 5 10-5",
1038
+ 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",
1039
+ eyeOff: "M3 3l18 18M9 5a10 10 0 013 0c6 0 10 7 10 7l-3 4M6 6C3 8 2 12 2 12s4 7 10 7c2 0 4-1 5-2",
1040
+ duplicate: "M8 8h13v13H8zM16 8V3H3v13h5",
1041
+ up: "M12 20V4M5 11l7-7 7 7",
1042
+ down: "M12 4v16M5 13l7 7 7-7",
1043
+ pencil: "M4 20l2-6L17 3l4 4L10 18l-6 2zM14 6l4 4",
1044
+ marker: "M5 16l9-12 7 5-9 12-7-5zM5 16l-3 4 6 1M12 7l7 5",
1045
+ image: "M4 4h16v16H4zM4 16l5-5 4 4 3-3 4 4M15 8h.01",
1046
+ close: "M6 6l12 12M18 6L6 18",
1047
+ stop: "M6 6h12v12H6z",
1048
+ pen: "M4 17c3-7 12-15 12-11S4 19 8 19s10-10 10-6-6 8-2 7l4-3",
1049
+ eraser: "M4 14l9-10 7 7-9 10H9l-5-5zM8 10l7 7M11 21h10",
1050
+ undo: "M9 5L4 10l5 5M5 10h9a6 6 0 010 12",
1051
+ redo: "M15 5l5 5-5 5M19 10h-9a6 6 0 000 12",
1052
+ check: "M5 12l5 5 9-11",
1053
+ clear: "M4 7h16M9 7V4h6v3M7 7l1 13h8l1-13",
1054
+ rectangle: "M5 5h14v14H5z",
1055
+ circle: "M20 12a8 8 0 11-16 0 8 8 0 0116 0"
964
1056
  };
1057
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1058
+ width: size,
1059
+ height: size,
1060
+ viewBox: "0 0 24 24",
1061
+ fill: "none",
1062
+ stroke: "currentColor",
1063
+ strokeWidth: "1.8",
1064
+ strokeLinecap: "round",
1065
+ strokeLinejoin: "round",
1066
+ "aria-hidden": "true",
1067
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: paths[name] ?? paths.pen })
1068
+ });
1069
+ }
1070
+ //#endregion
1071
+ //#region src/sketch-run-status.jsx
1072
+ function SketchRunStatus({ state, t, floating = false, onOpen, onStop, onResume, onDismiss }) {
1073
+ if (state === "idle") return null;
1074
+ const drawing = state === "drawing", recover = state === "stopped" || state === "failed";
1075
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1076
+ className: floating ? "codexSketchBackgroundStatus" : "codexSketchAgentStatus",
1077
+ role: "status",
1078
+ "aria-live": "polite",
1079
+ children: [
1080
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
1081
+ name: drawing ? "pen" : state === "finished" ? "check" : "rectangle",
1082
+ size: 15
1083
+ }),
1084
+ floating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1085
+ type: "button",
1086
+ onClick: onOpen,
1087
+ children: t(`sketchRun_${state}`)
1088
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`sketchRun_${state}`) }),
1089
+ drawing ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1090
+ type: "button",
1091
+ className: "codexSketchStop",
1092
+ onClick: onStop,
1093
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
1094
+ name: "stop",
1095
+ size: 12
1096
+ }), t("sketchRunStop")]
1097
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [recover ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1098
+ type: "button",
1099
+ title: t("sketchRunResumeHint"),
1100
+ onClick: onResume,
1101
+ children: t("sketchRunResume")
1102
+ }) : null, !recover ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1103
+ type: "button",
1104
+ "aria-label": t("sketchDismissStatus"),
1105
+ title: t("sketchDismissStatus"),
1106
+ onClick: onDismiss,
1107
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
1108
+ name: "close",
1109
+ size: 14
1110
+ })
1111
+ }) : null] })
1112
+ ]
1113
+ });
965
1114
  }
966
1115
  //#endregion
967
1116
  //#region src/sketch-layer-renderer.js
@@ -1169,8 +1318,7 @@ window.__ModuleLoader__.load({
1169
1318
  cursor.hidden = true;
1170
1319
  return;
1171
1320
  }
1172
- const pressure = node.hasPointerCapture(pointer.pointerId) && pointer.pointerType === "pen" ? heldPressure.current : 1;
1173
- const diameter = width * (brush === "pencil" ? .55 : 1) * pressure * rect.width / node.width;
1321
+ const diameter = width * (node.hasPointerCapture(pointer.pointerId) && pointer.pointerType === "pen" ? heldPressure.current : 1) * rect.width / node.width;
1174
1322
  cursor.hidden = false;
1175
1323
  cursor.style.width = `${diameter}px`;
1176
1324
  cursor.style.height = `${diameter}px`;
@@ -1515,7 +1663,8 @@ window.__ModuleLoader__.load({
1515
1663
  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.",
1516
1664
  commands: {
1517
1665
  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},...]}",
1518
- layer: "{op:\"layer\",action:\"add|select|rename|visible|duplicate|up|down|delete|clear\",id:1,value:\"name\"}",
1666
+ layer: "Add: {op:\"layer\",action:\"add\",value:\"name\"}; optional id is the NEW unique integer ID, otherwise allocated automatically. after is the existing insertion anchor, defaults to active layer. Other actions: {op:\"layer\",action:\"select|rename|visible|duplicate|up|down|delete|clear\",id:1,value:\"name\"}; id targets an existing layer.",
1667
+ curve: "Prefer {op:\"stroke\",shape:\"bezier\",start:{x:0,y:0},segments:[{control1:{x:0.2,y:0},control2:{x:0.8,y:1},end:{x:1,y:1}}],color:\"#123456\"}. Each segment has exactly two controls and an endpoint; no point counting required. Legacy points arrays still accepted. Do not provide both forms.",
1519
1668
  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.",
1520
1669
  resize: "{op:\"resize\",ratio:\"1:1|4:3|3:4|16:9|9:16\"}"
1521
1670
  },
@@ -1548,6 +1697,23 @@ window.__ModuleLoader__.load({
1548
1697
  "delete",
1549
1698
  "clear"
1550
1699
  ].includes(command.action)) throw Error("Unknown layer action");
1700
+ if (command.action === "add") {
1701
+ const id = command.id ?? doc.nextId, after = command.after ?? doc.active;
1702
+ if (!Number.isSafeInteger(id) || id < 1 || id === Number.MAX_SAFE_INTEGER || doc.layers.some((l) => l.id === id)) throw Error("New layer id must be a unique positive integer; omit id to allocate automatically");
1703
+ const next = changeSketchLayer(doc, "add", after);
1704
+ if (next === doc) throw Error("Cannot add layer: check the existing after layer and the 8-layer limit");
1705
+ doc = {
1706
+ ...next,
1707
+ active: id,
1708
+ nextId: Math.max(next.nextId, id + 1),
1709
+ layers: next.layers.map((l) => l.id === next.active ? {
1710
+ ...l,
1711
+ id,
1712
+ name: String(command.value ?? "").trim().slice(0, 40)
1713
+ } : l)
1714
+ };
1715
+ continue;
1716
+ }
1551
1717
  const next = changeSketchLayer(doc, command.action, command.id ?? doc.active, command.value);
1552
1718
  if (next === doc) throw Error("Layer action unavailable; inspect the document first");
1553
1719
  doc = next;
@@ -1593,7 +1759,8 @@ window.__ModuleLoader__.load({
1593
1759
  layer: layer.id
1594
1760
  }]).layers[0].strokes[0],
1595
1761
  brush: original.brush ?? "pen",
1596
- ...original.pressure !== void 0 ? { pressure: original.pressure } : {}
1762
+ ...original.pressure !== void 0 ? { pressure: original.pressure } : {},
1763
+ ...original.brushVersion === 2 ? { brushVersion: 2 } : {}
1597
1764
  };
1598
1765
  } else throw Error("Unknown object action");
1599
1766
  doc = {
@@ -1607,7 +1774,16 @@ window.__ModuleLoader__.load({
1607
1774
  }
1608
1775
  if (command.op !== "stroke") throw Error("Unknown command");
1609
1776
  const shape = command.shape === "ellipse" ? "circle" : command.shape ?? "pen";
1610
- const { points, color, width = shape === "text" ? 24 : 2, opacity = 1, fill = false } = command;
1777
+ const { color, width = shape === "text" ? 24 : 2, opacity = 1, fill = false } = command;
1778
+ let points = command.points;
1779
+ if (command.start !== void 0 || command.segments !== void 0) {
1780
+ if (shape !== "bezier" || points !== void 0 || !command.start || !Array.isArray(command.segments) || !command.segments.length || command.segments.length > 64) throw Error("Bezier requires start and 1–64 segments, without points");
1781
+ points = [command.start, ...command.segments.flatMap((s) => [
1782
+ s?.control1,
1783
+ s?.control2,
1784
+ s?.end
1785
+ ])];
1786
+ }
1611
1787
  if (![
1612
1788
  "pen",
1613
1789
  "line",
@@ -1675,6 +1851,7 @@ window.__ModuleLoader__.load({
1675
1851
  if (!Number.isInteger(offset) || offset < 0) throw Error("offset must be a non-negative integer");
1676
1852
  return {
1677
1853
  ...current,
1854
+ protocolVersion: 2,
1678
1855
  objects: objects.slice(offset, offset + 50),
1679
1856
  objectCount: objects.length,
1680
1857
  ...offset + 50 < objects.length ? { nextOffset: offset + 50 } : {},
@@ -1703,7 +1880,15 @@ window.__ModuleLoader__.load({
1703
1880
  if (request.revision !== current.revision || adapter.busy()) throw Error("Sketch changed or is being edited; inspect again");
1704
1881
  let changedObjects;
1705
1882
  if (request.action === "apply") {
1706
- const before = adapter.document(), next = applySketchCommands(before, request.commands);
1883
+ const before = adapter.document();
1884
+ let next;
1885
+ try {
1886
+ next = applySketchCommands(before, request.commands);
1887
+ } catch (cause) {
1888
+ const error = new Error(`${cause.message} Correct the batch and retry with the same runId and revision; nothing was applied.`, { cause });
1889
+ error.code = "SKETCH_INVALID_BATCH";
1890
+ throw error;
1891
+ }
1707
1892
  adapter.commit(next);
1708
1893
  const previous = new Map(before.layers.flatMap((l) => l.strokes.map((s) => [`${l.id}:${s.id}`, s])));
1709
1894
  changedObjects = next.layers.flatMap((l) => l.strokes.filter((s) => previous.get(`${l.id}:${s.id}`) !== s).map((s) => ({
@@ -1813,9 +1998,11 @@ window.__ModuleLoader__.load({
1813
1998
  "marker"
1814
1999
  ].includes(s.brush)) throw Error("Invalid brush");
1815
2000
  if (s.pressure !== void 0 && (!Number.isFinite(s.pressure) || s.pressure < .2 || s.pressure > 1)) throw Error("Invalid pressure");
2001
+ if (s.brushVersion !== void 0 && s.brushVersion !== 2) throw Error("Unsupported brush version");
1816
2002
  Object.assign(added[added.length - strokes.length + k], {
1817
2003
  brush: s.brush ?? "pen",
1818
- pressure: s.pressure ?? 1
2004
+ pressure: s.pressure ?? 1,
2005
+ ...s.brushVersion === 2 ? { brushVersion: 2 } : {}
1819
2006
  });
1820
2007
  }
1821
2008
  }
@@ -2087,46 +2274,6 @@ window.__ModuleLoader__.load({
2087
2274
  });
2088
2275
  }
2089
2276
  //#endregion
2090
- //#region src/workspace-icons.jsx
2091
- function WorkspaceIcon({ name, size = 24 }) {
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",
2096
- line: "M4 20L20 4",
2097
- layers: "M12 3L2 8l10 5 10-5-10-5zM2 12l10 5 10-5M2 16l10 5 10-5",
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",
2099
- eyeOff: "M3 3l18 18M9 5a10 10 0 013 0c6 0 10 7 10 7l-3 4M6 6C3 8 2 12 2 12s4 7 10 7c2 0 4-1 5-2",
2100
- duplicate: "M8 8h13v13H8zM16 8V3H3v13h5",
2101
- up: "M12 20V4M5 11l7-7 7 7",
2102
- down: "M12 4v16M5 13l7 7 7-7",
2103
- pencil: "M4 20l2-6L17 3l4 4L10 18l-6 2zM14 6l4 4",
2104
- marker: "M5 16l9-12 7 5-9 12-7-5zM5 16l-3 4 6 1M12 7l7 5",
2105
- image: "M4 4h16v16H4zM4 16l5-5 4 4 3-3 4 4M15 8h.01",
2106
- close: "M6 6l12 12M18 6L6 18",
2107
- pen: "M4 17c3-7 12-15 12-11S4 19 8 19s10-10 10-6-6 8-2 7l4-3",
2108
- eraser: "M4 14l9-10 7 7-9 10H9l-5-5zM8 10l7 7M11 21h10",
2109
- undo: "M9 5L4 10l5 5M5 10h9a6 6 0 010 12",
2110
- redo: "M15 5l5 5-5 5M19 10h-9a6 6 0 000 12",
2111
- check: "M5 12l5 5 9-11",
2112
- clear: "M4 7h16M9 7V4h6v3M7 7l1 13h8l1-13",
2113
- rectangle: "M5 5h14v14H5z",
2114
- circle: "M20 12a8 8 0 11-16 0 8 8 0 0116 0"
2115
- };
2116
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
2117
- width: size,
2118
- height: size,
2119
- viewBox: "0 0 24 24",
2120
- fill: "none",
2121
- stroke: "currentColor",
2122
- strokeWidth: "1.8",
2123
- strokeLinecap: "round",
2124
- strokeLinejoin: "round",
2125
- "aria-hidden": "true",
2126
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: paths[name] ?? paths.pen })
2127
- });
2128
- }
2129
- //#endregion
2130
2277
  //#region src/sketch-size-control.jsx
2131
2278
  function SketchSizeControl({ value, onChange, onStart, onEnd, label, disabled, min = 2, max = 128, mode, modes, onModeChange, suffix = "" }) {
2132
2279
  const active = (0, react.useRef)(false);
@@ -2263,6 +2410,7 @@ window.__ModuleLoader__.load({
2263
2410
  update("finished");
2264
2411
  return completed.value;
2265
2412
  } catch (error) {
2413
+ if (version === generation && error.code === "SKETCH_INVALID_BATCH") throw error;
2266
2414
  if (version === generation) {
2267
2415
  update("failed");
2268
2416
  throw new Error(`${error.message} Call inspect to obtain the current runId and revision before retrying.`, { cause: error });
@@ -2281,7 +2429,7 @@ window.__ModuleLoader__.load({
2281
2429
  //#endregion
2282
2430
  //#region src/sketch-agent-client.js
2283
2431
  function connectSketchAgent(rpc, sessionId, execute, report, pollDelay = () => 350) {
2284
- let stopped = false, token, timer, attempts = 0;
2432
+ let stopped = false, token, timer, attempts = 0, failures = 0;
2285
2433
  const call = (endpoint, payload) => rpc.call(CHANNEL, `sketch/${endpoint}`, {
2286
2434
  sessionId,
2287
2435
  token,
@@ -2311,13 +2459,24 @@ window.__ModuleLoader__.load({
2311
2459
  });
2312
2460
  }
2313
2461
  } catch (error) {
2314
- if (!stopped) report(error.message);
2462
+ if (!stopped) {
2463
+ if (++failures > 5) {
2464
+ report(`${error.message}; reconnect failed. Reopen this session to retry.`);
2465
+ return;
2466
+ }
2467
+ report(`${error.message}; reconnecting. Inspect recentRequests before retrying a write.`);
2468
+ if (token) call("disconnect").catch(() => {});
2469
+ token = void 0;
2470
+ timer = setTimeout(connect, Math.min(1e4, 1e3 * 2 ** (failures - 1)));
2471
+ }
2315
2472
  return;
2316
2473
  }
2474
+ failures = 0;
2317
2475
  if (!stopped) timer = setTimeout(poll, pollDelay());
2318
2476
  };
2319
2477
  const connect = () => void call("connect").then((value) => {
2320
2478
  token = value.token;
2479
+ attempts = 0;
2321
2480
  if (stopped) call("disconnect").catch(() => {});
2322
2481
  else poll();
2323
2482
  }, (error) => {
@@ -2344,17 +2503,23 @@ window.__ModuleLoader__.load({
2344
2503
  "#34c759",
2345
2504
  "#0088ff"
2346
2505
  ];
2347
- function SketchStudio({ open, agentEnabled, agentPreview, onOpen, onClose, attachSketch, enabled, t, incoming, sessionId, rpc }) {
2348
- const dialog = (0, react.useRef)(null), canvas = (0, react.useRef)(null), doc = (0, react.useRef)(createSketchLayers()), cache = (0, react.useRef)(/* @__PURE__ */ new Map());
2349
- const undo = (0, react.useRef)([]), redo = (0, react.useRef)([]), active = (0, react.useRef)(null), frame = (0, react.useRef)(null);
2350
- const images = (0, react.useRef)(/* @__PURE__ */ new Map()), saved = (0, react.useRef)(null), dirty = (0, react.useRef)(false), updateUi = (0, react.useRef)(false);
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);
2506
+ function SketchStudio({ open, agentEnabled, agentPreview, onOpen, onClose, attachSketch, enabled, t, incoming, sessionId, rpc, sessionState }) {
2507
+ const localSession = (0, react.useRef)(null);
2508
+ localSession.current ??= sessionState ?? createSketchSessionState();
2509
+ const { doc, undo, redo, images, saved, dirty, documentId, documentRevision, agentAdapter, agentSession, agentRun } = localSession.current;
2510
+ const dialog = (0, react.useRef)(null), canvas = (0, react.useRef)(null), cache = (0, react.useRef)(/* @__PURE__ */ new Map());
2511
+ const active = (0, react.useRef)(null), frame = (0, react.useRef)(null), updateUi = (0, react.useRef)(false);
2512
+ const [agentState, setAgentState] = (0, react.useState)(agentRun.current?.state ?? "idle");
2353
2513
  const agentLocked = agentState === "drawing";
2354
2514
  const [noticeHidden, setNoticeHidden] = (0, react.useState)(false);
2355
2515
  const [stability, setStability] = (0, react.useState)(0), [flow, setFlow] = (0, react.useState)(100), [picturesOpen, setPicturesOpen] = (0, react.useState)(false);
2356
2516
  const pictureInput = (0, react.useRef)(null), received = (0, react.useRef)(null);
2357
2517
  const navigation = useSketchView(canvas, open);
2518
+ const brushWidths = (0, react.useRef)({
2519
+ pen: 12,
2520
+ pencil: 6,
2521
+ marker: 28
2522
+ });
2358
2523
  const [revision, redraw] = (0, react.useState)(0), [tool, setTool] = (0, react.useState)("pen"), [brush, setBrush] = (0, react.useState)("pen");
2359
2524
  const [eraser, setEraser] = (0, react.useState)("pixel"), [color, setColor] = (0, react.useState)("#0088ff"), [width, setWidth] = (0, react.useState)(12);
2360
2525
  const [selection, setSelection] = (0, react.useState)(null), [textEdit, setTextEdit] = (0, react.useState)(null), [shapesOpen, setShapesOpen] = (0, react.useState)(false);
@@ -2384,6 +2549,13 @@ window.__ModuleLoader__.load({
2384
2549
  setColor(value);
2385
2550
  if (selected) editObject({ color: value });
2386
2551
  };
2552
+ const chooseBrush = (name) => {
2553
+ brushWidths.current[brush] = width;
2554
+ setWidth(brushWidths.current[name]);
2555
+ setBrush(name);
2556
+ setTool("pen");
2557
+ setSelection(null);
2558
+ };
2387
2559
  const [fillShape, setFillShape] = (0, react.useState)(false);
2388
2560
  const [layersOpen, setLayersOpen] = (0, react.useState)(false), [busy, setBusy] = (0, react.useState)(false), [error, setError] = (0, react.useState)("");
2389
2561
  const cursorRing = (0, react.useRef)(null);
@@ -2437,10 +2609,10 @@ window.__ModuleLoader__.load({
2437
2609
  (0, react.useEffect)(() => () => {
2438
2610
  cancelAnimationFrame(frame.current);
2439
2611
  cache.current.clear();
2440
- agentRun.current?.dispose();
2441
2612
  }, []);
2442
2613
  const hasContent = () => doc.current.layers.some((layer) => layer.image || layer.strokes.length);
2443
2614
  const save = async (name) => {
2615
+ const savingDocument = documentId.current, savingRevision = documentRevision.current;
2444
2616
  const row = {
2445
2617
  id: saved.current?.id ?? crypto.randomUUID(),
2446
2618
  name: name?.trim() || saved.current?.name || `${t("sketchTitle")} ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
@@ -2448,11 +2620,13 @@ window.__ModuleLoader__.load({
2448
2620
  doc: structuredClone(doc.current)
2449
2621
  };
2450
2622
  await sketchDrafts("save", row);
2451
- saved.current = {
2452
- id: row.id,
2453
- name: row.name
2454
- };
2455
- dirty.current = false;
2623
+ if (documentId.current === savingDocument) {
2624
+ saved.current = {
2625
+ id: row.id,
2626
+ name: row.name
2627
+ };
2628
+ if (documentRevision.current === savingRevision) dirty.current = false;
2629
+ }
2456
2630
  };
2457
2631
  const saveChanges = async () => {
2458
2632
  if (dirty.current && (hasContent() || saved.current)) await save();
@@ -2570,11 +2744,11 @@ window.__ModuleLoader__.load({
2570
2744
  editObject({}, "delete");
2571
2745
  return;
2572
2746
  }
2573
- if (event.key.toLowerCase() === "v") {
2747
+ if (!event.ctrlKey && !event.metaKey && !event.altKey && event.key.toLowerCase() === "v") {
2574
2748
  setTool("select");
2575
2749
  return;
2576
2750
  }
2577
- if (event.key.toLowerCase() === "t") {
2751
+ if (!event.ctrlKey && !event.metaKey && !event.altKey && event.key.toLowerCase() === "t") {
2578
2752
  setTool("text");
2579
2753
  return;
2580
2754
  }
@@ -2601,7 +2775,6 @@ window.__ModuleLoader__.load({
2601
2775
  if (tools[key]) {
2602
2776
  event.preventDefault();
2603
2777
  setTool(tools[key]);
2604
- if (tools[key] === "pen") setBrush("pen");
2605
2778
  }
2606
2779
  if (key === "[" || key === "]") {
2607
2780
  event.preventDefault();
@@ -2730,6 +2903,10 @@ window.__ModuleLoader__.load({
2730
2903
  schedule();
2731
2904
  };
2732
2905
  Object.assign(agentAdapter.current, {
2906
+ changed: (state) => {
2907
+ setAgentState(state);
2908
+ setNoticeHidden(false);
2909
+ },
2733
2910
  available: () => enabled && agentEnabled,
2734
2911
  previewEnabled: () => agentPreview,
2735
2912
  open: () => onOpen(),
@@ -2775,17 +2952,14 @@ window.__ModuleLoader__.load({
2775
2952
  agentRun.current ??= createSketchAgentRun({
2776
2953
  execute: (request) => agentSession.current(request),
2777
2954
  open: () => agentAdapter.current.open(),
2778
- changed: (state) => {
2779
- setAgentState(state);
2780
- setNoticeHidden(false);
2781
- },
2955
+ changed: (state) => agentAdapter.current.changed?.(state),
2782
2956
  busy: () => agentAdapter.current.busy(),
2783
2957
  previewEnabled: () => agentAdapter.current.previewEnabled()
2784
2958
  });
2785
2959
  (0, react.useEffect)(() => {
2786
2960
  if (!enabled || !agentEnabled) return;
2787
2961
  const api = Object.freeze({
2788
- version: 1,
2962
+ version: 2,
2789
2963
  sessionId,
2790
2964
  execute: (request) => agentRun.current.execute(request),
2791
2965
  export: async (format) => {
@@ -2812,9 +2986,12 @@ window.__ModuleLoader__.load({
2812
2986
  sessionId
2813
2987
  ]);
2814
2988
  (0, react.useEffect)(() => {
2815
- if (!enabled || !agentEnabled || !rpc || !sessionId) return;
2989
+ if (!enabled || !agentEnabled) {
2990
+ if (agentRun.current.locked) agentRun.current.stop();
2991
+ return;
2992
+ }
2993
+ if (!rpc || !sessionId) return;
2816
2994
  let live = true;
2817
- agentRun.current.resume();
2818
2995
  const disconnect = connectSketchAgent(rpc, sessionId, (request) => {
2819
2996
  if (!live) throw Error("Sketch session disconnected");
2820
2997
  return agentRun.current.execute(request);
@@ -2824,7 +3001,6 @@ window.__ModuleLoader__.load({
2824
3001
  }, () => 350);
2825
3002
  return () => {
2826
3003
  live = false;
2827
- agentRun.current.stop();
2828
3004
  disconnect();
2829
3005
  };
2830
3006
  }, [
@@ -2877,24 +3053,17 @@ window.__ModuleLoader__.load({
2877
3053
  link.remove();
2878
3054
  setTimeout(() => URL.revokeObjectURL(url), 1e4);
2879
3055
  };
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
- })]
3056
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [enabled && agentEnabled && !open && !noticeHidden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SketchRunStatus, {
3057
+ state: agentState,
3058
+ floating: true,
3059
+ t,
3060
+ onOpen,
3061
+ onStop: () => agentRun.current.stop(),
3062
+ onResume: () => {
3063
+ setError("");
3064
+ agentRun.current.resume();
3065
+ },
3066
+ onDismiss: () => setNoticeHidden(true)
2898
3067
  }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dialog", {
2899
3068
  ref: dialog,
2900
3069
  className: "codexSketchDialog codexSketchStudio codexLayerStudio",
@@ -3036,24 +3205,15 @@ window.__ModuleLoader__.load({
3036
3205
  })
3037
3206
  ]
3038
3207
  }),
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]
3208
+ enabled && agentEnabled && !noticeHidden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SketchRunStatus, {
3209
+ state: agentState,
3210
+ t,
3211
+ onStop: () => agentRun.current.stop(),
3212
+ onResume: () => {
3213
+ setError("");
3214
+ agentRun.current.resume();
3215
+ },
3216
+ onDismiss: () => setNoticeHidden(true)
3057
3217
  }) : null,
3058
3218
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3059
3219
  className: `codexLayerBody ${layersOpen ? "withLayers" : ""}`,
@@ -3169,6 +3329,7 @@ window.__ModuleLoader__.load({
3169
3329
  width,
3170
3330
  fill: fillShape && ["rectangle", "circle"].includes(tool),
3171
3331
  brush: tool === "pen" ? brush : "pen",
3332
+ ...tool === "pen" ? { brushVersion: 2 } : {},
3172
3333
  pressure: event.pointerType === "pen" ? Math.max(.2, event.pressure) : 1,
3173
3334
  points: [start]
3174
3335
  });
@@ -3464,6 +3625,8 @@ window.__ModuleLoader__.load({
3464
3625
  [
3465
3626
  "select",
3466
3627
  "pen",
3628
+ "pencil",
3629
+ "marker",
3467
3630
  "text",
3468
3631
  "eraser"
3469
3632
  ].map((name) => {
@@ -3476,13 +3639,15 @@ window.__ModuleLoader__.load({
3476
3639
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3477
3640
  type: "button",
3478
3641
  "aria-label": label,
3479
- title: label,
3642
+ title: drawing ? `${label} · ${t(`sketchBrushHint_${name}`)}` : label,
3480
3643
  "aria-pressed": drawing ? tool === "pen" && brush === name : tool === name,
3481
3644
  disabled: agentLocked || busy,
3482
3645
  onClick: () => {
3483
- setTool(drawing ? "pen" : name);
3484
- if (name !== "select") setSelection(null);
3485
- if (drawing) setBrush(name);
3646
+ if (drawing) chooseBrush(name);
3647
+ else {
3648
+ setTool(name);
3649
+ if (name !== "select") setSelection(null);
3650
+ }
3486
3651
  },
3487
3652
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
3488
3653
  name,
@@ -3493,13 +3658,30 @@ window.__ModuleLoader__.load({
3493
3658
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3494
3659
  className: "codexSketchShapeToggle",
3495
3660
  type: "button",
3661
+ "aria-label": t("sketchShapes"),
3496
3662
  "aria-expanded": shapesOpen,
3663
+ "aria-pressed": [
3664
+ "line",
3665
+ "arrow",
3666
+ "rectangle",
3667
+ "circle"
3668
+ ].includes(tool),
3497
3669
  disabled: agentLocked || busy,
3498
3670
  onClick: () => setShapesOpen((v) => !v),
3499
3671
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceIcon, {
3500
- name: "rectangle",
3672
+ name: [
3673
+ "line",
3674
+ "arrow",
3675
+ "rectangle",
3676
+ "circle"
3677
+ ].includes(tool) ? tool : "rectangle",
3501
3678
  size: 23
3502
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("sketchShapes") })]
3679
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t([
3680
+ "line",
3681
+ "arrow",
3682
+ "rectangle",
3683
+ "circle"
3684
+ ].includes(tool) ? `sketchTool_${tool}` : "sketchShapes") })]
3503
3685
  }),
3504
3686
  shapesOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3505
3687
  className: "codexSketchShapeMenu",
@@ -3510,6 +3692,7 @@ window.__ModuleLoader__.load({
3510
3692
  "circle"
3511
3693
  ].map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3512
3694
  type: "button",
3695
+ "aria-pressed": tool === name,
3513
3696
  onClick: () => {
3514
3697
  setTool(name);
3515
3698
  setSelection(null);
@@ -3529,20 +3712,6 @@ window.__ModuleLoader__.load({
3529
3712
  checked: fillShape,
3530
3713
  onChange: (e) => setFillShape(e.target.checked)
3531
3714
  }), 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
3715
  selected ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3547
3716
  className: "codexSketchObjectActions",
3548
3717
  children: [
@@ -3649,7 +3818,7 @@ window.__ModuleLoader__.load({
3649
3818
  .codexLayerBrush>select{background:var(--sketch-bg);color:inherit;border:1px solid var(--sketch-line);border-radius:8px;padding:6px}
3650
3819
 
3651
3820
  .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)}
3821
+ .codexSketchAgentStatus{display:flex;align-items:center;justify-content:center;gap:8px;min-height:24px;color:var(--sketch-muted);font-size:12px}.codexSketchAgentStatus button{min-height:30px;padding:4px 10px;border-radius:16px;background:var(--sketch-line)}.codexSketchAgentStatus{align-self:center;max-width:100%;min-height:34px;padding:0 6px;flex-wrap:wrap}.codexSketchBackgroundStatus button{min-height:30px;display:inline-flex;align-items:center;gap:5px}.codexSketchAgentStatus .codexSketchStop,.codexSketchBackgroundStatus .codexSketchStop{font-weight:500;background:var(--sketch-line,#8882);border-radius:16px;padding:4px 10px}
3653
3822
  .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}
3654
3823
  .codexSketchDialog[open]{display:flex;flex-direction:column;gap:8px}
3655
3824
  .codexSketchDialog::backdrop{background:#0005;backdrop-filter:blur(12px)}
@@ -3707,7 +3876,7 @@ window.__ModuleLoader__.load({
3707
3876
  `;
3708
3877
  //#endregion
3709
3878
  //#region src/sketch-workspace.jsx
3710
- function SketchWorkspace({ preference, attachSketch, registerOpen, t, sessionId, rpc }) {
3879
+ function SketchWorkspace({ preference, attachSketch, registerOpen, t, sessionId, rpc, sessionState }) {
3711
3880
  const settings = (0, react.useSyncExternalStore)(preference.subscribe, preference.getSnapshot);
3712
3881
  const [open, setOpen] = (0, react.useState)(false);
3713
3882
  const [incoming, setIncoming] = (0, react.useState)(null);
@@ -3720,6 +3889,7 @@ window.__ModuleLoader__.load({
3720
3889
  }
3721
3890
  }), [registerOpen]);
3722
3891
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SketchStudio, {
3892
+ sessionState,
3723
3893
  agentPreview: settings.imageSketchAgentPreview,
3724
3894
  agentEnabled: settings.imageSketchAgent,
3725
3895
  onOpen: () => {
@@ -3832,6 +4002,11 @@ window.__ModuleLoader__.load({
3832
4002
  sketchSizeShort: "粗细",
3833
4003
  sketchObjectDuplicate: "复制对象",
3834
4004
  sketchObjectDelete: "删除对象",
4005
+ sketchBrushHint_pen: "实色圆头墨线",
4006
+ sketchBrushHint_pencil: "细腻颗粒,叠画加深",
4007
+ sketchBrushHint_marker: "半透明平头,适合高亮",
4008
+ sketchDismissStatus: "收起提示",
4009
+ sketchRunResumeHint: "允许 Agent 接收后续绘图请求,不会自动重发消息",
3835
4010
  sketchTool_select: "选择",
3836
4011
  sketchTool_text: "文字",
3837
4012
  sketchTool_arrow: "箭头",
@@ -4238,6 +4413,11 @@ window.__ModuleLoader__.load({
4238
4413
  sketchSizeShort: "Size",
4239
4414
  sketchObjectDuplicate: "Duplicate object",
4240
4415
  sketchObjectDelete: "Delete object",
4416
+ sketchBrushHint_pen: "Solid round ink",
4417
+ sketchBrushHint_pencil: "Grain builds with repeated strokes",
4418
+ sketchBrushHint_marker: "Translucent flat tip for highlights",
4419
+ sketchDismissStatus: "Dismiss status",
4420
+ sketchRunResumeHint: "Allow subsequent drawing requests; does not resend a message",
4241
4421
  sketchTool_select: "Select",
4242
4422
  sketchTool_text: "Text",
4243
4423
  sketchTool_arrow: "Arrow",
@@ -6461,7 +6641,7 @@ window.__ModuleLoader__.load({
6461
6641
  }
6462
6642
  //#endregion
6463
6643
  //#region src/version.js
6464
- const PACKAGE_VERSION = "2.1.0-beta.4";
6644
+ const PACKAGE_VERSION = "2.1.0";
6465
6645
  //#endregion
6466
6646
  //#region src/client-recovery.js
6467
6647
  async function recoveryCall(rpc, endpoint, payload = {}, timeoutMs = 1e4) {
@@ -8778,6 +8958,8 @@ window.__ModuleLoader__.load({
8778
8958
  const conversation = ctx.get("conversation");
8779
8959
  const uiConversation = ctx.get("uiConversation");
8780
8960
  const sketchOpeners = /* @__PURE__ */ new Map();
8961
+ const sketchSessions = createSketchSessionRegistry();
8962
+ ctx.effect(() => () => sketchSessions.dispose(), "codex-subscription: sketch sessions");
8781
8963
  ctx.inject(["inputTriggers"], (triggerContext) => triggerContext.effect(() => triggerContext.get("inputTriggers").registerSource(createSketchTrigger({
8782
8964
  enabled: () => {
8783
8965
  const value = preference.getSnapshot();
@@ -8886,6 +9068,7 @@ window.__ModuleLoader__.load({
8886
9068
  t,
8887
9069
  sessionId,
8888
9070
  rpc,
9071
+ sessionState: sketchSessions.get(sessionId),
8889
9072
  registerOpen: (callback) => {
8890
9073
  sketchOpeners.set(sessionId, callback);
8891
9074
  return () => {