dsh-codex-subscription 1.14.2 → 1.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +3 -13
- package/README.md +3 -13
- package/lib/client.js +1917 -1470
- package/lib/index.js +193 -32
- package/package.json +3 -3
package/lib/client.js
CHANGED
|
@@ -28,26 +28,8 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
//#endregion
|
|
29
29
|
let react = require("react");
|
|
30
30
|
react = __toESM(react, 1);
|
|
31
|
-
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
32
31
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
33
|
-
|
|
34
|
-
function BoltIcon({ title, titleId, ...props }, svgRef) {
|
|
35
|
-
return /*#__PURE__*/ react.createElement("svg", Object.assign({
|
|
36
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
37
|
-
viewBox: "0 0 16 16",
|
|
38
|
-
fill: "currentColor",
|
|
39
|
-
"aria-hidden": "true",
|
|
40
|
-
"data-slot": "icon",
|
|
41
|
-
ref: svgRef,
|
|
42
|
-
"aria-labelledby": titleId
|
|
43
|
-
}, props), title ? /*#__PURE__*/ react.createElement("title", { id: titleId }, title) : null, /*#__PURE__*/ react.createElement("path", {
|
|
44
|
-
fillRule: "evenodd",
|
|
45
|
-
d: "M9.58 1.077a.75.75 0 0 1 .405.82L9.165 6h4.085a.75.75 0 0 1 .567 1.241l-6.5 7.5a.75.75 0 0 1-1.302-.638L6.835 10H2.75a.75.75 0 0 1-.567-1.241l6.5-7.5a.75.75 0 0 1 .897-.182Z",
|
|
46
|
-
clipRule: "evenodd"
|
|
47
|
-
}));
|
|
48
|
-
}
|
|
49
|
-
const ForwardRef = /*#__PURE__*/ react.forwardRef(BoltIcon);
|
|
50
|
-
//#endregion
|
|
32
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
51
33
|
//#region src/image-edit.js
|
|
52
34
|
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
53
35
|
const validCoordinate = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
|
|
@@ -115,128 +97,6 @@ window.__ModuleLoader__.load({
|
|
|
115
97
|
...notes
|
|
116
98
|
].join("\n");
|
|
117
99
|
}
|
|
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
|
-
}
|
|
239
|
-
}
|
|
240
100
|
const ORIGINAL_IMAGE_ID_PATTERN = /^img_[0-9a-f]{32}$/u;
|
|
241
101
|
const positiveInteger = (value) => Number.isSafeInteger(value) && value > 0;
|
|
242
102
|
function decodeOriginalImageRef(value) {
|
|
@@ -256,1005 +116,221 @@ window.__ModuleLoader__.load({
|
|
|
256
116
|
const original = decodeOriginalImageRef(value.original);
|
|
257
117
|
return original === void 0 ? void 0 : { original };
|
|
258
118
|
}
|
|
119
|
+
function originalImageRefsEqual(left, right) {
|
|
120
|
+
const a = decodeOriginalImageRef(left);
|
|
121
|
+
const b = decodeOriginalImageRef(right);
|
|
122
|
+
return a !== void 0 && b !== void 0 && a.assetId === b.assetId && a.mediaType === b.mediaType && a.bytes === b.bytes && a.width === b.width && a.height === b.height && a.name === b.name && a.sha256 === b.sha256;
|
|
123
|
+
}
|
|
259
124
|
//#endregion
|
|
260
|
-
//#region src/
|
|
261
|
-
|
|
262
|
-
.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
125
|
+
//#region src/original-image-download.js
|
|
126
|
+
function decodeBase64Chunk(value) {
|
|
127
|
+
if (typeof value !== "string" || value.length === 0 || value.length > Math.ceil(4194304 / 3) * 4 + 8) throw new Error("Invalid original image chunk");
|
|
128
|
+
let decoded;
|
|
129
|
+
try {
|
|
130
|
+
decoded = atob(value);
|
|
131
|
+
} catch {
|
|
132
|
+
throw new Error("Invalid original image chunk");
|
|
133
|
+
}
|
|
134
|
+
const bytes = new Uint8Array(decoded.length);
|
|
135
|
+
for (let index = 0; index < decoded.length; index += 1) bytes[index] = decoded.charCodeAt(index);
|
|
136
|
+
return bytes;
|
|
137
|
+
}
|
|
138
|
+
/** Keep only the destination and current chunk, while verifying every reply. */
|
|
139
|
+
async function readOriginalImage(rpc, sessionId, original) {
|
|
140
|
+
original = decodeOriginalImageRef(original);
|
|
141
|
+
if (original === void 0) throw new Error("Invalid original image reference");
|
|
142
|
+
const data = new Uint8Array(original.bytes);
|
|
143
|
+
let total = 0;
|
|
144
|
+
let done = false;
|
|
145
|
+
while (!done) {
|
|
146
|
+
const response = await rpc.call("/codex-subscription", "image/original/chunk", {
|
|
147
|
+
sessionId,
|
|
148
|
+
assetId: original.assetId,
|
|
149
|
+
offset: total
|
|
150
|
+
});
|
|
151
|
+
if (!response?.ok) throw new Error(response?.error?.message ?? "Codex RPC failed");
|
|
152
|
+
const chunk = response.value;
|
|
153
|
+
if (!originalImageRefsEqual(chunk?.ref, original) || chunk.offset !== total || typeof chunk.done !== "boolean") throw new Error("Original image metadata changed");
|
|
154
|
+
const bytes = decodeBase64Chunk(chunk.encoded);
|
|
155
|
+
if (bytes.byteLength === 0 || total + bytes.byteLength > original.bytes) throw new Error("Original image download is incomplete");
|
|
156
|
+
data.set(bytes, total);
|
|
157
|
+
total += bytes.byteLength;
|
|
158
|
+
done = chunk.done;
|
|
159
|
+
}
|
|
160
|
+
if (total !== original.bytes) throw new Error("Original image download is incomplete");
|
|
161
|
+
const digest = await crypto.subtle.digest("SHA-256", data);
|
|
162
|
+
if ([...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("") !== original.sha256) throw new Error("Original image integrity check failed");
|
|
163
|
+
return data;
|
|
164
|
+
}
|
|
278
165
|
//#endregion
|
|
279
|
-
//#region src/
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return cleaned
|
|
166
|
+
//#region src/client-images.jsx
|
|
167
|
+
const imageDownloadName = (attachment) => {
|
|
168
|
+
const fallback = "codex-generated-image.png";
|
|
169
|
+
if (typeof attachment?.name !== "string") return fallback;
|
|
170
|
+
const cleaned = attachment.name.replace(/[<>:"/\\|?*\u0000-\u001f]/gu, "-").replace(/[. ]+$/u, "").trim();
|
|
171
|
+
if (cleaned === "") return fallback;
|
|
172
|
+
return cleaned.toLowerCase().endsWith(".png") ? cleaned : `${cleaned}.png`;
|
|
286
173
|
};
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
});
|
|
174
|
+
function triggerBlobDownload(data, mediaType, filename) {
|
|
175
|
+
const url = URL.createObjectURL(new Blob([data], { type: mediaType }));
|
|
176
|
+
const anchor = document.createElement("a");
|
|
177
|
+
anchor.href = url;
|
|
178
|
+
anchor.download = filename;
|
|
179
|
+
anchor.rel = "noopener";
|
|
180
|
+
document.body.append(anchor);
|
|
181
|
+
try {
|
|
182
|
+
anchor.click();
|
|
183
|
+
} finally {
|
|
184
|
+
anchor.remove();
|
|
185
|
+
URL.revokeObjectURL(url);
|
|
186
|
+
}
|
|
320
187
|
}
|
|
321
|
-
function
|
|
322
|
-
const [
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
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;
|
|
188
|
+
function CodexGeneratedImage({ attachment, original, rpc, sessionId, loadImage, attachForEdit, getImageViewer, getInternalImageViewer, t }) {
|
|
189
|
+
const [attempt, setAttempt] = (0, react.useState)(0);
|
|
190
|
+
const [error, setError] = (0, react.useState)(false);
|
|
191
|
+
const [src, setSrc] = (0, react.useState)();
|
|
192
|
+
const triggerRef = (0, react.useRef)(null);
|
|
374
193
|
(0, react.useEffect)(() => {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
|
194
|
+
let live = true;
|
|
195
|
+
setError(false);
|
|
196
|
+
setSrc(void 0);
|
|
197
|
+
Promise.resolve().then(() => loadImage(attachment)).then((value) => {
|
|
198
|
+
if (live) setSrc(value);
|
|
199
|
+
}).catch(() => {
|
|
200
|
+
if (live) setError(true);
|
|
446
201
|
});
|
|
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
202
|
return () => {
|
|
496
|
-
|
|
497
|
-
document.removeEventListener("keydown", onKeyDown);
|
|
203
|
+
live = false;
|
|
498
204
|
};
|
|
499
205
|
}, [
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
|
206
|
+
attachment,
|
|
207
|
+
loadImage,
|
|
208
|
+
attempt
|
|
513
209
|
]);
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
}
|
|
210
|
+
const label = attachment.name ?? t("imageLabel");
|
|
211
|
+
const downloadName = imageDownloadName(attachment);
|
|
212
|
+
const downloadOriginal = async () => {
|
|
213
|
+
if (original === void 0) return;
|
|
214
|
+
triggerBlobDownload(await readOriginalImage(rpc, sessionId, original), original.mediaType, original.name);
|
|
559
215
|
};
|
|
560
|
-
const
|
|
561
|
-
if (
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
216
|
+
const openImage = () => {
|
|
217
|
+
if (src === void 0) return;
|
|
218
|
+
const request = {
|
|
219
|
+
items: [{
|
|
220
|
+
id: attachment.attachmentId ?? downloadName,
|
|
221
|
+
src,
|
|
222
|
+
name: label,
|
|
223
|
+
width: attachment.width,
|
|
224
|
+
height: attachment.height,
|
|
225
|
+
bytes: attachment.bytes,
|
|
226
|
+
download: original === void 0 ? void 0 : {
|
|
227
|
+
pendingLabel: t("imageDownloadPreparing"),
|
|
228
|
+
errorLabel: t("imageDownloadFailed"),
|
|
229
|
+
onInvoke: downloadOriginal
|
|
230
|
+
},
|
|
231
|
+
actions: [{
|
|
232
|
+
id: "continue-editing",
|
|
233
|
+
label: t("imageEdit"),
|
|
234
|
+
pendingLabel: t("imageEditPreparing"),
|
|
235
|
+
errorLabel: t("imageEditFailed"),
|
|
236
|
+
closeOnSuccess: true,
|
|
237
|
+
onInvoke: ({ annotations = [] }) => {
|
|
238
|
+
const imageKey = String(attachment.attachmentId ?? "image").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
239
|
+
const sourceName = annotations.length === 0 ? downloadName : `codex-edit-${imageKey}-source.png`;
|
|
240
|
+
const referenceName = `codex-edit-${imageKey}-annotations.png`;
|
|
241
|
+
return attachForEdit(src, sourceName, buildImageEditDraft({
|
|
242
|
+
annotations,
|
|
243
|
+
translate: t,
|
|
244
|
+
width: attachment.width,
|
|
245
|
+
height: attachment.height,
|
|
246
|
+
sourceName,
|
|
247
|
+
referenceName
|
|
248
|
+
}), annotations, referenceName);
|
|
249
|
+
}
|
|
250
|
+
}]
|
|
251
|
+
}],
|
|
252
|
+
opener: triggerRef.current,
|
|
253
|
+
source: "codex-generated",
|
|
254
|
+
annotations: true
|
|
595
255
|
};
|
|
596
|
-
|
|
597
|
-
|
|
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
|
-
}
|
|
256
|
+
if (getInternalImageViewer?.()?.open?.(request) === true) return;
|
|
257
|
+
(getImageViewer?.())?.open?.(request);
|
|
613
258
|
};
|
|
614
|
-
if (
|
|
615
|
-
|
|
616
|
-
|
|
259
|
+
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
260
|
+
type: "button",
|
|
261
|
+
className: "codexGeneratedImageRetry",
|
|
262
|
+
onClick: () => setAttempt((value) => value + 1),
|
|
263
|
+
children: t("imageLoadFailed")
|
|
264
|
+
});
|
|
265
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
266
|
+
ref: triggerRef,
|
|
267
|
+
type: "button",
|
|
268
|
+
className: "codexGeneratedImageFrame",
|
|
269
|
+
title: t("imageOpen"),
|
|
270
|
+
"aria-label": t("imageOpenNamed").replace("{value}", String(label)),
|
|
271
|
+
onClick: openImage,
|
|
272
|
+
children: src === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("imageLoading") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
273
|
+
src,
|
|
274
|
+
alt: label
|
|
275
|
+
})
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function CodexImageToolRow({ block, sessionId, rpc, loadImage, attachForEdit, getImageViewer, getInternalImageViewer, t }) {
|
|
279
|
+
const settled = block?.kind === "tool-result";
|
|
280
|
+
const image = settled ? block.content.find((item) => item?.type === "image" && item.attachment !== void 0) : void 0;
|
|
281
|
+
const failed = settled && block.isError === true;
|
|
282
|
+
const state = !settled ? "running" : failed ? "error" : "done";
|
|
283
|
+
const status = !settled ? t("imageGenerating") : failed ? t("imageFailed") : t("imageGenerated");
|
|
284
|
+
const error = failed ? block.content.find((item) => item?.type === "text" && typeof item.text === "string")?.text : void 0;
|
|
285
|
+
const original = decodeImagePresentation(block?.meta)?.original;
|
|
617
286
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
role: "dialog",
|
|
621
|
-
"aria-modal": "true",
|
|
622
|
-
"aria-label": t("imagePreview"),
|
|
623
|
-
tabIndex: -1,
|
|
287
|
+
className: "codexImageTool",
|
|
288
|
+
"data-state": state,
|
|
624
289
|
children: [
|
|
625
290
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
626
|
-
className: "
|
|
627
|
-
children: [
|
|
291
|
+
className: "codexImageToolRow",
|
|
292
|
+
children: [
|
|
293
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
294
|
+
className: "codexImageToolIcon",
|
|
295
|
+
"aria-hidden": "true"
|
|
296
|
+
}),
|
|
297
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
298
|
+
className: "codexImageToolTitle",
|
|
299
|
+
children: t("imageGenerate")
|
|
300
|
+
}),
|
|
301
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
302
|
+
className: "codexImageBeta",
|
|
303
|
+
children: t("imageBeta")
|
|
304
|
+
}),
|
|
305
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
306
|
+
className: "codexImageToolState",
|
|
307
|
+
children: status
|
|
308
|
+
})
|
|
309
|
+
]
|
|
628
310
|
}),
|
|
629
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
630
|
-
className: "
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
]
|
|
311
|
+
image === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
312
|
+
className: "codexImageToolGallery",
|
|
313
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexGeneratedImage, {
|
|
314
|
+
attachment: image.attachment,
|
|
315
|
+
original,
|
|
316
|
+
rpc,
|
|
317
|
+
sessionId,
|
|
318
|
+
loadImage,
|
|
319
|
+
attachForEdit,
|
|
320
|
+
getImageViewer,
|
|
321
|
+
getInternalImageViewer,
|
|
322
|
+
t
|
|
707
323
|
})
|
|
708
324
|
}),
|
|
709
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
710
|
-
|
|
711
|
-
|
|
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]
|
|
325
|
+
error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
326
|
+
className: "codexImageToolError",
|
|
327
|
+
children: error
|
|
854
328
|
})
|
|
855
329
|
]
|
|
856
330
|
});
|
|
857
331
|
}
|
|
858
332
|
//#endregion
|
|
859
|
-
//#region src/
|
|
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
|
|
958
|
-
//#region src/settings-contract.js
|
|
959
|
-
const SETTINGS_NAMESPACE = "codex-subscription";
|
|
960
|
-
const QUICK_QUOTA_MODE_FIELD = "quickQuotaMode";
|
|
961
|
-
const LEGACY_QUICK_QUOTA_FIELD = "quickQuotaVisible";
|
|
962
|
-
const QUICK_QUOTA_MODE_PERCENT = "percent";
|
|
963
|
-
const QUICK_QUOTA_MODE_FORECAST = "forecast";
|
|
964
|
-
const SEARCH_PROVIDER_FIELD = "searchProvider";
|
|
965
|
-
const SEARCH_PROVIDER_AUTO = "auto";
|
|
966
|
-
const SEARCH_PROVIDER_CODEX = "codex";
|
|
967
|
-
const DEFAULT_SEARCH_PROVIDER = SEARCH_PROVIDER_AUTO;
|
|
968
|
-
const SPEED_MODE_FIELD = "speedMode";
|
|
969
|
-
const SPEED_MODE_STANDARD = "standard";
|
|
970
|
-
const SPEED_MODE_FAST = "fast";
|
|
971
|
-
const DEFAULT_SPEED_MODE = SPEED_MODE_STANDARD;
|
|
972
|
-
const OUTPUT_VERBOSITY_FIELD = "outputVerbosity";
|
|
973
|
-
const OUTPUT_VERBOSITY_DEFAULT = "default";
|
|
974
|
-
const OUTPUT_VERBOSITY_MEDIUM = "medium";
|
|
975
|
-
const OUTPUT_VERBOSITY_HIGH = "high";
|
|
976
|
-
const DEFAULT_OUTPUT_VERBOSITY = OUTPUT_VERBOSITY_DEFAULT;
|
|
977
|
-
const CONTEXT_MODE_FIELD = "contextMode";
|
|
978
|
-
const CONTEXT_MODE_STANDARD = "standard";
|
|
979
|
-
const CONTEXT_MODE_EXTENDED = "extended";
|
|
980
|
-
const CONTEXT_MODE_CUSTOM = "custom";
|
|
981
|
-
const DEFAULT_CONTEXT_MODE = CONTEXT_MODE_STANDARD;
|
|
982
|
-
const CUSTOM_CONTEXT_WINDOW_FIELD = "customContextWindow";
|
|
983
|
-
const DEFAULT_CUSTOM_CONTEXT_WINDOW = 272e3;
|
|
984
|
-
const MIN_CUSTOM_CONTEXT_WINDOW = 128e3;
|
|
985
|
-
const MAX_CUSTOM_CONTEXT_WINDOW = 1e6;
|
|
986
|
-
const CUSTOM_CONTEXT_MODEL_FIELDS = Object.freeze({
|
|
987
|
-
"gpt-5.4": "customContextGpt54",
|
|
988
|
-
"gpt-5.4-mini": "customContextGpt54Mini",
|
|
989
|
-
"gpt-5.5": "customContextGpt55",
|
|
990
|
-
"gpt-5.6": "customContextGpt56",
|
|
991
|
-
"gpt-6-astra": "customContextGpt6Astra"
|
|
992
|
-
});
|
|
993
|
-
const CUSTOM_CONTEXT_MODEL_CAPS = Object.freeze({
|
|
994
|
-
"gpt-5.4": 1e6,
|
|
995
|
-
"gpt-5.4-mini": 4e5,
|
|
996
|
-
"gpt-5.5": 1e6,
|
|
997
|
-
"gpt-5.6": 1e6,
|
|
998
|
-
"gpt-6-astra": 872e3
|
|
999
|
-
});
|
|
1000
|
-
const CUSTOM_CONTEXT_MODEL_DEFAULTS = Object.freeze({
|
|
1001
|
-
"gpt-5.4": 272e3,
|
|
1002
|
-
"gpt-5.4-mini": 272e3,
|
|
1003
|
-
"gpt-5.5": 272e3,
|
|
1004
|
-
"gpt-5.6": 272e3,
|
|
1005
|
-
"gpt-6-astra": 272e3
|
|
1006
|
-
});
|
|
1007
|
-
const normalizeSearchProvider = (value) => [
|
|
1008
|
-
"auto",
|
|
1009
|
-
"dsh",
|
|
1010
|
-
"codex"
|
|
1011
|
-
].includes(value) ? value : DEFAULT_SEARCH_PROVIDER;
|
|
1012
|
-
const normalizeOutputVerbosity = (value) => [
|
|
1013
|
-
"default",
|
|
1014
|
-
"low",
|
|
1015
|
-
"medium",
|
|
1016
|
-
"high"
|
|
1017
|
-
].includes(value) ? value : DEFAULT_OUTPUT_VERBOSITY;
|
|
1018
|
-
const normalizeSpeedMode = (value) => ["standard", "fast"].includes(value) ? value : DEFAULT_SPEED_MODE;
|
|
1019
|
-
const normalizeContextMode = (value) => [
|
|
1020
|
-
"standard",
|
|
1021
|
-
"extended",
|
|
1022
|
-
"custom"
|
|
1023
|
-
].includes(value) ? value : DEFAULT_CONTEXT_MODE;
|
|
1024
|
-
const normalizeCustomContextWindow = (value, maximum = MAX_CUSTOM_CONTEXT_WINDOW) => {
|
|
1025
|
-
if (!Number.isInteger(value)) return DEFAULT_CUSTOM_CONTEXT_WINDOW;
|
|
1026
|
-
return Math.min(Math.max(value, MIN_CUSTOM_CONTEXT_WINDOW), maximum);
|
|
1027
|
-
};
|
|
1028
|
-
const formatContextWindow = (value) => value === 1e6 ? "1M" : `${Math.round(value / 1e3)}K`;
|
|
1029
|
-
const parseContextWindow = (value) => {
|
|
1030
|
-
const match = /^\s*(\d+)\s*$/u.exec(String(value));
|
|
1031
|
-
if (match === null) return NaN;
|
|
1032
|
-
return Number(match[1]);
|
|
1033
|
-
};
|
|
1034
|
-
const normalizeQuickQuotaMode = (value, legacyVisible = false) => [
|
|
1035
|
-
"off",
|
|
1036
|
-
"percent",
|
|
1037
|
-
"bar",
|
|
1038
|
-
"forecast"
|
|
1039
|
-
].includes(value) ? value : legacyVisible === true ? QUICK_QUOTA_MODE_PERCENT : "off";
|
|
1040
|
-
const supportsCodexFastMode = (modelId) => typeof modelId === "string" && (/^gpt-5\.(?:5|6)(?:$|-)/u.test(modelId) || modelId === "gpt-5.4");
|
|
1041
|
-
//#endregion
|
|
1042
|
-
//#region src/sidebar-quota.js
|
|
1043
|
-
const isDisplayableWindow = (window) => Number.isFinite(window?.remainingPercent) && window.remainingPercent >= 0 && window.remainingPercent <= 100 && Number.isFinite(window?.windowSeconds) && window.windowSeconds > 0;
|
|
1044
|
-
const normalized = (value) => String(value ?? "").toLocaleLowerCase("en-US").replaceAll(/[^a-z0-9]+/gu, "-");
|
|
1045
|
-
const limitMatchesModel = (limit, model) => {
|
|
1046
|
-
if (/\bspark\b/u.test(normalized(model))) return /\bspark\b/u.test(normalized(`${limit?.id ?? ""} ${limit?.name ?? ""}`));
|
|
1047
|
-
return limit?.id === "codex";
|
|
1048
|
-
};
|
|
1049
|
-
function selectModelQuotaWindows(usage, model) {
|
|
1050
|
-
return (Array.isArray(usage?.rateLimits) ? usage.rateLimits.filter((limit) => limitMatchesModel(limit, model) && Array.isArray(limit.windows)).flatMap((limit) => limit.windows).filter(isDisplayableWindow) : []).map((selected) => ({
|
|
1051
|
-
remainingPercent: selected.remainingPercent,
|
|
1052
|
-
windowSeconds: selected.windowSeconds,
|
|
1053
|
-
...Number.isSafeInteger(selected.resetsAt) ? { resetsAt: selected.resetsAt } : {},
|
|
1054
|
-
...selected.forecast === void 0 ? {} : { forecast: selected.forecast }
|
|
1055
|
-
})).sort((a, b) => a.windowSeconds - b.windowSeconds);
|
|
1056
|
-
}
|
|
1057
|
-
//#endregion
|
|
1058
|
-
//#region src/login-progress.js
|
|
1059
|
-
/** Reconcile a login flow with the credential store without exposing credentials. */
|
|
1060
|
-
async function readLoginProgress({ flow, readFlow, readAccount }) {
|
|
1061
|
-
try {
|
|
1062
|
-
const nextFlow = await readFlow();
|
|
1063
|
-
if (nextFlow.phase === "failed") try {
|
|
1064
|
-
const account = await readAccount();
|
|
1065
|
-
if (account?.authenticated === true) return {
|
|
1066
|
-
flow: {
|
|
1067
|
-
id: flow.id,
|
|
1068
|
-
method: flow.method,
|
|
1069
|
-
phase: "authenticated",
|
|
1070
|
-
authenticated: true
|
|
1071
|
-
},
|
|
1072
|
-
account,
|
|
1073
|
-
recovered: true
|
|
1074
|
-
};
|
|
1075
|
-
} catch {}
|
|
1076
|
-
if (nextFlow.phase !== "authenticated") return { flow: nextFlow };
|
|
1077
|
-
return {
|
|
1078
|
-
flow: nextFlow,
|
|
1079
|
-
account: await readAccount()
|
|
1080
|
-
};
|
|
1081
|
-
} catch (flowError) {
|
|
1082
|
-
try {
|
|
1083
|
-
const account = await readAccount();
|
|
1084
|
-
if (account?.authenticated === true) return {
|
|
1085
|
-
flow: {
|
|
1086
|
-
id: flow.id,
|
|
1087
|
-
method: flow.method,
|
|
1088
|
-
phase: "authenticated",
|
|
1089
|
-
authenticated: true
|
|
1090
|
-
},
|
|
1091
|
-
account,
|
|
1092
|
-
recovered: true
|
|
1093
|
-
};
|
|
1094
|
-
} catch {}
|
|
1095
|
-
throw flowError;
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
//#endregion
|
|
1099
|
-
//#region src/preference-controller.js
|
|
1100
|
-
const CHANNEL$1 = "/codex-subscription";
|
|
1101
|
-
const unwrap$1 = (response) => {
|
|
1102
|
-
if (!response?.ok) throw new Error(response?.error?.message ?? "Codex RPC failed");
|
|
1103
|
-
return response.value;
|
|
1104
|
-
};
|
|
1105
|
-
function createPreferenceController(scope, rpc) {
|
|
1106
|
-
let updating = false;
|
|
1107
|
-
let error = false;
|
|
1108
|
-
let fallbackStatus = "loading";
|
|
1109
|
-
let fallback;
|
|
1110
|
-
let pendingPatch;
|
|
1111
|
-
let failedPatch;
|
|
1112
|
-
let generation = 0;
|
|
1113
|
-
let contextModels = [];
|
|
1114
|
-
let verbosityModels = [];
|
|
1115
|
-
const nativeSnapshot = () => scope.getSnapshot();
|
|
1116
|
-
const read = () => {
|
|
1117
|
-
const native = nativeSnapshot();
|
|
1118
|
-
const current = native.status === "ready" ? native : fallbackStatus === "ready" ? fallback : native;
|
|
1119
|
-
const value = pendingPatch === void 0 ? current.value : {
|
|
1120
|
-
...current.value,
|
|
1121
|
-
...pendingPatch
|
|
1122
|
-
};
|
|
1123
|
-
return Object.freeze({
|
|
1124
|
-
status: current.status,
|
|
1125
|
-
quickQuotaMode: normalizeQuickQuotaMode(value?.[QUICK_QUOTA_MODE_FIELD], value?.[LEGACY_QUICK_QUOTA_FIELD]),
|
|
1126
|
-
searchProvider: normalizeSearchProvider(value?.[SEARCH_PROVIDER_FIELD]),
|
|
1127
|
-
speedMode: normalizeSpeedMode(value?.[SPEED_MODE_FIELD]),
|
|
1128
|
-
outputVerbosity: normalizeOutputVerbosity(value?.[OUTPUT_VERBOSITY_FIELD]),
|
|
1129
|
-
contextMode: normalizeContextMode(value?.[CONTEXT_MODE_FIELD]),
|
|
1130
|
-
customContextWindow: normalizeCustomContextWindow(value?.[CUSTOM_CONTEXT_WINDOW_FIELD]),
|
|
1131
|
-
customContextWindows: Object.fromEntries(Object.entries(CUSTOM_CONTEXT_MODEL_FIELDS).map(([modelKey, field]) => [modelKey, normalizeCustomContextWindow(value?.[field] ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[modelKey], CUSTOM_CONTEXT_MODEL_CAPS[modelKey])])),
|
|
1132
|
-
contextModels,
|
|
1133
|
-
verbosityModels,
|
|
1134
|
-
writable: !updating && current.status === "ready" && current.writable === true,
|
|
1135
|
-
saving: updating,
|
|
1136
|
-
error
|
|
1137
|
-
});
|
|
1138
|
-
};
|
|
1139
|
-
let snapshot = read();
|
|
1140
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
1141
|
-
const publish = () => {
|
|
1142
|
-
snapshot = read();
|
|
1143
|
-
for (const listener of listeners) listener();
|
|
1144
|
-
};
|
|
1145
|
-
const disposeScope = scope.subscribe(() => {
|
|
1146
|
-
error = false;
|
|
1147
|
-
if (!updating) failedPatch = void 0;
|
|
1148
|
-
publish();
|
|
1149
|
-
});
|
|
1150
|
-
const acceptFallback = (value) => {
|
|
1151
|
-
contextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
|
|
1152
|
-
verbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
|
|
1153
|
-
fallbackStatus = "ready";
|
|
1154
|
-
fallback = {
|
|
1155
|
-
status: "ready",
|
|
1156
|
-
value: {
|
|
1157
|
-
[QUICK_QUOTA_MODE_FIELD]: normalizeQuickQuotaMode(value?.[QUICK_QUOTA_MODE_FIELD], value?.[LEGACY_QUICK_QUOTA_FIELD]),
|
|
1158
|
-
[SEARCH_PROVIDER_FIELD]: normalizeSearchProvider(value?.[SEARCH_PROVIDER_FIELD]),
|
|
1159
|
-
[SPEED_MODE_FIELD]: normalizeSpeedMode(value?.[SPEED_MODE_FIELD]),
|
|
1160
|
-
[OUTPUT_VERBOSITY_FIELD]: normalizeOutputVerbosity(value?.[OUTPUT_VERBOSITY_FIELD]),
|
|
1161
|
-
[CONTEXT_MODE_FIELD]: normalizeContextMode(value?.[CONTEXT_MODE_FIELD]),
|
|
1162
|
-
[CUSTOM_CONTEXT_WINDOW_FIELD]: normalizeCustomContextWindow(value?.[CUSTOM_CONTEXT_WINDOW_FIELD]),
|
|
1163
|
-
...Object.fromEntries(Object.entries(CUSTOM_CONTEXT_MODEL_FIELDS).map(([modelKey, field]) => [field, normalizeCustomContextWindow(value?.[field] ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[modelKey], CUSTOM_CONTEXT_MODEL_CAPS[modelKey])]))
|
|
1164
|
-
},
|
|
1165
|
-
writable: value?.writable === true
|
|
1166
|
-
};
|
|
1167
|
-
};
|
|
1168
|
-
const load = async () => {
|
|
1169
|
-
const current = ++generation;
|
|
1170
|
-
updating = false;
|
|
1171
|
-
pendingPatch = void 0;
|
|
1172
|
-
fallbackStatus = "loading";
|
|
1173
|
-
fallback = void 0;
|
|
1174
|
-
error = false;
|
|
1175
|
-
publish();
|
|
1176
|
-
try {
|
|
1177
|
-
const value = unwrap$1(await rpc.call(CHANNEL$1, "preferences/status", {}));
|
|
1178
|
-
if (current !== generation) return;
|
|
1179
|
-
if (nativeSnapshot().status === "ready") {
|
|
1180
|
-
contextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
|
|
1181
|
-
verbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
|
|
1182
|
-
} else acceptFallback(value);
|
|
1183
|
-
publish();
|
|
1184
|
-
} catch {
|
|
1185
|
-
if (current !== generation || nativeSnapshot().status === "ready") return;
|
|
1186
|
-
fallbackStatus = "unavailable";
|
|
1187
|
-
publish();
|
|
1188
|
-
}
|
|
1189
|
-
};
|
|
1190
|
-
const set = async (patch) => {
|
|
1191
|
-
if (snapshot.status !== "ready" || snapshot.writable !== true) return;
|
|
1192
|
-
const current = ++generation;
|
|
1193
|
-
const entries = Object.entries(patch);
|
|
1194
|
-
updating = true;
|
|
1195
|
-
pendingPatch = patch;
|
|
1196
|
-
error = false;
|
|
1197
|
-
failedPatch = void 0;
|
|
1198
|
-
publish();
|
|
1199
|
-
try {
|
|
1200
|
-
if (nativeSnapshot().status === "ready") {
|
|
1201
|
-
for (const [field, value] of entries) {
|
|
1202
|
-
if (current !== generation) return;
|
|
1203
|
-
await scope.set(field, value);
|
|
1204
|
-
}
|
|
1205
|
-
if (current !== generation) return;
|
|
1206
|
-
const accepted = nativeSnapshot().value;
|
|
1207
|
-
error = entries.some(([field, value]) => accepted?.[field] !== value);
|
|
1208
|
-
pendingPatch = void 0;
|
|
1209
|
-
} else {
|
|
1210
|
-
const value = unwrap$1(await rpc.call(CHANNEL$1, "preferences/update", patch));
|
|
1211
|
-
if (current !== generation) return;
|
|
1212
|
-
acceptFallback(value);
|
|
1213
|
-
pendingPatch = void 0;
|
|
1214
|
-
}
|
|
1215
|
-
} catch {
|
|
1216
|
-
if (current === generation) {
|
|
1217
|
-
pendingPatch = void 0;
|
|
1218
|
-
error = true;
|
|
1219
|
-
failedPatch = patch;
|
|
1220
|
-
}
|
|
1221
|
-
} finally {
|
|
1222
|
-
if (current === generation) {
|
|
1223
|
-
updating = false;
|
|
1224
|
-
publish();
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
};
|
|
1228
|
-
return {
|
|
1229
|
-
getSnapshot: () => snapshot,
|
|
1230
|
-
subscribe: (listener) => {
|
|
1231
|
-
listeners.add(listener);
|
|
1232
|
-
return () => listeners.delete(listener);
|
|
1233
|
-
},
|
|
1234
|
-
load,
|
|
1235
|
-
set,
|
|
1236
|
-
retry: () => failedPatch === void 0 ? load() : set(failedPatch),
|
|
1237
|
-
dispose: disposeScope
|
|
1238
|
-
};
|
|
1239
|
-
}
|
|
1240
|
-
//#endregion
|
|
1241
|
-
//#region src/client.jsx
|
|
1242
|
-
const inject = [
|
|
1243
|
-
"slots",
|
|
1244
|
-
"locale",
|
|
1245
|
-
"connection",
|
|
1246
|
-
"remote",
|
|
1247
|
-
"settingsScope",
|
|
1248
|
-
"modelDirectories",
|
|
1249
|
-
"conversation",
|
|
1250
|
-
"uiConversation",
|
|
1251
|
-
"sessions"
|
|
1252
|
-
];
|
|
1253
|
-
const NS = "settings.codexSubscription";
|
|
1254
|
-
const CHANNEL = "/codex-subscription";
|
|
1255
|
-
const SUPPORT_ISSUE_URL = "https://github.com/WSL043/dsh-codex-subscription/issues/new?template=install-problem.yml";
|
|
1256
|
-
const QUICK_QUOTA_REFRESH_EVENT = "dsh-codex-subscription:refresh-quick-quota";
|
|
1257
|
-
const QUICK_QUOTA_REFRESH_MS = 6e4;
|
|
333
|
+
//#region src/client-locales.js
|
|
1258
334
|
const zh = {
|
|
1259
335
|
imageEditLocation: "位置",
|
|
1260
336
|
imageEditReferenceGuide: "本次编辑的干净源图为「{sourceName}」,编号定位参考图为「{referenceName}」。坐标以图片左上角为原点,x 向右、y 向下,百分比相对于整张图片。请查看这两张图片,将它们同时作为编辑工具的参考图,并在工具提示词中完整保留下方编号、位置和修改要求。只修改源图中对应位置的内容;定位参考图上的编号、圆点和引线仅用于定位,不得绘入最终结果。若无法读取两张图片或确定位置,请说明问题,不要猜测或忽略标注。",
|
|
@@ -1279,8 +355,13 @@ window.__ModuleLoader__.load({
|
|
|
1279
355
|
deviceHint: "在登录页输入此设备代码:",
|
|
1280
356
|
waiting: "正在等待登录完成…",
|
|
1281
357
|
failed: "登录失败,请重试。",
|
|
1282
|
-
loadFailed: "无法读取账户状态。",
|
|
1283
358
|
accountRetry: "重试",
|
|
359
|
+
accountRetrying: "正在重试账户状态…",
|
|
360
|
+
accountCredentialUnavailable: "登录凭据暂时不可用。请重试;不会删除已保存的登录信息。",
|
|
361
|
+
accountCredentialMalformed: "登录凭据格式异常,无法读取账户状态。重试不会删除已保存的登录信息。",
|
|
362
|
+
accountStatusTimeout: "读取账户状态超时,请重试。",
|
|
363
|
+
accountStatusTransport: "无法连接账户服务,请检查连接后重试。",
|
|
364
|
+
accountStatusUnknown: "无法读取账户状态,请重试。",
|
|
1284
365
|
diagnostics: "支持诊断",
|
|
1285
366
|
diagnosticsLoad: "生成诊断",
|
|
1286
367
|
diagnosticsLoading: "生成中…",
|
|
@@ -1409,6 +490,7 @@ window.__ModuleLoader__.load({
|
|
|
1409
490
|
modelsEmpty: "没有可用模型。",
|
|
1410
491
|
effortsEmpty: "当前模型未提供推理等级。",
|
|
1411
492
|
modelRetry: "重试",
|
|
493
|
+
modelDirectoryFailed: "模型目录加载失败,请重试。",
|
|
1412
494
|
modelFailed: "模型目录加载失败:{value}",
|
|
1413
495
|
groupFailed: "{name}:{value}",
|
|
1414
496
|
imageGenerate: "生成图片",
|
|
@@ -1422,13 +504,10 @@ window.__ModuleLoader__.load({
|
|
|
1422
504
|
imageLoading: "正在加载图片…",
|
|
1423
505
|
imageLoadFailed: "图片加载失败,点击重试",
|
|
1424
506
|
imagePreview: "图片预览",
|
|
1425
|
-
imagePreviewShort: "预览图",
|
|
1426
507
|
imageClosePreview: "关闭预览",
|
|
1427
508
|
imageDownload: "下载",
|
|
1428
509
|
imageDownloadPreparing: "正在准备原图…",
|
|
1429
510
|
imageDownloadFailed: "下载失败,重试",
|
|
1430
|
-
imageZoomOut: "缩小",
|
|
1431
|
-
imageZoomIn: "放大",
|
|
1432
511
|
imageFit: "适合窗口",
|
|
1433
512
|
imageAnnotate: "标注部位",
|
|
1434
513
|
imageAnnotateCancel: "取消标注",
|
|
@@ -1442,7 +521,6 @@ window.__ModuleLoader__.load({
|
|
|
1442
521
|
imageNext: "下一张图片",
|
|
1443
522
|
imageZoomHint: "滚轮缩放 · 拖动查看 · 双击切换原始大小",
|
|
1444
523
|
imageActual: "原始大小",
|
|
1445
|
-
imageEditPrompt: "描述你想怎样修改这张图",
|
|
1446
524
|
imageEditDefault: "编辑这张图片。",
|
|
1447
525
|
imageRegionNotes: "部位修改:",
|
|
1448
526
|
imageEdit: "在输入框中继续编辑",
|
|
@@ -1474,8 +552,13 @@ window.__ModuleLoader__.load({
|
|
|
1474
552
|
deviceHint: "Enter this device code on the sign-in page:",
|
|
1475
553
|
waiting: "Waiting for sign-in to finish…",
|
|
1476
554
|
failed: "Sign-in failed. Try again.",
|
|
1477
|
-
loadFailed: "Could not read account status.",
|
|
1478
555
|
accountRetry: "Retry",
|
|
556
|
+
accountRetrying: "Retrying account status…",
|
|
557
|
+
accountCredentialUnavailable: "The saved sign-in credentials are temporarily unavailable. Retry; saved sign-in information will not be deleted.",
|
|
558
|
+
accountCredentialMalformed: "The saved sign-in credentials are malformed, so account status cannot be read. Retrying will not delete saved sign-in information.",
|
|
559
|
+
accountStatusTimeout: "Reading account status timed out. Retry.",
|
|
560
|
+
accountStatusTransport: "The account service is unavailable. Check the connection and retry.",
|
|
561
|
+
accountStatusUnknown: "Could not read account status. Retry.",
|
|
1479
562
|
diagnostics: "Support diagnostics",
|
|
1480
563
|
diagnosticsLoad: "Create report",
|
|
1481
564
|
diagnosticsLoading: "Creating…",
|
|
@@ -1604,6 +687,7 @@ window.__ModuleLoader__.load({
|
|
|
1604
687
|
modelsEmpty: "No models available.",
|
|
1605
688
|
effortsEmpty: "This model provides no reasoning effort levels.",
|
|
1606
689
|
modelRetry: "Retry",
|
|
690
|
+
modelDirectoryFailed: "Could not load the model directory. Try again.",
|
|
1607
691
|
modelFailed: "Could not load models: {value}",
|
|
1608
692
|
groupFailed: "{name}: {value}",
|
|
1609
693
|
imageGenerate: "Generate image",
|
|
@@ -1617,13 +701,10 @@ window.__ModuleLoader__.load({
|
|
|
1617
701
|
imageLoading: "Loading image…",
|
|
1618
702
|
imageLoadFailed: "Image failed to load. Click to retry",
|
|
1619
703
|
imagePreview: "Image preview",
|
|
1620
|
-
imagePreviewShort: "Preview",
|
|
1621
704
|
imageClosePreview: "Close preview",
|
|
1622
705
|
imageDownload: "Download",
|
|
1623
706
|
imageDownloadPreparing: "Preparing original…",
|
|
1624
707
|
imageDownloadFailed: "Download failed. Retry",
|
|
1625
|
-
imageZoomOut: "Zoom out",
|
|
1626
|
-
imageZoomIn: "Zoom in",
|
|
1627
708
|
imageFit: "Fit to window",
|
|
1628
709
|
imageAnnotate: "Annotate",
|
|
1629
710
|
imageAnnotateCancel: "Cancel marking",
|
|
@@ -1637,7 +718,6 @@ window.__ModuleLoader__.load({
|
|
|
1637
718
|
imageNext: "Next image",
|
|
1638
719
|
imageZoomHint: "Wheel to zoom · drag to pan · double-click for 100%",
|
|
1639
720
|
imageActual: "100%",
|
|
1640
|
-
imageEditPrompt: "Describe how you want to change this image",
|
|
1641
721
|
imageEditDefault: "Edit this image.",
|
|
1642
722
|
imageRegionNotes: "Region changes:",
|
|
1643
723
|
imageEdit: "Continue editing in composer",
|
|
@@ -1645,55 +725,1598 @@ window.__ModuleLoader__.load({
|
|
|
1645
725
|
imageEditFailed: "Handoff failed. Add a note to every marker and ensure the composer accepts images, then retry.",
|
|
1646
726
|
imageRemoveAnnotation: "Remove note"
|
|
1647
727
|
};
|
|
728
|
+
//#endregion
|
|
729
|
+
//#region src/client-styles.js
|
|
1648
730
|
const STYLE = `
|
|
1649
|
-
.codexSubscriptionSearchHead{display:flex;flex-direction:column;gap:1px}
|
|
731
|
+
.codexSubscriptionSearchHead{display:flex;flex-direction:column;gap:1px}
|
|
732
|
+
.codexSubscriptionSearchScope{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
1650
733
|
.codexSubscription{display:flex;flex-direction:column;gap:10px;max-width:720px;color:var(--dsw-alias-label-primary);container-type:inline-size}
|
|
1651
|
-
.codexSubscription h2,.codexSubscription h3,.codexSubscription p{margin:0}
|
|
1652
|
-
.
|
|
1653
|
-
.
|
|
1654
|
-
.
|
|
734
|
+
.codexSubscription h2,.codexSubscription h3,.codexSubscription p{margin:0}
|
|
735
|
+
.codexSubscriptionHead{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
736
|
+
.codexSubscription h2{font-size:16px;line-height:24px;font-weight:500}
|
|
737
|
+
.codexSubscription h3{font-size:14px;line-height:22px;font-weight:500}
|
|
1655
738
|
.codexSubscriptionCard{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:14px 16px;display:flex;flex-direction:column;gap:12px}
|
|
1656
|
-
.codexSubscriptionUsageCard{padding:12px 14px;gap:9px}
|
|
1657
|
-
.
|
|
1658
|
-
.
|
|
1659
|
-
.
|
|
1660
|
-
.
|
|
1661
|
-
.
|
|
1662
|
-
.
|
|
1663
|
-
.
|
|
1664
|
-
.
|
|
1665
|
-
.
|
|
1666
|
-
.
|
|
1667
|
-
.
|
|
1668
|
-
.
|
|
1669
|
-
.
|
|
1670
|
-
.
|
|
1671
|
-
.
|
|
739
|
+
.codexSubscriptionUsageCard{padding:12px 14px;gap:9px}
|
|
740
|
+
.codexSubscriptionPreferencesCard{padding:12px 14px;gap:10px}
|
|
741
|
+
.codexSubscriptionPreference{min-height:32px;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}
|
|
742
|
+
.codexSubscriptionPreferenceCopy{display:flex;min-width:0;flex-direction:column;gap:2px}
|
|
743
|
+
.codexSubscriptionPreferenceLabel{display:flex;align-items:center;gap:6px}
|
|
744
|
+
.codexSubscriptionPreferenceHint{max-width:300px;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
745
|
+
.codexSubscriptionQuotaModes{display:flex;align-items:center;gap:3px;padding:2px;border-radius:9px;background:var(--dsw-alias-bg-module-platform)}
|
|
746
|
+
.codexSubscriptionQuotaMode{position:relative;display:flex;align-items:center;justify-content:center;min-height:26px;padding:0 9px;border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;cursor:pointer;white-space:nowrap}
|
|
747
|
+
.codexSubscriptionQuotaMode small{margin-left:3px;font-size:9px;line-height:1;color:var(--dsw-alias-label-tertiary)}
|
|
748
|
+
.codexSubscriptionQuotaMode:has(input:checked){background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);box-shadow:0 0 0 1px var(--dsw-alias-border-l3)}
|
|
749
|
+
.codexSubscriptionQuotaMode:has(input:focus-visible){outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}
|
|
750
|
+
.codexSubscriptionQuotaMode:has(input:disabled){cursor:not-allowed;opacity:.5}
|
|
751
|
+
.codexSubscriptionQuotaMode input{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
|
|
752
|
+
.codexSubscriptionContext{display:flex;flex-direction:column;gap:8px}
|
|
753
|
+
.codexSubscriptionContextHead{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
|
|
754
|
+
.codexSubscriptionContextCopy{display:flex;min-width:0;flex:1;flex-direction:column;gap:2px}
|
|
755
|
+
.codexSubscriptionContextHint{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
756
|
+
.codexSubscriptionContextTrigger{height:32px;min-width:108px;display:inline-flex;align-items:center;justify-content:space-between;gap:10px;padding:0 10px 0 12px;border:0;border-radius:999px;outline:0;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;cursor:pointer}
|
|
757
|
+
.codexSubscriptionContextTrigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
758
|
+
.codexSubscriptionContextTrigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
759
|
+
.codexSubscriptionContextTrigger:disabled{color:var(--dsw-alias-label-dimmed);cursor:not-allowed}
|
|
760
|
+
.codexSubscriptionContextTrigger svg{color:var(--dsw-alias-label-tertiary);transition:transform 120ms var(--ds-ease-in-out)}
|
|
761
|
+
.codexSubscriptionContextTrigger[aria-expanded=true] svg{transform:rotate(180deg)}
|
|
762
|
+
.codexSubscriptionContextModels{display:flex;flex-direction:column;border-top:1px solid var(--dsw-alias-border-l2)}
|
|
763
|
+
.codexSubscriptionContextModel{min-height:42px;display:flex;align-items:center;justify-content:space-between;gap:12px;border-bottom:1px solid var(--dsw-alias-border-l2)}
|
|
764
|
+
.codexSubscriptionContextModel:last-child{border-bottom:0}
|
|
765
|
+
.codexSubscriptionContextModelCopy{display:flex;min-width:0;flex-direction:column}
|
|
766
|
+
.codexSubscriptionContextModelCopy strong{font-size:12px;line-height:18px;font-weight:500}
|
|
767
|
+
.codexSubscriptionContextModelCopy span{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}
|
|
768
|
+
.codexSubscriptionContextInput{width:116px}
|
|
769
|
+
.codexSubscriptionSearch{display:flex;flex-direction:column;gap:7px}
|
|
770
|
+
.codexSubscriptionSearchChoices{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:6px}
|
|
771
|
+
.codexSubscriptionSearchChoice{display:grid;grid-template-columns:14px minmax(0,1fr);align-items:center;column-gap:8px;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);padding:9px 10px;text-align:left;cursor:pointer}
|
|
772
|
+
.codexSubscriptionSearchChoice:has(input:disabled){cursor:not-allowed;opacity:.5}
|
|
773
|
+
.codexSubscriptionSearchChoice:has(input:checked){border-color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}
|
|
774
|
+
.codexSubscriptionSearchChoice:has(input:focus-visible){outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}
|
|
775
|
+
.codexSubscriptionSearchInput{width:14px;height:14px;margin:0;accent-color:var(--dsw-alias-label-primary);cursor:inherit}
|
|
776
|
+
.codexSubscriptionSearchCopy{display:block;min-width:0;pointer-events:none}
|
|
777
|
+
.codexSubscriptionSearchCopy strong,.codexSubscriptionSearchCopy span{display:block}
|
|
778
|
+
.codexSubscriptionSearchCopy strong{font-size:12px;line-height:18px;font-weight:500;color:var(--dsw-alias-label-secondary)}
|
|
779
|
+
.codexSubscriptionSearchChoice:has(input:checked) strong{color:var(--dsw-alias-label-primary)}
|
|
780
|
+
.codexSubscriptionSearchCopy span{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}
|
|
781
|
+
.codexSubscriptionDivider{height:1px;background:var(--dsw-alias-border-l2)}
|
|
782
|
+
.codexSubscriptionQuotaModes[data-saving=true] .codexSubscriptionQuotaMode:has(input:disabled){cursor:wait;opacity:1}
|
|
783
|
+
.codexSubscriptionSearchChoices[data-saving=true] .codexSubscriptionSearchChoice:has(input:disabled){cursor:wait;opacity:1}
|
|
784
|
+
.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{display:flex;align-items:center;justify-content:space-between;gap:12px}
|
|
785
|
+
.codexSubscriptionStatus{display:flex;align-items:center;gap:8px;font-size:14px;line-height:22px;font-weight:500}
|
|
786
|
+
.codexSubscriptionAccounts{display:flex;flex-direction:column;border-top:1px solid var(--dsw-alias-border-l2)}
|
|
787
|
+
.codexSubscriptionAccount{display:flex;align-items:center;justify-content:space-between;gap:10px;min-height:42px;border-bottom:1px solid var(--dsw-alias-border-l2);font-size:13px}
|
|
788
|
+
.codexSubscriptionAccount:last-child{border-bottom:0}
|
|
789
|
+
.codexSubscriptionAccount[data-active=true] .codexSubscriptionEmail,.codexSubscriptionAccount[data-active=true]>span{font-weight:600}
|
|
790
|
+
.codexSubscriptionEmail{max-width:100%;overflow:hidden;padding:2px 4px;border:0;border-radius:5px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}
|
|
791
|
+
.codexSubscriptionEmail:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
792
|
+
.codexSubscriptionEmail:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}
|
|
793
|
+
.codexSubscriptionFlow label{display:flex;flex-direction:column;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary)}
|
|
794
|
+
.codexSubscriptionDot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-dimmed)}
|
|
795
|
+
.codexSubscriptionDot[data-state=connected]{background:var(--dsw-alias-state-success-primary)}
|
|
796
|
+
.codexSubscriptionDot[data-state=disconnected]{background:var(--dsw-alias-state-error-primary)}
|
|
797
|
+
.codexSubscriptionActions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
798
|
+
.codexSubscriptionFlow{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border-radius:10px;background:var(--dsw-alias-bg-module-platform)}
|
|
799
|
+
.codexSubscriptionFlow p{font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}
|
|
800
|
+
.codexSubscriptionCode{width:max-content;max-width:100%;font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:.08em;overflow-wrap:anywhere}
|
|
801
|
+
.codexSubscriptionError{font-size:13px;line-height:20px;color:var(--dsw-alias-state-error-primary)}
|
|
802
|
+
.codexSubscriptionInput{width:100%;box-sizing:border-box}
|
|
803
|
+
.codexSubscriptionRecover{display:flex;align-items:center;justify-content:space-between;gap:12px}
|
|
804
|
+
.codexSubscriptionRecover .codexSubscriptionError{flex:1}
|
|
805
|
+
.codexSubscriptionRecover button{flex:0 0 auto}
|
|
806
|
+
.codexSubscriptionDiagnostics{padding:8px 12px;gap:8px;background:transparent;color:var(--dsw-alias-label-secondary)}
|
|
807
|
+
.codexSubscriptionDiagnostics pre{max-height:240px;margin:0;padding:10px 12px;border-radius:8px;background:var(--dsw-alias-bg-module-platform);overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/17px ui-monospace,SFMono-Regular,Consolas,monospace;color:var(--dsw-alias-label-secondary)}
|
|
808
|
+
.codexSubscriptionLink{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;min-height:32px;padding:0 13px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:transparent;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;text-decoration:none;white-space:nowrap}
|
|
809
|
+
.codexSubscriptionLink:hover{background:var(--dsw-alias-bg-module-platform)}
|
|
810
|
+
.codexSubscriptionLink:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}
|
|
811
|
+
.codexSubscriptionSectionTitle{display:flex;flex:1;min-width:0;flex-direction:column;gap:2px}
|
|
812
|
+
.codexSubscriptionFreshness{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
813
|
+
.codexSubscriptionRefresh{flex:0 0 auto;min-width:72px;width:max-content;white-space:nowrap!important;word-break:keep-all!important;overflow-wrap:normal!important;writing-mode:horizontal-tb!important}
|
|
814
|
+
.codexSubscriptionRefresh *{white-space:nowrap!important;word-break:keep-all!important;writing-mode:horizontal-tb!important}
|
|
1672
815
|
.codexSubscriptionEmpty{padding:18px;border:1px dashed var(--dsw-alias-border-l3);border-radius:10px;text-align:center;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
|
|
1673
|
-
.codexSubscriptionLimits{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:6px}
|
|
1674
|
-
.
|
|
816
|
+
.codexSubscriptionLimits{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:6px}
|
|
817
|
+
.codexSubscriptionLimit{min-width:0;border-radius:10px;padding:9px 12px;background:var(--dsw-alias-bg-module-platform);display:flex;flex-direction:column;gap:6px}
|
|
818
|
+
.codexSubscriptionLimitTop{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
|
|
819
|
+
.codexSubscriptionLimitLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
|
|
820
|
+
.codexSubscriptionLimit strong{font:600 18px/24px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums}
|
|
1675
821
|
.codexSubscriptionLimit progress{width:100%;height:4px;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}
|
|
1676
|
-
.codexSubscriptionLimit progress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}
|
|
1677
|
-
.
|
|
1678
|
-
.
|
|
1679
|
-
.
|
|
1680
|
-
.
|
|
1681
|
-
.
|
|
1682
|
-
.
|
|
1683
|
-
.
|
|
1684
|
-
.
|
|
822
|
+
.codexSubscriptionLimit progress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}
|
|
823
|
+
.codexSubscriptionLimit progress::-webkit-progress-value{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}
|
|
824
|
+
.codexSubscriptionLimit progress::-moz-progress-bar{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}
|
|
825
|
+
.codexSubscriptionLimitMeta{display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
826
|
+
.codexSubscriptionCreditSection{display:flex;flex-direction:column;gap:7px}
|
|
827
|
+
.codexSubscriptionCreditNote{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
828
|
+
.codexSubscriptionCreditRows{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px}
|
|
829
|
+
.codexSubscriptionCreditBalance,.codexSubscriptionSpendLimit{min-width:0;border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-module-platform)}
|
|
830
|
+
.codexSubscriptionCreditBalance{display:flex;flex-direction:column;gap:6px}
|
|
831
|
+
.codexSubscriptionCreditBalance span,.codexSubscriptionCreditLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
|
|
832
|
+
.codexSubscriptionCreditBalance strong{font:600 18px/24px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
|
|
833
|
+
.codexSubscriptionCreditRows{display:flex;flex-direction:column;gap:6px}
|
|
834
|
+
.codexSubscriptionResetMeta{display:flex;min-width:0;flex-direction:column;gap:1px}
|
|
835
|
+
.codexSubscriptionResetBalance{display:flex;flex-direction:column;gap:8px}
|
|
836
|
+
.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)}
|
|
837
|
+
.codexSubscriptionResetCard .codexSubscriptionResetMeta{flex:1}
|
|
838
|
+
.codexSubscriptionResetCard strong{overflow:hidden;font-size:12px;line-height:18px;font-weight:500;text-overflow:ellipsis;white-space:nowrap}
|
|
839
|
+
.codexSubscriptionResetCard .codexSubscriptionActions{flex:0 0 auto}
|
|
840
|
+
.codexSubscriptionResetCard .codexSubscriptionResetUse{min-height:28px;padding:0 10px}
|
|
841
|
+
.codexSubscriptionResetBalance .codexSubscriptionActions{justify-content:flex-start}
|
|
842
|
+
.codexSubscriptionResetFlow{display:flex;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:10px}
|
|
843
|
+
.codexSubscriptionResetFlow h4{margin:0;font-size:13px;line-height:20px;font-weight:500}
|
|
844
|
+
.codexSubscriptionResetWarning{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
|
|
845
|
+
.codexSubscriptionResetExpiry{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
846
|
+
.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}
|
|
847
|
+
.codexSubscriptionResetCheck input{margin:3px 0 0;accent-color:var(--dsw-alias-label-primary)}
|
|
848
|
+
.codexSubscriptionResetFinal{border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}
|
|
849
|
+
.codexSubscriptionResetResult{font-size:12px;line-height:18px;color:var(--dsw-alias-state-success-primary)}
|
|
850
|
+
.codexSubscriptionResetUse:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
851
|
+
.codexSubscriptionResetUse:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}
|
|
852
|
+
.codexSubscriptionSpendLimit{display:flex;flex-direction:column;gap:8px}
|
|
853
|
+
.codexSubscriptionSpendTop{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
|
|
854
|
+
.codexSubscriptionSpendTop strong{font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums}
|
|
855
|
+
.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}
|
|
856
|
+
.codexSubscriptionSpendLimit progress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}
|
|
857
|
+
.codexSubscriptionSpendLimit progress::-webkit-progress-value{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}
|
|
858
|
+
.codexSubscriptionSpendLimit progress::-moz-progress-bar{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}
|
|
859
|
+
.codexComposerQuotaWindows{display:inline-flex;align-items:center;gap:10px;flex-wrap:wrap}
|
|
860
|
+
.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}
|
|
861
|
+
.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}
|
|
862
|
+
.codexComposerQuotaBar::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}
|
|
863
|
+
.codexComposerQuotaBar::-webkit-progress-value{background:var(--dsw-alias-label-secondary);border-radius:999px}
|
|
864
|
+
.codexComposerQuotaBar::-moz-progress-bar{background:var(--dsw-alias-label-secondary);border-radius:999px}
|
|
865
|
+
.codexModelSelect{position:relative;min-width:0}
|
|
866
|
+
.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}
|
|
867
|
+
.codexModelSelectTrigger:hover:not(:disabled),.codexModelSelectTrigger[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover)}
|
|
868
|
+
.codexModelSelectTrigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
869
|
+
.codexModelSelectTrigger:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}
|
|
870
|
+
.codexModelSelectBolt{display:block;flex:none;width:14px;height:14px;color:var(--dsw-alias-label-primary)}
|
|
871
|
+
.codexModelSelectLabel{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
872
|
+
.codexModelSelectEffort{flex:none;color:var(--dsw-alias-label-caption)}
|
|
873
|
+
.codexModelSelectChevron{flex:none;color:var(--dsw-alias-label-caption);transition:transform 120ms}
|
|
874
|
+
.codexModelSelectTrigger[aria-expanded=true] .codexModelSelectChevron{transform:rotate(180deg)}
|
|
875
|
+
.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}
|
|
876
|
+
.codexModelSelectMenu{right:0;bottom:calc(100% + 8px)}
|
|
877
|
+
.codexModelSelectSubmenu{right:calc(100% + 8px);bottom:0;min-width:min(230px,calc(100vw - 32px))}
|
|
878
|
+
.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}
|
|
879
|
+
.codexModelSelectCell:hover,.codexModelSelectCell:focus-visible,.codexModelSelectCell[data-open=true]{background:var(--dsw-alias-interactive-bg-hover);outline:0}
|
|
880
|
+
.codexModelSelectCell:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}
|
|
881
|
+
.codexModelSelectCellLabel{flex:none;white-space:nowrap}
|
|
882
|
+
.codexModelSelectCellValue{flex:auto;min-width:0;overflow:hidden;color:var(--dsw-alias-label-tertiary);text-align:right;text-overflow:ellipsis;white-space:nowrap}
|
|
883
|
+
.codexModelSelectCellChevron{flex:none;color:var(--dsw-alias-label-tertiary)}
|
|
884
|
+
.codexModelSelectGroups{min-height:0;max-height:352px;overflow-y:auto}
|
|
885
|
+
.codexModelSelectGroup+.codexModelSelectGroup{margin-top:4px}
|
|
886
|
+
.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}
|
|
887
|
+
.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}
|
|
888
|
+
.codexModelSelectOption:hover:not(:disabled),.codexModelSelectOption:focus-visible{background:var(--dsw-alias-interactive-bg-hover)}
|
|
889
|
+
.codexModelSelectOption:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}
|
|
890
|
+
.codexModelSelectOptionCopy{display:flex;flex:1;min-width:0;flex-direction:column}
|
|
891
|
+
.codexModelSelectOptionName{overflow:hidden;color:inherit;font-size:14px;font-weight:500;line-height:20px;text-overflow:ellipsis;white-space:nowrap}
|
|
892
|
+
.codexModelSelectOptionDescription{overflow:hidden;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;text-overflow:ellipsis;white-space:nowrap}
|
|
893
|
+
.codexModelSelectCheck{display:grid;place-items:center;flex:0 0 18px;color:var(--dsw-alias-label-primary)}
|
|
894
|
+
.codexModelSelectStatus,.codexModelSelectEmpty{padding:10px;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}
|
|
895
|
+
.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}
|
|
896
|
+
.codexModelSelectWarning{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-state-warn-label)}
|
|
897
|
+
.codexModelSelectRetry{flex:none;padding:0;border:0;background:transparent;color:inherit;font:inherit;font-weight:600;cursor:pointer}
|
|
1685
898
|
.codexModelSelectMenu{overflow:visible}
|
|
1686
|
-
.codexImageTool{display:flex;flex-direction:column;gap:8px;margin:4px 0;color:var(--dsw-alias-label-primary)}
|
|
899
|
+
.codexImageTool{display:flex;flex-direction:column;gap:8px;margin:4px 0;color:var(--dsw-alias-label-primary)}
|
|
900
|
+
.codexImageToolRow{display:flex;align-items:center;min-height:24px;gap:8px;font-size:13px;line-height:20px}
|
|
901
|
+
.codexImageToolIcon{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;color:var(--dsw-alias-label-secondary)}
|
|
902
|
+
.codexImageToolIcon::before{content:'';width:8px;height:8px;border:1.5px solid currentColor;border-radius:3px}
|
|
903
|
+
.codexImageTool[data-state=running] .codexImageToolIcon::before{border-radius:50%;border-right-color:transparent;animation:codexImageSpin 800ms linear infinite}
|
|
904
|
+
.codexImageTool[data-state=error] .codexImageToolIcon::before{border-color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-state-error-primary)}
|
|
905
|
+
.codexImageToolTitle{font-weight:500}
|
|
906
|
+
.codexImageToolState{color:var(--dsw-alias-label-tertiary)}
|
|
907
|
+
.codexImageToolError{margin:0 0 0 24px;font-size:12px;line-height:18px;color:var(--dsw-alias-state-error-primary)}
|
|
908
|
+
.codexImageToolGallery{margin-left:24px}
|
|
909
|
+
.codexGeneratedImageFrame{display:flex;align-items:center;justify-content:center;width:min(240px,100%);height:240px;padding:0;overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:16px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-tertiary);cursor:pointer}
|
|
910
|
+
.codexGeneratedImageFrame img{display:block;width:100%;height:100%;object-fit:cover}
|
|
911
|
+
.codexGeneratedImageRetry{min-height:36px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);cursor:pointer}
|
|
912
|
+
@keyframes codexImageSpin{to{transform:rotate(360deg)}}
|
|
1687
913
|
.codexImageBeta{padding:0 5px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:16px}
|
|
1688
914
|
.codexImageToolGallery{display:flex;align-items:flex-start;flex-direction:column;gap:8px}
|
|
1689
915
|
@container (max-width:560px){.codexSubscriptionCreditRows{grid-template-columns:1fr}}
|
|
1690
|
-
@container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}
|
|
916
|
+
@container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}
|
|
917
|
+
.codexSubscriptionActions{width:100%}
|
|
918
|
+
.codexSubscriptionSearchChoices{grid-template-columns:1fr}}
|
|
1691
919
|
@media(max-width:640px){.codexSubscriptionCard{padding:14px}}
|
|
1692
920
|
`;
|
|
921
|
+
//#endregion
|
|
922
|
+
//#region node_modules/.pnpm/@heroicons+react@2.2.0_react@18.3.1/node_modules/@heroicons/react/16/solid/esm/BoltIcon.js
|
|
923
|
+
function BoltIcon({ title, titleId, ...props }, svgRef) {
|
|
924
|
+
return /*#__PURE__*/ react.createElement("svg", Object.assign({
|
|
925
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
926
|
+
viewBox: "0 0 16 16",
|
|
927
|
+
fill: "currentColor",
|
|
928
|
+
"aria-hidden": "true",
|
|
929
|
+
"data-slot": "icon",
|
|
930
|
+
ref: svgRef,
|
|
931
|
+
"aria-labelledby": titleId
|
|
932
|
+
}, props), title ? /*#__PURE__*/ react.createElement("title", { id: titleId }, title) : null, /*#__PURE__*/ react.createElement("path", {
|
|
933
|
+
fillRule: "evenodd",
|
|
934
|
+
d: "M9.58 1.077a.75.75 0 0 1 .405.82L9.165 6h4.085a.75.75 0 0 1 .567 1.241l-6.5 7.5a.75.75 0 0 1-1.302-.638L6.835 10H2.75a.75.75 0 0 1-.567-1.241l6.5-7.5a.75.75 0 0 1 .897-.182Z",
|
|
935
|
+
clipRule: "evenodd"
|
|
936
|
+
}));
|
|
937
|
+
}
|
|
938
|
+
const ForwardRef = /*#__PURE__*/ react.forwardRef(BoltIcon);
|
|
939
|
+
//#endregion
|
|
940
|
+
//#region src/image-edit-reference.js
|
|
941
|
+
const TWO_PI = Math.PI * 2;
|
|
942
|
+
const PIN_FILL = "#e11d48";
|
|
943
|
+
const PIN_OUTER_STROKE = "#111827";
|
|
944
|
+
const PIN_INNER_STROKE = "#ffffff";
|
|
945
|
+
const finiteDimension = (value) => Number.isSafeInteger(value) && value > 0;
|
|
946
|
+
const clamp$1 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
947
|
+
const clampCenter = (value, size, edge) => {
|
|
948
|
+
const margin = Math.min(edge, size / 2);
|
|
949
|
+
return clamp$1(value, margin, size - margin);
|
|
950
|
+
};
|
|
951
|
+
function getBitmapFactory(options) {
|
|
952
|
+
const factory = options?.createImageBitmap ?? options?.bitmapFactory ?? globalThis.createImageBitmap;
|
|
953
|
+
if (typeof factory !== "function") throw new Error("createImageBitmap is not available");
|
|
954
|
+
return factory;
|
|
955
|
+
}
|
|
956
|
+
function getCanvasFactory(options) {
|
|
957
|
+
const injected = options?.createCanvas ?? options?.canvasFactory;
|
|
958
|
+
if (typeof injected === "function") return injected;
|
|
959
|
+
const document = globalThis.document;
|
|
960
|
+
if (document !== void 0 && typeof document.createElement === "function") return (width, height) => {
|
|
961
|
+
const canvas = document.createElement("canvas");
|
|
962
|
+
canvas.width = width;
|
|
963
|
+
canvas.height = height;
|
|
964
|
+
return canvas;
|
|
965
|
+
};
|
|
966
|
+
throw new Error("A canvas factory is not available");
|
|
967
|
+
}
|
|
968
|
+
function drawPin(context, number, x, y, width, height) {
|
|
969
|
+
const label = String(number);
|
|
970
|
+
const fontSize = Math.max(12, Math.min(40, Math.round(Math.min(width, height) * .03)));
|
|
971
|
+
const radius = Math.max(12, fontSize * .8, fontSize * (.3 * label.length + .35));
|
|
972
|
+
const outerStroke = Math.max(2, Math.min(4, radius * .25));
|
|
973
|
+
const innerStroke = Math.max(1, Math.min(2, radius * .13));
|
|
974
|
+
const edge = radius + outerStroke + 2;
|
|
975
|
+
const targetX = x * Math.max(0, width - 1);
|
|
976
|
+
const targetY = y * Math.max(0, height - 1);
|
|
977
|
+
const centerX = clampCenter(targetX, width, edge);
|
|
978
|
+
const centerY = clampCenter(targetY, height, edge);
|
|
979
|
+
context.save?.();
|
|
980
|
+
if ((centerX !== targetX || centerY !== targetY) && typeof context.moveTo === "function" && typeof context.lineTo === "function") {
|
|
981
|
+
context.beginPath();
|
|
982
|
+
context.moveTo(targetX, targetY);
|
|
983
|
+
context.lineTo(centerX, centerY);
|
|
984
|
+
context.lineWidth = outerStroke * 2;
|
|
985
|
+
context.strokeStyle = PIN_OUTER_STROKE;
|
|
986
|
+
context.stroke();
|
|
987
|
+
context.beginPath();
|
|
988
|
+
context.moveTo(targetX, targetY);
|
|
989
|
+
context.lineTo(centerX, centerY);
|
|
990
|
+
context.lineWidth = innerStroke;
|
|
991
|
+
context.strokeStyle = PIN_INNER_STROKE;
|
|
992
|
+
context.stroke();
|
|
993
|
+
}
|
|
994
|
+
context.beginPath();
|
|
995
|
+
context.arc(centerX, centerY, radius, 0, TWO_PI);
|
|
996
|
+
context.fillStyle = PIN_FILL;
|
|
997
|
+
context.fill();
|
|
998
|
+
context.lineWidth = outerStroke;
|
|
999
|
+
context.strokeStyle = PIN_OUTER_STROKE;
|
|
1000
|
+
context.stroke();
|
|
1001
|
+
context.lineWidth = innerStroke;
|
|
1002
|
+
context.strokeStyle = PIN_INNER_STROKE;
|
|
1003
|
+
context.stroke();
|
|
1004
|
+
context.font = `700 ${fontSize}px sans-serif`;
|
|
1005
|
+
context.fillStyle = PIN_INNER_STROKE;
|
|
1006
|
+
context.textAlign = "center";
|
|
1007
|
+
context.textBaseline = "middle";
|
|
1008
|
+
context.fillText(label, centerX, centerY);
|
|
1009
|
+
context.restore?.();
|
|
1010
|
+
}
|
|
1011
|
+
function encodePng(canvas) {
|
|
1012
|
+
if (typeof canvas?.toBlob !== "function") throw new Error("Canvas PNG encoding is not available");
|
|
1013
|
+
return new Promise((resolve, reject) => {
|
|
1014
|
+
let settled = false;
|
|
1015
|
+
const finish = (callback, value) => {
|
|
1016
|
+
if (settled) return;
|
|
1017
|
+
settled = true;
|
|
1018
|
+
callback(value);
|
|
1019
|
+
};
|
|
1020
|
+
try {
|
|
1021
|
+
canvas.toBlob((value) => {
|
|
1022
|
+
if (value === null || value === void 0) finish(reject, /* @__PURE__ */ new Error("Canvas failed to encode the annotation reference as PNG"));
|
|
1023
|
+
else finish(resolve, value);
|
|
1024
|
+
}, "image/png");
|
|
1025
|
+
} catch (error) {
|
|
1026
|
+
finish(reject, error);
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Create the second image sent with an annotated edit. It contains the clean
|
|
1032
|
+
* source plus numbered pins, with no note text. `options` is intentionally
|
|
1033
|
+
* injectable so the rendering path can be exercised without a browser:
|
|
1034
|
+
* `{ createImageBitmap, createCanvas }`.
|
|
1035
|
+
*/
|
|
1036
|
+
async function createAnnotatedImageReference(blob, annotations, options = {}) {
|
|
1037
|
+
const normalized = normalizeImageEditAnnotations(annotations);
|
|
1038
|
+
const createImageBitmap = getBitmapFactory(options);
|
|
1039
|
+
const createCanvas = getCanvasFactory(options);
|
|
1040
|
+
const bitmap = await createImageBitmap(blob);
|
|
1041
|
+
try {
|
|
1042
|
+
const width = bitmap?.width;
|
|
1043
|
+
const height = bitmap?.height;
|
|
1044
|
+
if (!finiteDimension(width) || !finiteDimension(height)) throw new Error("The source image has invalid dimensions");
|
|
1045
|
+
if (Math.min(width, height) < 64) throw new Error("The source image is too small for readable annotation pins");
|
|
1046
|
+
const canvas = await createCanvas(width, height);
|
|
1047
|
+
if (canvas === null || canvas === void 0) throw new Error("Canvas factory returned no canvas");
|
|
1048
|
+
canvas.width = width;
|
|
1049
|
+
canvas.height = height;
|
|
1050
|
+
const context = canvas.getContext?.("2d");
|
|
1051
|
+
if (context === null || context === void 0) throw new Error("Canvas 2D context is not available");
|
|
1052
|
+
context.clearRect?.(0, 0, width, height);
|
|
1053
|
+
context.drawImage?.(bitmap, 0, 0, width, height);
|
|
1054
|
+
if (typeof context.drawImage !== "function") throw new Error("Canvas 2D context cannot draw the source image");
|
|
1055
|
+
for (const annotation of normalized) drawPin(context, annotation.number, annotation.x, annotation.y, width, height);
|
|
1056
|
+
return await encodePng(canvas);
|
|
1057
|
+
} finally {
|
|
1058
|
+
if (typeof bitmap?.close === "function") bitmap.close();
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
//#endregion
|
|
1062
|
+
//#region src/subscription-image-viewer-styles.js
|
|
1063
|
+
const SUBSCRIPTION_IMAGE_VIEWER_CSS = String.raw`
|
|
1064
|
+
.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)}
|
|
1065
|
+
.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}
|
|
1066
|
+
.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)}
|
|
1067
|
+
.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}
|
|
1068
|
+
.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)}
|
|
1069
|
+
.dcsiv-workspace{position:absolute;inset:0;display:grid;min-width:0;min-height:0;padding:68px 24px 72px;box-sizing:border-box}
|
|
1070
|
+
.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}
|
|
1071
|
+
.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}
|
|
1072
|
+
.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)}
|
|
1073
|
+
.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}
|
|
1074
|
+
.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)}
|
|
1075
|
+
.dcsiv-hint{display:none}
|
|
1076
|
+
.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)}
|
|
1077
|
+
@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}}
|
|
1078
|
+
@media(prefers-reduced-motion:reduce){.dcsiv-surface{transition:none}}
|
|
1079
|
+
`;
|
|
1080
|
+
//#endregion
|
|
1081
|
+
//#region src/subscription-image-viewer.jsx
|
|
1082
|
+
const fill$1 = (value, variables) => Object.entries(variables).reduce((text, [key, replacement]) => text.replaceAll(`{${key}}`, String(replacement)), value);
|
|
1083
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
1084
|
+
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`;
|
|
1085
|
+
const downloadName = (name) => {
|
|
1086
|
+
const cleaned = String(name || "image.png").replace(/[<>:"/\\|?*\u0000-\u001f]/gu, "-").replace(/[. ]+$/u, "").trim();
|
|
1087
|
+
return cleaned === "" ? "image.png" : cleaned;
|
|
1088
|
+
};
|
|
1089
|
+
const noteText = (annotations, t) => annotations.map((annotation, index) => {
|
|
1090
|
+
return `${fill$1(t("imageAnnotation"), { value: index + 1 })} (${Math.round(annotation.x * 100)}%, ${Math.round(annotation.y * 100)}%): ${annotation.note.trim()}`;
|
|
1091
|
+
}).filter((line) => !line.endsWith(": ")).join("\n");
|
|
1092
|
+
function ViewerAction({ action, annotations, item, service, t }) {
|
|
1093
|
+
const [state, setState] = (0, react.useState)("idle");
|
|
1094
|
+
const invoke = async () => {
|
|
1095
|
+
if (state === "pending") return;
|
|
1096
|
+
setState("pending");
|
|
1097
|
+
try {
|
|
1098
|
+
await action.onInvoke({
|
|
1099
|
+
annotations,
|
|
1100
|
+
item,
|
|
1101
|
+
src: item.src
|
|
1102
|
+
});
|
|
1103
|
+
setState("idle");
|
|
1104
|
+
if (action.closeOnSuccess) service.close();
|
|
1105
|
+
} catch {
|
|
1106
|
+
setState("failed");
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
const label = state === "pending" ? action.pendingLabel : state === "failed" ? action.errorLabel : action.label;
|
|
1110
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1111
|
+
type: "button",
|
|
1112
|
+
className: "dcsiv-button",
|
|
1113
|
+
disabled: state === "pending",
|
|
1114
|
+
onClick: () => {
|
|
1115
|
+
invoke();
|
|
1116
|
+
},
|
|
1117
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1118
|
+
className: "dcsiv-label",
|
|
1119
|
+
children: label ?? t("imageEdit")
|
|
1120
|
+
})
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
function ViewerDownload({ download, item, t }) {
|
|
1124
|
+
const [state, setState] = (0, react.useState)("idle");
|
|
1125
|
+
const invoke = async () => {
|
|
1126
|
+
if (state === "pending") return;
|
|
1127
|
+
setState("pending");
|
|
1128
|
+
try {
|
|
1129
|
+
await download.onInvoke({
|
|
1130
|
+
item,
|
|
1131
|
+
src: item.src
|
|
1132
|
+
});
|
|
1133
|
+
setState("idle");
|
|
1134
|
+
} catch {
|
|
1135
|
+
setState("failed");
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
1138
|
+
const label = state === "pending" ? download.pendingLabel ?? t("imageDownloadPreparing") : state === "failed" ? download.errorLabel ?? t("imageDownloadFailed") : t("imageDownload");
|
|
1139
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1140
|
+
type: "button",
|
|
1141
|
+
className: "dcsiv-download",
|
|
1142
|
+
disabled: state === "pending",
|
|
1143
|
+
onClick: () => {
|
|
1144
|
+
invoke();
|
|
1145
|
+
},
|
|
1146
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1147
|
+
className: "dcsiv-label",
|
|
1148
|
+
children: label
|
|
1149
|
+
})]
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
function SubscriptionImageViewerOverlay({ service, t }) {
|
|
1153
|
+
const request = (0, react.useSyncExternalStore)(service.subscribe, service.getSnapshot);
|
|
1154
|
+
const [index, setIndex] = (0, react.useState)(0);
|
|
1155
|
+
const [transform, setTransform] = (0, react.useState)({
|
|
1156
|
+
zoom: 1,
|
|
1157
|
+
x: 0,
|
|
1158
|
+
y: 0
|
|
1159
|
+
});
|
|
1160
|
+
const [dragging, setDragging] = (0, react.useState)(false);
|
|
1161
|
+
const [annotating, setAnnotating] = (0, react.useState)(false);
|
|
1162
|
+
const [annotationsByImage, setAnnotationsByImage] = (0, react.useState)(service.getAnnotationsSnapshot);
|
|
1163
|
+
const annotationsByImageRef = (0, react.useRef)(annotationsByImage);
|
|
1164
|
+
const [selected, setSelected] = (0, react.useState)();
|
|
1165
|
+
const [focusNote, setFocusNote] = (0, react.useState)();
|
|
1166
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
1167
|
+
const rootRef = (0, react.useRef)(null);
|
|
1168
|
+
const stageRef = (0, react.useRef)(null);
|
|
1169
|
+
const surfaceRef = (0, react.useRef)(null);
|
|
1170
|
+
const imageRef = (0, react.useRef)(null);
|
|
1171
|
+
const pointersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
1172
|
+
const gestureRef = (0, react.useRef)();
|
|
1173
|
+
const transformRef = (0, react.useRef)(transform);
|
|
1174
|
+
transformRef.current = transform;
|
|
1175
|
+
annotationsByImageRef.current = annotationsByImage;
|
|
1176
|
+
(0, react.useEffect)(() => {
|
|
1177
|
+
if (request === void 0) return;
|
|
1178
|
+
setIndex(request.index);
|
|
1179
|
+
setTransform({
|
|
1180
|
+
zoom: 1,
|
|
1181
|
+
x: 0,
|
|
1182
|
+
y: 0
|
|
1183
|
+
});
|
|
1184
|
+
setDragging(false);
|
|
1185
|
+
setAnnotating(false);
|
|
1186
|
+
setSelected(void 0);
|
|
1187
|
+
setCopied(false);
|
|
1188
|
+
}, [request?.revision]);
|
|
1189
|
+
const item = request?.items[index];
|
|
1190
|
+
const annotations = item === void 0 ? [] : annotationsByImage[item.id] ?? [];
|
|
1191
|
+
const setAnnotations = (0, react.useCallback)((update) => {
|
|
1192
|
+
if (item === void 0) return;
|
|
1193
|
+
const previous = annotationsByImageRef.current[item.id] ?? [];
|
|
1194
|
+
const next = typeof update === "function" ? update(previous) : update;
|
|
1195
|
+
const snapshot = {
|
|
1196
|
+
...annotationsByImageRef.current,
|
|
1197
|
+
[item.id]: next
|
|
1198
|
+
};
|
|
1199
|
+
annotationsByImageRef.current = snapshot;
|
|
1200
|
+
service.setAnnotations(item.id, next);
|
|
1201
|
+
setAnnotationsByImage(snapshot);
|
|
1202
|
+
}, [item?.id, service]);
|
|
1203
|
+
const boundedPan = (0, react.useCallback)((zoom, x, y) => {
|
|
1204
|
+
const stage = stageRef.current;
|
|
1205
|
+
const surface = surfaceRef.current;
|
|
1206
|
+
if (stage === null || surface === null || zoom <= 1) return {
|
|
1207
|
+
x: 0,
|
|
1208
|
+
y: 0
|
|
1209
|
+
};
|
|
1210
|
+
const limitX = Math.max(0, (surface.offsetWidth * zoom - stage.clientWidth) / 2) + 28;
|
|
1211
|
+
const limitY = Math.max(0, (surface.offsetHeight * zoom - stage.clientHeight) / 2) + 28;
|
|
1212
|
+
return {
|
|
1213
|
+
x: clamp(x, -limitX, limitX),
|
|
1214
|
+
y: clamp(y, -limitY, limitY)
|
|
1215
|
+
};
|
|
1216
|
+
}, []);
|
|
1217
|
+
const setZoomAt = (0, react.useCallback)((nextZoom, clientX, clientY) => {
|
|
1218
|
+
const stage = stageRef.current;
|
|
1219
|
+
if (stage === null) return;
|
|
1220
|
+
setTransform((current) => {
|
|
1221
|
+
const next = clamp(nextZoom, .5, 8);
|
|
1222
|
+
const box = stage.getBoundingClientRect();
|
|
1223
|
+
const px = clientX - box.left - box.width / 2;
|
|
1224
|
+
const py = clientY - box.top - box.height / 2;
|
|
1225
|
+
const ratio = next / current.zoom;
|
|
1226
|
+
return {
|
|
1227
|
+
zoom: next,
|
|
1228
|
+
...boundedPan(next, px - (px - current.x) * ratio, py - (py - current.y) * ratio)
|
|
1229
|
+
};
|
|
1230
|
+
});
|
|
1231
|
+
}, [boundedPan]);
|
|
1232
|
+
const fit = (0, react.useCallback)(() => {
|
|
1233
|
+
setTransform({
|
|
1234
|
+
zoom: 1,
|
|
1235
|
+
x: 0,
|
|
1236
|
+
y: 0
|
|
1237
|
+
});
|
|
1238
|
+
}, []);
|
|
1239
|
+
const actual = (0, react.useCallback)(() => {
|
|
1240
|
+
const image = imageRef.current;
|
|
1241
|
+
const surface = surfaceRef.current;
|
|
1242
|
+
if (image === null || surface === null || image.naturalWidth === 0) return;
|
|
1243
|
+
const zoom = clamp(image.naturalWidth / Math.max(1, surface.offsetWidth), 1, 8);
|
|
1244
|
+
setTransform({
|
|
1245
|
+
zoom,
|
|
1246
|
+
x: 0,
|
|
1247
|
+
y: 0
|
|
1248
|
+
});
|
|
1249
|
+
}, []);
|
|
1250
|
+
(0, react.useEffect)(() => {
|
|
1251
|
+
if (request === void 0) return void 0;
|
|
1252
|
+
const previousOverflow = document.body.style.overflow;
|
|
1253
|
+
document.body.style.overflow = "hidden";
|
|
1254
|
+
rootRef.current?.focus();
|
|
1255
|
+
const onKeyDown = (event) => {
|
|
1256
|
+
if (event.key === "Escape") {
|
|
1257
|
+
event.preventDefault();
|
|
1258
|
+
if (event.target instanceof Element && event.target.closest(".dcsiv-inline-note") !== null) {
|
|
1259
|
+
setSelected(void 0);
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
service.close();
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
const editing = event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement;
|
|
1266
|
+
if (!editing && event.key === "ArrowLeft" && request.items.length > 1) {
|
|
1267
|
+
event.preventDefault();
|
|
1268
|
+
setIndex((value) => (value - 1 + request.items.length) % request.items.length);
|
|
1269
|
+
} else if (!editing && event.key === "ArrowRight" && request.items.length > 1) {
|
|
1270
|
+
event.preventDefault();
|
|
1271
|
+
setIndex((value) => (value + 1) % request.items.length);
|
|
1272
|
+
} else if (!editing && (event.key === "+" || event.key === "=")) {
|
|
1273
|
+
event.preventDefault();
|
|
1274
|
+
const box = stageRef.current?.getBoundingClientRect();
|
|
1275
|
+
if (box) setZoomAt(transformRef.current.zoom * 1.2, box.left + box.width / 2, box.top + box.height / 2);
|
|
1276
|
+
} else if (!editing && event.key === "-") {
|
|
1277
|
+
event.preventDefault();
|
|
1278
|
+
const box = stageRef.current?.getBoundingClientRect();
|
|
1279
|
+
if (box) setZoomAt(transformRef.current.zoom / 1.2, box.left + box.width / 2, box.top + box.height / 2);
|
|
1280
|
+
} else if (!editing && event.key.toLowerCase() === "f") {
|
|
1281
|
+
event.preventDefault();
|
|
1282
|
+
fit();
|
|
1283
|
+
} else if (event.key === "Tab") {
|
|
1284
|
+
const controls = [...rootRef.current.querySelectorAll("button:not(:disabled),a[href],input:not(:disabled),textarea:not(:disabled),[tabindex]:not([tabindex=\"-1\"])")];
|
|
1285
|
+
const first = controls[0];
|
|
1286
|
+
const last = controls.at(-1);
|
|
1287
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
1288
|
+
event.preventDefault();
|
|
1289
|
+
last?.focus();
|
|
1290
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
1291
|
+
event.preventDefault();
|
|
1292
|
+
first?.focus();
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
document.addEventListener("keydown", onKeyDown);
|
|
1297
|
+
return () => {
|
|
1298
|
+
document.body.style.overflow = previousOverflow;
|
|
1299
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
1300
|
+
};
|
|
1301
|
+
}, [
|
|
1302
|
+
request,
|
|
1303
|
+
service,
|
|
1304
|
+
fit,
|
|
1305
|
+
setZoomAt
|
|
1306
|
+
]);
|
|
1307
|
+
(0, react.useEffect)(() => {
|
|
1308
|
+
if (focusNote === void 0) return;
|
|
1309
|
+
(rootRef.current?.querySelector(`[data-note-id="${CSS.escape(focusNote)}"] textarea`))?.focus();
|
|
1310
|
+
setFocusNote(void 0);
|
|
1311
|
+
}, [
|
|
1312
|
+
focusNote,
|
|
1313
|
+
selected,
|
|
1314
|
+
annotations.length
|
|
1315
|
+
]);
|
|
1316
|
+
(0, react.useEffect)(() => {
|
|
1317
|
+
setTransform({
|
|
1318
|
+
zoom: 1,
|
|
1319
|
+
x: 0,
|
|
1320
|
+
y: 0
|
|
1321
|
+
});
|
|
1322
|
+
setDragging(false);
|
|
1323
|
+
setAnnotating(false);
|
|
1324
|
+
setSelected(void 0);
|
|
1325
|
+
}, [item?.id]);
|
|
1326
|
+
const onWheel = (0, react.useCallback)((event) => {
|
|
1327
|
+
event.preventDefault();
|
|
1328
|
+
setZoomAt(transformRef.current.zoom * Math.exp(-event.deltaY * .0015), event.clientX, event.clientY);
|
|
1329
|
+
}, [setZoomAt]);
|
|
1330
|
+
(0, react.useEffect)(() => {
|
|
1331
|
+
const stage = stageRef.current;
|
|
1332
|
+
if (stage === null || request === void 0) return void 0;
|
|
1333
|
+
stage.addEventListener("wheel", onWheel, { passive: false });
|
|
1334
|
+
return () => stage.removeEventListener("wheel", onWheel);
|
|
1335
|
+
}, [onWheel, request]);
|
|
1336
|
+
const onPointerDown = (event) => {
|
|
1337
|
+
const target = event.target;
|
|
1338
|
+
if (event.button !== 0 || annotating || target instanceof Element && target.closest("button,textarea,input,a,select,[contenteditable=true]") !== null) return;
|
|
1339
|
+
pointersRef.current.set(event.pointerId, {
|
|
1340
|
+
x: event.clientX,
|
|
1341
|
+
y: event.clientY
|
|
1342
|
+
});
|
|
1343
|
+
if (pointersRef.current.size === 2) {
|
|
1344
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
1345
|
+
const [a, b] = [...pointersRef.current.values()];
|
|
1346
|
+
gestureRef.current = {
|
|
1347
|
+
kind: "pinch",
|
|
1348
|
+
distance: Math.hypot(a.x - b.x, a.y - b.y),
|
|
1349
|
+
transform
|
|
1350
|
+
};
|
|
1351
|
+
} else if (!annotating && transform.zoom > 1) {
|
|
1352
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
1353
|
+
gestureRef.current = {
|
|
1354
|
+
kind: "pan",
|
|
1355
|
+
x: event.clientX,
|
|
1356
|
+
y: event.clientY,
|
|
1357
|
+
transform
|
|
1358
|
+
};
|
|
1359
|
+
setDragging(true);
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
const onPointerMove = (event) => {
|
|
1363
|
+
if (!pointersRef.current.has(event.pointerId)) return;
|
|
1364
|
+
pointersRef.current.set(event.pointerId, {
|
|
1365
|
+
x: event.clientX,
|
|
1366
|
+
y: event.clientY
|
|
1367
|
+
});
|
|
1368
|
+
const gesture = gestureRef.current;
|
|
1369
|
+
if (gesture?.kind === "pinch" && pointersRef.current.size >= 2) {
|
|
1370
|
+
const [a, b] = [...pointersRef.current.values()];
|
|
1371
|
+
const distance = Math.max(1, Math.hypot(a.x - b.x, a.y - b.y));
|
|
1372
|
+
setZoomAt(gesture.transform.zoom * distance / Math.max(1, gesture.distance), (a.x + b.x) / 2, (a.y + b.y) / 2);
|
|
1373
|
+
} else if (gesture?.kind === "pan") {
|
|
1374
|
+
const pan = boundedPan(gesture.transform.zoom, gesture.transform.x + event.clientX - gesture.x, gesture.transform.y + event.clientY - gesture.y);
|
|
1375
|
+
setTransform({
|
|
1376
|
+
zoom: gesture.transform.zoom,
|
|
1377
|
+
...pan
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
const endPointer = (event) => {
|
|
1382
|
+
pointersRef.current.delete(event.pointerId);
|
|
1383
|
+
if (pointersRef.current.size === 0) {
|
|
1384
|
+
gestureRef.current = void 0;
|
|
1385
|
+
setDragging(false);
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
const addAnnotation = (event) => {
|
|
1389
|
+
if (!annotating || event.target.closest(".dcsiv-annotation")) return;
|
|
1390
|
+
const bounds = surfaceRef.current?.getBoundingClientRect();
|
|
1391
|
+
if (bounds === void 0) return;
|
|
1392
|
+
const annotation = {
|
|
1393
|
+
id: crypto.randomUUID(),
|
|
1394
|
+
x: clamp((event.clientX - bounds.left) / bounds.width, 0, 1),
|
|
1395
|
+
y: clamp((event.clientY - bounds.top) / bounds.height, 0, 1),
|
|
1396
|
+
note: ""
|
|
1397
|
+
};
|
|
1398
|
+
setAnnotations((current) => [...current, annotation]);
|
|
1399
|
+
setAnnotating(false);
|
|
1400
|
+
setSelected(annotation.id);
|
|
1401
|
+
setFocusNote(annotation.id);
|
|
1402
|
+
};
|
|
1403
|
+
const copyNotes = async () => {
|
|
1404
|
+
const text = noteText(annotations, t);
|
|
1405
|
+
if (text === "" || typeof navigator?.clipboard?.writeText !== "function") return;
|
|
1406
|
+
try {
|
|
1407
|
+
await navigator.clipboard.writeText(text);
|
|
1408
|
+
setCopied(true);
|
|
1409
|
+
window.setTimeout(() => {
|
|
1410
|
+
setCopied(false);
|
|
1411
|
+
}, 1200);
|
|
1412
|
+
} catch {
|
|
1413
|
+
setCopied(false);
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
if (request === void 0 || item === void 0) return null;
|
|
1417
|
+
const meta = [item.width && item.height ? `${item.width} × ${item.height}` : void 0, bytesLabel(item.bytes)].filter(Boolean).join(" · ");
|
|
1418
|
+
const showCounter = request.items.length > 1;
|
|
1419
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1420
|
+
ref: rootRef,
|
|
1421
|
+
className: "dcsiv-root",
|
|
1422
|
+
role: "dialog",
|
|
1423
|
+
"aria-modal": "true",
|
|
1424
|
+
"aria-label": t("imagePreview"),
|
|
1425
|
+
tabIndex: -1,
|
|
1426
|
+
children: [
|
|
1427
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1428
|
+
className: "dcsiv-title dcsiv-sr-only",
|
|
1429
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: item.name }), meta !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: meta }) : null]
|
|
1430
|
+
}),
|
|
1431
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
1432
|
+
className: "dcsiv-topbar",
|
|
1433
|
+
role: "toolbar",
|
|
1434
|
+
"aria-label": t("imagePreview"),
|
|
1435
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1436
|
+
className: "dcsiv-actions",
|
|
1437
|
+
children: [
|
|
1438
|
+
request.annotations ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1439
|
+
type: "button",
|
|
1440
|
+
className: "dcsiv-button",
|
|
1441
|
+
"data-active": annotating,
|
|
1442
|
+
"aria-label": annotating ? t("imageAnnotateCancel") : t("imageAnnotate"),
|
|
1443
|
+
"aria-pressed": annotating,
|
|
1444
|
+
onClick: () => setAnnotating((value) => !value),
|
|
1445
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1446
|
+
className: "dcsiv-label",
|
|
1447
|
+
children: annotating ? t("imageAnnotateCancel") : t("imageAnnotate")
|
|
1448
|
+
})]
|
|
1449
|
+
}) : null,
|
|
1450
|
+
annotations.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1451
|
+
type: "button",
|
|
1452
|
+
className: "dcsiv-button",
|
|
1453
|
+
"data-active": selected !== void 0,
|
|
1454
|
+
onClick: () => {
|
|
1455
|
+
const first = annotations[0];
|
|
1456
|
+
setSelected((current) => current === void 0 ? first.id : void 0);
|
|
1457
|
+
if (selected === void 0) setFocusNote(first.id);
|
|
1458
|
+
},
|
|
1459
|
+
children: [
|
|
1460
|
+
annotations.length,
|
|
1461
|
+
" ",
|
|
1462
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1463
|
+
className: "dcsiv-label",
|
|
1464
|
+
children: t("imageRegions")
|
|
1465
|
+
})
|
|
1466
|
+
]
|
|
1467
|
+
}) : null,
|
|
1468
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1469
|
+
type: "button",
|
|
1470
|
+
className: "dcsiv-button",
|
|
1471
|
+
"aria-label": t("imageFit"),
|
|
1472
|
+
onClick: fit,
|
|
1473
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1474
|
+
className: "dcsiv-label",
|
|
1475
|
+
children: t("imageFit")
|
|
1476
|
+
})]
|
|
1477
|
+
}),
|
|
1478
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1479
|
+
type: "button",
|
|
1480
|
+
className: "dcsiv-button",
|
|
1481
|
+
onClick: actual,
|
|
1482
|
+
children: t("imageActual")
|
|
1483
|
+
}),
|
|
1484
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1485
|
+
className: "dcsiv-zoom",
|
|
1486
|
+
children: [Math.round(transform.zoom * 100), "%"]
|
|
1487
|
+
}),
|
|
1488
|
+
item.download === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
1489
|
+
className: "dcsiv-download",
|
|
1490
|
+
href: item.src,
|
|
1491
|
+
download: downloadName(item.name),
|
|
1492
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1493
|
+
className: "dcsiv-label",
|
|
1494
|
+
children: t("imageDownload")
|
|
1495
|
+
})]
|
|
1496
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewerDownload, {
|
|
1497
|
+
download: item.download,
|
|
1498
|
+
item,
|
|
1499
|
+
t
|
|
1500
|
+
}),
|
|
1501
|
+
item.actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewerAction, {
|
|
1502
|
+
action,
|
|
1503
|
+
annotations,
|
|
1504
|
+
item,
|
|
1505
|
+
service,
|
|
1506
|
+
t
|
|
1507
|
+
}, action.id))
|
|
1508
|
+
]
|
|
1509
|
+
})
|
|
1510
|
+
}),
|
|
1511
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1512
|
+
type: "button",
|
|
1513
|
+
className: "dcsiv-close-floating",
|
|
1514
|
+
"aria-label": t("imageClosePreview"),
|
|
1515
|
+
onClick: () => service.close(),
|
|
1516
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, {})
|
|
1517
|
+
}),
|
|
1518
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1519
|
+
className: "dcsiv-workspace",
|
|
1520
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
1521
|
+
ref: stageRef,
|
|
1522
|
+
className: "dcsiv-stage",
|
|
1523
|
+
"data-dragging": dragging,
|
|
1524
|
+
"data-annotating": annotating,
|
|
1525
|
+
onClick: (event) => {
|
|
1526
|
+
if (event.target === event.currentTarget && !annotating && transform.zoom === 1) service.close();
|
|
1527
|
+
},
|
|
1528
|
+
onPointerDown,
|
|
1529
|
+
onPointerMove,
|
|
1530
|
+
onPointerUp: endPointer,
|
|
1531
|
+
onPointerCancel: endPointer,
|
|
1532
|
+
onDoubleClick: () => {
|
|
1533
|
+
if (transform.zoom === 1) actual();
|
|
1534
|
+
else fit();
|
|
1535
|
+
},
|
|
1536
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1537
|
+
ref: surfaceRef,
|
|
1538
|
+
className: "dcsiv-surface",
|
|
1539
|
+
onClick: addAnnotation,
|
|
1540
|
+
style: { transform: `translate3d(${transform.x}px,${transform.y}px,0) scale(${transform.zoom})` },
|
|
1541
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
1542
|
+
ref: imageRef,
|
|
1543
|
+
className: "dcsiv-image",
|
|
1544
|
+
src: item.src,
|
|
1545
|
+
alt: item.name,
|
|
1546
|
+
draggable: "false"
|
|
1547
|
+
}), annotations.map((annotation, position) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1548
|
+
className: "dcsiv-annotation",
|
|
1549
|
+
"data-x": annotation.x < .38 ? "right" : annotation.x > .62 ? "left" : "center",
|
|
1550
|
+
"data-y": annotation.y < .28 ? "down" : "up",
|
|
1551
|
+
style: {
|
|
1552
|
+
left: `${annotation.x * 100}%`,
|
|
1553
|
+
top: `${annotation.y * 100}%`,
|
|
1554
|
+
transform: `translate(-50%,-50%) scale(${1 / transform.zoom})`
|
|
1555
|
+
},
|
|
1556
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1557
|
+
type: "button",
|
|
1558
|
+
className: "dcsiv-pin",
|
|
1559
|
+
"data-active": selected === annotation.id,
|
|
1560
|
+
"aria-label": fill$1(t("imageAnnotation"), { value: position + 1 }),
|
|
1561
|
+
onClick: (event) => {
|
|
1562
|
+
event.stopPropagation();
|
|
1563
|
+
const opening = selected !== annotation.id;
|
|
1564
|
+
setSelected(opening ? annotation.id : void 0);
|
|
1565
|
+
if (opening) setFocusNote(annotation.id);
|
|
1566
|
+
},
|
|
1567
|
+
children: position + 1
|
|
1568
|
+
}), selected === annotation.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1569
|
+
className: "dcsiv-inline-note",
|
|
1570
|
+
"data-note-id": annotation.id,
|
|
1571
|
+
onClick: (event) => event.stopPropagation(),
|
|
1572
|
+
children: [
|
|
1573
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1574
|
+
className: "dcsiv-inline-index",
|
|
1575
|
+
children: position + 1
|
|
1576
|
+
}),
|
|
1577
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
1578
|
+
value: annotation.note,
|
|
1579
|
+
rows: 1,
|
|
1580
|
+
"aria-label": fill$1(t("imageAnnotation"), { value: position + 1 }),
|
|
1581
|
+
placeholder: t("imageAnnotationPlaceholder"),
|
|
1582
|
+
onChange: (event) => {
|
|
1583
|
+
const note = event.target.value;
|
|
1584
|
+
setAnnotations((current) => current.map((entry) => entry.id === annotation.id ? {
|
|
1585
|
+
...entry,
|
|
1586
|
+
note
|
|
1587
|
+
} : entry));
|
|
1588
|
+
},
|
|
1589
|
+
onKeyDown: (event) => {
|
|
1590
|
+
if (event.key === "Enter" && !event.shiftKey || event.key === "Escape") {
|
|
1591
|
+
event.preventDefault();
|
|
1592
|
+
event.stopPropagation();
|
|
1593
|
+
event.nativeEvent?.stopImmediatePropagation?.();
|
|
1594
|
+
setSelected(void 0);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}),
|
|
1598
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1599
|
+
type: "button",
|
|
1600
|
+
className: "dcsiv-note-remove",
|
|
1601
|
+
"aria-label": t("imageRemoveAnnotation"),
|
|
1602
|
+
onClick: (event) => {
|
|
1603
|
+
event.stopPropagation();
|
|
1604
|
+
setAnnotations((current) => current.filter((entry) => entry.id !== annotation.id));
|
|
1605
|
+
setSelected(void 0);
|
|
1606
|
+
},
|
|
1607
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, {})
|
|
1608
|
+
})
|
|
1609
|
+
]
|
|
1610
|
+
}) : null]
|
|
1611
|
+
}, annotation.id))]
|
|
1612
|
+
}), showCounter ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1613
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1614
|
+
type: "button",
|
|
1615
|
+
className: "dcsiv-button dcsiv-icon-only dcsiv-nav dcsiv-prev",
|
|
1616
|
+
"aria-label": t("imagePrevious"),
|
|
1617
|
+
onClick: (event) => {
|
|
1618
|
+
event.stopPropagation();
|
|
1619
|
+
setIndex((value) => (value - 1 + request.items.length) % request.items.length);
|
|
1620
|
+
},
|
|
1621
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronLeftOutline14, {})
|
|
1622
|
+
}),
|
|
1623
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1624
|
+
type: "button",
|
|
1625
|
+
className: "dcsiv-button dcsiv-icon-only dcsiv-nav dcsiv-next",
|
|
1626
|
+
"aria-label": t("imageNext"),
|
|
1627
|
+
onClick: (event) => {
|
|
1628
|
+
event.stopPropagation();
|
|
1629
|
+
setIndex((value) => (value + 1) % request.items.length);
|
|
1630
|
+
},
|
|
1631
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
|
|
1632
|
+
}),
|
|
1633
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1634
|
+
className: "dcsiv-counter",
|
|
1635
|
+
children: [
|
|
1636
|
+
index + 1,
|
|
1637
|
+
" / ",
|
|
1638
|
+
request.items.length
|
|
1639
|
+
]
|
|
1640
|
+
})
|
|
1641
|
+
] }) : annotating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1642
|
+
className: "dcsiv-hint",
|
|
1643
|
+
children: t("imageAnnotateHint")
|
|
1644
|
+
}) : transform.zoom === 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1645
|
+
className: "dcsiv-hint",
|
|
1646
|
+
children: t("imageZoomHint")
|
|
1647
|
+
}) : null]
|
|
1648
|
+
}), annotations.some((annotation) => annotation.note.trim() !== "") ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1649
|
+
type: "button",
|
|
1650
|
+
className: "dcsiv-copy-notes",
|
|
1651
|
+
onClick: () => {
|
|
1652
|
+
copyNotes();
|
|
1653
|
+
},
|
|
1654
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {}), copied ? t("imageCopied") : t("imageCopyNotes")]
|
|
1655
|
+
}) : null]
|
|
1656
|
+
})
|
|
1657
|
+
]
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
//#endregion
|
|
1661
|
+
//#region src/subscription-image-viewer.js
|
|
1662
|
+
const boundedNumber = (value, fallback) => Number.isFinite(value) && value > 0 ? value : fallback;
|
|
1663
|
+
const downloadOf = (value) => typeof value?.onInvoke === "function" ? {
|
|
1664
|
+
pendingLabel: typeof value.pendingLabel === "string" && value.pendingLabel !== "" ? value.pendingLabel : void 0,
|
|
1665
|
+
errorLabel: typeof value.errorLabel === "string" && value.errorLabel !== "" ? value.errorLabel : void 0,
|
|
1666
|
+
onInvoke: value.onInvoke
|
|
1667
|
+
} : void 0;
|
|
1668
|
+
const actionsOf = (value) => Array.isArray(value) ? value.flatMap((action, position) => {
|
|
1669
|
+
if (typeof action?.onInvoke !== "function" || typeof action?.label !== "string" || action.label.trim() === "") return [];
|
|
1670
|
+
return [{
|
|
1671
|
+
id: typeof action.id === "string" && action.id !== "" ? action.id : `action-${position + 1}`,
|
|
1672
|
+
label: action.label,
|
|
1673
|
+
pendingLabel: typeof action.pendingLabel === "string" && action.pendingLabel !== "" ? action.pendingLabel : action.label,
|
|
1674
|
+
errorLabel: typeof action.errorLabel === "string" && action.errorLabel !== "" ? action.errorLabel : action.label,
|
|
1675
|
+
closeOnSuccess: action.closeOnSuccess === true,
|
|
1676
|
+
onInvoke: action.onInvoke
|
|
1677
|
+
}];
|
|
1678
|
+
}) : [];
|
|
1679
|
+
function normalizeSubscriptionViewerRequest(request) {
|
|
1680
|
+
const items = (Array.isArray(request?.items) ? request.items : []).flatMap((item, position) => {
|
|
1681
|
+
if (typeof item?.src !== "string" || item.src === "") return [];
|
|
1682
|
+
return [{
|
|
1683
|
+
id: typeof item.id === "string" && item.id !== "" ? item.id : `image-${position + 1}`,
|
|
1684
|
+
src: item.src,
|
|
1685
|
+
name: typeof item.name === "string" && item.name !== "" ? item.name : `Image ${position + 1}`,
|
|
1686
|
+
width: boundedNumber(item.width, void 0),
|
|
1687
|
+
height: boundedNumber(item.height, void 0),
|
|
1688
|
+
bytes: boundedNumber(item.bytes, void 0),
|
|
1689
|
+
download: downloadOf(item.download),
|
|
1690
|
+
actions: actionsOf(item.actions)
|
|
1691
|
+
}];
|
|
1692
|
+
});
|
|
1693
|
+
if (items.length === 0) return void 0;
|
|
1694
|
+
const requestedIndex = Number.isInteger(request?.index) ? request.index : 0;
|
|
1695
|
+
return {
|
|
1696
|
+
items,
|
|
1697
|
+
index: Math.max(0, Math.min(items.length - 1, requestedIndex)),
|
|
1698
|
+
opener: typeof HTMLElement !== "undefined" && request?.opener instanceof HTMLElement ? request.opener : void 0,
|
|
1699
|
+
source: typeof request?.source === "string" ? request.source : "dsh-codex-subscription",
|
|
1700
|
+
annotations: request?.annotations !== false
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
const copyAnnotations = (annotations) => annotations.map((annotation) => ({ ...annotation }));
|
|
1704
|
+
/**
|
|
1705
|
+
* Local image viewer state for subscription-generated images.
|
|
1706
|
+
*
|
|
1707
|
+
* This stays private to subscription image cards, which need annotation and
|
|
1708
|
+
* edit actions that a host's generic native viewer may not implement.
|
|
1709
|
+
*/
|
|
1710
|
+
var SubscriptionImageViewerService = class {
|
|
1711
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
1712
|
+
#revision = 0;
|
|
1713
|
+
#snapshot;
|
|
1714
|
+
#annotationsByImage = /* @__PURE__ */ new Map();
|
|
1715
|
+
constructor() {
|
|
1716
|
+
this.subscribe = (listener) => {
|
|
1717
|
+
this.#listeners.add(listener);
|
|
1718
|
+
return () => {
|
|
1719
|
+
this.#listeners.delete(listener);
|
|
1720
|
+
};
|
|
1721
|
+
};
|
|
1722
|
+
this.getSnapshot = () => this.#snapshot;
|
|
1723
|
+
this.getAnnotationsSnapshot = () => Object.fromEntries([...this.#annotationsByImage].map(([id, annotations]) => [id, copyAnnotations(annotations)]));
|
|
1724
|
+
}
|
|
1725
|
+
setAnnotations(imageId, annotations) {
|
|
1726
|
+
if (typeof imageId !== "string" || imageId === "" || !Array.isArray(annotations)) return;
|
|
1727
|
+
if (annotations.length === 0) this.#annotationsByImage.delete(imageId);
|
|
1728
|
+
else this.#annotationsByImage.set(imageId, copyAnnotations(annotations));
|
|
1729
|
+
}
|
|
1730
|
+
open(request) {
|
|
1731
|
+
const normalized = normalizeSubscriptionViewerRequest(request);
|
|
1732
|
+
if (normalized === void 0) return false;
|
|
1733
|
+
this.#revision += 1;
|
|
1734
|
+
this.#snapshot = {
|
|
1735
|
+
...normalized,
|
|
1736
|
+
revision: this.#revision
|
|
1737
|
+
};
|
|
1738
|
+
this.#emit();
|
|
1739
|
+
return true;
|
|
1740
|
+
}
|
|
1741
|
+
close() {
|
|
1742
|
+
if (this.#snapshot === void 0) return;
|
|
1743
|
+
const opener = this.#snapshot.opener;
|
|
1744
|
+
this.#snapshot = void 0;
|
|
1745
|
+
this.#emit();
|
|
1746
|
+
if (typeof window === "undefined") opener?.focus();
|
|
1747
|
+
else {
|
|
1748
|
+
const focus = () => {
|
|
1749
|
+
opener?.focus();
|
|
1750
|
+
};
|
|
1751
|
+
if (typeof window.requestAnimationFrame === "function") window.requestAnimationFrame(focus);
|
|
1752
|
+
else focus();
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
#emit() {
|
|
1756
|
+
for (const listener of this.#listeners) listener();
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
//#endregion
|
|
1760
|
+
//#region src/settings-contract.js
|
|
1761
|
+
const SETTINGS_NAMESPACE = "codex-subscription";
|
|
1762
|
+
const QUICK_QUOTA_MODE_FIELD = "quickQuotaMode";
|
|
1763
|
+
const LEGACY_QUICK_QUOTA_FIELD = "quickQuotaVisible";
|
|
1764
|
+
const QUICK_QUOTA_MODE_PERCENT = "percent";
|
|
1765
|
+
const QUICK_QUOTA_MODE_FORECAST = "forecast";
|
|
1766
|
+
const SEARCH_PROVIDER_FIELD = "searchProvider";
|
|
1767
|
+
const SEARCH_PROVIDER_AUTO = "auto";
|
|
1768
|
+
const SEARCH_PROVIDER_CODEX = "codex";
|
|
1769
|
+
const DEFAULT_SEARCH_PROVIDER = SEARCH_PROVIDER_AUTO;
|
|
1770
|
+
const SPEED_MODE_FIELD = "speedMode";
|
|
1771
|
+
const SPEED_MODE_STANDARD = "standard";
|
|
1772
|
+
const SPEED_MODE_FAST = "fast";
|
|
1773
|
+
const DEFAULT_SPEED_MODE = SPEED_MODE_STANDARD;
|
|
1774
|
+
const OUTPUT_VERBOSITY_FIELD = "outputVerbosity";
|
|
1775
|
+
const OUTPUT_VERBOSITY_DEFAULT = "default";
|
|
1776
|
+
const OUTPUT_VERBOSITY_MEDIUM = "medium";
|
|
1777
|
+
const OUTPUT_VERBOSITY_HIGH = "high";
|
|
1778
|
+
const DEFAULT_OUTPUT_VERBOSITY = OUTPUT_VERBOSITY_DEFAULT;
|
|
1779
|
+
const CONTEXT_MODE_FIELD = "contextMode";
|
|
1780
|
+
const CONTEXT_MODE_STANDARD = "standard";
|
|
1781
|
+
const CONTEXT_MODE_EXTENDED = "extended";
|
|
1782
|
+
const CONTEXT_MODE_CUSTOM = "custom";
|
|
1783
|
+
const DEFAULT_CONTEXT_MODE = CONTEXT_MODE_STANDARD;
|
|
1784
|
+
const CUSTOM_CONTEXT_WINDOW_FIELD = "customContextWindow";
|
|
1785
|
+
const DEFAULT_CUSTOM_CONTEXT_WINDOW = 272e3;
|
|
1786
|
+
const MIN_CUSTOM_CONTEXT_WINDOW = 128e3;
|
|
1787
|
+
const MAX_CUSTOM_CONTEXT_WINDOW = 1e6;
|
|
1788
|
+
const CUSTOM_CONTEXT_MODEL_FIELDS = Object.freeze({
|
|
1789
|
+
"gpt-5.4": "customContextGpt54",
|
|
1790
|
+
"gpt-5.4-mini": "customContextGpt54Mini",
|
|
1791
|
+
"gpt-5.5": "customContextGpt55",
|
|
1792
|
+
"gpt-5.6": "customContextGpt56",
|
|
1793
|
+
"gpt-6-astra": "customContextGpt6Astra"
|
|
1794
|
+
});
|
|
1795
|
+
const CUSTOM_CONTEXT_MODEL_CAPS = Object.freeze({
|
|
1796
|
+
"gpt-5.4": 1e6,
|
|
1797
|
+
"gpt-5.4-mini": 4e5,
|
|
1798
|
+
"gpt-5.5": 1e6,
|
|
1799
|
+
"gpt-5.6": 1e6,
|
|
1800
|
+
"gpt-6-astra": 872e3
|
|
1801
|
+
});
|
|
1802
|
+
const CUSTOM_CONTEXT_MODEL_DEFAULTS = Object.freeze({
|
|
1803
|
+
"gpt-5.4": 272e3,
|
|
1804
|
+
"gpt-5.4-mini": 272e3,
|
|
1805
|
+
"gpt-5.5": 272e3,
|
|
1806
|
+
"gpt-5.6": 272e3,
|
|
1807
|
+
"gpt-6-astra": 272e3
|
|
1808
|
+
});
|
|
1809
|
+
const normalizeSearchProvider = (value) => [
|
|
1810
|
+
"auto",
|
|
1811
|
+
"dsh",
|
|
1812
|
+
"codex"
|
|
1813
|
+
].includes(value) ? value : DEFAULT_SEARCH_PROVIDER;
|
|
1814
|
+
const normalizeOutputVerbosity = (value) => [
|
|
1815
|
+
"default",
|
|
1816
|
+
"low",
|
|
1817
|
+
"medium",
|
|
1818
|
+
"high"
|
|
1819
|
+
].includes(value) ? value : DEFAULT_OUTPUT_VERBOSITY;
|
|
1820
|
+
const normalizeSpeedMode = (value) => ["standard", "fast"].includes(value) ? value : DEFAULT_SPEED_MODE;
|
|
1821
|
+
const normalizeContextMode = (value) => [
|
|
1822
|
+
"standard",
|
|
1823
|
+
"extended",
|
|
1824
|
+
"custom"
|
|
1825
|
+
].includes(value) ? value : DEFAULT_CONTEXT_MODE;
|
|
1826
|
+
const normalizeCustomContextWindow = (value, maximum = MAX_CUSTOM_CONTEXT_WINDOW) => {
|
|
1827
|
+
if (!Number.isInteger(value)) return DEFAULT_CUSTOM_CONTEXT_WINDOW;
|
|
1828
|
+
return Math.min(Math.max(value, MIN_CUSTOM_CONTEXT_WINDOW), maximum);
|
|
1829
|
+
};
|
|
1830
|
+
const formatContextWindow = (value) => value === 1e6 ? "1M" : `${Math.round(value / 1e3)}K`;
|
|
1831
|
+
const parseContextWindow = (value) => {
|
|
1832
|
+
const match = /^\s*(\d+)\s*$/u.exec(String(value));
|
|
1833
|
+
if (match === null) return NaN;
|
|
1834
|
+
return Number(match[1]);
|
|
1835
|
+
};
|
|
1836
|
+
const normalizeQuickQuotaMode = (value, legacyVisible = false) => [
|
|
1837
|
+
"off",
|
|
1838
|
+
"percent",
|
|
1839
|
+
"bar",
|
|
1840
|
+
"forecast"
|
|
1841
|
+
].includes(value) ? value : legacyVisible === true ? QUICK_QUOTA_MODE_PERCENT : "off";
|
|
1842
|
+
const supportsCodexFastMode = (modelId) => typeof modelId === "string" && (/^gpt-5\.(?:5|6)(?:$|-)/u.test(modelId) || modelId === "gpt-5.4");
|
|
1843
|
+
//#endregion
|
|
1844
|
+
//#region src/sidebar-quota.js
|
|
1845
|
+
const isDisplayableWindow = (window) => Number.isFinite(window?.remainingPercent) && window.remainingPercent >= 0 && window.remainingPercent <= 100 && Number.isFinite(window?.windowSeconds) && window.windowSeconds > 0;
|
|
1846
|
+
const normalized = (value) => String(value ?? "").toLocaleLowerCase("en-US").replaceAll(/[^a-z0-9]+/gu, "-");
|
|
1847
|
+
const limitMatchesModel = (limit, model) => {
|
|
1848
|
+
if (/\bspark\b/u.test(normalized(model))) return /\bspark\b/u.test(normalized(`${limit?.id ?? ""} ${limit?.name ?? ""}`));
|
|
1849
|
+
return limit?.id === "codex";
|
|
1850
|
+
};
|
|
1851
|
+
function selectModelQuotaWindows(usage, model) {
|
|
1852
|
+
return (Array.isArray(usage?.rateLimits) ? usage.rateLimits.filter((limit) => limitMatchesModel(limit, model) && Array.isArray(limit.windows)).flatMap((limit) => limit.windows).filter(isDisplayableWindow) : []).map((selected) => ({
|
|
1853
|
+
remainingPercent: selected.remainingPercent,
|
|
1854
|
+
windowSeconds: selected.windowSeconds,
|
|
1855
|
+
...Number.isSafeInteger(selected.resetsAt) ? { resetsAt: selected.resetsAt } : {},
|
|
1856
|
+
...selected.forecast === void 0 ? {} : { forecast: selected.forecast }
|
|
1857
|
+
})).sort((a, b) => a.windowSeconds - b.windowSeconds);
|
|
1858
|
+
}
|
|
1859
|
+
//#endregion
|
|
1860
|
+
//#region src/login-progress.js
|
|
1861
|
+
/** Reconcile a login flow with the credential store without exposing credentials. */
|
|
1862
|
+
async function readLoginProgress({ flow, readFlow, readAccount }) {
|
|
1863
|
+
try {
|
|
1864
|
+
const nextFlow = await readFlow();
|
|
1865
|
+
if (nextFlow.phase === "failed") try {
|
|
1866
|
+
const account = await readAccount();
|
|
1867
|
+
if (account?.authenticated === true) return {
|
|
1868
|
+
flow: {
|
|
1869
|
+
id: flow.id,
|
|
1870
|
+
method: flow.method,
|
|
1871
|
+
phase: "authenticated",
|
|
1872
|
+
authenticated: true
|
|
1873
|
+
},
|
|
1874
|
+
account,
|
|
1875
|
+
recovered: true
|
|
1876
|
+
};
|
|
1877
|
+
} catch {}
|
|
1878
|
+
if (nextFlow.phase !== "authenticated") return { flow: nextFlow };
|
|
1879
|
+
return {
|
|
1880
|
+
flow: nextFlow,
|
|
1881
|
+
account: await readAccount()
|
|
1882
|
+
};
|
|
1883
|
+
} catch (flowError) {
|
|
1884
|
+
try {
|
|
1885
|
+
const account = await readAccount();
|
|
1886
|
+
if (account?.authenticated === true) return {
|
|
1887
|
+
flow: {
|
|
1888
|
+
id: flow.id,
|
|
1889
|
+
method: flow.method,
|
|
1890
|
+
phase: "authenticated",
|
|
1891
|
+
authenticated: true
|
|
1892
|
+
},
|
|
1893
|
+
account,
|
|
1894
|
+
recovered: true
|
|
1895
|
+
};
|
|
1896
|
+
} catch {}
|
|
1897
|
+
throw flowError;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
//#endregion
|
|
1901
|
+
//#region src/preference-controller.js
|
|
1902
|
+
const CHANNEL$2 = "/codex-subscription";
|
|
1903
|
+
const unwrap$1 = (response) => {
|
|
1904
|
+
if (!response?.ok) throw new Error(response?.error?.message ?? "Codex RPC failed");
|
|
1905
|
+
return response.value;
|
|
1906
|
+
};
|
|
1907
|
+
function createPreferenceController(scope, rpc) {
|
|
1908
|
+
let updating = false;
|
|
1909
|
+
let error = false;
|
|
1910
|
+
let fallbackStatus = "loading";
|
|
1911
|
+
let fallback;
|
|
1912
|
+
let pendingPatch;
|
|
1913
|
+
let failedPatch;
|
|
1914
|
+
let generation = 0;
|
|
1915
|
+
let contextModels = [];
|
|
1916
|
+
let verbosityModels = [];
|
|
1917
|
+
let modelError = false;
|
|
1918
|
+
let modelRefreshGeneration = 0;
|
|
1919
|
+
let modelRefreshStarted = false;
|
|
1920
|
+
let disposed = false;
|
|
1921
|
+
const sameModels = (left, right) => left.length === right.length && left.every((model, index) => JSON.stringify(model) === JSON.stringify(right[index]));
|
|
1922
|
+
const nativeSnapshot = () => scope.getSnapshot();
|
|
1923
|
+
const read = () => {
|
|
1924
|
+
const native = nativeSnapshot();
|
|
1925
|
+
const current = native.status === "ready" ? native : fallbackStatus === "ready" ? fallback : native;
|
|
1926
|
+
const value = pendingPatch === void 0 ? current.value : {
|
|
1927
|
+
...current.value,
|
|
1928
|
+
...pendingPatch
|
|
1929
|
+
};
|
|
1930
|
+
return Object.freeze({
|
|
1931
|
+
status: current.status,
|
|
1932
|
+
quickQuotaMode: normalizeQuickQuotaMode(value?.[QUICK_QUOTA_MODE_FIELD], value?.[LEGACY_QUICK_QUOTA_FIELD]),
|
|
1933
|
+
searchProvider: normalizeSearchProvider(value?.[SEARCH_PROVIDER_FIELD]),
|
|
1934
|
+
speedMode: normalizeSpeedMode(value?.[SPEED_MODE_FIELD]),
|
|
1935
|
+
outputVerbosity: normalizeOutputVerbosity(value?.[OUTPUT_VERBOSITY_FIELD]),
|
|
1936
|
+
contextMode: normalizeContextMode(value?.[CONTEXT_MODE_FIELD]),
|
|
1937
|
+
customContextWindow: normalizeCustomContextWindow(value?.[CUSTOM_CONTEXT_WINDOW_FIELD]),
|
|
1938
|
+
customContextWindows: Object.fromEntries(Object.entries(CUSTOM_CONTEXT_MODEL_FIELDS).map(([modelKey, field]) => [modelKey, normalizeCustomContextWindow(value?.[field] ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[modelKey], CUSTOM_CONTEXT_MODEL_CAPS[modelKey])])),
|
|
1939
|
+
contextModels,
|
|
1940
|
+
verbosityModels,
|
|
1941
|
+
modelError,
|
|
1942
|
+
writable: !updating && current.status === "ready" && current.writable === true,
|
|
1943
|
+
saving: updating,
|
|
1944
|
+
error
|
|
1945
|
+
});
|
|
1946
|
+
};
|
|
1947
|
+
let snapshot = read();
|
|
1948
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1949
|
+
const publish = () => {
|
|
1950
|
+
snapshot = read();
|
|
1951
|
+
for (const listener of listeners) listener();
|
|
1952
|
+
};
|
|
1953
|
+
const disposeScope = scope.subscribe(() => {
|
|
1954
|
+
error = false;
|
|
1955
|
+
if (!updating) failedPatch = void 0;
|
|
1956
|
+
publish();
|
|
1957
|
+
});
|
|
1958
|
+
const acceptFallback = (value) => {
|
|
1959
|
+
if (!modelRefreshStarted) {
|
|
1960
|
+
contextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
|
|
1961
|
+
verbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
|
|
1962
|
+
}
|
|
1963
|
+
fallbackStatus = "ready";
|
|
1964
|
+
fallback = {
|
|
1965
|
+
status: "ready",
|
|
1966
|
+
value: {
|
|
1967
|
+
[QUICK_QUOTA_MODE_FIELD]: normalizeQuickQuotaMode(value?.[QUICK_QUOTA_MODE_FIELD], value?.[LEGACY_QUICK_QUOTA_FIELD]),
|
|
1968
|
+
[SEARCH_PROVIDER_FIELD]: normalizeSearchProvider(value?.[SEARCH_PROVIDER_FIELD]),
|
|
1969
|
+
[SPEED_MODE_FIELD]: normalizeSpeedMode(value?.[SPEED_MODE_FIELD]),
|
|
1970
|
+
[OUTPUT_VERBOSITY_FIELD]: normalizeOutputVerbosity(value?.[OUTPUT_VERBOSITY_FIELD]),
|
|
1971
|
+
[CONTEXT_MODE_FIELD]: normalizeContextMode(value?.[CONTEXT_MODE_FIELD]),
|
|
1972
|
+
[CUSTOM_CONTEXT_WINDOW_FIELD]: normalizeCustomContextWindow(value?.[CUSTOM_CONTEXT_WINDOW_FIELD]),
|
|
1973
|
+
...Object.fromEntries(Object.entries(CUSTOM_CONTEXT_MODEL_FIELDS).map(([modelKey, field]) => [field, normalizeCustomContextWindow(value?.[field] ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[modelKey], CUSTOM_CONTEXT_MODEL_CAPS[modelKey])]))
|
|
1974
|
+
},
|
|
1975
|
+
writable: value?.writable === true
|
|
1976
|
+
};
|
|
1977
|
+
};
|
|
1978
|
+
const load = async () => {
|
|
1979
|
+
const current = ++generation;
|
|
1980
|
+
updating = false;
|
|
1981
|
+
pendingPatch = void 0;
|
|
1982
|
+
fallbackStatus = "loading";
|
|
1983
|
+
fallback = void 0;
|
|
1984
|
+
error = false;
|
|
1985
|
+
publish();
|
|
1986
|
+
try {
|
|
1987
|
+
const value = unwrap$1(await rpc.call(CHANNEL$2, "preferences/status", {}));
|
|
1988
|
+
if (current !== generation || disposed) return;
|
|
1989
|
+
if (nativeSnapshot().status === "ready") {
|
|
1990
|
+
if (!modelRefreshStarted) {
|
|
1991
|
+
contextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
|
|
1992
|
+
verbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
|
|
1993
|
+
}
|
|
1994
|
+
} else acceptFallback(value);
|
|
1995
|
+
publish();
|
|
1996
|
+
} catch {
|
|
1997
|
+
if (current !== generation || disposed || nativeSnapshot().status === "ready") return;
|
|
1998
|
+
fallbackStatus = "unavailable";
|
|
1999
|
+
publish();
|
|
2000
|
+
}
|
|
2001
|
+
};
|
|
2002
|
+
const refreshModels = async () => {
|
|
2003
|
+
const current = ++modelRefreshGeneration;
|
|
2004
|
+
modelRefreshStarted = true;
|
|
2005
|
+
const hadError = modelError;
|
|
2006
|
+
modelError = false;
|
|
2007
|
+
if (hadError) publish();
|
|
2008
|
+
try {
|
|
2009
|
+
const value = unwrap$1(await rpc.call(CHANNEL$2, "preferences/models", {}));
|
|
2010
|
+
if (disposed || current !== modelRefreshGeneration) return false;
|
|
2011
|
+
const nextContextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
|
|
2012
|
+
const nextVerbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
|
|
2013
|
+
const changed = !sameModels(contextModels, nextContextModels) || !sameModels(verbosityModels, nextVerbosityModels);
|
|
2014
|
+
if (changed) {
|
|
2015
|
+
contextModels = nextContextModels;
|
|
2016
|
+
verbosityModels = nextVerbosityModels;
|
|
2017
|
+
publish();
|
|
2018
|
+
}
|
|
2019
|
+
return changed;
|
|
2020
|
+
} catch {
|
|
2021
|
+
if (disposed || current !== modelRefreshGeneration) return false;
|
|
2022
|
+
if (!modelError) {
|
|
2023
|
+
modelError = true;
|
|
2024
|
+
publish();
|
|
2025
|
+
}
|
|
2026
|
+
return false;
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2029
|
+
const set = async (patch) => {
|
|
2030
|
+
if (disposed || snapshot.status !== "ready" || snapshot.writable !== true) return;
|
|
2031
|
+
const current = ++generation;
|
|
2032
|
+
const entries = Object.entries(patch);
|
|
2033
|
+
updating = true;
|
|
2034
|
+
pendingPatch = patch;
|
|
2035
|
+
error = false;
|
|
2036
|
+
failedPatch = void 0;
|
|
2037
|
+
publish();
|
|
2038
|
+
try {
|
|
2039
|
+
if (nativeSnapshot().status === "ready") {
|
|
2040
|
+
for (const [field, value] of entries) {
|
|
2041
|
+
if (current !== generation) return;
|
|
2042
|
+
await scope.set(field, value);
|
|
2043
|
+
}
|
|
2044
|
+
if (current !== generation) return;
|
|
2045
|
+
const accepted = nativeSnapshot().value;
|
|
2046
|
+
error = entries.some(([field, value]) => accepted?.[field] !== value);
|
|
2047
|
+
pendingPatch = void 0;
|
|
2048
|
+
} else {
|
|
2049
|
+
const value = unwrap$1(await rpc.call(CHANNEL$2, "preferences/update", patch));
|
|
2050
|
+
if (current !== generation) return;
|
|
2051
|
+
acceptFallback(value);
|
|
2052
|
+
pendingPatch = void 0;
|
|
2053
|
+
}
|
|
2054
|
+
} catch {
|
|
2055
|
+
if (current === generation) {
|
|
2056
|
+
pendingPatch = void 0;
|
|
2057
|
+
error = true;
|
|
2058
|
+
failedPatch = patch;
|
|
2059
|
+
}
|
|
2060
|
+
} finally {
|
|
2061
|
+
if (current === generation) {
|
|
2062
|
+
updating = false;
|
|
2063
|
+
publish();
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
};
|
|
2067
|
+
return {
|
|
2068
|
+
getSnapshot: () => snapshot,
|
|
2069
|
+
subscribe: (listener) => {
|
|
2070
|
+
listeners.add(listener);
|
|
2071
|
+
return () => listeners.delete(listener);
|
|
2072
|
+
},
|
|
2073
|
+
load,
|
|
2074
|
+
set,
|
|
2075
|
+
retry: () => failedPatch === void 0 ? load() : set(failedPatch),
|
|
2076
|
+
refreshModels,
|
|
2077
|
+
dispose: () => {
|
|
2078
|
+
disposed = true;
|
|
2079
|
+
generation += 1;
|
|
2080
|
+
modelRefreshGeneration += 1;
|
|
2081
|
+
disposeScope();
|
|
2082
|
+
}
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
//#endregion
|
|
2086
|
+
//#region src/account-status-controller.js
|
|
2087
|
+
const CHANNEL$1 = "/codex-subscription";
|
|
2088
|
+
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
2089
|
+
const STATUS_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
2090
|
+
"credential-unavailable",
|
|
2091
|
+
"credential-malformed",
|
|
2092
|
+
"transport",
|
|
2093
|
+
"timeout",
|
|
2094
|
+
"unknown"
|
|
2095
|
+
]);
|
|
2096
|
+
const STATUS_ERROR_MESSAGES = /* @__PURE__ */ new Map([
|
|
2097
|
+
["Codex account credentials are unavailable", "credential-unavailable"],
|
|
2098
|
+
["Codex account credentials are malformed", "credential-malformed"],
|
|
2099
|
+
["Codex account status service is unavailable", "transport"],
|
|
2100
|
+
["Could not read Codex account status", "unknown"]
|
|
2101
|
+
]);
|
|
2102
|
+
const TIMEOUT_CODES = /* @__PURE__ */ new Set([
|
|
2103
|
+
"TIMEOUT",
|
|
2104
|
+
"ETIMEDOUT",
|
|
2105
|
+
"ERR_TIMEOUT",
|
|
2106
|
+
"UND_ERR_CONNECT_TIMEOUT"
|
|
2107
|
+
]);
|
|
2108
|
+
const TRANSPORT_CODES = /* @__PURE__ */ new Set([
|
|
2109
|
+
"ECONNRESET",
|
|
2110
|
+
"ECONNREFUSED",
|
|
2111
|
+
"ENOTFOUND",
|
|
2112
|
+
"EAI_AGAIN",
|
|
2113
|
+
"NETWORK",
|
|
2114
|
+
"NETWORK_ERROR",
|
|
2115
|
+
"TRANSPORT",
|
|
2116
|
+
"CONNECTION_CLOSED",
|
|
2117
|
+
"DISCONNECTED"
|
|
2118
|
+
]);
|
|
2119
|
+
const asCode = (value) => typeof value === "string" ? value.trim().toLowerCase() : void 0;
|
|
2120
|
+
function rpcError(response) {
|
|
2121
|
+
const code = asCode(response?.error?.code);
|
|
2122
|
+
const message = typeof response?.error?.message === "string" ? response.error.message : "";
|
|
2123
|
+
const error = /* @__PURE__ */ new Error("Codex account status request failed");
|
|
2124
|
+
error.code = code === "internal" && STATUS_ERROR_MESSAGES.has(message) ? STATUS_ERROR_MESSAGES.get(message) : "unknown";
|
|
2125
|
+
return error;
|
|
2126
|
+
}
|
|
2127
|
+
function timeoutError() {
|
|
2128
|
+
const error = /* @__PURE__ */ new Error("Codex account status request timed out");
|
|
2129
|
+
error.code = "timeout";
|
|
2130
|
+
error.name = "TimeoutError";
|
|
2131
|
+
return error;
|
|
2132
|
+
}
|
|
2133
|
+
function classifyAccountStatusError(error) {
|
|
2134
|
+
const code = typeof error?.code === "string" ? error.code.trim().toUpperCase() : "";
|
|
2135
|
+
if (code === "TIMEOUT" || TIMEOUT_CODES.has(code) || error?.name === "TimeoutError") return "timeout";
|
|
2136
|
+
if (STATUS_ERROR_CODES.has(asCode(error?.code))) return asCode(error.code);
|
|
2137
|
+
if (TRANSPORT_CODES.has(code) || error?.name === "NetworkError") return "transport";
|
|
2138
|
+
return "unknown";
|
|
2139
|
+
}
|
|
2140
|
+
function publicAccountStatusError(error) {
|
|
2141
|
+
return Object.freeze({ code: classifyAccountStatusError(error) });
|
|
2142
|
+
}
|
|
2143
|
+
/** Own the account-status request lifecycle independently from account actions. */
|
|
2144
|
+
function createAccountStatusController(rpc, options = {}) {
|
|
2145
|
+
const request = options.request ?? (() => rpc.call(CHANNEL$1, "status", {}));
|
|
2146
|
+
const scheduleTimeout = options.setTimeout ?? setTimeout;
|
|
2147
|
+
const cancelTimeout = options.clearTimeout ?? clearTimeout;
|
|
2148
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
2149
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) throw new Error("Account status timeout must be positive");
|
|
2150
|
+
let snapshot = Object.freeze({
|
|
2151
|
+
status: "loading",
|
|
2152
|
+
account: void 0,
|
|
2153
|
+
error: void 0,
|
|
2154
|
+
retrying: false
|
|
2155
|
+
});
|
|
2156
|
+
let generation = 0;
|
|
2157
|
+
let active;
|
|
2158
|
+
let disposed = false;
|
|
2159
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2160
|
+
const publish = (next) => {
|
|
2161
|
+
snapshot = Object.freeze(next);
|
|
2162
|
+
for (const listener of [...listeners]) listener();
|
|
2163
|
+
};
|
|
2164
|
+
const load = () => {
|
|
2165
|
+
if (disposed) return Promise.resolve(void 0);
|
|
2166
|
+
if (active !== void 0) return active.promise;
|
|
2167
|
+
const id = ++generation;
|
|
2168
|
+
const retrying = snapshot.error !== void 0;
|
|
2169
|
+
publish({
|
|
2170
|
+
status: retrying ? "error" : "loading",
|
|
2171
|
+
account: retrying ? snapshot.account : void 0,
|
|
2172
|
+
error: retrying ? snapshot.error : void 0,
|
|
2173
|
+
retrying: true
|
|
2174
|
+
});
|
|
2175
|
+
const controller = new AbortController();
|
|
2176
|
+
let timer;
|
|
2177
|
+
let onAbort;
|
|
2178
|
+
const cancelled = new Promise((resolve, reject) => {
|
|
2179
|
+
onAbort = () => reject(controller.signal.reason);
|
|
2180
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
2181
|
+
if (controller.signal.aborted) onAbort();
|
|
2182
|
+
});
|
|
2183
|
+
const timeout = new Promise((resolve, reject) => {
|
|
2184
|
+
timer = scheduleTimeout(() => {
|
|
2185
|
+
const error = timeoutError();
|
|
2186
|
+
controller.abort(error);
|
|
2187
|
+
reject(error);
|
|
2188
|
+
}, timeoutMs);
|
|
2189
|
+
});
|
|
2190
|
+
const work = Promise.resolve().then(() => request(controller.signal)).then((response) => {
|
|
2191
|
+
if (!response?.ok) throw rpcError(response);
|
|
2192
|
+
return response.value;
|
|
2193
|
+
});
|
|
2194
|
+
const promise = Promise.race([
|
|
2195
|
+
work,
|
|
2196
|
+
timeout,
|
|
2197
|
+
cancelled
|
|
2198
|
+
]).then((account) => {
|
|
2199
|
+
if (disposed || id !== generation || controller.signal.aborted) return void 0;
|
|
2200
|
+
if (account === null || typeof account !== "object" || Array.isArray(account) || typeof account.authenticated !== "boolean") throw new Error("Invalid account status");
|
|
2201
|
+
publish({
|
|
2202
|
+
status: "ready",
|
|
2203
|
+
account,
|
|
2204
|
+
error: void 0,
|
|
2205
|
+
retrying: false
|
|
2206
|
+
});
|
|
2207
|
+
return account;
|
|
2208
|
+
}).catch((error) => {
|
|
2209
|
+
if (disposed || id !== generation || controller.signal.aborted && error?.code !== "timeout") return void 0;
|
|
2210
|
+
publish({
|
|
2211
|
+
status: "error",
|
|
2212
|
+
account: void 0,
|
|
2213
|
+
error: publicAccountStatusError(error),
|
|
2214
|
+
retrying: false
|
|
2215
|
+
});
|
|
2216
|
+
}).finally(() => {
|
|
2217
|
+
cancelTimeout(timer);
|
|
2218
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
2219
|
+
if (active?.id === id) active = void 0;
|
|
2220
|
+
});
|
|
2221
|
+
active = {
|
|
2222
|
+
id,
|
|
2223
|
+
controller,
|
|
2224
|
+
promise
|
|
2225
|
+
};
|
|
2226
|
+
return promise;
|
|
2227
|
+
};
|
|
2228
|
+
const acceptAccount = (account) => {
|
|
2229
|
+
if (disposed) return false;
|
|
2230
|
+
generation += 1;
|
|
2231
|
+
active?.controller.abort(/* @__PURE__ */ new Error("Account status superseded by an account action"));
|
|
2232
|
+
active = void 0;
|
|
2233
|
+
publish({
|
|
2234
|
+
status: "ready",
|
|
2235
|
+
account,
|
|
2236
|
+
error: void 0,
|
|
2237
|
+
retrying: false
|
|
2238
|
+
});
|
|
2239
|
+
return true;
|
|
2240
|
+
};
|
|
2241
|
+
const reload = () => {
|
|
2242
|
+
if (disposed) return Promise.resolve(void 0);
|
|
2243
|
+
if (active !== void 0) {
|
|
2244
|
+
generation += 1;
|
|
2245
|
+
active.controller.abort(/* @__PURE__ */ new Error("Account status reload superseded the previous request"));
|
|
2246
|
+
active = void 0;
|
|
2247
|
+
}
|
|
2248
|
+
return load();
|
|
2249
|
+
};
|
|
2250
|
+
const dispose = () => {
|
|
2251
|
+
if (disposed) return;
|
|
2252
|
+
disposed = true;
|
|
2253
|
+
generation += 1;
|
|
2254
|
+
active?.controller.abort(/* @__PURE__ */ new Error("Account status controller disposed"));
|
|
2255
|
+
active = void 0;
|
|
2256
|
+
listeners.clear();
|
|
2257
|
+
};
|
|
2258
|
+
return Object.freeze({
|
|
2259
|
+
getSnapshot: () => snapshot,
|
|
2260
|
+
subscribe(listener) {
|
|
2261
|
+
listeners.add(listener);
|
|
2262
|
+
return () => listeners.delete(listener);
|
|
2263
|
+
},
|
|
2264
|
+
load,
|
|
2265
|
+
retry: load,
|
|
2266
|
+
reload,
|
|
2267
|
+
acceptAccount,
|
|
2268
|
+
dispose
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
//#endregion
|
|
2272
|
+
//#region src/context-draft-state.js
|
|
2273
|
+
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value ?? {}, key);
|
|
2274
|
+
/**
|
|
2275
|
+
* Reconcile saved context values with the inputs currently shown in Settings.
|
|
2276
|
+
* A draft survives a catalog refresh while its saved value is unchanged. New
|
|
2277
|
+
* rows and rows whose saved value changed start from the new saved value.
|
|
2278
|
+
*/
|
|
2279
|
+
function reconcileContextDrafts({ modelRows, drafts, previousSavedValues, savedValues }) {
|
|
2280
|
+
const next = {};
|
|
2281
|
+
for (const model of modelRows) {
|
|
2282
|
+
const key = model.key;
|
|
2283
|
+
const saved = String(savedValues?.[key] ?? "");
|
|
2284
|
+
next[key] = hasOwn(previousSavedValues, key) && previousSavedValues[key] === saved && hasOwn(drafts, key) ? drafts[key] : saved;
|
|
2285
|
+
}
|
|
2286
|
+
return next;
|
|
2287
|
+
}
|
|
2288
|
+
//#endregion
|
|
2289
|
+
//#region src/client.jsx
|
|
2290
|
+
const inject = [
|
|
2291
|
+
"slots",
|
|
2292
|
+
"locale",
|
|
2293
|
+
"connection",
|
|
2294
|
+
"remote",
|
|
2295
|
+
"settingsScope",
|
|
2296
|
+
"modelDirectories",
|
|
2297
|
+
"conversation",
|
|
2298
|
+
"uiConversation",
|
|
2299
|
+
"sessions"
|
|
2300
|
+
];
|
|
2301
|
+
const NS = "settings.codexSubscription";
|
|
2302
|
+
const CHANNEL = "/codex-subscription";
|
|
2303
|
+
const SUPPORT_ISSUE_URL = "https://github.com/WSL043/dsh-codex-subscription/issues/new?template=install-problem.yml";
|
|
2304
|
+
const QUICK_QUOTA_REFRESH_EVENT = "dsh-codex-subscription:refresh-quick-quota";
|
|
2305
|
+
const QUICK_QUOTA_REFRESH_MS = 6e4;
|
|
1693
2306
|
const unwrap = (response) => {
|
|
1694
2307
|
if (!response?.ok) throw new Error(response?.error?.message ?? "Codex RPC failed");
|
|
1695
2308
|
return response.value;
|
|
1696
2309
|
};
|
|
2310
|
+
const accountStatusErrorText = (error, t) => {
|
|
2311
|
+
const key = {
|
|
2312
|
+
"credential-unavailable": "accountCredentialUnavailable",
|
|
2313
|
+
"credential-malformed": "accountCredentialMalformed",
|
|
2314
|
+
timeout: "accountStatusTimeout",
|
|
2315
|
+
transport: "accountStatusTransport",
|
|
2316
|
+
unknown: "accountStatusUnknown"
|
|
2317
|
+
}[error?.code];
|
|
2318
|
+
return t(key ?? "accountStatusUnknown");
|
|
2319
|
+
};
|
|
1697
2320
|
const fill = (text, values) => Object.entries(values).reduce((next, [key, value]) => next.replace(`{${key}}`, String(value)), text);
|
|
1698
2321
|
const maskEmail = (value) => {
|
|
1699
2322
|
if (typeof value !== "string" || !value.includes("@")) return "••••";
|
|
@@ -1716,217 +2339,8 @@ window.__ModuleLoader__.load({
|
|
|
1716
2339
|
const date = new Date(value);
|
|
1717
2340
|
return Number.isFinite(date.getTime()) ? date : void 0;
|
|
1718
2341
|
};
|
|
1719
|
-
const imageDownloadName = (attachment) => {
|
|
1720
|
-
const fallback = "codex-generated-image.png";
|
|
1721
|
-
if (typeof attachment?.name !== "string") return fallback;
|
|
1722
|
-
const cleaned = attachment.name.replace(/[<>:"/\\|?*\u0000-\u001f]/gu, "-").replace(/[. ]+$/u, "").trim();
|
|
1723
|
-
if (cleaned === "") return fallback;
|
|
1724
|
-
return cleaned.toLowerCase().endsWith(".png") ? cleaned : `${cleaned}.png`;
|
|
1725
|
-
};
|
|
1726
|
-
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;
|
|
1727
|
-
async function sha256Hex(data) {
|
|
1728
|
-
const value = await crypto.subtle.digest("SHA-256", data);
|
|
1729
|
-
return [...new Uint8Array(value)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1730
|
-
}
|
|
1731
|
-
function decodeBase64Chunk(value) {
|
|
1732
|
-
if (typeof value !== "string" || value.length === 0 || value.length > Math.ceil(4194304 / 3) * 4 + 8) throw new Error("Invalid original image chunk");
|
|
1733
|
-
let decoded;
|
|
1734
|
-
try {
|
|
1735
|
-
decoded = atob(value);
|
|
1736
|
-
} catch {
|
|
1737
|
-
throw new Error("Invalid original image chunk");
|
|
1738
|
-
}
|
|
1739
|
-
const bytes = new Uint8Array(decoded.length);
|
|
1740
|
-
for (let index = 0; index < decoded.length; index += 1) bytes[index] = decoded.charCodeAt(index);
|
|
1741
|
-
return bytes;
|
|
1742
|
-
}
|
|
1743
|
-
async function readOriginalImage(rpc, sessionId, original) {
|
|
1744
|
-
const parts = [];
|
|
1745
|
-
let total = 0;
|
|
1746
|
-
let done = false;
|
|
1747
|
-
while (!done) {
|
|
1748
|
-
const chunk = unwrap(await rpc.call(CHANNEL, "image/original/chunk", {
|
|
1749
|
-
sessionId,
|
|
1750
|
-
assetId: original.assetId,
|
|
1751
|
-
offset: total
|
|
1752
|
-
}));
|
|
1753
|
-
const ref = decodeOriginalImageRef(chunk?.ref);
|
|
1754
|
-
if (ref === void 0 || !originalRefMatches(ref, original) || chunk.offset !== total || typeof chunk.done !== "boolean") throw new Error("Original image metadata changed");
|
|
1755
|
-
const bytes = decodeBase64Chunk(chunk.encoded);
|
|
1756
|
-
if (bytes.byteLength === 0 || total + bytes.byteLength > original.bytes) throw new Error("Original image download is incomplete");
|
|
1757
|
-
parts.push(bytes);
|
|
1758
|
-
total += bytes.byteLength;
|
|
1759
|
-
done = chunk.done;
|
|
1760
|
-
}
|
|
1761
|
-
if (total !== original.bytes) throw new Error("Original image download is incomplete");
|
|
1762
|
-
const data = new Uint8Array(total);
|
|
1763
|
-
let offset = 0;
|
|
1764
|
-
for (const part of parts) {
|
|
1765
|
-
data.set(part, offset);
|
|
1766
|
-
offset += part.byteLength;
|
|
1767
|
-
}
|
|
1768
|
-
if (await sha256Hex(data) !== original.sha256) throw new Error("Original image integrity check failed");
|
|
1769
|
-
return data;
|
|
1770
|
-
}
|
|
1771
|
-
function triggerBlobDownload(data, mediaType, filename) {
|
|
1772
|
-
const url = URL.createObjectURL(new Blob([data], { type: mediaType }));
|
|
1773
|
-
const anchor = document.createElement("a");
|
|
1774
|
-
anchor.href = url;
|
|
1775
|
-
anchor.download = filename;
|
|
1776
|
-
anchor.rel = "noopener";
|
|
1777
|
-
document.body.append(anchor);
|
|
1778
|
-
try {
|
|
1779
|
-
anchor.click();
|
|
1780
|
-
} finally {
|
|
1781
|
-
anchor.remove();
|
|
1782
|
-
URL.revokeObjectURL(url);
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
function CodexGeneratedImage({ attachment, original, rpc, sessionId, loadImage, attachForEdit, getImageViewer, getInternalImageViewer, t }) {
|
|
1786
|
-
const [attempt, setAttempt] = (0, react.useState)(0);
|
|
1787
|
-
const [error, setError] = (0, react.useState)(false);
|
|
1788
|
-
const [src, setSrc] = (0, react.useState)();
|
|
1789
|
-
const triggerRef = (0, react.useRef)(null);
|
|
1790
|
-
(0, react.useEffect)(() => {
|
|
1791
|
-
let live = true;
|
|
1792
|
-
setError(false);
|
|
1793
|
-
setSrc(void 0);
|
|
1794
|
-
Promise.resolve().then(() => loadImage(attachment)).then((value) => {
|
|
1795
|
-
if (live) setSrc(value);
|
|
1796
|
-
}).catch(() => {
|
|
1797
|
-
if (live) setError(true);
|
|
1798
|
-
});
|
|
1799
|
-
return () => {
|
|
1800
|
-
live = false;
|
|
1801
|
-
};
|
|
1802
|
-
}, [
|
|
1803
|
-
attachment,
|
|
1804
|
-
loadImage,
|
|
1805
|
-
attempt
|
|
1806
|
-
]);
|
|
1807
|
-
const label = attachment.name ?? t("imageLabel");
|
|
1808
|
-
const downloadName = imageDownloadName(attachment);
|
|
1809
|
-
const downloadOriginal = async () => {
|
|
1810
|
-
if (original === void 0) return;
|
|
1811
|
-
triggerBlobDownload(await readOriginalImage(rpc, sessionId, original), original.mediaType, original.name);
|
|
1812
|
-
};
|
|
1813
|
-
const openImage = () => {
|
|
1814
|
-
if (src === void 0) return;
|
|
1815
|
-
const request = {
|
|
1816
|
-
items: [{
|
|
1817
|
-
id: attachment.attachmentId ?? downloadName,
|
|
1818
|
-
src,
|
|
1819
|
-
name: label,
|
|
1820
|
-
width: attachment.width,
|
|
1821
|
-
height: attachment.height,
|
|
1822
|
-
bytes: attachment.bytes,
|
|
1823
|
-
download: original === void 0 ? void 0 : {
|
|
1824
|
-
pendingLabel: t("imageDownloadPreparing"),
|
|
1825
|
-
errorLabel: t("imageDownloadFailed"),
|
|
1826
|
-
onInvoke: downloadOriginal
|
|
1827
|
-
},
|
|
1828
|
-
actions: [{
|
|
1829
|
-
id: "continue-editing",
|
|
1830
|
-
label: t("imageEdit"),
|
|
1831
|
-
pendingLabel: t("imageEditPreparing"),
|
|
1832
|
-
errorLabel: t("imageEditFailed"),
|
|
1833
|
-
closeOnSuccess: true,
|
|
1834
|
-
onInvoke: ({ annotations = [] }) => {
|
|
1835
|
-
const imageKey = String(attachment.attachmentId ?? "image").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1836
|
-
const sourceName = annotations.length === 0 ? downloadName : `codex-edit-${imageKey}-source.png`;
|
|
1837
|
-
const referenceName = `codex-edit-${imageKey}-annotations.png`;
|
|
1838
|
-
return attachForEdit(src, sourceName, buildImageEditDraft({
|
|
1839
|
-
annotations,
|
|
1840
|
-
translate: t,
|
|
1841
|
-
width: attachment.width,
|
|
1842
|
-
height: attachment.height,
|
|
1843
|
-
sourceName,
|
|
1844
|
-
referenceName
|
|
1845
|
-
}), annotations, referenceName);
|
|
1846
|
-
}
|
|
1847
|
-
}]
|
|
1848
|
-
}],
|
|
1849
|
-
opener: triggerRef.current,
|
|
1850
|
-
source: "codex-generated",
|
|
1851
|
-
annotations: true
|
|
1852
|
-
};
|
|
1853
|
-
if (getInternalImageViewer?.()?.open?.(request) === true) return;
|
|
1854
|
-
(getImageViewer?.())?.open?.(request);
|
|
1855
|
-
};
|
|
1856
|
-
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1857
|
-
type: "button",
|
|
1858
|
-
className: "codexGeneratedImageRetry",
|
|
1859
|
-
onClick: () => setAttempt((value) => value + 1),
|
|
1860
|
-
children: t("imageLoadFailed")
|
|
1861
|
-
});
|
|
1862
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1863
|
-
ref: triggerRef,
|
|
1864
|
-
type: "button",
|
|
1865
|
-
className: "codexGeneratedImageFrame",
|
|
1866
|
-
title: t("imageOpen"),
|
|
1867
|
-
"aria-label": fill(t("imageOpenNamed"), { value: label }),
|
|
1868
|
-
onClick: openImage,
|
|
1869
|
-
children: src === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("imageLoading") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
1870
|
-
src,
|
|
1871
|
-
alt: label
|
|
1872
|
-
})
|
|
1873
|
-
});
|
|
1874
|
-
}
|
|
1875
|
-
function CodexImageToolRow({ block, sessionId, rpc, loadImage, attachForEdit, getImageViewer, getInternalImageViewer, t }) {
|
|
1876
|
-
const settled = block?.kind === "tool-result";
|
|
1877
|
-
const image = settled ? block.content.find((item) => item?.type === "image" && item.attachment !== void 0) : void 0;
|
|
1878
|
-
const failed = settled && block.isError === true;
|
|
1879
|
-
const state = !settled ? "running" : failed ? "error" : "done";
|
|
1880
|
-
const status = !settled ? t("imageGenerating") : failed ? t("imageFailed") : t("imageGenerated");
|
|
1881
|
-
const error = failed ? block.content.find((item) => item?.type === "text" && typeof item.text === "string")?.text : void 0;
|
|
1882
|
-
const original = decodeImagePresentation(block?.meta)?.original;
|
|
1883
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1884
|
-
className: "codexImageTool",
|
|
1885
|
-
"data-state": state,
|
|
1886
|
-
children: [
|
|
1887
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1888
|
-
className: "codexImageToolRow",
|
|
1889
|
-
children: [
|
|
1890
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1891
|
-
className: "codexImageToolIcon",
|
|
1892
|
-
"aria-hidden": "true"
|
|
1893
|
-
}),
|
|
1894
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1895
|
-
className: "codexImageToolTitle",
|
|
1896
|
-
children: t("imageGenerate")
|
|
1897
|
-
}),
|
|
1898
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1899
|
-
className: "codexImageBeta",
|
|
1900
|
-
children: t("imageBeta")
|
|
1901
|
-
}),
|
|
1902
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1903
|
-
className: "codexImageToolState",
|
|
1904
|
-
children: status
|
|
1905
|
-
})
|
|
1906
|
-
]
|
|
1907
|
-
}),
|
|
1908
|
-
image === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1909
|
-
className: "codexImageToolGallery",
|
|
1910
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexGeneratedImage, {
|
|
1911
|
-
attachment: image.attachment,
|
|
1912
|
-
original,
|
|
1913
|
-
rpc,
|
|
1914
|
-
sessionId,
|
|
1915
|
-
loadImage,
|
|
1916
|
-
attachForEdit,
|
|
1917
|
-
getImageViewer,
|
|
1918
|
-
getInternalImageViewer,
|
|
1919
|
-
t
|
|
1920
|
-
})
|
|
1921
|
-
}),
|
|
1922
|
-
error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1923
|
-
className: "codexImageToolError",
|
|
1924
|
-
children: error
|
|
1925
|
-
})
|
|
1926
|
-
]
|
|
1927
|
-
});
|
|
1928
|
-
}
|
|
1929
2342
|
const usePreferenceSnapshot = (preference) => (0, react.useSyncExternalStore)(preference.subscribe, preference.getSnapshot);
|
|
2343
|
+
const useAccountStatusSnapshot = (accountStatus) => (0, react.useSyncExternalStore)(accountStatus.subscribe, accountStatus.getSnapshot);
|
|
1930
2344
|
const notifyQuickQuota = () => window.dispatchEvent(new Event(QUICK_QUOTA_REFRESH_EVENT));
|
|
1931
2345
|
const formatRunway = (seconds, t) => {
|
|
1932
2346
|
if (!Number.isFinite(seconds) || seconds <= 0) return void 0;
|
|
@@ -2089,7 +2503,18 @@ window.__ModuleLoader__.load({
|
|
|
2089
2503
|
const modelRows = snapshot.contextModels.filter((model) => model.fixed !== true);
|
|
2090
2504
|
const fixedRows = snapshot.contextModels.filter((model) => model.fixed === true);
|
|
2091
2505
|
const [drafts, setDrafts] = (0, react.useState)({});
|
|
2092
|
-
(0, react.
|
|
2506
|
+
const previousSavedValues = (0, react.useRef)();
|
|
2507
|
+
(0, react.useEffect)(() => {
|
|
2508
|
+
const savedValues = Object.fromEntries(modelRows.map((model) => [model.key, String(snapshot.customContextWindows[model.key])]));
|
|
2509
|
+
const previous = previousSavedValues.current;
|
|
2510
|
+
setDrafts((current) => reconcileContextDrafts({
|
|
2511
|
+
modelRows,
|
|
2512
|
+
drafts: current,
|
|
2513
|
+
previousSavedValues: previous,
|
|
2514
|
+
savedValues
|
|
2515
|
+
}));
|
|
2516
|
+
previousSavedValues.current = savedValues;
|
|
2517
|
+
}, [modelRows.map((model) => `${model.key}\u0000${snapshot.customContextWindows[model.key]}`).join("")]);
|
|
2093
2518
|
const hint = snapshot.contextMode === "extended" ? t("contextExtendedHint") : snapshot.contextMode === "custom" ? t("contextCustomHint") : t("contextStandardHint");
|
|
2094
2519
|
const commit = (modelKey) => {
|
|
2095
2520
|
const parsed = parseContextWindow(drafts[modelKey]);
|
|
@@ -2124,81 +2549,99 @@ window.__ModuleLoader__.load({
|
|
|
2124
2549
|
const selectedMode = contextModeItems.find((item) => item.id === snapshot.contextMode)?.label ?? t("contextStandard");
|
|
2125
2550
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2126
2551
|
className: "codexSubscriptionContext",
|
|
2127
|
-
children: [
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2552
|
+
children: [
|
|
2553
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2554
|
+
className: "codexSubscriptionContextHead",
|
|
2555
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2556
|
+
className: "codexSubscriptionContextCopy",
|
|
2557
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2558
|
+
className: "codexSubscriptionPreferenceLabel",
|
|
2559
|
+
children: t("contextTitle")
|
|
2560
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2561
|
+
className: "codexSubscriptionContextHint",
|
|
2562
|
+
children: hint
|
|
2563
|
+
})]
|
|
2564
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
2565
|
+
open: menuOpen,
|
|
2566
|
+
items: contextModeItems,
|
|
2567
|
+
selectedId: snapshot.contextMode,
|
|
2568
|
+
onSelect: (value) => {
|
|
2569
|
+
setMenuOpen(false);
|
|
2570
|
+
preference.set({ [CONTEXT_MODE_FIELD]: value });
|
|
2571
|
+
},
|
|
2572
|
+
onClose: () => setMenuOpen(false),
|
|
2573
|
+
align: "end",
|
|
2574
|
+
side: "bottom",
|
|
2575
|
+
portal: true,
|
|
2576
|
+
compact: true,
|
|
2577
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2578
|
+
className: "codexSubscriptionContextTrigger",
|
|
2579
|
+
type: "button",
|
|
2580
|
+
"aria-label": t("contextTitle"),
|
|
2581
|
+
"aria-haspopup": "menu",
|
|
2582
|
+
"aria-expanded": menuOpen,
|
|
2583
|
+
disabled: !writable,
|
|
2584
|
+
onClick: () => setMenuOpen((value) => !value),
|
|
2585
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: selectedMode }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})]
|
|
2586
|
+
})
|
|
2137
2587
|
})]
|
|
2138
|
-
}),
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2588
|
+
}),
|
|
2589
|
+
snapshot.contextMode === "custom" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2590
|
+
className: "codexSubscriptionContextModels",
|
|
2591
|
+
children: [modelRows.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2592
|
+
className: "codexSubscriptionContextModel",
|
|
2593
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2594
|
+
className: "codexSubscriptionContextModelCopy",
|
|
2595
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: model.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fill(t("contextMaximum"), { value: String(model.maximum) }) })]
|
|
2596
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
2597
|
+
"aria-label": `${model.label} ${t("contextTokens")}`,
|
|
2598
|
+
className: "codexSubscriptionContextInput",
|
|
2599
|
+
type: "number",
|
|
2600
|
+
inputMode: "numeric",
|
|
2601
|
+
min: MIN_CUSTOM_CONTEXT_WINDOW,
|
|
2602
|
+
max: model.maximum,
|
|
2603
|
+
step: 1,
|
|
2604
|
+
value: drafts[model.key] ?? "",
|
|
2605
|
+
disabled: !writable,
|
|
2606
|
+
onChange: (event) => {
|
|
2607
|
+
const nextValue = event.currentTarget.value;
|
|
2608
|
+
setDrafts((current) => ({
|
|
2609
|
+
...current,
|
|
2610
|
+
[model.key]: nextValue
|
|
2611
|
+
}));
|
|
2612
|
+
},
|
|
2613
|
+
onBlur: () => commit(model.key),
|
|
2614
|
+
onKeyDown: (event) => {
|
|
2615
|
+
if (event.key === "Enter") event.currentTarget.blur();
|
|
2616
|
+
}
|
|
2617
|
+
})]
|
|
2618
|
+
}, model.key)), fixedRows.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2619
|
+
className: "codexSubscriptionContextModel",
|
|
2620
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2621
|
+
className: "codexSubscriptionContextModelCopy",
|
|
2622
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: model.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fill(t("contextFixed"), { value: formatContextWindow(model.maximum) }) })]
|
|
2623
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2624
|
+
className: "codexSubscriptionContextHint",
|
|
2625
|
+
children: formatContextWindow(model.maximum)
|
|
2626
|
+
})]
|
|
2627
|
+
}, model.key))]
|
|
2628
|
+
}) : null,
|
|
2629
|
+
snapshot.modelError ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2630
|
+
className: "codexSubscriptionRecover",
|
|
2631
|
+
role: "alert",
|
|
2632
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2633
|
+
className: "codexSubscriptionError",
|
|
2634
|
+
children: t("modelDirectoryFailed")
|
|
2635
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2153
2636
|
type: "button",
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
disabled: !writable,
|
|
2158
|
-
onClick: () => setMenuOpen((value) => !value),
|
|
2159
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: selectedMode }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})]
|
|
2160
|
-
})
|
|
2161
|
-
})]
|
|
2162
|
-
}), snapshot.contextMode === "custom" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2163
|
-
className: "codexSubscriptionContextModels",
|
|
2164
|
-
children: [modelRows.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2165
|
-
className: "codexSubscriptionContextModel",
|
|
2166
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2167
|
-
className: "codexSubscriptionContextModelCopy",
|
|
2168
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: model.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fill(t("contextMaximum"), { value: String(model.maximum) }) })]
|
|
2169
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
2170
|
-
"aria-label": `${model.label} ${t("contextTokens")}`,
|
|
2171
|
-
className: "codexSubscriptionContextInput",
|
|
2172
|
-
type: "number",
|
|
2173
|
-
inputMode: "numeric",
|
|
2174
|
-
min: MIN_CUSTOM_CONTEXT_WINDOW,
|
|
2175
|
-
max: model.maximum,
|
|
2176
|
-
step: 1,
|
|
2177
|
-
value: drafts[model.key] ?? "",
|
|
2178
|
-
disabled: !writable,
|
|
2179
|
-
onChange: (event) => {
|
|
2180
|
-
const nextValue = event.currentTarget.value;
|
|
2181
|
-
setDrafts((current) => ({
|
|
2182
|
-
...current,
|
|
2183
|
-
[model.key]: nextValue
|
|
2184
|
-
}));
|
|
2637
|
+
variant: "outline",
|
|
2638
|
+
onClick: () => {
|
|
2639
|
+
preference.refreshModels();
|
|
2185
2640
|
},
|
|
2186
|
-
|
|
2187
|
-
onKeyDown: (event) => {
|
|
2188
|
-
if (event.key === "Enter") event.currentTarget.blur();
|
|
2189
|
-
}
|
|
2190
|
-
})]
|
|
2191
|
-
}, model.key)), fixedRows.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2192
|
-
className: "codexSubscriptionContextModel",
|
|
2193
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2194
|
-
className: "codexSubscriptionContextModelCopy",
|
|
2195
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: model.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fill(t("contextFixed"), { value: formatContextWindow(model.maximum) }) })]
|
|
2196
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2197
|
-
className: "codexSubscriptionContextHint",
|
|
2198
|
-
children: formatContextWindow(model.maximum)
|
|
2641
|
+
children: t("modelRetry")
|
|
2199
2642
|
})]
|
|
2200
|
-
}
|
|
2201
|
-
|
|
2643
|
+
}) : null
|
|
2644
|
+
]
|
|
2202
2645
|
});
|
|
2203
2646
|
}
|
|
2204
2647
|
function PreferencesCard({ preference, t }) {
|
|
@@ -2935,18 +3378,23 @@ window.__ModuleLoader__.load({
|
|
|
2935
3378
|
]
|
|
2936
3379
|
});
|
|
2937
3380
|
}
|
|
2938
|
-
function AccountFailureCard({
|
|
3381
|
+
function AccountFailureCard({ accountStatus, snapshot, t }) {
|
|
3382
|
+
const retrying = snapshot.retrying === true;
|
|
2939
3383
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2940
3384
|
className: "codexSubscriptionCard codexSubscriptionRecover",
|
|
2941
3385
|
role: "alert",
|
|
2942
3386
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2943
3387
|
className: "codexSubscriptionError",
|
|
2944
|
-
children: t("
|
|
3388
|
+
children: retrying ? t("accountRetrying") : accountStatusErrorText(snapshot.error, t)
|
|
2945
3389
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2946
3390
|
type: "button",
|
|
2947
3391
|
variant: "outline",
|
|
2948
|
-
|
|
2949
|
-
|
|
3392
|
+
disabled: retrying,
|
|
3393
|
+
"aria-busy": retrying,
|
|
3394
|
+
onClick: () => {
|
|
3395
|
+
accountStatus.retry();
|
|
3396
|
+
},
|
|
3397
|
+
children: retrying ? t("accountRetrying") : t("accountRetry")
|
|
2950
3398
|
})]
|
|
2951
3399
|
});
|
|
2952
3400
|
}
|
|
@@ -3406,27 +3854,19 @@ window.__ModuleLoader__.load({
|
|
|
3406
3854
|
]
|
|
3407
3855
|
});
|
|
3408
3856
|
}
|
|
3409
|
-
function CodexSection({ preference, rpc, t }) {
|
|
3410
|
-
const
|
|
3411
|
-
const
|
|
3857
|
+
function CodexSection({ preference, rpc, accountStatus, t }) {
|
|
3858
|
+
const accountSnapshot = useAccountStatusSnapshot(accountStatus);
|
|
3859
|
+
const account = accountSnapshot.account;
|
|
3412
3860
|
const [resetKey, setResetKey] = (0, react.useState)(0);
|
|
3413
|
-
const
|
|
3414
|
-
const
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
setAccountError(void 0);
|
|
3418
|
-
rpc.call(CHANNEL, "status", {}).then(unwrap).then((next) => {
|
|
3419
|
-
if (accountRequest.current === id) setAccount(next);
|
|
3420
|
-
}).catch(() => {
|
|
3421
|
-
if (accountRequest.current === id) setAccountError(true);
|
|
3422
|
-
});
|
|
3861
|
+
const setAccount = accountStatus.acceptAccount;
|
|
3862
|
+
const accountChanged = () => {
|
|
3863
|
+
setResetKey((value) => value + 1);
|
|
3864
|
+
preference.refreshModels();
|
|
3423
3865
|
};
|
|
3424
3866
|
(0, react.useEffect)(() => {
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
};
|
|
3429
|
-
}, []);
|
|
3867
|
+
accountStatus.load();
|
|
3868
|
+
preference.refreshModels();
|
|
3869
|
+
}, [accountStatus, preference]);
|
|
3430
3870
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3431
3871
|
className: "codexSubscription",
|
|
3432
3872
|
children: [
|
|
@@ -3434,15 +3874,16 @@ window.__ModuleLoader__.load({
|
|
|
3434
3874
|
className: "codexSubscriptionHead",
|
|
3435
3875
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("title") })
|
|
3436
3876
|
}),
|
|
3437
|
-
|
|
3877
|
+
accountSnapshot.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountFailureCard, {
|
|
3878
|
+
accountStatus,
|
|
3879
|
+
snapshot: accountSnapshot,
|
|
3880
|
+
t
|
|
3881
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountCard, {
|
|
3438
3882
|
rpc,
|
|
3439
3883
|
t,
|
|
3440
3884
|
account,
|
|
3441
3885
|
setAccount,
|
|
3442
|
-
onSignedOut:
|
|
3443
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountFailureCard, {
|
|
3444
|
-
retry: loadAccount,
|
|
3445
|
-
t
|
|
3886
|
+
onSignedOut: accountChanged
|
|
3446
3887
|
}),
|
|
3447
3888
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PreferencesCard, {
|
|
3448
3889
|
preference,
|
|
@@ -3476,16 +3917,21 @@ window.__ModuleLoader__.load({
|
|
|
3476
3917
|
}, "codex-subscription: style");
|
|
3477
3918
|
const connection = ctx.get("connection");
|
|
3478
3919
|
const preference = createPreferenceController(ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE }), connection.rpc);
|
|
3920
|
+
const accountStatus = createAccountStatusController(connection.rpc);
|
|
3479
3921
|
ctx.effect(() => {
|
|
3480
3922
|
preference.load();
|
|
3923
|
+
accountStatus.load();
|
|
3481
3924
|
const disposeReset = ctx.on("connection/reset", () => {
|
|
3482
3925
|
preference.load();
|
|
3926
|
+
preference.refreshModels();
|
|
3927
|
+
accountStatus.reload();
|
|
3483
3928
|
});
|
|
3484
3929
|
return () => {
|
|
3485
3930
|
disposeReset?.();
|
|
3486
3931
|
preference.dispose();
|
|
3932
|
+
accountStatus.dispose();
|
|
3487
3933
|
};
|
|
3488
|
-
}, "codex-subscription: preferences");
|
|
3934
|
+
}, "codex-subscription: preferences and account status");
|
|
3489
3935
|
const t = ctx.locale.bind(NS);
|
|
3490
3936
|
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
3491
3937
|
name: "shell.overlay",
|
|
@@ -3505,6 +3951,7 @@ window.__ModuleLoader__.load({
|
|
|
3505
3951
|
inject: () => ({
|
|
3506
3952
|
preference,
|
|
3507
3953
|
rpc: connection.rpc,
|
|
3954
|
+
accountStatus,
|
|
3508
3955
|
t
|
|
3509
3956
|
})
|
|
3510
3957
|
}, CodexSection));
|