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/README.en.md +35 -5
- package/README.md +35 -5
- package/lib/client.js +623 -440
- package/lib/index.js +49 -16
- package/package.json +1 -1
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
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
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
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
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
|
|
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
|
|
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 {
|
|
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()
|
|
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)
|
|
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
|
|
2349
|
-
|
|
2350
|
-
const
|
|
2351
|
-
const
|
|
2352
|
-
const
|
|
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
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
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:
|
|
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
|
|
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
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
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 &&
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
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
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
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:
|
|
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(
|
|
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:
|
|
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
|
|
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 () => {
|