dsh-codex-subscription 1.13.1 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DIRECTORY.md +1 -1
- package/README.en.md +35 -27
- package/README.md +35 -25
- package/THIRD_PARTY_NOTICES.md +1 -0
- package/lib/client.js +990 -297
- package/lib/index.js +99 -13
- package/package.json +1 -2
- package/AGENTS.md +0 -93
package/lib/client.js
CHANGED
|
@@ -28,7 +28,6 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
//#endregion
|
|
29
29
|
let react = require("react");
|
|
30
30
|
react = __toESM(react, 1);
|
|
31
|
-
let react_dom = require("react-dom");
|
|
32
31
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
33
32
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
34
33
|
//#region node_modules/.pnpm/@heroicons+react@2.2.0_react@18.3.1/node_modules/@heroicons/react/16/solid/esm/BoltIcon.js
|
|
@@ -50,14 +49,193 @@ window.__ModuleLoader__.load({
|
|
|
50
49
|
const ForwardRef = /*#__PURE__*/ react.forwardRef(BoltIcon);
|
|
51
50
|
//#endregion
|
|
52
51
|
//#region src/image-edit.js
|
|
53
|
-
|
|
52
|
+
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
53
|
+
const validCoordinate = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
|
|
54
|
+
const cleanNote = (value) => typeof value === "string" ? value.trim() : "";
|
|
55
|
+
const coordinateError = (number) => {
|
|
56
|
+
const error = /* @__PURE__ */ new Error(`Annotation ${number} must have finite x and y coordinates between 0 and 1`);
|
|
57
|
+
error.code = "ANNOTATION_INVALID";
|
|
58
|
+
return error;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Validate the annotation contract shared by the draft builder and the
|
|
62
|
+
* reference-image renderer. Annotation numbers are their array positions so
|
|
63
|
+
* that they stay aligned with the pins shown to the user.
|
|
64
|
+
*/
|
|
65
|
+
function normalizeImageEditAnnotations(annotations, { requireNotes = false } = {}) {
|
|
66
|
+
if (!Array.isArray(annotations)) throw new TypeError("annotations must be an array");
|
|
67
|
+
return annotations.map((annotation, index) => {
|
|
68
|
+
const number = index + 1;
|
|
69
|
+
const note = cleanNote(annotation?.note);
|
|
70
|
+
if (requireNotes && note === "") {
|
|
71
|
+
const error = /* @__PURE__ */ new Error(`Annotation ${number} is missing a note; describe what should change`);
|
|
72
|
+
error.code = "ANNOTATION_INVALID";
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
if (!isRecord(annotation) || !validCoordinate(annotation.x) || !validCoordinate(annotation.y)) throw coordinateError(number);
|
|
76
|
+
return {
|
|
77
|
+
number,
|
|
78
|
+
x: annotation.x,
|
|
79
|
+
y: annotation.y,
|
|
80
|
+
note
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const formatPercent = (value) => {
|
|
85
|
+
return `${Number((value * 100).toFixed(2))}%`;
|
|
86
|
+
};
|
|
87
|
+
const formatPixel = (value) => {
|
|
88
|
+
const rounded = Number(value.toFixed(2));
|
|
89
|
+
return String(rounded);
|
|
90
|
+
};
|
|
91
|
+
const withNames = (value, sourceName, referenceName) => String(value).replaceAll("{sourceName}", sourceName).replaceAll("{referenceName}", referenceName);
|
|
92
|
+
const positiveImageDimension = (value) => Number.isSafeInteger(value) && value > 0;
|
|
93
|
+
function buildImageEditDraft({ prompt = "", annotations = [], translate, width, height, sourceName = "source.png", referenceName = "annotated-reference.png" }) {
|
|
54
94
|
if (typeof translate !== "function") throw new TypeError("translate must be a function");
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
95
|
+
const base = typeof prompt === "string" && prompt.trim() !== "" ? prompt.trim() : translate("imageEditDefault");
|
|
96
|
+
if (!Array.isArray(annotations)) throw new TypeError("annotations must be an array");
|
|
97
|
+
if (annotations.length === 0) return base;
|
|
98
|
+
const hasWidth = width !== void 0;
|
|
99
|
+
if (hasWidth !== (height !== void 0) || hasWidth && (!positiveImageDimension(width) || !positiveImageDimension(height))) throw new Error("width and height must be positive integers when provided");
|
|
100
|
+
const normalized = normalizeImageEditAnnotations(annotations, { requireNotes: true });
|
|
101
|
+
const source = typeof sourceName === "string" && sourceName.trim() !== "" ? sourceName.trim() : "source.png";
|
|
102
|
+
const reference = typeof referenceName === "string" && referenceName.trim() !== "" ? referenceName.trim() : "annotated-reference.png";
|
|
103
|
+
const guide = withNames(translate("imageEditReferenceGuide"), source, reference);
|
|
104
|
+
const location = translate("imageEditLocation");
|
|
105
|
+
const notes = normalized.map(({ number, x, y, note }) => {
|
|
106
|
+
const pixels = hasWidth ? ` (pixel x=${formatPixel(x * Math.max(0, width - 1))} of ${width}, y=${formatPixel(y * Math.max(0, height - 1))} of ${height})` : "";
|
|
107
|
+
return `${number}. ${location}: x=${formatPercent(x)} (normalized ${x}), y=${formatPercent(y)} (normalized ${y})${pixels}; ${note}`;
|
|
108
|
+
});
|
|
109
|
+
return [
|
|
110
|
+
base,
|
|
111
|
+
"",
|
|
112
|
+
guide,
|
|
113
|
+
"",
|
|
114
|
+
translate("imageRegionNotes"),
|
|
115
|
+
...notes
|
|
116
|
+
].join("\n");
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/image-edit-reference.js
|
|
120
|
+
const TWO_PI = Math.PI * 2;
|
|
121
|
+
const PIN_FILL = "#e11d48";
|
|
122
|
+
const PIN_OUTER_STROKE = "#111827";
|
|
123
|
+
const PIN_INNER_STROKE = "#ffffff";
|
|
124
|
+
const finiteDimension = (value) => Number.isSafeInteger(value) && value > 0;
|
|
125
|
+
const clamp$1 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
126
|
+
const clampCenter = (value, size, edge) => {
|
|
127
|
+
const margin = Math.min(edge, size / 2);
|
|
128
|
+
return clamp$1(value, margin, size - margin);
|
|
129
|
+
};
|
|
130
|
+
function getBitmapFactory(options) {
|
|
131
|
+
const factory = options?.createImageBitmap ?? options?.bitmapFactory ?? globalThis.createImageBitmap;
|
|
132
|
+
if (typeof factory !== "function") throw new Error("createImageBitmap is not available");
|
|
133
|
+
return factory;
|
|
134
|
+
}
|
|
135
|
+
function getCanvasFactory(options) {
|
|
136
|
+
const injected = options?.createCanvas ?? options?.canvasFactory;
|
|
137
|
+
if (typeof injected === "function") return injected;
|
|
138
|
+
const document = globalThis.document;
|
|
139
|
+
if (document !== void 0 && typeof document.createElement === "function") return (width, height) => {
|
|
140
|
+
const canvas = document.createElement("canvas");
|
|
141
|
+
canvas.width = width;
|
|
142
|
+
canvas.height = height;
|
|
143
|
+
return canvas;
|
|
144
|
+
};
|
|
145
|
+
throw new Error("A canvas factory is not available");
|
|
146
|
+
}
|
|
147
|
+
function drawPin(context, number, x, y, width, height) {
|
|
148
|
+
const label = String(number);
|
|
149
|
+
const fontSize = Math.max(12, Math.min(40, Math.round(Math.min(width, height) * .03)));
|
|
150
|
+
const radius = Math.max(12, fontSize * .8, fontSize * (.3 * label.length + .35));
|
|
151
|
+
const outerStroke = Math.max(2, Math.min(4, radius * .25));
|
|
152
|
+
const innerStroke = Math.max(1, Math.min(2, radius * .13));
|
|
153
|
+
const edge = radius + outerStroke + 2;
|
|
154
|
+
const targetX = x * Math.max(0, width - 1);
|
|
155
|
+
const targetY = y * Math.max(0, height - 1);
|
|
156
|
+
const centerX = clampCenter(targetX, width, edge);
|
|
157
|
+
const centerY = clampCenter(targetY, height, edge);
|
|
158
|
+
context.save?.();
|
|
159
|
+
if ((centerX !== targetX || centerY !== targetY) && typeof context.moveTo === "function" && typeof context.lineTo === "function") {
|
|
160
|
+
context.beginPath();
|
|
161
|
+
context.moveTo(targetX, targetY);
|
|
162
|
+
context.lineTo(centerX, centerY);
|
|
163
|
+
context.lineWidth = outerStroke * 2;
|
|
164
|
+
context.strokeStyle = PIN_OUTER_STROKE;
|
|
165
|
+
context.stroke();
|
|
166
|
+
context.beginPath();
|
|
167
|
+
context.moveTo(targetX, targetY);
|
|
168
|
+
context.lineTo(centerX, centerY);
|
|
169
|
+
context.lineWidth = innerStroke;
|
|
170
|
+
context.strokeStyle = PIN_INNER_STROKE;
|
|
171
|
+
context.stroke();
|
|
172
|
+
}
|
|
173
|
+
context.beginPath();
|
|
174
|
+
context.arc(centerX, centerY, radius, 0, TWO_PI);
|
|
175
|
+
context.fillStyle = PIN_FILL;
|
|
176
|
+
context.fill();
|
|
177
|
+
context.lineWidth = outerStroke;
|
|
178
|
+
context.strokeStyle = PIN_OUTER_STROKE;
|
|
179
|
+
context.stroke();
|
|
180
|
+
context.lineWidth = innerStroke;
|
|
181
|
+
context.strokeStyle = PIN_INNER_STROKE;
|
|
182
|
+
context.stroke();
|
|
183
|
+
context.font = `700 ${fontSize}px sans-serif`;
|
|
184
|
+
context.fillStyle = PIN_INNER_STROKE;
|
|
185
|
+
context.textAlign = "center";
|
|
186
|
+
context.textBaseline = "middle";
|
|
187
|
+
context.fillText(label, centerX, centerY);
|
|
188
|
+
context.restore?.();
|
|
189
|
+
}
|
|
190
|
+
function encodePng(canvas) {
|
|
191
|
+
if (typeof canvas?.toBlob !== "function") throw new Error("Canvas PNG encoding is not available");
|
|
192
|
+
return new Promise((resolve, reject) => {
|
|
193
|
+
let settled = false;
|
|
194
|
+
const finish = (callback, value) => {
|
|
195
|
+
if (settled) return;
|
|
196
|
+
settled = true;
|
|
197
|
+
callback(value);
|
|
198
|
+
};
|
|
199
|
+
try {
|
|
200
|
+
canvas.toBlob((value) => {
|
|
201
|
+
if (value === null || value === void 0) finish(reject, /* @__PURE__ */ new Error("Canvas failed to encode the annotation reference as PNG"));
|
|
202
|
+
else finish(resolve, value);
|
|
203
|
+
}, "image/png");
|
|
204
|
+
} catch (error) {
|
|
205
|
+
finish(reject, error);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Create the second image sent with an annotated edit. It contains the clean
|
|
211
|
+
* source plus numbered pins, with no note text. `options` is intentionally
|
|
212
|
+
* injectable so the rendering path can be exercised without a browser:
|
|
213
|
+
* `{ createImageBitmap, createCanvas }`.
|
|
214
|
+
*/
|
|
215
|
+
async function createAnnotatedImageReference(blob, annotations, options = {}) {
|
|
216
|
+
const normalized = normalizeImageEditAnnotations(annotations);
|
|
217
|
+
const createImageBitmap = getBitmapFactory(options);
|
|
218
|
+
const createCanvas = getCanvasFactory(options);
|
|
219
|
+
const bitmap = await createImageBitmap(blob);
|
|
220
|
+
try {
|
|
221
|
+
const width = bitmap?.width;
|
|
222
|
+
const height = bitmap?.height;
|
|
223
|
+
if (!finiteDimension(width) || !finiteDimension(height)) throw new Error("The source image has invalid dimensions");
|
|
224
|
+
if (Math.min(width, height) < 64) throw new Error("The source image is too small for readable annotation pins");
|
|
225
|
+
const canvas = await createCanvas(width, height);
|
|
226
|
+
if (canvas === null || canvas === void 0) throw new Error("Canvas factory returned no canvas");
|
|
227
|
+
canvas.width = width;
|
|
228
|
+
canvas.height = height;
|
|
229
|
+
const context = canvas.getContext?.("2d");
|
|
230
|
+
if (context === null || context === void 0) throw new Error("Canvas 2D context is not available");
|
|
231
|
+
context.clearRect?.(0, 0, width, height);
|
|
232
|
+
context.drawImage?.(bitmap, 0, 0, width, height);
|
|
233
|
+
if (typeof context.drawImage !== "function") throw new Error("Canvas 2D context cannot draw the source image");
|
|
234
|
+
for (const annotation of normalized) drawPin(context, annotation.number, annotation.x, annotation.y, width, height);
|
|
235
|
+
return await encodePng(canvas);
|
|
236
|
+
} finally {
|
|
237
|
+
if (typeof bitmap?.close === "function") bitmap.close();
|
|
238
|
+
}
|
|
61
239
|
}
|
|
62
240
|
const ORIGINAL_IMAGE_ID_PATTERN = /^img_[0-9a-f]{32}$/u;
|
|
63
241
|
const positiveInteger = (value) => Number.isSafeInteger(value) && value > 0;
|
|
@@ -79,6 +257,704 @@ window.__ModuleLoader__.load({
|
|
|
79
257
|
return original === void 0 ? void 0 : { original };
|
|
80
258
|
}
|
|
81
259
|
//#endregion
|
|
260
|
+
//#region src/subscription-image-viewer-styles.js
|
|
261
|
+
const SUBSCRIPTION_IMAGE_VIEWER_CSS = String.raw`
|
|
262
|
+
.dcsiv-root{position:fixed;inset:0;z-index:1000;pointer-events:auto;overflow:hidden;background:rgba(7,8,10,.68);color:var(--dsw-alias-label-primary-inverted,#fff);outline:0;backdrop-filter:blur(13px) saturate(.72);-webkit-backdrop-filter:blur(13px) saturate(.72)}
|
|
263
|
+
.dcsiv-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
|
|
264
|
+
.dcsiv-topbar{position:absolute;right:50%;bottom:22px;z-index:6;max-width:calc(100vw - 36px);padding:5px;border:1px solid rgba(255,255,255,.12);border-radius:999px;background:rgba(38,39,43,.86);box-shadow:0 10px 34px rgba(0,0,0,.3);transform:translateX(50%);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px)}
|
|
265
|
+
.dcsiv-actions{display:flex;align-items:center;gap:2px;overflow-x:auto;scrollbar-width:none}.dcsiv-actions::-webkit-scrollbar{display:none}.dcsiv-button,.dcsiv-download{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 10px;border:0;border-radius:999px;background:transparent;color:rgba(255,255,255,.9);font:inherit;font-size:12px;text-decoration:none;white-space:nowrap;cursor:pointer}.dcsiv-button:hover,.dcsiv-download:hover,.dcsiv-button[data-active=true]{background:rgba(255,255,255,.12)}.dcsiv-button:focus-visible,.dcsiv-download:focus-visible,.dcsiv-close-floating:focus-visible{outline:2px solid rgba(255,255,255,.9);outline-offset:2px}.dcsiv-button:disabled,.dcsiv-download:disabled{opacity:.38;cursor:default}.dcsiv-icon-only{width:32px;padding:0}.dcsiv-zoom{min-width:44px;color:rgba(255,255,255,.68);font-size:12px;font-variant-numeric:tabular-nums;text-align:center}
|
|
266
|
+
.dcsiv-close-floating{position:absolute;top:20px;right:20px;z-index:8;display:grid;place-items:center;width:42px;height:42px;padding:0;border:1px solid rgba(255,255,255,.1);border-radius:50%;background:rgba(48,49,53,.82);box-shadow:0 8px 24px rgba(0,0,0,.28);color:#fff;cursor:pointer;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.dcsiv-close-floating:hover{background:rgba(66,67,72,.92)}
|
|
267
|
+
.dcsiv-workspace{position:absolute;inset:0;display:grid;min-width:0;min-height:0;padding:68px 24px 72px;box-sizing:border-box}
|
|
268
|
+
.dcsiv-stage{position:relative;display:grid;place-items:center;min-width:0;min-height:0;overflow:hidden;padding:0;touch-action:none;user-select:none}.dcsiv-stage[data-dragging=true]{cursor:grabbing}.dcsiv-stage[data-annotating=true]{cursor:crosshair}
|
|
269
|
+
.dcsiv-surface{position:relative;display:inline-flex;max-width:100%;max-height:100%;transform-origin:center;will-change:transform}.dcsiv-image{display:block;max-width:calc(100vw - 72px);max-height:calc(100vh - 154px);border-radius:12px;object-fit:contain;box-shadow:0 22px 60px rgba(0,0,0,.46);user-select:none;-webkit-user-drag:none}
|
|
270
|
+
.dcsiv-annotation{position:absolute;z-index:5;width:24px;height:24px;transform-origin:center;pointer-events:none}.dcsiv-pin{position:absolute;inset:0;display:grid;place-items:center;width:24px;height:24px;padding:0;border:2px solid #fff;border-radius:50%;background:rgba(23,24,27,.94);box-shadow:0 4px 16px rgba(0,0,0,.35);color:#fff;font:inherit;font-size:11px;font-weight:700;cursor:pointer;pointer-events:auto}.dcsiv-pin[data-active=true]{background:var(--dsw-alias-state-business-primary,#3964fe)}
|
|
271
|
+
.dcsiv-inline-note{position:absolute;bottom:34px;box-sizing:border-box;display:grid;width:min(300px,calc(100vw - 40px));grid-template-columns:24px minmax(0,1fr) 24px;align-items:center;gap:7px;padding:7px 8px;border:1px solid rgba(255,255,255,.12);border-radius:18px;background:rgba(32,33,37,.94);box-shadow:0 14px 38px rgba(0,0,0,.38);color:#fff;pointer-events:auto;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}.dcsiv-annotation[data-x=right] .dcsiv-inline-note{left:-8px}.dcsiv-annotation[data-x=left] .dcsiv-inline-note{right:-8px}.dcsiv-annotation[data-x=center] .dcsiv-inline-note{left:50%;transform:translateX(-50%)}.dcsiv-annotation[data-y=down] .dcsiv-inline-note{top:34px;bottom:auto}.dcsiv-inline-note::after{position:absolute;width:9px;height:9px;background:rgba(32,33,37,.94);content:'';transform:rotate(45deg)}.dcsiv-annotation[data-y=up] .dcsiv-inline-note::after{bottom:-5px}.dcsiv-annotation[data-y=down] .dcsiv-inline-note::after{top:-5px}.dcsiv-annotation[data-x=right] .dcsiv-inline-note::after{left:13px}.dcsiv-annotation[data-x=left] .dcsiv-inline-note::after{right:13px}.dcsiv-annotation[data-x=center] .dcsiv-inline-note::after{left:calc(50% - 4px)}.dcsiv-inline-index{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--dsw-alias-state-business-primary,#3964fe);color:#fff;font-size:10px;font-weight:700}.dcsiv-inline-note textarea{box-sizing:border-box;width:100%;min-height:24px;max-height:92px;resize:none;overflow:auto;border:0;outline:0;background:transparent;color:#fff;font:inherit;font-size:12px;line-height:18px}.dcsiv-inline-note textarea::placeholder{color:rgba(255,255,255,.44)}.dcsiv-note-remove{display:grid;place-items:center;width:24px;height:24px;padding:0;border:0;border-radius:50%;background:transparent;color:rgba(255,255,255,.68);cursor:pointer}.dcsiv-note-remove:hover{background:rgba(255,255,255,.1);color:#fff}
|
|
272
|
+
.dcsiv-nav{position:absolute;top:50%;z-index:4;width:42px;height:42px;padding:0;transform:translateY(-50%);background:rgba(48,49,53,.82);box-shadow:0 8px 24px rgba(0,0,0,.28);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.dcsiv-prev{left:6px}.dcsiv-next{right:6px}.dcsiv-counter{position:absolute;bottom:8px;left:50%;padding:5px 10px;border-radius:999px;background:rgba(38,39,43,.86);color:rgba(255,255,255,.72);font-size:11px;transform:translateX(-50%);backdrop-filter:blur(16px)}
|
|
273
|
+
.dcsiv-hint{display:none}
|
|
274
|
+
.dcsiv-copy-notes{position:absolute;right:20px;bottom:22px;z-index:6;display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 11px;border:1px solid rgba(255,255,255,.1);border-radius:999px;background:rgba(38,39,43,.86);color:rgba(255,255,255,.82);font:inherit;font-size:12px;cursor:pointer;backdrop-filter:blur(16px)}
|
|
275
|
+
@media(max-width:760px){.dcsiv-close-floating{top:12px;right:12px;width:40px;height:40px}.dcsiv-workspace{padding:60px 12px 68px}.dcsiv-image{max-width:calc(100vw - 24px);max-height:calc(100vh - 136px)}.dcsiv-topbar{bottom:12px;max-width:calc(100vw - 24px)}.dcsiv-button{padding:0 8px}.dcsiv-button span.dcsiv-label{display:none}.dcsiv-inline-note{width:min(260px,calc(100vw - 40px))}.dcsiv-copy-notes{display:none}}
|
|
276
|
+
@media(prefers-reduced-motion:reduce){.dcsiv-surface{transition:none}}
|
|
277
|
+
`;
|
|
278
|
+
//#endregion
|
|
279
|
+
//#region src/subscription-image-viewer.jsx
|
|
280
|
+
const fill$1 = (value, variables) => Object.entries(variables).reduce((text, [key, replacement]) => text.replaceAll(`{${key}}`, String(replacement)), value);
|
|
281
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
282
|
+
const bytesLabel = (bytes) => bytes === void 0 ? void 0 : bytes < 1024 * 1024 ? `${Math.max(.1, bytes / 1024).toLocaleString(void 0, { maximumFractionDigits: 1 })} KB` : `${(bytes / 1024 / 1024).toLocaleString(void 0, { maximumFractionDigits: 1 })} MB`;
|
|
283
|
+
const downloadName = (name) => {
|
|
284
|
+
const cleaned = String(name || "image.png").replace(/[<>:"/\\|?*\u0000-\u001f]/gu, "-").replace(/[. ]+$/u, "").trim();
|
|
285
|
+
return cleaned === "" ? "image.png" : cleaned;
|
|
286
|
+
};
|
|
287
|
+
const noteText = (annotations, t) => annotations.map((annotation, index) => {
|
|
288
|
+
return `${fill$1(t("imageAnnotation"), { value: index + 1 })} (${Math.round(annotation.x * 100)}%, ${Math.round(annotation.y * 100)}%): ${annotation.note.trim()}`;
|
|
289
|
+
}).filter((line) => !line.endsWith(": ")).join("\n");
|
|
290
|
+
function ViewerAction({ action, annotations, item, service, t }) {
|
|
291
|
+
const [state, setState] = (0, react.useState)("idle");
|
|
292
|
+
const invoke = async () => {
|
|
293
|
+
if (state === "pending") return;
|
|
294
|
+
setState("pending");
|
|
295
|
+
try {
|
|
296
|
+
await action.onInvoke({
|
|
297
|
+
annotations,
|
|
298
|
+
item,
|
|
299
|
+
src: item.src
|
|
300
|
+
});
|
|
301
|
+
setState("idle");
|
|
302
|
+
if (action.closeOnSuccess) service.close();
|
|
303
|
+
} catch {
|
|
304
|
+
setState("failed");
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
const label = state === "pending" ? action.pendingLabel : state === "failed" ? action.errorLabel : action.label;
|
|
308
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
309
|
+
type: "button",
|
|
310
|
+
className: "dcsiv-button",
|
|
311
|
+
disabled: state === "pending",
|
|
312
|
+
onClick: () => {
|
|
313
|
+
invoke();
|
|
314
|
+
},
|
|
315
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
316
|
+
className: "dcsiv-label",
|
|
317
|
+
children: label ?? t("imageEdit")
|
|
318
|
+
})
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
function ViewerDownload({ download, item, t }) {
|
|
322
|
+
const [state, setState] = (0, react.useState)("idle");
|
|
323
|
+
const invoke = async () => {
|
|
324
|
+
if (state === "pending") return;
|
|
325
|
+
setState("pending");
|
|
326
|
+
try {
|
|
327
|
+
await download.onInvoke({
|
|
328
|
+
item,
|
|
329
|
+
src: item.src
|
|
330
|
+
});
|
|
331
|
+
setState("idle");
|
|
332
|
+
} catch {
|
|
333
|
+
setState("failed");
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
const label = state === "pending" ? download.pendingLabel ?? t("imageDownloadPreparing") : state === "failed" ? download.errorLabel ?? t("imageDownloadFailed") : t("imageDownload");
|
|
337
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
338
|
+
type: "button",
|
|
339
|
+
className: "dcsiv-download",
|
|
340
|
+
disabled: state === "pending",
|
|
341
|
+
onClick: () => {
|
|
342
|
+
invoke();
|
|
343
|
+
},
|
|
344
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
345
|
+
className: "dcsiv-label",
|
|
346
|
+
children: label
|
|
347
|
+
})]
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function SubscriptionImageViewerOverlay({ service, t }) {
|
|
351
|
+
const request = (0, react.useSyncExternalStore)(service.subscribe, service.getSnapshot);
|
|
352
|
+
const [index, setIndex] = (0, react.useState)(0);
|
|
353
|
+
const [transform, setTransform] = (0, react.useState)({
|
|
354
|
+
zoom: 1,
|
|
355
|
+
x: 0,
|
|
356
|
+
y: 0
|
|
357
|
+
});
|
|
358
|
+
const [dragging, setDragging] = (0, react.useState)(false);
|
|
359
|
+
const [annotating, setAnnotating] = (0, react.useState)(false);
|
|
360
|
+
const [annotationsByImage, setAnnotationsByImage] = (0, react.useState)(service.getAnnotationsSnapshot);
|
|
361
|
+
const annotationsByImageRef = (0, react.useRef)(annotationsByImage);
|
|
362
|
+
const [selected, setSelected] = (0, react.useState)();
|
|
363
|
+
const [focusNote, setFocusNote] = (0, react.useState)();
|
|
364
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
365
|
+
const rootRef = (0, react.useRef)(null);
|
|
366
|
+
const stageRef = (0, react.useRef)(null);
|
|
367
|
+
const surfaceRef = (0, react.useRef)(null);
|
|
368
|
+
const imageRef = (0, react.useRef)(null);
|
|
369
|
+
const pointersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
370
|
+
const gestureRef = (0, react.useRef)();
|
|
371
|
+
const transformRef = (0, react.useRef)(transform);
|
|
372
|
+
transformRef.current = transform;
|
|
373
|
+
annotationsByImageRef.current = annotationsByImage;
|
|
374
|
+
(0, react.useEffect)(() => {
|
|
375
|
+
if (request === void 0) return;
|
|
376
|
+
setIndex(request.index);
|
|
377
|
+
setTransform({
|
|
378
|
+
zoom: 1,
|
|
379
|
+
x: 0,
|
|
380
|
+
y: 0
|
|
381
|
+
});
|
|
382
|
+
setDragging(false);
|
|
383
|
+
setAnnotating(false);
|
|
384
|
+
setSelected(void 0);
|
|
385
|
+
setCopied(false);
|
|
386
|
+
}, [request?.revision]);
|
|
387
|
+
const item = request?.items[index];
|
|
388
|
+
const annotations = item === void 0 ? [] : annotationsByImage[item.id] ?? [];
|
|
389
|
+
const setAnnotations = (0, react.useCallback)((update) => {
|
|
390
|
+
if (item === void 0) return;
|
|
391
|
+
const previous = annotationsByImageRef.current[item.id] ?? [];
|
|
392
|
+
const next = typeof update === "function" ? update(previous) : update;
|
|
393
|
+
const snapshot = {
|
|
394
|
+
...annotationsByImageRef.current,
|
|
395
|
+
[item.id]: next
|
|
396
|
+
};
|
|
397
|
+
annotationsByImageRef.current = snapshot;
|
|
398
|
+
service.setAnnotations(item.id, next);
|
|
399
|
+
setAnnotationsByImage(snapshot);
|
|
400
|
+
}, [item?.id, service]);
|
|
401
|
+
const boundedPan = (0, react.useCallback)((zoom, x, y) => {
|
|
402
|
+
const stage = stageRef.current;
|
|
403
|
+
const surface = surfaceRef.current;
|
|
404
|
+
if (stage === null || surface === null || zoom <= 1) return {
|
|
405
|
+
x: 0,
|
|
406
|
+
y: 0
|
|
407
|
+
};
|
|
408
|
+
const limitX = Math.max(0, (surface.offsetWidth * zoom - stage.clientWidth) / 2) + 28;
|
|
409
|
+
const limitY = Math.max(0, (surface.offsetHeight * zoom - stage.clientHeight) / 2) + 28;
|
|
410
|
+
return {
|
|
411
|
+
x: clamp(x, -limitX, limitX),
|
|
412
|
+
y: clamp(y, -limitY, limitY)
|
|
413
|
+
};
|
|
414
|
+
}, []);
|
|
415
|
+
const setZoomAt = (0, react.useCallback)((nextZoom, clientX, clientY) => {
|
|
416
|
+
const stage = stageRef.current;
|
|
417
|
+
if (stage === null) return;
|
|
418
|
+
setTransform((current) => {
|
|
419
|
+
const next = clamp(nextZoom, .5, 8);
|
|
420
|
+
const box = stage.getBoundingClientRect();
|
|
421
|
+
const px = clientX - box.left - box.width / 2;
|
|
422
|
+
const py = clientY - box.top - box.height / 2;
|
|
423
|
+
const ratio = next / current.zoom;
|
|
424
|
+
return {
|
|
425
|
+
zoom: next,
|
|
426
|
+
...boundedPan(next, px - (px - current.x) * ratio, py - (py - current.y) * ratio)
|
|
427
|
+
};
|
|
428
|
+
});
|
|
429
|
+
}, [boundedPan]);
|
|
430
|
+
const fit = (0, react.useCallback)(() => {
|
|
431
|
+
setTransform({
|
|
432
|
+
zoom: 1,
|
|
433
|
+
x: 0,
|
|
434
|
+
y: 0
|
|
435
|
+
});
|
|
436
|
+
}, []);
|
|
437
|
+
const actual = (0, react.useCallback)(() => {
|
|
438
|
+
const image = imageRef.current;
|
|
439
|
+
const surface = surfaceRef.current;
|
|
440
|
+
if (image === null || surface === null || image.naturalWidth === 0) return;
|
|
441
|
+
const zoom = clamp(image.naturalWidth / Math.max(1, surface.offsetWidth), 1, 8);
|
|
442
|
+
setTransform({
|
|
443
|
+
zoom,
|
|
444
|
+
x: 0,
|
|
445
|
+
y: 0
|
|
446
|
+
});
|
|
447
|
+
}, []);
|
|
448
|
+
(0, react.useEffect)(() => {
|
|
449
|
+
if (request === void 0) return void 0;
|
|
450
|
+
const previousOverflow = document.body.style.overflow;
|
|
451
|
+
document.body.style.overflow = "hidden";
|
|
452
|
+
rootRef.current?.focus();
|
|
453
|
+
const onKeyDown = (event) => {
|
|
454
|
+
if (event.key === "Escape") {
|
|
455
|
+
event.preventDefault();
|
|
456
|
+
if (event.target instanceof Element && event.target.closest(".dcsiv-inline-note") !== null) {
|
|
457
|
+
setSelected(void 0);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
service.close();
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const editing = event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement;
|
|
464
|
+
if (!editing && event.key === "ArrowLeft" && request.items.length > 1) {
|
|
465
|
+
event.preventDefault();
|
|
466
|
+
setIndex((value) => (value - 1 + request.items.length) % request.items.length);
|
|
467
|
+
} else if (!editing && event.key === "ArrowRight" && request.items.length > 1) {
|
|
468
|
+
event.preventDefault();
|
|
469
|
+
setIndex((value) => (value + 1) % request.items.length);
|
|
470
|
+
} else if (!editing && (event.key === "+" || event.key === "=")) {
|
|
471
|
+
event.preventDefault();
|
|
472
|
+
const box = stageRef.current?.getBoundingClientRect();
|
|
473
|
+
if (box) setZoomAt(transformRef.current.zoom * 1.2, box.left + box.width / 2, box.top + box.height / 2);
|
|
474
|
+
} else if (!editing && event.key === "-") {
|
|
475
|
+
event.preventDefault();
|
|
476
|
+
const box = stageRef.current?.getBoundingClientRect();
|
|
477
|
+
if (box) setZoomAt(transformRef.current.zoom / 1.2, box.left + box.width / 2, box.top + box.height / 2);
|
|
478
|
+
} else if (!editing && event.key.toLowerCase() === "f") {
|
|
479
|
+
event.preventDefault();
|
|
480
|
+
fit();
|
|
481
|
+
} else if (event.key === "Tab") {
|
|
482
|
+
const controls = [...rootRef.current.querySelectorAll("button:not(:disabled),a[href],input:not(:disabled),textarea:not(:disabled),[tabindex]:not([tabindex=\"-1\"])")];
|
|
483
|
+
const first = controls[0];
|
|
484
|
+
const last = controls.at(-1);
|
|
485
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
486
|
+
event.preventDefault();
|
|
487
|
+
last?.focus();
|
|
488
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
489
|
+
event.preventDefault();
|
|
490
|
+
first?.focus();
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
document.addEventListener("keydown", onKeyDown);
|
|
495
|
+
return () => {
|
|
496
|
+
document.body.style.overflow = previousOverflow;
|
|
497
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
498
|
+
};
|
|
499
|
+
}, [
|
|
500
|
+
request,
|
|
501
|
+
service,
|
|
502
|
+
fit,
|
|
503
|
+
setZoomAt
|
|
504
|
+
]);
|
|
505
|
+
(0, react.useEffect)(() => {
|
|
506
|
+
if (focusNote === void 0) return;
|
|
507
|
+
(rootRef.current?.querySelector(`[data-note-id="${CSS.escape(focusNote)}"] textarea`))?.focus();
|
|
508
|
+
setFocusNote(void 0);
|
|
509
|
+
}, [
|
|
510
|
+
focusNote,
|
|
511
|
+
selected,
|
|
512
|
+
annotations.length
|
|
513
|
+
]);
|
|
514
|
+
(0, react.useEffect)(() => {
|
|
515
|
+
setTransform({
|
|
516
|
+
zoom: 1,
|
|
517
|
+
x: 0,
|
|
518
|
+
y: 0
|
|
519
|
+
});
|
|
520
|
+
setDragging(false);
|
|
521
|
+
setAnnotating(false);
|
|
522
|
+
setSelected(void 0);
|
|
523
|
+
}, [item?.id]);
|
|
524
|
+
const onWheel = (0, react.useCallback)((event) => {
|
|
525
|
+
event.preventDefault();
|
|
526
|
+
setZoomAt(transformRef.current.zoom * Math.exp(-event.deltaY * .0015), event.clientX, event.clientY);
|
|
527
|
+
}, [setZoomAt]);
|
|
528
|
+
(0, react.useEffect)(() => {
|
|
529
|
+
const stage = stageRef.current;
|
|
530
|
+
if (stage === null || request === void 0) return void 0;
|
|
531
|
+
stage.addEventListener("wheel", onWheel, { passive: false });
|
|
532
|
+
return () => stage.removeEventListener("wheel", onWheel);
|
|
533
|
+
}, [onWheel, request]);
|
|
534
|
+
const onPointerDown = (event) => {
|
|
535
|
+
const target = event.target;
|
|
536
|
+
if (event.button !== 0 || annotating || target instanceof Element && target.closest("button,textarea,input,a,select,[contenteditable=true]") !== null) return;
|
|
537
|
+
pointersRef.current.set(event.pointerId, {
|
|
538
|
+
x: event.clientX,
|
|
539
|
+
y: event.clientY
|
|
540
|
+
});
|
|
541
|
+
if (pointersRef.current.size === 2) {
|
|
542
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
543
|
+
const [a, b] = [...pointersRef.current.values()];
|
|
544
|
+
gestureRef.current = {
|
|
545
|
+
kind: "pinch",
|
|
546
|
+
distance: Math.hypot(a.x - b.x, a.y - b.y),
|
|
547
|
+
transform
|
|
548
|
+
};
|
|
549
|
+
} else if (!annotating && transform.zoom > 1) {
|
|
550
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
551
|
+
gestureRef.current = {
|
|
552
|
+
kind: "pan",
|
|
553
|
+
x: event.clientX,
|
|
554
|
+
y: event.clientY,
|
|
555
|
+
transform
|
|
556
|
+
};
|
|
557
|
+
setDragging(true);
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
const onPointerMove = (event) => {
|
|
561
|
+
if (!pointersRef.current.has(event.pointerId)) return;
|
|
562
|
+
pointersRef.current.set(event.pointerId, {
|
|
563
|
+
x: event.clientX,
|
|
564
|
+
y: event.clientY
|
|
565
|
+
});
|
|
566
|
+
const gesture = gestureRef.current;
|
|
567
|
+
if (gesture?.kind === "pinch" && pointersRef.current.size >= 2) {
|
|
568
|
+
const [a, b] = [...pointersRef.current.values()];
|
|
569
|
+
const distance = Math.max(1, Math.hypot(a.x - b.x, a.y - b.y));
|
|
570
|
+
setZoomAt(gesture.transform.zoom * distance / Math.max(1, gesture.distance), (a.x + b.x) / 2, (a.y + b.y) / 2);
|
|
571
|
+
} else if (gesture?.kind === "pan") {
|
|
572
|
+
const pan = boundedPan(gesture.transform.zoom, gesture.transform.x + event.clientX - gesture.x, gesture.transform.y + event.clientY - gesture.y);
|
|
573
|
+
setTransform({
|
|
574
|
+
zoom: gesture.transform.zoom,
|
|
575
|
+
...pan
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
const endPointer = (event) => {
|
|
580
|
+
pointersRef.current.delete(event.pointerId);
|
|
581
|
+
if (pointersRef.current.size === 0) {
|
|
582
|
+
gestureRef.current = void 0;
|
|
583
|
+
setDragging(false);
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
const addAnnotation = (event) => {
|
|
587
|
+
if (!annotating || event.target.closest(".dcsiv-annotation")) return;
|
|
588
|
+
const bounds = surfaceRef.current?.getBoundingClientRect();
|
|
589
|
+
if (bounds === void 0) return;
|
|
590
|
+
const annotation = {
|
|
591
|
+
id: crypto.randomUUID(),
|
|
592
|
+
x: clamp((event.clientX - bounds.left) / bounds.width, 0, 1),
|
|
593
|
+
y: clamp((event.clientY - bounds.top) / bounds.height, 0, 1),
|
|
594
|
+
note: ""
|
|
595
|
+
};
|
|
596
|
+
setAnnotations((current) => [...current, annotation]);
|
|
597
|
+
setAnnotating(false);
|
|
598
|
+
setSelected(annotation.id);
|
|
599
|
+
setFocusNote(annotation.id);
|
|
600
|
+
};
|
|
601
|
+
const copyNotes = async () => {
|
|
602
|
+
const text = noteText(annotations, t);
|
|
603
|
+
if (text === "" || typeof navigator?.clipboard?.writeText !== "function") return;
|
|
604
|
+
try {
|
|
605
|
+
await navigator.clipboard.writeText(text);
|
|
606
|
+
setCopied(true);
|
|
607
|
+
window.setTimeout(() => {
|
|
608
|
+
setCopied(false);
|
|
609
|
+
}, 1200);
|
|
610
|
+
} catch {
|
|
611
|
+
setCopied(false);
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
if (request === void 0 || item === void 0) return null;
|
|
615
|
+
const meta = [item.width && item.height ? `${item.width} × ${item.height}` : void 0, bytesLabel(item.bytes)].filter(Boolean).join(" · ");
|
|
616
|
+
const showCounter = request.items.length > 1;
|
|
617
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
618
|
+
ref: rootRef,
|
|
619
|
+
className: "dcsiv-root",
|
|
620
|
+
role: "dialog",
|
|
621
|
+
"aria-modal": "true",
|
|
622
|
+
"aria-label": t("imagePreview"),
|
|
623
|
+
tabIndex: -1,
|
|
624
|
+
children: [
|
|
625
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
626
|
+
className: "dcsiv-title dcsiv-sr-only",
|
|
627
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: item.name }), meta !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: meta }) : null]
|
|
628
|
+
}),
|
|
629
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
630
|
+
className: "dcsiv-topbar",
|
|
631
|
+
role: "toolbar",
|
|
632
|
+
"aria-label": t("imagePreview"),
|
|
633
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
634
|
+
className: "dcsiv-actions",
|
|
635
|
+
children: [
|
|
636
|
+
request.annotations ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
637
|
+
type: "button",
|
|
638
|
+
className: "dcsiv-button",
|
|
639
|
+
"data-active": annotating,
|
|
640
|
+
"aria-label": annotating ? t("imageAnnotateCancel") : t("imageAnnotate"),
|
|
641
|
+
"aria-pressed": annotating,
|
|
642
|
+
onClick: () => setAnnotating((value) => !value),
|
|
643
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
644
|
+
className: "dcsiv-label",
|
|
645
|
+
children: annotating ? t("imageAnnotateCancel") : t("imageAnnotate")
|
|
646
|
+
})]
|
|
647
|
+
}) : null,
|
|
648
|
+
annotations.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
649
|
+
type: "button",
|
|
650
|
+
className: "dcsiv-button",
|
|
651
|
+
"data-active": selected !== void 0,
|
|
652
|
+
onClick: () => {
|
|
653
|
+
const first = annotations[0];
|
|
654
|
+
setSelected((current) => current === void 0 ? first.id : void 0);
|
|
655
|
+
if (selected === void 0) setFocusNote(first.id);
|
|
656
|
+
},
|
|
657
|
+
children: [
|
|
658
|
+
annotations.length,
|
|
659
|
+
" ",
|
|
660
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
661
|
+
className: "dcsiv-label",
|
|
662
|
+
children: t("imageRegions")
|
|
663
|
+
})
|
|
664
|
+
]
|
|
665
|
+
}) : null,
|
|
666
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
667
|
+
type: "button",
|
|
668
|
+
className: "dcsiv-button",
|
|
669
|
+
"aria-label": t("imageFit"),
|
|
670
|
+
onClick: fit,
|
|
671
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
672
|
+
className: "dcsiv-label",
|
|
673
|
+
children: t("imageFit")
|
|
674
|
+
})]
|
|
675
|
+
}),
|
|
676
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
677
|
+
type: "button",
|
|
678
|
+
className: "dcsiv-button",
|
|
679
|
+
onClick: actual,
|
|
680
|
+
children: t("imageActual")
|
|
681
|
+
}),
|
|
682
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
683
|
+
className: "dcsiv-zoom",
|
|
684
|
+
children: [Math.round(transform.zoom * 100), "%"]
|
|
685
|
+
}),
|
|
686
|
+
item.download === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
687
|
+
className: "dcsiv-download",
|
|
688
|
+
href: item.src,
|
|
689
|
+
download: downloadName(item.name),
|
|
690
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
691
|
+
className: "dcsiv-label",
|
|
692
|
+
children: t("imageDownload")
|
|
693
|
+
})]
|
|
694
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewerDownload, {
|
|
695
|
+
download: item.download,
|
|
696
|
+
item,
|
|
697
|
+
t
|
|
698
|
+
}),
|
|
699
|
+
item.actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewerAction, {
|
|
700
|
+
action,
|
|
701
|
+
annotations,
|
|
702
|
+
item,
|
|
703
|
+
service,
|
|
704
|
+
t
|
|
705
|
+
}, action.id))
|
|
706
|
+
]
|
|
707
|
+
})
|
|
708
|
+
}),
|
|
709
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
710
|
+
type: "button",
|
|
711
|
+
className: "dcsiv-close-floating",
|
|
712
|
+
"aria-label": t("imageClosePreview"),
|
|
713
|
+
onClick: () => service.close(),
|
|
714
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, {})
|
|
715
|
+
}),
|
|
716
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
717
|
+
className: "dcsiv-workspace",
|
|
718
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
719
|
+
ref: stageRef,
|
|
720
|
+
className: "dcsiv-stage",
|
|
721
|
+
"data-dragging": dragging,
|
|
722
|
+
"data-annotating": annotating,
|
|
723
|
+
onClick: (event) => {
|
|
724
|
+
if (event.target === event.currentTarget && !annotating && transform.zoom === 1) service.close();
|
|
725
|
+
},
|
|
726
|
+
onPointerDown,
|
|
727
|
+
onPointerMove,
|
|
728
|
+
onPointerUp: endPointer,
|
|
729
|
+
onPointerCancel: endPointer,
|
|
730
|
+
onDoubleClick: () => {
|
|
731
|
+
if (transform.zoom === 1) actual();
|
|
732
|
+
else fit();
|
|
733
|
+
},
|
|
734
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
735
|
+
ref: surfaceRef,
|
|
736
|
+
className: "dcsiv-surface",
|
|
737
|
+
onClick: addAnnotation,
|
|
738
|
+
style: { transform: `translate3d(${transform.x}px,${transform.y}px,0) scale(${transform.zoom})` },
|
|
739
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
740
|
+
ref: imageRef,
|
|
741
|
+
className: "dcsiv-image",
|
|
742
|
+
src: item.src,
|
|
743
|
+
alt: item.name,
|
|
744
|
+
draggable: "false"
|
|
745
|
+
}), annotations.map((annotation, position) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
746
|
+
className: "dcsiv-annotation",
|
|
747
|
+
"data-x": annotation.x < .38 ? "right" : annotation.x > .62 ? "left" : "center",
|
|
748
|
+
"data-y": annotation.y < .28 ? "down" : "up",
|
|
749
|
+
style: {
|
|
750
|
+
left: `${annotation.x * 100}%`,
|
|
751
|
+
top: `${annotation.y * 100}%`,
|
|
752
|
+
transform: `translate(-50%,-50%) scale(${1 / transform.zoom})`
|
|
753
|
+
},
|
|
754
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
755
|
+
type: "button",
|
|
756
|
+
className: "dcsiv-pin",
|
|
757
|
+
"data-active": selected === annotation.id,
|
|
758
|
+
"aria-label": fill$1(t("imageAnnotation"), { value: position + 1 }),
|
|
759
|
+
onClick: (event) => {
|
|
760
|
+
event.stopPropagation();
|
|
761
|
+
const opening = selected !== annotation.id;
|
|
762
|
+
setSelected(opening ? annotation.id : void 0);
|
|
763
|
+
if (opening) setFocusNote(annotation.id);
|
|
764
|
+
},
|
|
765
|
+
children: position + 1
|
|
766
|
+
}), selected === annotation.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
767
|
+
className: "dcsiv-inline-note",
|
|
768
|
+
"data-note-id": annotation.id,
|
|
769
|
+
onClick: (event) => event.stopPropagation(),
|
|
770
|
+
children: [
|
|
771
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
772
|
+
className: "dcsiv-inline-index",
|
|
773
|
+
children: position + 1
|
|
774
|
+
}),
|
|
775
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
776
|
+
value: annotation.note,
|
|
777
|
+
rows: 1,
|
|
778
|
+
"aria-label": fill$1(t("imageAnnotation"), { value: position + 1 }),
|
|
779
|
+
placeholder: t("imageAnnotationPlaceholder"),
|
|
780
|
+
onChange: (event) => {
|
|
781
|
+
const note = event.target.value;
|
|
782
|
+
setAnnotations((current) => current.map((entry) => entry.id === annotation.id ? {
|
|
783
|
+
...entry,
|
|
784
|
+
note
|
|
785
|
+
} : entry));
|
|
786
|
+
},
|
|
787
|
+
onKeyDown: (event) => {
|
|
788
|
+
if (event.key === "Enter" && !event.shiftKey || event.key === "Escape") {
|
|
789
|
+
event.preventDefault();
|
|
790
|
+
event.stopPropagation();
|
|
791
|
+
event.nativeEvent?.stopImmediatePropagation?.();
|
|
792
|
+
setSelected(void 0);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}),
|
|
796
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
797
|
+
type: "button",
|
|
798
|
+
className: "dcsiv-note-remove",
|
|
799
|
+
"aria-label": t("imageRemoveAnnotation"),
|
|
800
|
+
onClick: (event) => {
|
|
801
|
+
event.stopPropagation();
|
|
802
|
+
setAnnotations((current) => current.filter((entry) => entry.id !== annotation.id));
|
|
803
|
+
setSelected(void 0);
|
|
804
|
+
},
|
|
805
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, {})
|
|
806
|
+
})
|
|
807
|
+
]
|
|
808
|
+
}) : null]
|
|
809
|
+
}, annotation.id))]
|
|
810
|
+
}), showCounter ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
811
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
812
|
+
type: "button",
|
|
813
|
+
className: "dcsiv-button dcsiv-icon-only dcsiv-nav dcsiv-prev",
|
|
814
|
+
"aria-label": t("imagePrevious"),
|
|
815
|
+
onClick: (event) => {
|
|
816
|
+
event.stopPropagation();
|
|
817
|
+
setIndex((value) => (value - 1 + request.items.length) % request.items.length);
|
|
818
|
+
},
|
|
819
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronLeftOutline14, {})
|
|
820
|
+
}),
|
|
821
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
822
|
+
type: "button",
|
|
823
|
+
className: "dcsiv-button dcsiv-icon-only dcsiv-nav dcsiv-next",
|
|
824
|
+
"aria-label": t("imageNext"),
|
|
825
|
+
onClick: (event) => {
|
|
826
|
+
event.stopPropagation();
|
|
827
|
+
setIndex((value) => (value + 1) % request.items.length);
|
|
828
|
+
},
|
|
829
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
|
|
830
|
+
}),
|
|
831
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
832
|
+
className: "dcsiv-counter",
|
|
833
|
+
children: [
|
|
834
|
+
index + 1,
|
|
835
|
+
" / ",
|
|
836
|
+
request.items.length
|
|
837
|
+
]
|
|
838
|
+
})
|
|
839
|
+
] }) : annotating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
840
|
+
className: "dcsiv-hint",
|
|
841
|
+
children: t("imageAnnotateHint")
|
|
842
|
+
}) : transform.zoom === 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
843
|
+
className: "dcsiv-hint",
|
|
844
|
+
children: t("imageZoomHint")
|
|
845
|
+
}) : null]
|
|
846
|
+
}), annotations.some((annotation) => annotation.note.trim() !== "") ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
847
|
+
type: "button",
|
|
848
|
+
className: "dcsiv-copy-notes",
|
|
849
|
+
onClick: () => {
|
|
850
|
+
copyNotes();
|
|
851
|
+
},
|
|
852
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {}), copied ? t("imageCopied") : t("imageCopyNotes")]
|
|
853
|
+
}) : null]
|
|
854
|
+
})
|
|
855
|
+
]
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
//#endregion
|
|
859
|
+
//#region src/subscription-image-viewer.js
|
|
860
|
+
const boundedNumber = (value, fallback) => Number.isFinite(value) && value > 0 ? value : fallback;
|
|
861
|
+
const downloadOf = (value) => typeof value?.onInvoke === "function" ? {
|
|
862
|
+
pendingLabel: typeof value.pendingLabel === "string" && value.pendingLabel !== "" ? value.pendingLabel : void 0,
|
|
863
|
+
errorLabel: typeof value.errorLabel === "string" && value.errorLabel !== "" ? value.errorLabel : void 0,
|
|
864
|
+
onInvoke: value.onInvoke
|
|
865
|
+
} : void 0;
|
|
866
|
+
const actionsOf = (value) => Array.isArray(value) ? value.flatMap((action, position) => {
|
|
867
|
+
if (typeof action?.onInvoke !== "function" || typeof action?.label !== "string" || action.label.trim() === "") return [];
|
|
868
|
+
return [{
|
|
869
|
+
id: typeof action.id === "string" && action.id !== "" ? action.id : `action-${position + 1}`,
|
|
870
|
+
label: action.label,
|
|
871
|
+
pendingLabel: typeof action.pendingLabel === "string" && action.pendingLabel !== "" ? action.pendingLabel : action.label,
|
|
872
|
+
errorLabel: typeof action.errorLabel === "string" && action.errorLabel !== "" ? action.errorLabel : action.label,
|
|
873
|
+
closeOnSuccess: action.closeOnSuccess === true,
|
|
874
|
+
onInvoke: action.onInvoke
|
|
875
|
+
}];
|
|
876
|
+
}) : [];
|
|
877
|
+
function normalizeSubscriptionViewerRequest(request) {
|
|
878
|
+
const items = (Array.isArray(request?.items) ? request.items : []).flatMap((item, position) => {
|
|
879
|
+
if (typeof item?.src !== "string" || item.src === "") return [];
|
|
880
|
+
return [{
|
|
881
|
+
id: typeof item.id === "string" && item.id !== "" ? item.id : `image-${position + 1}`,
|
|
882
|
+
src: item.src,
|
|
883
|
+
name: typeof item.name === "string" && item.name !== "" ? item.name : `Image ${position + 1}`,
|
|
884
|
+
width: boundedNumber(item.width, void 0),
|
|
885
|
+
height: boundedNumber(item.height, void 0),
|
|
886
|
+
bytes: boundedNumber(item.bytes, void 0),
|
|
887
|
+
download: downloadOf(item.download),
|
|
888
|
+
actions: actionsOf(item.actions)
|
|
889
|
+
}];
|
|
890
|
+
});
|
|
891
|
+
if (items.length === 0) return void 0;
|
|
892
|
+
const requestedIndex = Number.isInteger(request?.index) ? request.index : 0;
|
|
893
|
+
return {
|
|
894
|
+
items,
|
|
895
|
+
index: Math.max(0, Math.min(items.length - 1, requestedIndex)),
|
|
896
|
+
opener: typeof HTMLElement !== "undefined" && request?.opener instanceof HTMLElement ? request.opener : void 0,
|
|
897
|
+
source: typeof request?.source === "string" ? request.source : "dsh-codex-subscription",
|
|
898
|
+
annotations: request?.annotations !== false
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
const copyAnnotations = (annotations) => annotations.map((annotation) => ({ ...annotation }));
|
|
902
|
+
/**
|
|
903
|
+
* Local image viewer state for subscription-generated images.
|
|
904
|
+
*
|
|
905
|
+
* This stays private to subscription image cards, which need annotation and
|
|
906
|
+
* edit actions that a host's generic native viewer may not implement.
|
|
907
|
+
*/
|
|
908
|
+
var SubscriptionImageViewerService = class {
|
|
909
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
910
|
+
#revision = 0;
|
|
911
|
+
#snapshot;
|
|
912
|
+
#annotationsByImage = /* @__PURE__ */ new Map();
|
|
913
|
+
constructor() {
|
|
914
|
+
this.subscribe = (listener) => {
|
|
915
|
+
this.#listeners.add(listener);
|
|
916
|
+
return () => {
|
|
917
|
+
this.#listeners.delete(listener);
|
|
918
|
+
};
|
|
919
|
+
};
|
|
920
|
+
this.getSnapshot = () => this.#snapshot;
|
|
921
|
+
this.getAnnotationsSnapshot = () => Object.fromEntries([...this.#annotationsByImage].map(([id, annotations]) => [id, copyAnnotations(annotations)]));
|
|
922
|
+
}
|
|
923
|
+
setAnnotations(imageId, annotations) {
|
|
924
|
+
if (typeof imageId !== "string" || imageId === "" || !Array.isArray(annotations)) return;
|
|
925
|
+
if (annotations.length === 0) this.#annotationsByImage.delete(imageId);
|
|
926
|
+
else this.#annotationsByImage.set(imageId, copyAnnotations(annotations));
|
|
927
|
+
}
|
|
928
|
+
open(request) {
|
|
929
|
+
const normalized = normalizeSubscriptionViewerRequest(request);
|
|
930
|
+
if (normalized === void 0) return false;
|
|
931
|
+
this.#revision += 1;
|
|
932
|
+
this.#snapshot = {
|
|
933
|
+
...normalized,
|
|
934
|
+
revision: this.#revision
|
|
935
|
+
};
|
|
936
|
+
this.#emit();
|
|
937
|
+
return true;
|
|
938
|
+
}
|
|
939
|
+
close() {
|
|
940
|
+
if (this.#snapshot === void 0) return;
|
|
941
|
+
const opener = this.#snapshot.opener;
|
|
942
|
+
this.#snapshot = void 0;
|
|
943
|
+
this.#emit();
|
|
944
|
+
if (typeof window === "undefined") opener?.focus();
|
|
945
|
+
else {
|
|
946
|
+
const focus = () => {
|
|
947
|
+
opener?.focus();
|
|
948
|
+
};
|
|
949
|
+
if (typeof window.requestAnimationFrame === "function") window.requestAnimationFrame(focus);
|
|
950
|
+
else focus();
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
#emit() {
|
|
954
|
+
for (const listener of this.#listeners) listener();
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
//#endregion
|
|
82
958
|
//#region src/settings-contract.js
|
|
83
959
|
const SETTINGS_NAMESPACE = "codex-subscription";
|
|
84
960
|
const QUICK_QUOTA_MODE_FIELD = "quickQuotaMode";
|
|
@@ -167,16 +1043,13 @@ window.__ModuleLoader__.load({
|
|
|
167
1043
|
if (/\bspark\b/u.test(normalized(model))) return /\bspark\b/u.test(normalized(`${limit?.id ?? ""} ${limit?.name ?? ""}`));
|
|
168
1044
|
return limit?.id === "codex";
|
|
169
1045
|
};
|
|
170
|
-
function
|
|
171
|
-
|
|
172
|
-
if (windows.length === 0) return void 0;
|
|
173
|
-
const selected = windows.reduce((lowest, candidate) => candidate.remainingPercent < lowest.remainingPercent ? candidate : lowest);
|
|
174
|
-
return {
|
|
1046
|
+
function selectModelQuotaWindows(usage, model) {
|
|
1047
|
+
return (Array.isArray(usage?.rateLimits) ? usage.rateLimits.filter((limit) => limitMatchesModel(limit, model) && Array.isArray(limit.windows)).flatMap((limit) => limit.windows).filter(isDisplayableWindow) : []).map((selected) => ({
|
|
175
1048
|
remainingPercent: selected.remainingPercent,
|
|
176
1049
|
windowSeconds: selected.windowSeconds,
|
|
177
1050
|
...Number.isSafeInteger(selected.resetsAt) ? { resetsAt: selected.resetsAt } : {},
|
|
178
1051
|
...selected.forecast === void 0 ? {} : { forecast: selected.forecast }
|
|
179
|
-
};
|
|
1052
|
+
})).sort((a, b) => a.windowSeconds - b.windowSeconds);
|
|
180
1053
|
}
|
|
181
1054
|
//#endregion
|
|
182
1055
|
//#region src/login-progress.js
|
|
@@ -371,6 +1244,7 @@ window.__ModuleLoader__.load({
|
|
|
371
1244
|
"settingsScope",
|
|
372
1245
|
"modelDirectories",
|
|
373
1246
|
"conversation",
|
|
1247
|
+
"uiConversation",
|
|
374
1248
|
"sessions"
|
|
375
1249
|
];
|
|
376
1250
|
const NS = "settings.codexSubscription";
|
|
@@ -379,6 +1253,8 @@ window.__ModuleLoader__.load({
|
|
|
379
1253
|
const QUICK_QUOTA_REFRESH_EVENT = "dsh-codex-subscription:refresh-quick-quota";
|
|
380
1254
|
const QUICK_QUOTA_REFRESH_MS = 6e4;
|
|
381
1255
|
const zh = {
|
|
1256
|
+
imageEditLocation: "位置",
|
|
1257
|
+
imageEditReferenceGuide: "本次编辑的干净源图为「{sourceName}」,编号定位参考图为「{referenceName}」。坐标以图片左上角为原点,x 向右、y 向下,百分比相对于整张图片。请查看这两张图片,将它们同时作为编辑工具的参考图,并在工具提示词中完整保留下方编号、位置和修改要求。只修改源图中对应位置的内容;定位参考图上的编号、圆点和引线仅用于定位,不得绘入最终结果。若无法读取两张图片或确定位置,请说明问题,不要猜测或忽略标注。",
|
|
382
1258
|
nav: "Codex 订阅",
|
|
383
1259
|
title: "Codex 订阅",
|
|
384
1260
|
connected: "已登录",
|
|
@@ -552,18 +1428,28 @@ window.__ModuleLoader__.load({
|
|
|
552
1428
|
imageZoomIn: "放大",
|
|
553
1429
|
imageFit: "适合窗口",
|
|
554
1430
|
imageAnnotate: "标注部位",
|
|
1431
|
+
imageAnnotateCancel: "取消标注",
|
|
555
1432
|
imageAnnotateHint: "点击图片添加编号标注",
|
|
556
1433
|
imageAnnotation: "标注 {value}",
|
|
557
1434
|
imageAnnotationPlaceholder: "描述这个部位要修改什么",
|
|
1435
|
+
imageRegions: "区域备注",
|
|
1436
|
+
imageCopyNotes: "复制备注",
|
|
1437
|
+
imageCopied: "已复制",
|
|
1438
|
+
imagePrevious: "上一张图片",
|
|
1439
|
+
imageNext: "下一张图片",
|
|
1440
|
+
imageZoomHint: "滚轮缩放 · 拖动查看 · 双击切换原始大小",
|
|
1441
|
+
imageActual: "原始大小",
|
|
558
1442
|
imageEditPrompt: "描述你想怎样修改这张图",
|
|
559
1443
|
imageEditDefault: "编辑这张图片。",
|
|
560
1444
|
imageRegionNotes: "部位修改:",
|
|
561
1445
|
imageEdit: "在输入框中继续编辑",
|
|
562
1446
|
imageEditPreparing: "正在添加到输入框…",
|
|
563
|
-
imageEditFailed: "
|
|
1447
|
+
imageEditFailed: "回填失败:请填写每个标记的备注,并确认输入框可接收图片后重试。",
|
|
564
1448
|
imageRemoveAnnotation: "删除标注"
|
|
565
1449
|
};
|
|
566
1450
|
const en = {
|
|
1451
|
+
imageEditLocation: "Location",
|
|
1452
|
+
imageEditReferenceGuide: "The clean source for this edit is \"{sourceName}\"; \"{referenceName}\" is the numbered location reference. Coordinates start at the top-left, x increases rightward and y downward; percentages refer to the whole image. Inspect both images and pass both as references to the image-editing tool. Preserve every number, position and requested change below in the tool prompt. Edit the corresponding content in the clean source; numbers, dots and leader lines on the location reference are guidance only and must not appear in the final result. If either image cannot be read or a location is unclear, explain the problem instead of guessing or ignoring annotations.",
|
|
567
1453
|
nav: "Codex",
|
|
568
1454
|
title: "Codex subscription",
|
|
569
1455
|
connected: "Signed in",
|
|
@@ -737,15 +1623,23 @@ window.__ModuleLoader__.load({
|
|
|
737
1623
|
imageZoomIn: "Zoom in",
|
|
738
1624
|
imageFit: "Fit to window",
|
|
739
1625
|
imageAnnotate: "Annotate",
|
|
1626
|
+
imageAnnotateCancel: "Cancel marking",
|
|
740
1627
|
imageAnnotateHint: "Click the image to add a numbered note",
|
|
741
1628
|
imageAnnotation: "Note {value}",
|
|
742
1629
|
imageAnnotationPlaceholder: "Describe what should change in this area",
|
|
1630
|
+
imageRegions: "Region notes",
|
|
1631
|
+
imageCopyNotes: "Copy notes",
|
|
1632
|
+
imageCopied: "Copied",
|
|
1633
|
+
imagePrevious: "Previous image",
|
|
1634
|
+
imageNext: "Next image",
|
|
1635
|
+
imageZoomHint: "Wheel to zoom · drag to pan · double-click for 100%",
|
|
1636
|
+
imageActual: "100%",
|
|
743
1637
|
imageEditPrompt: "Describe how you want to change this image",
|
|
744
1638
|
imageEditDefault: "Edit this image.",
|
|
745
1639
|
imageRegionNotes: "Region changes:",
|
|
746
1640
|
imageEdit: "Continue editing in composer",
|
|
747
1641
|
imageEditPreparing: "Adding to composer…",
|
|
748
|
-
imageEditFailed: "
|
|
1642
|
+
imageEditFailed: "Handoff failed. Add a note to every marker and ensure the composer accepts images, then retry.",
|
|
749
1643
|
imageRemoveAnnotation: "Remove note"
|
|
750
1644
|
};
|
|
751
1645
|
const STYLE = `
|
|
@@ -782,7 +1676,7 @@ window.__ModuleLoader__.load({
|
|
|
782
1676
|
.codexSubscriptionCreditRows{display:flex;flex-direction:column;gap:6px}.codexSubscriptionResetMeta{display:flex;min-width:0;flex-direction:column;gap:1px}.codexSubscriptionResetBalance{display:flex;flex-direction:column;gap:8px}.codexSubscriptionResetCard{display:flex;align-items:center;justify-content:space-between;gap:10px;min-width:0;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-bg-module-platform)}.codexSubscriptionResetCard .codexSubscriptionResetMeta{flex:1}.codexSubscriptionResetCard strong{overflow:hidden;font-size:12px;line-height:18px;font-weight:500;text-overflow:ellipsis;white-space:nowrap}.codexSubscriptionResetCard .codexSubscriptionActions{flex:0 0 auto}.codexSubscriptionResetCard .codexSubscriptionResetUse{min-height:28px;padding:0 10px}.codexSubscriptionResetBalance .codexSubscriptionActions{justify-content:flex-start}.codexSubscriptionResetFlow{display:flex;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:10px}.codexSubscriptionResetFlow h4{margin:0;font-size:13px;line-height:20px;font-weight:500}.codexSubscriptionResetWarning{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionResetExpiry{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionResetCheck{display:flex;align-items:flex-start;gap:8px;padding:9px 10px;border-radius:8px;background:var(--dsw-alias-bg-module-platform);font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary);cursor:pointer}.codexSubscriptionResetCheck input{margin:3px 0 0;accent-color:var(--dsw-alias-label-primary)}.codexSubscriptionResetFinal{border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}.codexSubscriptionResetResult{font-size:12px;line-height:18px;color:var(--dsw-alias-state-success-primary)}
|
|
783
1677
|
.codexSubscriptionResetUse:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.codexSubscriptionResetUse:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}
|
|
784
1678
|
.codexSubscriptionSpendLimit{display:flex;flex-direction:column;gap:8px}.codexSubscriptionSpendTop{display:flex;align-items:baseline;justify-content:space-between;gap:12px}.codexSubscriptionSpendTop strong{font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums}.codexSubscriptionSpendLimit progress{width:100%;height:6px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-brand-primary,#3964fe);-webkit-appearance:none;appearance:none}.codexSubscriptionSpendLimit progress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexSubscriptionSpendLimit progress::-webkit-progress-value{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionSpendLimit progress::-moz-progress-bar{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}
|
|
785
|
-
.codexComposerQuota{display:inline-flex;align-items:center;flex:0 0 auto;height:28px;box-sizing:border-box;padding:0;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12px;line-height:20px;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap;user-select:none}.codexComposerQuotaBar{display:block;width:40px;height:4px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-label-secondary);-webkit-appearance:none;appearance:none}.codexComposerQuotaBar::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexComposerQuotaBar::-webkit-progress-value{background:var(--dsw-alias-label-secondary);border-radius:999px}.codexComposerQuotaBar::-moz-progress-bar{background:var(--dsw-alias-label-secondary);border-radius:999px}
|
|
1679
|
+
.codexComposerQuotaWindows{display:inline-flex;align-items:center;gap:10px;flex-wrap:wrap}.codexComposerQuota{display:inline-flex;align-items:center;gap:5px;flex:0 0 auto;height:28px;box-sizing:border-box;padding:0;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12px;line-height:20px;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap;user-select:none}.codexComposerQuotaBar{display:block;width:40px;height:4px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-label-secondary);-webkit-appearance:none;appearance:none}.codexComposerQuotaBar::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexComposerQuotaBar::-webkit-progress-value{background:var(--dsw-alias-label-secondary);border-radius:999px}.codexComposerQuotaBar::-moz-progress-bar{background:var(--dsw-alias-label-secondary);border-radius:999px}
|
|
786
1680
|
.codexModelSelect{position:relative;min-width:0}.codexModelSelectTrigger{display:flex;align-items:center;gap:4px;min-width:0;max-width:min(360px,45cqw);height:28px;padding:0 4px 0 8px;border:0;border-radius:24px;outline:0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:500;line-height:20px;cursor:pointer}.codexModelSelectTrigger:hover:not(:disabled),.codexModelSelectTrigger[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover)}.codexModelSelectTrigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.codexModelSelectTrigger:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectBolt{display:block;flex:none;width:14px;height:14px;color:var(--dsw-alias-label-primary)}.codexModelSelectLabel{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectEffort{flex:none;color:var(--dsw-alias-label-caption)}.codexModelSelectChevron{flex:none;color:var(--dsw-alias-label-caption);transition:transform 120ms}.codexModelSelectTrigger[aria-expanded=true] .codexModelSelectChevron{transform:rotate(180deg)}
|
|
787
1681
|
.codexModelSelectMenu,.codexModelSelectSubmenu{position:absolute;z-index:30;box-sizing:border-box;width:max-content;min-width:min(240px,calc(100vw - 32px));max-width:min(420px,calc(100vw - 32px));max-height:min(360px,calc(100vh - 96px));padding:4px;border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);overflow:hidden}.codexModelSelectMenu{right:0;bottom:calc(100% + 8px)}.codexModelSelectSubmenu{right:calc(100% + 8px);bottom:0;min-width:min(230px,calc(100vw - 32px))}.codexModelSelectCell{display:flex;align-items:center;gap:8px;width:100%;min-width:100%;height:40px;box-sizing:border-box;padding:0 10px;border:0;border-radius:10px;background:transparent;color:inherit;font-size:14px;line-height:22px;text-align:left;cursor:pointer}.codexModelSelectCell:hover,.codexModelSelectCell:focus-visible,.codexModelSelectCell[data-open=true]{background:var(--dsw-alias-interactive-bg-hover);outline:0}.codexModelSelectCell:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectCellLabel{flex:none;white-space:nowrap}.codexModelSelectCellValue{flex:auto;min-width:0;overflow:hidden;color:var(--dsw-alias-label-tertiary);text-align:right;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectCellChevron{flex:none;color:var(--dsw-alias-label-tertiary)}.codexModelSelectGroups{min-height:0;max-height:352px;overflow-y:auto}.codexModelSelectGroup+.codexModelSelectGroup{margin-top:4px}.codexModelSelectGroupTitle{position:sticky;top:0;z-index:1;padding:5px 8px 3px;background:var(--dsw-specific-menu);color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:500;line-height:18px}.codexModelSelectOption{display:flex;align-items:center;gap:8px;width:100%;min-width:100%;min-height:38px;box-sizing:border-box;padding:6px 8px;border:0;border-radius:10px;outline:0;background:transparent;color:inherit;text-align:left;cursor:pointer}.codexModelSelectOption:hover:not(:disabled),.codexModelSelectOption:focus-visible{background:var(--dsw-alias-interactive-bg-hover)}.codexModelSelectOption:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectOptionCopy{display:flex;flex:1;min-width:0;flex-direction:column}.codexModelSelectOptionName{overflow:hidden;color:inherit;font-size:14px;font-weight:500;line-height:20px;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectOptionDescription{overflow:hidden;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectCheck{display:grid;place-items:center;flex:0 0 18px;color:var(--dsw-alias-label-primary)}.codexModelSelectStatus,.codexModelSelectEmpty{padding:10px;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.codexModelSelectError,.codexModelSelectWarning{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:4px;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}.codexModelSelectWarning{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-state-warn-label)}.codexModelSelectRetry{flex:none;padding:0;border:0;background:transparent;color:inherit;font:inherit;font-weight:600;cursor:pointer}
|
|
788
1682
|
.codexModelSelectMenu{overflow:visible}
|
|
@@ -792,19 +1686,6 @@ window.__ModuleLoader__.load({
|
|
|
792
1686
|
@container (max-width:560px){.codexSubscriptionCreditRows{grid-template-columns:1fr}}
|
|
793
1687
|
@container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}.codexSubscriptionActions{width:100%}.codexSubscriptionSearchChoices{grid-template-columns:1fr}}
|
|
794
1688
|
@media(max-width:640px){.codexSubscriptionCard{padding:14px}}
|
|
795
|
-
`;
|
|
796
|
-
const IMAGE_STYLE = String.raw`
|
|
797
|
-
.codexGeneratedImageLightbox{position:fixed;inset:0;z-index:1000;display:grid;grid-template-rows:auto minmax(0,1fr);background:var(--dsw-alias-bg-base,#111);color:var(--dsw-alias-label-primary);outline:0}.codexGeneratedImageTopbar{display:flex;align-items:center;justify-content:space-between;gap:20px;min-height:58px;padding:8px 14px;border-bottom:1px solid var(--dsw-alias-border-l2);background:color-mix(in srgb,var(--dsw-alias-bg-base,#111) 92%,transparent);backdrop-filter:blur(18px)}.codexGeneratedImageTopbar>div:first-child{display:flex;min-width:0;flex-direction:column}.codexGeneratedImageTopbar strong{overflow:hidden;font-size:13px;font-weight:600;line-height:20px;text-overflow:ellipsis;white-space:nowrap}.codexGeneratedImageTopbar small{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:17px}.codexGeneratedImageActions{display:flex;align-items:center;gap:6px}.codexGeneratedImageActions button,.codexGeneratedImageActions a{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:5px;height:32px;padding:0 10px;border:1px solid transparent;border-radius:16px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;text-decoration:none;cursor:pointer}.codexGeneratedImageActions button:hover,.codexGeneratedImageActions a:hover,.codexGeneratedImageActions button.is-active{background:var(--dsw-alias-interactive-bg-hover)}.codexGeneratedImageActions button:focus-visible,.codexGeneratedImageActions a:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}.codexGeneratedImageActions button:disabled{opacity:.4;cursor:default}.codexGeneratedImageActions>span{min-width:38px;color:var(--dsw-alias-label-tertiary);font-size:11px;text-align:center}.codexGeneratedImageClose{font-size:20px!important}.codexGeneratedImageCanvas{position:relative;display:grid;place-items:center;min-height:0;overflow:auto;padding:24px;background:var(--dsw-alias-bg-base,#111)}.codexGeneratedImageSurface{position:relative;display:inline-flex;max-width:calc(100vw - 48px);max-height:calc(100vh - 150px);transform-origin:center;transition:transform 120ms ease}.codexGeneratedImageSurface img{display:block;max-width:100%;max-height:calc(100vh - 150px);border-radius:12px;object-fit:contain}.codexGeneratedImageCanvas.is-annotating .codexGeneratedImageSurface{cursor:crosshair}.codexGeneratedImagePin{position:absolute;display:grid;place-items:center;width:24px;height:24px;padding:0;border:2px solid white;border-radius:50%;background:var(--dsw-alias-label-primary);box-shadow:0 1px 4px rgba(0,0,0,.35);color:var(--dsw-alias-bg-base,#111);font:inherit;font-size:11px;font-weight:700;transform:translate(-50%,-50%);cursor:pointer}.codexGeneratedImagePin.is-active{background:var(--dsw-alias-state-business-primary,#3964fe);color:white}.codexGeneratedImageAnnotateHint{position:absolute;bottom:14px;left:50%;padding:6px 10px;border-radius:14px;background:color-mix(in srgb,var(--dsw-alias-bg-layer-3) 90%,transparent);box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-secondary);font-size:11px;transform:translateX(-50%)}@media(max-width:760px){.codexGeneratedImageTopbar{align-items:flex-start;flex-direction:column}.codexGeneratedImageActions{width:100%;overflow-x:auto}.codexGeneratedImageActions>span{display:none}.codexGeneratedImageCanvas{padding:12px}.codexGeneratedImageSurface{max-width:calc(100vw - 24px);max-height:calc(100vh - 180px)}.codexGeneratedImageSurface img{max-height:calc(100vh - 180px)}}
|
|
798
|
-
`;
|
|
799
|
-
const IMAGE_LAYOUT_STYLE = String.raw`
|
|
800
|
-
.codexGeneratedImageWorkspace{display:grid;min-height:0;grid-template-columns:minmax(0,1fr)}
|
|
801
|
-
.codexGeneratedImageWorkspace.has-comments{grid-template-columns:minmax(0,1fr) 296px}
|
|
802
|
-
.codexGeneratedImageComments{display:flex;min-width:0;flex-direction:column;gap:12px;overflow:auto;padding:18px 14px;border-left:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-base))}
|
|
803
|
-
.codexGeneratedImageComments>header{display:flex;flex-direction:column;gap:2px}.codexGeneratedImageComments>header strong{font-size:13px;font-weight:600;line-height:20px}.codexGeneratedImageComments>header small{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:17px}
|
|
804
|
-
.codexGeneratedImageCommentList{display:flex;flex-direction:column;gap:8px}.codexGeneratedImageCommentList article{display:grid;grid-template-columns:24px minmax(0,1fr) 24px;align-items:start;gap:7px;padding:9px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3)}.codexGeneratedImageCommentList article.is-active{border-color:var(--dsw-alias-state-business-primary,#3964fe)}
|
|
805
|
-
.codexGeneratedImageCommentList article>span{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-base,#111);font-size:10px;font-weight:700}.codexGeneratedImageCommentList textarea{box-sizing:border-box;width:100%;min-height:64px;resize:vertical;border:0;outline:0;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:18px}.codexGeneratedImageCommentList textarea::placeholder{color:var(--dsw-alias-label-dimmed)}
|
|
806
|
-
.codexGeneratedImageCommentList article>button{display:grid;place-items:center;width:22px;height:22px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:17px;cursor:pointer}.codexGeneratedImageCommentList article>button:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
807
|
-
@media(max-width:760px){.codexGeneratedImageWorkspace.has-comments{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr) minmax(118px,34vh)}.codexGeneratedImageComments{border-top:1px solid var(--dsw-alias-border-l2);border-left:0;padding:10px 12px}}
|
|
808
1689
|
`;
|
|
809
1690
|
const unwrap = (response) => {
|
|
810
1691
|
if (!response?.ok) throw new Error(response?.error?.message ?? "Codex RPC failed");
|
|
@@ -839,7 +1720,6 @@ window.__ModuleLoader__.load({
|
|
|
839
1720
|
if (cleaned === "") return fallback;
|
|
840
1721
|
return cleaned.toLowerCase().endsWith(".png") ? cleaned : `${cleaned}.png`;
|
|
841
1722
|
};
|
|
842
|
-
const imageByteSize = (bytes) => bytes < 1024 * 1024 ? `${Math.max(.1, bytes / 1024).toLocaleString(void 0, { maximumFractionDigits: 1 })} KB` : `${(bytes / 1024 / 1024).toLocaleString(void 0, { maximumFractionDigits: 1 })} MB`;
|
|
843
1723
|
const originalRefMatches = (left, right) => left.assetId === right.assetId && left.mediaType === right.mediaType && left.bytes === right.bytes && left.width === right.width && left.height === right.height && left.name === right.name && left.sha256 === right.sha256;
|
|
844
1724
|
async function sha256Hex(data) {
|
|
845
1725
|
const value = await crypto.subtle.digest("SHA-256", data);
|
|
@@ -899,25 +1779,16 @@ window.__ModuleLoader__.load({
|
|
|
899
1779
|
URL.revokeObjectURL(url);
|
|
900
1780
|
}
|
|
901
1781
|
}
|
|
902
|
-
function CodexGeneratedImage({ attachment, original, rpc, sessionId, loadImage, attachForEdit, getImageViewer, t }) {
|
|
1782
|
+
function CodexGeneratedImage({ attachment, original, rpc, sessionId, loadImage, attachForEdit, getImageViewer, getInternalImageViewer, t }) {
|
|
903
1783
|
const [attempt, setAttempt] = (0, react.useState)(0);
|
|
904
1784
|
const [error, setError] = (0, react.useState)(false);
|
|
905
|
-
const [open, setOpen] = (0, react.useState)(false);
|
|
906
1785
|
const [src, setSrc] = (0, react.useState)();
|
|
907
|
-
const [zoom, setZoom] = (0, react.useState)(1);
|
|
908
|
-
const [annotationMode, setAnnotationMode] = (0, react.useState)(false);
|
|
909
|
-
const [annotations, setAnnotations] = (0, react.useState)([]);
|
|
910
|
-
const [selectedAnnotation, setSelectedAnnotation] = (0, react.useState)();
|
|
911
|
-
const [editBusy, setEditBusy] = (0, react.useState)(false);
|
|
912
|
-
const [editError, setEditError] = (0, react.useState)(false);
|
|
913
|
-
const [downloadState, setDownloadState] = (0, react.useState)("idle");
|
|
914
1786
|
const triggerRef = (0, react.useRef)(null);
|
|
915
|
-
const dialogRef = (0, react.useRef)(null);
|
|
916
1787
|
(0, react.useEffect)(() => {
|
|
917
1788
|
let live = true;
|
|
918
1789
|
setError(false);
|
|
919
1790
|
setSrc(void 0);
|
|
920
|
-
loadImage(attachment).then((value) => {
|
|
1791
|
+
Promise.resolve().then(() => loadImage(attachment)).then((value) => {
|
|
921
1792
|
if (live) setSrc(value);
|
|
922
1793
|
}).catch(() => {
|
|
923
1794
|
if (live) setError(true);
|
|
@@ -930,100 +1801,15 @@ window.__ModuleLoader__.load({
|
|
|
930
1801
|
loadImage,
|
|
931
1802
|
attempt
|
|
932
1803
|
]);
|
|
933
|
-
const close = () => {
|
|
934
|
-
setOpen(false);
|
|
935
|
-
window.requestAnimationFrame(() => triggerRef.current?.focus());
|
|
936
|
-
};
|
|
937
|
-
(0, react.useEffect)(() => {
|
|
938
|
-
if (!open) return void 0;
|
|
939
|
-
const previousOverflow = document.body.style.overflow;
|
|
940
|
-
document.body.style.overflow = "hidden";
|
|
941
|
-
dialogRef.current?.focus();
|
|
942
|
-
const keydown = (event) => {
|
|
943
|
-
if (event.key === "Escape") {
|
|
944
|
-
event.preventDefault();
|
|
945
|
-
close();
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
948
|
-
if (event.key === "Tab") {
|
|
949
|
-
const focusable = [...dialogRef.current.querySelectorAll("button:not(:disabled),a[href],input:not(:disabled),textarea:not(:disabled),[tabindex]:not([tabindex=\"-1\"])")];
|
|
950
|
-
if (focusable.length === 0) return;
|
|
951
|
-
const first = focusable[0];
|
|
952
|
-
const last = focusable.at(-1);
|
|
953
|
-
if (event.shiftKey && document.activeElement === first) {
|
|
954
|
-
event.preventDefault();
|
|
955
|
-
last.focus();
|
|
956
|
-
} else if (!event.shiftKey && document.activeElement === last) {
|
|
957
|
-
event.preventDefault();
|
|
958
|
-
first.focus();
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
};
|
|
962
|
-
document.addEventListener("keydown", keydown);
|
|
963
|
-
return () => {
|
|
964
|
-
document.body.style.overflow = previousOverflow;
|
|
965
|
-
document.removeEventListener("keydown", keydown);
|
|
966
|
-
};
|
|
967
|
-
}, [open]);
|
|
968
1804
|
const label = attachment.name ?? t("imageLabel");
|
|
969
1805
|
const downloadName = imageDownloadName(attachment);
|
|
970
|
-
const previewMeta = attachment.width + " × " + attachment.height + " · " + imageByteSize(attachment.bytes);
|
|
971
|
-
const imageMeta = original === void 0 ? previewMeta : original.width + " × " + original.height + " · " + imageByteSize(original.bytes) + " · " + t("imagePreviewShort") + " " + previewMeta;
|
|
972
|
-
const addAnnotation = (event) => {
|
|
973
|
-
if (!annotationMode || event.target.closest(".codexGeneratedImagePin")) return;
|
|
974
|
-
const bounds = event.currentTarget.getBoundingClientRect();
|
|
975
|
-
const annotation = {
|
|
976
|
-
id: crypto.randomUUID(),
|
|
977
|
-
x: Math.max(0, Math.min(1, (event.clientX - bounds.left) / bounds.width)),
|
|
978
|
-
y: Math.max(0, Math.min(1, (event.clientY - bounds.top) / bounds.height)),
|
|
979
|
-
note: ""
|
|
980
|
-
};
|
|
981
|
-
setAnnotations((value) => [...value, annotation]);
|
|
982
|
-
setSelectedAnnotation(annotation.id);
|
|
983
|
-
};
|
|
984
|
-
const continueEditing = async () => {
|
|
985
|
-
if (editBusy || src === void 0) return;
|
|
986
|
-
setEditBusy(true);
|
|
987
|
-
setEditError(false);
|
|
988
|
-
try {
|
|
989
|
-
await attachForEdit(src, downloadName, buildImageEditDraft({
|
|
990
|
-
annotations,
|
|
991
|
-
translate: t
|
|
992
|
-
}));
|
|
993
|
-
close();
|
|
994
|
-
} catch {
|
|
995
|
-
setEditError(true);
|
|
996
|
-
} finally {
|
|
997
|
-
setEditBusy(false);
|
|
998
|
-
}
|
|
999
|
-
};
|
|
1000
1806
|
const downloadOriginal = async () => {
|
|
1001
|
-
if (
|
|
1002
|
-
|
|
1003
|
-
try {
|
|
1004
|
-
triggerBlobDownload(await readOriginalImage(rpc, sessionId, original), original.mediaType, original.name);
|
|
1005
|
-
setDownloadState("idle");
|
|
1006
|
-
} catch {
|
|
1007
|
-
setDownloadState("failed");
|
|
1008
|
-
throw new Error("original image download failed");
|
|
1009
|
-
}
|
|
1807
|
+
if (original === void 0) return;
|
|
1808
|
+
triggerBlobDownload(await readOriginalImage(rpc, sessionId, original), original.mediaType, original.name);
|
|
1010
1809
|
};
|
|
1011
1810
|
const openImage = () => {
|
|
1012
1811
|
if (src === void 0) return;
|
|
1013
|
-
const
|
|
1014
|
-
const actions = [];
|
|
1015
|
-
actions.push({
|
|
1016
|
-
id: "continue-editing",
|
|
1017
|
-
label: t("imageEdit"),
|
|
1018
|
-
pendingLabel: t("imageEditPreparing"),
|
|
1019
|
-
errorLabel: t("imageEditFailed"),
|
|
1020
|
-
closeOnSuccess: true,
|
|
1021
|
-
onInvoke: ({ annotations: nextAnnotations }) => attachForEdit(src, downloadName, buildImageEditDraft({
|
|
1022
|
-
annotations: nextAnnotations,
|
|
1023
|
-
translate: t
|
|
1024
|
-
}))
|
|
1025
|
-
});
|
|
1026
|
-
if (viewer?.open?.({
|
|
1812
|
+
const request = {
|
|
1027
1813
|
items: [{
|
|
1028
1814
|
id: attachment.attachmentId ?? downloadName,
|
|
1029
1815
|
src,
|
|
@@ -1036,14 +1822,33 @@ window.__ModuleLoader__.load({
|
|
|
1036
1822
|
errorLabel: t("imageDownloadFailed"),
|
|
1037
1823
|
onInvoke: downloadOriginal
|
|
1038
1824
|
},
|
|
1039
|
-
actions
|
|
1825
|
+
actions: [{
|
|
1826
|
+
id: "continue-editing",
|
|
1827
|
+
label: t("imageEdit"),
|
|
1828
|
+
pendingLabel: t("imageEditPreparing"),
|
|
1829
|
+
errorLabel: t("imageEditFailed"),
|
|
1830
|
+
closeOnSuccess: true,
|
|
1831
|
+
onInvoke: ({ annotations = [] }) => {
|
|
1832
|
+
const imageKey = String(attachment.attachmentId ?? "image").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1833
|
+
const sourceName = annotations.length === 0 ? downloadName : `codex-edit-${imageKey}-source.png`;
|
|
1834
|
+
const referenceName = `codex-edit-${imageKey}-annotations.png`;
|
|
1835
|
+
return attachForEdit(src, sourceName, buildImageEditDraft({
|
|
1836
|
+
annotations,
|
|
1837
|
+
translate: t,
|
|
1838
|
+
width: attachment.width,
|
|
1839
|
+
height: attachment.height,
|
|
1840
|
+
sourceName,
|
|
1841
|
+
referenceName
|
|
1842
|
+
}), annotations, referenceName);
|
|
1843
|
+
}
|
|
1844
|
+
}]
|
|
1040
1845
|
}],
|
|
1041
1846
|
opener: triggerRef.current,
|
|
1042
1847
|
source: "codex-generated",
|
|
1043
1848
|
annotations: true
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1849
|
+
};
|
|
1850
|
+
if (getInternalImageViewer?.()?.open?.(request) === true) return;
|
|
1851
|
+
(getImageViewer?.())?.open?.(request);
|
|
1047
1852
|
};
|
|
1048
1853
|
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1049
1854
|
type: "button",
|
|
@@ -1051,151 +1856,7 @@ window.__ModuleLoader__.load({
|
|
|
1051
1856
|
onClick: () => setAttempt((value) => value + 1),
|
|
1052
1857
|
children: t("imageLoadFailed")
|
|
1053
1858
|
});
|
|
1054
|
-
|
|
1055
|
-
ref: dialogRef,
|
|
1056
|
-
className: "codexGeneratedImageLightbox",
|
|
1057
|
-
role: "dialog",
|
|
1058
|
-
"aria-modal": "true",
|
|
1059
|
-
"aria-label": t("imagePreview"),
|
|
1060
|
-
tabIndex: -1,
|
|
1061
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
1062
|
-
className: "codexGeneratedImageTopbar",
|
|
1063
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: imageMeta })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1064
|
-
className: "codexGeneratedImageActions",
|
|
1065
|
-
children: [
|
|
1066
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1067
|
-
type: "button",
|
|
1068
|
-
className: annotationMode ? "is-active" : "",
|
|
1069
|
-
"aria-pressed": annotationMode,
|
|
1070
|
-
onClick: () => setAnnotationMode((value) => !value),
|
|
1071
|
-
children: t("imageAnnotate")
|
|
1072
|
-
}),
|
|
1073
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1074
|
-
type: "button",
|
|
1075
|
-
"aria-label": t("imageZoomOut"),
|
|
1076
|
-
disabled: zoom <= .5,
|
|
1077
|
-
onClick: () => setZoom((value) => Math.max(.5, value - .25)),
|
|
1078
|
-
children: "−"
|
|
1079
|
-
}),
|
|
1080
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [Math.round(zoom * 100), "%"] }),
|
|
1081
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1082
|
-
type: "button",
|
|
1083
|
-
"aria-label": t("imageZoomIn"),
|
|
1084
|
-
disabled: zoom >= 3,
|
|
1085
|
-
onClick: () => setZoom((value) => Math.min(3, value + .25)),
|
|
1086
|
-
children: "+"
|
|
1087
|
-
}),
|
|
1088
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1089
|
-
type: "button",
|
|
1090
|
-
onClick: () => setZoom(1),
|
|
1091
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, { "aria-hidden": "true" }), t("imageFit")]
|
|
1092
|
-
}),
|
|
1093
|
-
original === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
1094
|
-
href: src,
|
|
1095
|
-
download: downloadName,
|
|
1096
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, { "aria-hidden": "true" }), t("imageDownload")]
|
|
1097
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1098
|
-
type: "button",
|
|
1099
|
-
disabled: downloadState === "pending",
|
|
1100
|
-
onClick: () => {
|
|
1101
|
-
downloadOriginal().catch(() => void 0);
|
|
1102
|
-
},
|
|
1103
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, { "aria-hidden": "true" }), downloadState === "pending" ? t("imageDownloadPreparing") : downloadState === "failed" ? t("imageDownloadFailed") : t("imageDownload")]
|
|
1104
|
-
}),
|
|
1105
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1106
|
-
type: "button",
|
|
1107
|
-
disabled: editBusy,
|
|
1108
|
-
onClick: () => {
|
|
1109
|
-
continueEditing();
|
|
1110
|
-
},
|
|
1111
|
-
children: editBusy ? t("imageEditPreparing") : editError ? t("imageEditFailed") : t("imageEdit")
|
|
1112
|
-
}),
|
|
1113
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1114
|
-
type: "button",
|
|
1115
|
-
className: "codexGeneratedImageClose",
|
|
1116
|
-
"aria-label": t("imageClosePreview"),
|
|
1117
|
-
onClick: close,
|
|
1118
|
-
children: "×"
|
|
1119
|
-
})
|
|
1120
|
-
]
|
|
1121
|
-
})]
|
|
1122
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1123
|
-
className: "codexGeneratedImageWorkspace " + (annotationMode || annotations.length > 0 ? "has-comments" : ""),
|
|
1124
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
1125
|
-
className: "codexGeneratedImageCanvas " + (annotationMode ? "is-annotating" : ""),
|
|
1126
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1127
|
-
className: "codexGeneratedImageSurface",
|
|
1128
|
-
onClick: addAnnotation,
|
|
1129
|
-
style: { transform: "scale(" + zoom + ")" },
|
|
1130
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
1131
|
-
src,
|
|
1132
|
-
alt: label
|
|
1133
|
-
}), annotations.map((annotation, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1134
|
-
type: "button",
|
|
1135
|
-
className: "codexGeneratedImagePin " + (annotation.id === selectedAnnotation ? "is-active" : ""),
|
|
1136
|
-
"aria-label": fill(t("imageAnnotation"), { value: index + 1 }),
|
|
1137
|
-
style: {
|
|
1138
|
-
left: annotation.x * 100 + "%",
|
|
1139
|
-
top: annotation.y * 100 + "%"
|
|
1140
|
-
},
|
|
1141
|
-
onClick: (event) => {
|
|
1142
|
-
event.stopPropagation();
|
|
1143
|
-
setSelectedAnnotation(annotation.id);
|
|
1144
|
-
},
|
|
1145
|
-
children: index + 1
|
|
1146
|
-
}, annotation.id))]
|
|
1147
|
-
}), annotationMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1148
|
-
className: "codexGeneratedImageAnnotateHint",
|
|
1149
|
-
children: t("imageAnnotateHint")
|
|
1150
|
-
}) : null]
|
|
1151
|
-
}), annotationMode || annotations.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
|
|
1152
|
-
className: "codexGeneratedImageComments",
|
|
1153
|
-
"aria-label": t("imageAnnotate"),
|
|
1154
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("imageAnnotate") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("imageAnnotateHint") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1155
|
-
className: "codexGeneratedImageCommentList",
|
|
1156
|
-
children: annotations.map((annotation, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
|
|
1157
|
-
className: annotation.id === selectedAnnotation ? "is-active" : "",
|
|
1158
|
-
onClick: () => setSelectedAnnotation(annotation.id),
|
|
1159
|
-
children: [
|
|
1160
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: index + 1 }),
|
|
1161
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
1162
|
-
value: annotation.note,
|
|
1163
|
-
rows: 3,
|
|
1164
|
-
"aria-label": fill(t("imageAnnotation"), { value: index + 1 }),
|
|
1165
|
-
placeholder: t("imageAnnotationPlaceholder"),
|
|
1166
|
-
onFocus: () => setSelectedAnnotation(annotation.id),
|
|
1167
|
-
onChange: (event) => {
|
|
1168
|
-
const note = event.target.value;
|
|
1169
|
-
setAnnotations((value) => value.map((item) => item.id === annotation.id ? {
|
|
1170
|
-
...item,
|
|
1171
|
-
note
|
|
1172
|
-
} : item));
|
|
1173
|
-
},
|
|
1174
|
-
onKeyDown: (event) => {
|
|
1175
|
-
if (event.key === "Enter" && !event.shiftKey || event.key === "Escape") {
|
|
1176
|
-
event.preventDefault();
|
|
1177
|
-
event.stopPropagation();
|
|
1178
|
-
setSelectedAnnotation(void 0);
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
}),
|
|
1182
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1183
|
-
type: "button",
|
|
1184
|
-
"aria-label": t("imageRemoveAnnotation"),
|
|
1185
|
-
onClick: (event) => {
|
|
1186
|
-
event.stopPropagation();
|
|
1187
|
-
setAnnotations((value) => value.filter((item) => item.id !== annotation.id));
|
|
1188
|
-
if (selectedAnnotation === annotation.id) setSelectedAnnotation(void 0);
|
|
1189
|
-
},
|
|
1190
|
-
children: "×"
|
|
1191
|
-
})
|
|
1192
|
-
]
|
|
1193
|
-
}, annotation.id))
|
|
1194
|
-
})]
|
|
1195
|
-
}) : null]
|
|
1196
|
-
})]
|
|
1197
|
-
}), document.body);
|
|
1198
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1859
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1199
1860
|
ref: triggerRef,
|
|
1200
1861
|
type: "button",
|
|
1201
1862
|
className: "codexGeneratedImageFrame",
|
|
@@ -1206,9 +1867,9 @@ window.__ModuleLoader__.load({
|
|
|
1206
1867
|
src,
|
|
1207
1868
|
alt: label
|
|
1208
1869
|
})
|
|
1209
|
-
})
|
|
1870
|
+
});
|
|
1210
1871
|
}
|
|
1211
|
-
function CodexImageToolRow({ block, sessionId, rpc, loadImage, attachForEdit, getImageViewer, t }) {
|
|
1872
|
+
function CodexImageToolRow({ block, sessionId, rpc, loadImage, attachForEdit, getImageViewer, getInternalImageViewer, t }) {
|
|
1212
1873
|
const settled = block?.kind === "tool-result";
|
|
1213
1874
|
const image = settled ? block.content.find((item) => item?.type === "image" && item.attachment !== void 0) : void 0;
|
|
1214
1875
|
const failed = settled && block.isError === true;
|
|
@@ -1251,6 +1912,7 @@ window.__ModuleLoader__.load({
|
|
|
1251
1912
|
loadImage,
|
|
1252
1913
|
attachForEdit,
|
|
1253
1914
|
getImageViewer,
|
|
1915
|
+
getInternalImageViewer,
|
|
1254
1916
|
t
|
|
1255
1917
|
})
|
|
1256
1918
|
}),
|
|
@@ -1306,7 +1968,7 @@ window.__ModuleLoader__.load({
|
|
|
1306
1968
|
return;
|
|
1307
1969
|
}
|
|
1308
1970
|
const usage = unwrap(await rpc.call(CHANNEL, "usage", { force: false }));
|
|
1309
|
-
if (live) setQuota(
|
|
1971
|
+
if (live) setQuota(selectModelQuotaWindows(usage, model));
|
|
1310
1972
|
} catch {
|
|
1311
1973
|
if (live) setQuota(void 0);
|
|
1312
1974
|
} finally {
|
|
@@ -1579,8 +2241,19 @@ window.__ModuleLoader__.load({
|
|
|
1579
2241
|
const codex = current?.provider === "openai-codex";
|
|
1580
2242
|
const quotaEnabled = preferenceSnapshot.status === "ready" && preferenceSnapshot.quickQuotaMode !== "off" && codex;
|
|
1581
2243
|
const forecastMode = preferenceSnapshot.quickQuotaMode === QUICK_QUOTA_MODE_FORECAST;
|
|
1582
|
-
const
|
|
1583
|
-
if (!quotaEnabled ||
|
|
2244
|
+
const quotas = useQuickQuota(rpc, quotaEnabled, current?.model);
|
|
2245
|
+
if (!quotaEnabled || quotas === void 0 || quotas.length === 0) return null;
|
|
2246
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2247
|
+
className: "codexComposerQuotaWindows",
|
|
2248
|
+
children: quotas.map((quota, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexComposerQuotaWindow, {
|
|
2249
|
+
quota,
|
|
2250
|
+
mode: preferenceSnapshot.quickQuotaMode,
|
|
2251
|
+
forecastMode,
|
|
2252
|
+
t
|
|
2253
|
+
}, `${quota.windowSeconds}-${index}`))
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
function CodexComposerQuotaWindow({ quota, mode, forecastMode, t }) {
|
|
1584
2257
|
const value = Math.round(Number(quota.remainingPercent) * 10) / 10;
|
|
1585
2258
|
const display = percent(value);
|
|
1586
2259
|
const forecast = forecastMode ? quota.forecast : void 0;
|
|
@@ -1589,17 +2262,20 @@ window.__ModuleLoader__.load({
|
|
|
1589
2262
|
value: display,
|
|
1590
2263
|
duration
|
|
1591
2264
|
});
|
|
1592
|
-
|
|
2265
|
+
const content = mode === "bar" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("progress", {
|
|
2266
|
+
className: "codexComposerQuotaBar",
|
|
2267
|
+
max: 100,
|
|
2268
|
+
value,
|
|
2269
|
+
"aria-hidden": "true"
|
|
2270
|
+
}) : forecast?.status === "calibrating" ? `${display}% · ${t("quickQuotaForecastCalibrating")}` : forecast?.status === "idle" ? `${display}% · ${t("quickQuotaForecastIdle")}` : forecast?.status === "ready" && forecast.survivesReset ? `${display}% · ${t("quickQuotaForecastUntilReset")}` : forecastMode && duration !== void 0 ? `${display}% · ≈${duration}` : `${display}%`;
|
|
2271
|
+
const durationLabel = windowLabel(quota.windowSeconds, t);
|
|
2272
|
+
const accessibleLabel = `${durationLabel}: ${label}`;
|
|
2273
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1593
2274
|
className: "codexComposerQuota",
|
|
1594
2275
|
role: "status",
|
|
1595
|
-
"aria-label":
|
|
1596
|
-
title:
|
|
1597
|
-
children:
|
|
1598
|
-
className: "codexComposerQuotaBar",
|
|
1599
|
-
max: 100,
|
|
1600
|
-
value,
|
|
1601
|
-
"aria-hidden": "true"
|
|
1602
|
-
}) : forecast?.status === "calibrating" ? `${display}% · ${t("quickQuotaForecastCalibrating")}` : forecast?.status === "idle" ? `${display}% · ${t("quickQuotaForecastIdle")}` : forecast?.status === "ready" && forecast.survivesReset ? `${display}% · ${t("quickQuotaForecastUntilReset")}` : forecastMode && duration !== void 0 ? `${display}% · ≈${duration}` : `${display}%`
|
|
2276
|
+
"aria-label": accessibleLabel,
|
|
2277
|
+
title: accessibleLabel,
|
|
2278
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: durationLabel }), content]
|
|
1603
2279
|
});
|
|
1604
2280
|
}
|
|
1605
2281
|
function CodexModelSelect({ locked, available, directory, load, select, preference, t }) {
|
|
@@ -2783,6 +3459,7 @@ window.__ModuleLoader__.load({
|
|
|
2783
3459
|
});
|
|
2784
3460
|
}
|
|
2785
3461
|
function apply(ctx) {
|
|
3462
|
+
const imageViewer = new SubscriptionImageViewerService();
|
|
2786
3463
|
ctx.effect(() => ctx.locale.register(NS, {
|
|
2787
3464
|
zh,
|
|
2788
3465
|
en
|
|
@@ -2790,7 +3467,7 @@ window.__ModuleLoader__.load({
|
|
|
2790
3467
|
ctx.effect(() => {
|
|
2791
3468
|
const tag = document.createElement("style");
|
|
2792
3469
|
tag.dataset.plugin = "dsh-codex-subscription";
|
|
2793
|
-
tag.textContent = STYLE +
|
|
3470
|
+
tag.textContent = STYLE + SUBSCRIPTION_IMAGE_VIEWER_CSS;
|
|
2794
3471
|
document.head.append(tag);
|
|
2795
3472
|
return () => tag.remove();
|
|
2796
3473
|
}, "codex-subscription: style");
|
|
@@ -2807,6 +3484,15 @@ window.__ModuleLoader__.load({
|
|
|
2807
3484
|
};
|
|
2808
3485
|
}, "codex-subscription: preferences");
|
|
2809
3486
|
const t = ctx.locale.bind(NS);
|
|
3487
|
+
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
3488
|
+
name: "shell.overlay",
|
|
3489
|
+
id: "codex-subscription-image-viewer",
|
|
3490
|
+
order: 20,
|
|
3491
|
+
inject: () => ({
|
|
3492
|
+
service: imageViewer,
|
|
3493
|
+
t
|
|
3494
|
+
})
|
|
3495
|
+
}, SubscriptionImageViewerOverlay));
|
|
2810
3496
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
2811
3497
|
name: "settings.section",
|
|
2812
3498
|
id: "codex-subscription",
|
|
@@ -2856,6 +3542,7 @@ window.__ModuleLoader__.load({
|
|
|
2856
3542
|
if (ctx.get("remote.session") === void 0) installDirectorySlots(ctx);
|
|
2857
3543
|
else ctx.inject(["remote.session"], installDirectorySlots);
|
|
2858
3544
|
const conversation = ctx.get("conversation");
|
|
3545
|
+
const uiConversation = ctx.get("uiConversation");
|
|
2859
3546
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
2860
3547
|
name: "tool.call.toolview",
|
|
2861
3548
|
key: "codex_image_generate",
|
|
@@ -2864,7 +3551,7 @@ window.__ModuleLoader__.load({
|
|
|
2864
3551
|
sessionId,
|
|
2865
3552
|
rpc: connection.rpc,
|
|
2866
3553
|
t,
|
|
2867
|
-
loadImage: (attachment) =>
|
|
3554
|
+
loadImage: (attachment) => uiConversation.imageUrl(sessionId, attachment),
|
|
2868
3555
|
getImageViewer: () => {
|
|
2869
3556
|
try {
|
|
2870
3557
|
return ctx.get("nativeImageViewer");
|
|
@@ -2872,13 +3559,19 @@ window.__ModuleLoader__.load({
|
|
|
2872
3559
|
return;
|
|
2873
3560
|
}
|
|
2874
3561
|
},
|
|
2875
|
-
|
|
3562
|
+
getInternalImageViewer: () => imageViewer,
|
|
3563
|
+
attachForEdit: async (src, filename, draft, annotations = [], referenceName) => {
|
|
2876
3564
|
const actx = sessions.scope(sessionId);
|
|
2877
3565
|
if (actx === void 0 || typeof conversation.createDraftImages !== "function" || conversation.input?.for === void 0) throw new Error("This DSH version does not provide the image composer bridge");
|
|
2878
3566
|
const response = await fetch(src);
|
|
2879
3567
|
if (!response.ok) throw new Error("Could not read generated image");
|
|
2880
3568
|
const blob = await response.blob();
|
|
2881
|
-
const
|
|
3569
|
+
const files = [new File([blob], filename, { type: blob.type || "image/png" })];
|
|
3570
|
+
if (annotations.length > 0) {
|
|
3571
|
+
const reference = await createAnnotatedImageReference(blob, annotations);
|
|
3572
|
+
files.push(new File([reference], referenceName, { type: "image/png" }));
|
|
3573
|
+
}
|
|
3574
|
+
const created = conversation.createDraftImages(files);
|
|
2882
3575
|
const input = conversation.input.for(actx);
|
|
2883
3576
|
if (!input.addImages(created.map((item) => item.id))) {
|
|
2884
3577
|
conversation.releaseDraftImages(created);
|