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/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
- //#region node_modules/.pnpm/@heroicons+react@2.2.0_react@18.3.1/node_modules/@heroicons/react/16/solid/esm/BoltIcon.js
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/subscription-image-viewer-styles.js
261
- const SUBSCRIPTION_IMAGE_VIEWER_CSS = String.raw`
262
- .dcsiv-root{position:fixed;inset:0;z-index:1000;pointer-events:auto;overflow:hidden;background:rgba(7,8,10,.68);color:var(--dsw-alias-label-primary-inverted,#fff);outline:0;backdrop-filter:blur(13px) saturate(.72);-webkit-backdrop-filter:blur(13px) saturate(.72)}
263
- .dcsiv-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
264
- .dcsiv-topbar{position:absolute;right:50%;bottom:22px;z-index:6;max-width:calc(100vw - 36px);padding:5px;border:1px solid rgba(255,255,255,.12);border-radius:999px;background:rgba(38,39,43,.86);box-shadow:0 10px 34px rgba(0,0,0,.3);transform:translateX(50%);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px)}
265
- .dcsiv-actions{display:flex;align-items:center;gap:2px;overflow-x:auto;scrollbar-width:none}.dcsiv-actions::-webkit-scrollbar{display:none}.dcsiv-button,.dcsiv-download{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 10px;border:0;border-radius:999px;background:transparent;color:rgba(255,255,255,.9);font:inherit;font-size:12px;text-decoration:none;white-space:nowrap;cursor:pointer}.dcsiv-button:hover,.dcsiv-download:hover,.dcsiv-button[data-active=true]{background:rgba(255,255,255,.12)}.dcsiv-button:focus-visible,.dcsiv-download:focus-visible,.dcsiv-close-floating:focus-visible{outline:2px solid rgba(255,255,255,.9);outline-offset:2px}.dcsiv-button:disabled,.dcsiv-download:disabled{opacity:.38;cursor:default}.dcsiv-icon-only{width:32px;padding:0}.dcsiv-zoom{min-width:44px;color:rgba(255,255,255,.68);font-size:12px;font-variant-numeric:tabular-nums;text-align:center}
266
- .dcsiv-close-floating{position:absolute;top:20px;right:20px;z-index:8;display:grid;place-items:center;width:42px;height:42px;padding:0;border:1px solid rgba(255,255,255,.1);border-radius:50%;background:rgba(48,49,53,.82);box-shadow:0 8px 24px rgba(0,0,0,.28);color:#fff;cursor:pointer;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.dcsiv-close-floating:hover{background:rgba(66,67,72,.92)}
267
- .dcsiv-workspace{position:absolute;inset:0;display:grid;min-width:0;min-height:0;padding:68px 24px 72px;box-sizing:border-box}
268
- .dcsiv-stage{position:relative;display:grid;place-items:center;min-width:0;min-height:0;overflow:hidden;padding:0;touch-action:none;user-select:none}.dcsiv-stage[data-dragging=true]{cursor:grabbing}.dcsiv-stage[data-annotating=true]{cursor:crosshair}
269
- .dcsiv-surface{position:relative;display:inline-flex;max-width:100%;max-height:100%;transform-origin:center;will-change:transform}.dcsiv-image{display:block;max-width:calc(100vw - 72px);max-height:calc(100vh - 154px);border-radius:12px;object-fit:contain;box-shadow:0 22px 60px rgba(0,0,0,.46);user-select:none;-webkit-user-drag:none}
270
- .dcsiv-annotation{position:absolute;z-index:5;width:24px;height:24px;transform-origin:center;pointer-events:none}.dcsiv-pin{position:absolute;inset:0;display:grid;place-items:center;width:24px;height:24px;padding:0;border:2px solid #fff;border-radius:50%;background:rgba(23,24,27,.94);box-shadow:0 4px 16px rgba(0,0,0,.35);color:#fff;font:inherit;font-size:11px;font-weight:700;cursor:pointer;pointer-events:auto}.dcsiv-pin[data-active=true]{background:var(--dsw-alias-state-business-primary,#3964fe)}
271
- .dcsiv-inline-note{position:absolute;bottom:34px;box-sizing:border-box;display:grid;width:min(300px,calc(100vw - 40px));grid-template-columns:24px minmax(0,1fr) 24px;align-items:center;gap:7px;padding:7px 8px;border:1px solid rgba(255,255,255,.12);border-radius:18px;background:rgba(32,33,37,.94);box-shadow:0 14px 38px rgba(0,0,0,.38);color:#fff;pointer-events:auto;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}.dcsiv-annotation[data-x=right] .dcsiv-inline-note{left:-8px}.dcsiv-annotation[data-x=left] .dcsiv-inline-note{right:-8px}.dcsiv-annotation[data-x=center] .dcsiv-inline-note{left:50%;transform:translateX(-50%)}.dcsiv-annotation[data-y=down] .dcsiv-inline-note{top:34px;bottom:auto}.dcsiv-inline-note::after{position:absolute;width:9px;height:9px;background:rgba(32,33,37,.94);content:'';transform:rotate(45deg)}.dcsiv-annotation[data-y=up] .dcsiv-inline-note::after{bottom:-5px}.dcsiv-annotation[data-y=down] .dcsiv-inline-note::after{top:-5px}.dcsiv-annotation[data-x=right] .dcsiv-inline-note::after{left:13px}.dcsiv-annotation[data-x=left] .dcsiv-inline-note::after{right:13px}.dcsiv-annotation[data-x=center] .dcsiv-inline-note::after{left:calc(50% - 4px)}.dcsiv-inline-index{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--dsw-alias-state-business-primary,#3964fe);color:#fff;font-size:10px;font-weight:700}.dcsiv-inline-note textarea{box-sizing:border-box;width:100%;min-height:24px;max-height:92px;resize:none;overflow:auto;border:0;outline:0;background:transparent;color:#fff;font:inherit;font-size:12px;line-height:18px}.dcsiv-inline-note textarea::placeholder{color:rgba(255,255,255,.44)}.dcsiv-note-remove{display:grid;place-items:center;width:24px;height:24px;padding:0;border:0;border-radius:50%;background:transparent;color:rgba(255,255,255,.68);cursor:pointer}.dcsiv-note-remove:hover{background:rgba(255,255,255,.1);color:#fff}
272
- .dcsiv-nav{position:absolute;top:50%;z-index:4;width:42px;height:42px;padding:0;transform:translateY(-50%);background:rgba(48,49,53,.82);box-shadow:0 8px 24px rgba(0,0,0,.28);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.dcsiv-prev{left:6px}.dcsiv-next{right:6px}.dcsiv-counter{position:absolute;bottom:8px;left:50%;padding:5px 10px;border-radius:999px;background:rgba(38,39,43,.86);color:rgba(255,255,255,.72);font-size:11px;transform:translateX(-50%);backdrop-filter:blur(16px)}
273
- .dcsiv-hint{display:none}
274
- .dcsiv-copy-notes{position:absolute;right:20px;bottom:22px;z-index:6;display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 11px;border:1px solid rgba(255,255,255,.1);border-radius:999px;background:rgba(38,39,43,.86);color:rgba(255,255,255,.82);font:inherit;font-size:12px;cursor:pointer;backdrop-filter:blur(16px)}
275
- @media(max-width:760px){.dcsiv-close-floating{top:12px;right:12px;width:40px;height:40px}.dcsiv-workspace{padding:60px 12px 68px}.dcsiv-image{max-width:calc(100vw - 24px);max-height:calc(100vh - 136px)}.dcsiv-topbar{bottom:12px;max-width:calc(100vw - 24px)}.dcsiv-button{padding:0 8px}.dcsiv-button span.dcsiv-label{display:none}.dcsiv-inline-note{width:min(260px,calc(100vw - 40px))}.dcsiv-copy-notes{display:none}}
276
- @media(prefers-reduced-motion:reduce){.dcsiv-surface{transition:none}}
277
- `;
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/subscription-image-viewer.jsx
280
- const fill$1 = (value, variables) => Object.entries(variables).reduce((text, [key, replacement]) => text.replaceAll(`{${key}}`, String(replacement)), value);
281
- const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
282
- const bytesLabel = (bytes) => bytes === void 0 ? void 0 : bytes < 1024 * 1024 ? `${Math.max(.1, bytes / 1024).toLocaleString(void 0, { maximumFractionDigits: 1 })} KB` : `${(bytes / 1024 / 1024).toLocaleString(void 0, { maximumFractionDigits: 1 })} MB`;
283
- const downloadName = (name) => {
284
- const cleaned = String(name || "image.png").replace(/[<>:"/\\|?*\u0000-\u001f]/gu, "-").replace(/[. ]+$/u, "").trim();
285
- return cleaned === "" ? "image.png" : cleaned;
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
- const noteText = (annotations, t) => annotations.map((annotation, index) => {
288
- return `${fill$1(t("imageAnnotation"), { value: index + 1 })} (${Math.round(annotation.x * 100)}%, ${Math.round(annotation.y * 100)}%): ${annotation.note.trim()}`;
289
- }).filter((line) => !line.endsWith(": ")).join("\n");
290
- function ViewerAction({ action, annotations, item, service, t }) {
291
- const [state, setState] = (0, react.useState)("idle");
292
- const invoke = async () => {
293
- if (state === "pending") return;
294
- setState("pending");
295
- try {
296
- await action.onInvoke({
297
- annotations,
298
- item,
299
- src: item.src
300
- });
301
- setState("idle");
302
- if (action.closeOnSuccess) service.close();
303
- } catch {
304
- setState("failed");
305
- }
306
- };
307
- const label = state === "pending" ? action.pendingLabel : state === "failed" ? action.errorLabel : action.label;
308
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
309
- type: "button",
310
- className: "dcsiv-button",
311
- disabled: state === "pending",
312
- onClick: () => {
313
- invoke();
314
- },
315
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
316
- className: "dcsiv-label",
317
- children: label ?? t("imageEdit")
318
- })
319
- });
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 ViewerDownload({ download, item, t }) {
322
- const [state, setState] = (0, react.useState)("idle");
323
- const invoke = async () => {
324
- if (state === "pending") return;
325
- setState("pending");
326
- try {
327
- await download.onInvoke({
328
- item,
329
- src: item.src
330
- });
331
- setState("idle");
332
- } catch {
333
- setState("failed");
334
- }
335
- };
336
- const label = state === "pending" ? download.pendingLabel ?? t("imageDownloadPreparing") : state === "failed" ? download.errorLabel ?? t("imageDownloadFailed") : t("imageDownload");
337
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
338
- type: "button",
339
- className: "dcsiv-download",
340
- disabled: state === "pending",
341
- onClick: () => {
342
- invoke();
343
- },
344
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
345
- className: "dcsiv-label",
346
- children: label
347
- })]
348
- });
349
- }
350
- function SubscriptionImageViewerOverlay({ service, t }) {
351
- const request = (0, react.useSyncExternalStore)(service.subscribe, service.getSnapshot);
352
- const [index, setIndex] = (0, react.useState)(0);
353
- const [transform, setTransform] = (0, react.useState)({
354
- zoom: 1,
355
- x: 0,
356
- y: 0
357
- });
358
- const [dragging, setDragging] = (0, react.useState)(false);
359
- const [annotating, setAnnotating] = (0, react.useState)(false);
360
- const [annotationsByImage, setAnnotationsByImage] = (0, react.useState)(service.getAnnotationsSnapshot);
361
- const annotationsByImageRef = (0, react.useRef)(annotationsByImage);
362
- const [selected, setSelected] = (0, react.useState)();
363
- const [focusNote, setFocusNote] = (0, react.useState)();
364
- const [copied, setCopied] = (0, react.useState)(false);
365
- const rootRef = (0, react.useRef)(null);
366
- const stageRef = (0, react.useRef)(null);
367
- const surfaceRef = (0, react.useRef)(null);
368
- const imageRef = (0, react.useRef)(null);
369
- const pointersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
370
- const gestureRef = (0, react.useRef)();
371
- const transformRef = (0, react.useRef)(transform);
372
- transformRef.current = transform;
373
- annotationsByImageRef.current = annotationsByImage;
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
- if (request === void 0) return;
376
- setIndex(request.index);
377
- setTransform({
378
- zoom: 1,
379
- x: 0,
380
- y: 0
381
- });
382
- setDragging(false);
383
- setAnnotating(false);
384
- setSelected(void 0);
385
- setCopied(false);
386
- }, [request?.revision]);
387
- const item = request?.items[index];
388
- const annotations = item === void 0 ? [] : annotationsByImage[item.id] ?? [];
389
- const setAnnotations = (0, react.useCallback)((update) => {
390
- if (item === void 0) return;
391
- const previous = annotationsByImageRef.current[item.id] ?? [];
392
- const next = typeof update === "function" ? update(previous) : update;
393
- const snapshot = {
394
- ...annotationsByImageRef.current,
395
- [item.id]: next
396
- };
397
- annotationsByImageRef.current = snapshot;
398
- service.setAnnotations(item.id, next);
399
- setAnnotationsByImage(snapshot);
400
- }, [item?.id, service]);
401
- const boundedPan = (0, react.useCallback)((zoom, x, y) => {
402
- const stage = stageRef.current;
403
- const surface = surfaceRef.current;
404
- if (stage === null || surface === null || zoom <= 1) return {
405
- x: 0,
406
- y: 0
407
- };
408
- const limitX = Math.max(0, (surface.offsetWidth * zoom - stage.clientWidth) / 2) + 28;
409
- const limitY = Math.max(0, (surface.offsetHeight * zoom - stage.clientHeight) / 2) + 28;
410
- return {
411
- x: clamp(x, -limitX, limitX),
412
- y: clamp(y, -limitY, limitY)
413
- };
414
- }, []);
415
- const setZoomAt = (0, react.useCallback)((nextZoom, clientX, clientY) => {
416
- const stage = stageRef.current;
417
- if (stage === null) return;
418
- setTransform((current) => {
419
- const next = clamp(nextZoom, .5, 8);
420
- const box = stage.getBoundingClientRect();
421
- const px = clientX - box.left - box.width / 2;
422
- const py = clientY - box.top - box.height / 2;
423
- const ratio = next / current.zoom;
424
- return {
425
- zoom: next,
426
- ...boundedPan(next, px - (px - current.x) * ratio, py - (py - current.y) * ratio)
427
- };
428
- });
429
- }, [boundedPan]);
430
- const fit = (0, react.useCallback)(() => {
431
- setTransform({
432
- zoom: 1,
433
- x: 0,
434
- y: 0
435
- });
436
- }, []);
437
- const actual = (0, react.useCallback)(() => {
438
- const image = imageRef.current;
439
- const surface = surfaceRef.current;
440
- if (image === null || surface === null || image.naturalWidth === 0) return;
441
- const zoom = clamp(image.naturalWidth / Math.max(1, surface.offsetWidth), 1, 8);
442
- setTransform({
443
- zoom,
444
- x: 0,
445
- y: 0
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
- document.body.style.overflow = previousOverflow;
497
- document.removeEventListener("keydown", onKeyDown);
203
+ live = false;
498
204
  };
499
205
  }, [
500
- request,
501
- service,
502
- fit,
503
- setZoomAt
504
- ]);
505
- (0, react.useEffect)(() => {
506
- if (focusNote === void 0) return;
507
- (rootRef.current?.querySelector(`[data-note-id="${CSS.escape(focusNote)}"] textarea`))?.focus();
508
- setFocusNote(void 0);
509
- }, [
510
- focusNote,
511
- selected,
512
- annotations.length
206
+ attachment,
207
+ loadImage,
208
+ attempt
513
209
  ]);
514
- (0, react.useEffect)(() => {
515
- setTransform({
516
- zoom: 1,
517
- x: 0,
518
- y: 0
519
- });
520
- setDragging(false);
521
- setAnnotating(false);
522
- setSelected(void 0);
523
- }, [item?.id]);
524
- const onWheel = (0, react.useCallback)((event) => {
525
- event.preventDefault();
526
- setZoomAt(transformRef.current.zoom * Math.exp(-event.deltaY * .0015), event.clientX, event.clientY);
527
- }, [setZoomAt]);
528
- (0, react.useEffect)(() => {
529
- const stage = stageRef.current;
530
- if (stage === null || request === void 0) return void 0;
531
- stage.addEventListener("wheel", onWheel, { passive: false });
532
- return () => stage.removeEventListener("wheel", onWheel);
533
- }, [onWheel, request]);
534
- const onPointerDown = (event) => {
535
- const target = event.target;
536
- if (event.button !== 0 || annotating || target instanceof Element && target.closest("button,textarea,input,a,select,[contenteditable=true]") !== null) return;
537
- pointersRef.current.set(event.pointerId, {
538
- x: event.clientX,
539
- y: event.clientY
540
- });
541
- if (pointersRef.current.size === 2) {
542
- event.currentTarget.setPointerCapture(event.pointerId);
543
- const [a, b] = [...pointersRef.current.values()];
544
- gestureRef.current = {
545
- kind: "pinch",
546
- distance: Math.hypot(a.x - b.x, a.y - b.y),
547
- transform
548
- };
549
- } else if (!annotating && transform.zoom > 1) {
550
- event.currentTarget.setPointerCapture(event.pointerId);
551
- gestureRef.current = {
552
- kind: "pan",
553
- x: event.clientX,
554
- y: event.clientY,
555
- transform
556
- };
557
- setDragging(true);
558
- }
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 onPointerMove = (event) => {
561
- if (!pointersRef.current.has(event.pointerId)) return;
562
- pointersRef.current.set(event.pointerId, {
563
- x: event.clientX,
564
- y: event.clientY
565
- });
566
- const gesture = gestureRef.current;
567
- if (gesture?.kind === "pinch" && pointersRef.current.size >= 2) {
568
- const [a, b] = [...pointersRef.current.values()];
569
- const distance = Math.max(1, Math.hypot(a.x - b.x, a.y - b.y));
570
- setZoomAt(gesture.transform.zoom * distance / Math.max(1, gesture.distance), (a.x + b.x) / 2, (a.y + b.y) / 2);
571
- } else if (gesture?.kind === "pan") {
572
- const pan = boundedPan(gesture.transform.zoom, gesture.transform.x + event.clientX - gesture.x, gesture.transform.y + event.clientY - gesture.y);
573
- setTransform({
574
- zoom: gesture.transform.zoom,
575
- ...pan
576
- });
577
- }
578
- };
579
- const endPointer = (event) => {
580
- pointersRef.current.delete(event.pointerId);
581
- if (pointersRef.current.size === 0) {
582
- gestureRef.current = void 0;
583
- setDragging(false);
584
- }
585
- };
586
- const addAnnotation = (event) => {
587
- if (!annotating || event.target.closest(".dcsiv-annotation")) return;
588
- const bounds = surfaceRef.current?.getBoundingClientRect();
589
- if (bounds === void 0) return;
590
- const annotation = {
591
- id: crypto.randomUUID(),
592
- x: clamp((event.clientX - bounds.left) / bounds.width, 0, 1),
593
- y: clamp((event.clientY - bounds.top) / bounds.height, 0, 1),
594
- note: ""
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
- setAnnotations((current) => [...current, annotation]);
597
- setAnnotating(false);
598
- setSelected(annotation.id);
599
- setFocusNote(annotation.id);
600
- };
601
- const copyNotes = async () => {
602
- const text = noteText(annotations, t);
603
- if (text === "" || typeof navigator?.clipboard?.writeText !== "function") return;
604
- try {
605
- await navigator.clipboard.writeText(text);
606
- setCopied(true);
607
- window.setTimeout(() => {
608
- setCopied(false);
609
- }, 1200);
610
- } catch {
611
- setCopied(false);
612
- }
256
+ if (getInternalImageViewer?.()?.open?.(request) === true) return;
257
+ (getImageViewer?.())?.open?.(request);
613
258
  };
614
- if (request === void 0 || item === void 0) return null;
615
- const meta = [item.width && item.height ? `${item.width} × ${item.height}` : void 0, bytesLabel(item.bytes)].filter(Boolean).join(" · ");
616
- const showCounter = request.items.length > 1;
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
- ref: rootRef,
619
- className: "dcsiv-root",
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: "dcsiv-title dcsiv-sr-only",
627
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: item.name }), meta !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: meta }) : null]
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)("header", {
630
- className: "dcsiv-topbar",
631
- role: "toolbar",
632
- "aria-label": t("imagePreview"),
633
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
634
- className: "dcsiv-actions",
635
- children: [
636
- request.annotations ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
637
- type: "button",
638
- className: "dcsiv-button",
639
- "data-active": annotating,
640
- "aria-label": annotating ? t("imageAnnotateCancel") : t("imageAnnotate"),
641
- "aria-pressed": annotating,
642
- onClick: () => setAnnotating((value) => !value),
643
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
644
- className: "dcsiv-label",
645
- children: annotating ? t("imageAnnotateCancel") : t("imageAnnotate")
646
- })]
647
- }) : null,
648
- annotations.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
649
- type: "button",
650
- className: "dcsiv-button",
651
- "data-active": selected !== void 0,
652
- onClick: () => {
653
- const first = annotations[0];
654
- setSelected((current) => current === void 0 ? first.id : void 0);
655
- if (selected === void 0) setFocusNote(first.id);
656
- },
657
- children: [
658
- annotations.length,
659
- " ",
660
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
661
- className: "dcsiv-label",
662
- children: t("imageRegions")
663
- })
664
- ]
665
- }) : null,
666
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
667
- type: "button",
668
- className: "dcsiv-button",
669
- "aria-label": t("imageFit"),
670
- onClick: fit,
671
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
672
- className: "dcsiv-label",
673
- children: t("imageFit")
674
- })]
675
- }),
676
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
677
- type: "button",
678
- className: "dcsiv-button",
679
- onClick: actual,
680
- children: t("imageActual")
681
- }),
682
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
683
- className: "dcsiv-zoom",
684
- children: [Math.round(transform.zoom * 100), "%"]
685
- }),
686
- item.download === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
687
- className: "dcsiv-download",
688
- href: item.src,
689
- download: downloadName(item.name),
690
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
691
- className: "dcsiv-label",
692
- children: t("imageDownload")
693
- })]
694
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewerDownload, {
695
- download: item.download,
696
- item,
697
- t
698
- }),
699
- item.actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewerAction, {
700
- action,
701
- annotations,
702
- item,
703
- service,
704
- t
705
- }, action.id))
706
- ]
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)("button", {
710
- type: "button",
711
- className: "dcsiv-close-floating",
712
- "aria-label": t("imageClosePreview"),
713
- onClick: () => service.close(),
714
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, {})
715
- }),
716
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
717
- className: "dcsiv-workspace",
718
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
719
- ref: stageRef,
720
- className: "dcsiv-stage",
721
- "data-dragging": dragging,
722
- "data-annotating": annotating,
723
- onClick: (event) => {
724
- if (event.target === event.currentTarget && !annotating && transform.zoom === 1) service.close();
725
- },
726
- onPointerDown,
727
- onPointerMove,
728
- onPointerUp: endPointer,
729
- onPointerCancel: endPointer,
730
- onDoubleClick: () => {
731
- if (transform.zoom === 1) actual();
732
- else fit();
733
- },
734
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
735
- ref: surfaceRef,
736
- className: "dcsiv-surface",
737
- onClick: addAnnotation,
738
- style: { transform: `translate3d(${transform.x}px,${transform.y}px,0) scale(${transform.zoom})` },
739
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
740
- ref: imageRef,
741
- className: "dcsiv-image",
742
- src: item.src,
743
- alt: item.name,
744
- draggable: "false"
745
- }), annotations.map((annotation, position) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
746
- className: "dcsiv-annotation",
747
- "data-x": annotation.x < .38 ? "right" : annotation.x > .62 ? "left" : "center",
748
- "data-y": annotation.y < .28 ? "down" : "up",
749
- style: {
750
- left: `${annotation.x * 100}%`,
751
- top: `${annotation.y * 100}%`,
752
- transform: `translate(-50%,-50%) scale(${1 / transform.zoom})`
753
- },
754
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
755
- type: "button",
756
- className: "dcsiv-pin",
757
- "data-active": selected === annotation.id,
758
- "aria-label": fill$1(t("imageAnnotation"), { value: position + 1 }),
759
- onClick: (event) => {
760
- event.stopPropagation();
761
- const opening = selected !== annotation.id;
762
- setSelected(opening ? annotation.id : void 0);
763
- if (opening) setFocusNote(annotation.id);
764
- },
765
- children: position + 1
766
- }), selected === annotation.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
767
- className: "dcsiv-inline-note",
768
- "data-note-id": annotation.id,
769
- onClick: (event) => event.stopPropagation(),
770
- children: [
771
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
772
- className: "dcsiv-inline-index",
773
- children: position + 1
774
- }),
775
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
776
- value: annotation.note,
777
- rows: 1,
778
- "aria-label": fill$1(t("imageAnnotation"), { value: position + 1 }),
779
- placeholder: t("imageAnnotationPlaceholder"),
780
- onChange: (event) => {
781
- const note = event.target.value;
782
- setAnnotations((current) => current.map((entry) => entry.id === annotation.id ? {
783
- ...entry,
784
- note
785
- } : entry));
786
- },
787
- onKeyDown: (event) => {
788
- if (event.key === "Enter" && !event.shiftKey || event.key === "Escape") {
789
- event.preventDefault();
790
- event.stopPropagation();
791
- event.nativeEvent?.stopImmediatePropagation?.();
792
- setSelected(void 0);
793
- }
794
- }
795
- }),
796
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
797
- type: "button",
798
- className: "dcsiv-note-remove",
799
- "aria-label": t("imageRemoveAnnotation"),
800
- onClick: (event) => {
801
- event.stopPropagation();
802
- setAnnotations((current) => current.filter((entry) => entry.id !== annotation.id));
803
- setSelected(void 0);
804
- },
805
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, {})
806
- })
807
- ]
808
- }) : null]
809
- }, annotation.id))]
810
- }), showCounter ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
811
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
812
- type: "button",
813
- className: "dcsiv-button dcsiv-icon-only dcsiv-nav dcsiv-prev",
814
- "aria-label": t("imagePrevious"),
815
- onClick: (event) => {
816
- event.stopPropagation();
817
- setIndex((value) => (value - 1 + request.items.length) % request.items.length);
818
- },
819
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronLeftOutline14, {})
820
- }),
821
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
822
- type: "button",
823
- className: "dcsiv-button dcsiv-icon-only dcsiv-nav dcsiv-next",
824
- "aria-label": t("imageNext"),
825
- onClick: (event) => {
826
- event.stopPropagation();
827
- setIndex((value) => (value + 1) % request.items.length);
828
- },
829
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
830
- }),
831
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
832
- className: "dcsiv-counter",
833
- children: [
834
- index + 1,
835
- " / ",
836
- request.items.length
837
- ]
838
- })
839
- ] }) : annotating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
840
- className: "dcsiv-hint",
841
- children: t("imageAnnotateHint")
842
- }) : transform.zoom === 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
843
- className: "dcsiv-hint",
844
- children: t("imageZoomHint")
845
- }) : null]
846
- }), annotations.some((annotation) => annotation.note.trim() !== "") ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
847
- type: "button",
848
- className: "dcsiv-copy-notes",
849
- onClick: () => {
850
- copyNotes();
851
- },
852
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {}), copied ? t("imageCopied") : t("imageCopyNotes")]
853
- }) : null]
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/subscription-image-viewer.js
860
- const boundedNumber = (value, fallback) => Number.isFinite(value) && value > 0 ? value : fallback;
861
- const downloadOf = (value) => typeof value?.onInvoke === "function" ? {
862
- pendingLabel: typeof value.pendingLabel === "string" && value.pendingLabel !== "" ? value.pendingLabel : void 0,
863
- errorLabel: typeof value.errorLabel === "string" && value.errorLabel !== "" ? value.errorLabel : void 0,
864
- onInvoke: value.onInvoke
865
- } : void 0;
866
- const actionsOf = (value) => Array.isArray(value) ? value.flatMap((action, position) => {
867
- if (typeof action?.onInvoke !== "function" || typeof action?.label !== "string" || action.label.trim() === "") return [];
868
- return [{
869
- id: typeof action.id === "string" && action.id !== "" ? action.id : `action-${position + 1}`,
870
- label: action.label,
871
- pendingLabel: typeof action.pendingLabel === "string" && action.pendingLabel !== "" ? action.pendingLabel : action.label,
872
- errorLabel: typeof action.errorLabel === "string" && action.errorLabel !== "" ? action.errorLabel : action.label,
873
- closeOnSuccess: action.closeOnSuccess === true,
874
- onInvoke: action.onInvoke
875
- }];
876
- }) : [];
877
- function normalizeSubscriptionViewerRequest(request) {
878
- const items = (Array.isArray(request?.items) ? request.items : []).flatMap((item, position) => {
879
- if (typeof item?.src !== "string" || item.src === "") return [];
880
- return [{
881
- id: typeof item.id === "string" && item.id !== "" ? item.id : `image-${position + 1}`,
882
- src: item.src,
883
- name: typeof item.name === "string" && item.name !== "" ? item.name : `Image ${position + 1}`,
884
- width: boundedNumber(item.width, void 0),
885
- height: boundedNumber(item.height, void 0),
886
- bytes: boundedNumber(item.bytes, void 0),
887
- download: downloadOf(item.download),
888
- actions: actionsOf(item.actions)
889
- }];
890
- });
891
- if (items.length === 0) return void 0;
892
- const requestedIndex = Number.isInteger(request?.index) ? request.index : 0;
893
- return {
894
- items,
895
- index: Math.max(0, Math.min(items.length - 1, requestedIndex)),
896
- opener: typeof HTMLElement !== "undefined" && request?.opener instanceof HTMLElement ? request.opener : void 0,
897
- source: typeof request?.source === "string" ? request.source : "dsh-codex-subscription",
898
- annotations: request?.annotations !== false
899
- };
900
- }
901
- const copyAnnotations = (annotations) => annotations.map((annotation) => ({ ...annotation }));
902
- /**
903
- * Local image viewer state for subscription-generated images.
904
- *
905
- * This stays private to subscription image cards, which need annotation and
906
- * edit actions that a host's generic native viewer may not implement.
907
- */
908
- var SubscriptionImageViewerService = class {
909
- #listeners = /* @__PURE__ */ new Set();
910
- #revision = 0;
911
- #snapshot;
912
- #annotationsByImage = /* @__PURE__ */ new Map();
913
- constructor() {
914
- this.subscribe = (listener) => {
915
- this.#listeners.add(listener);
916
- return () => {
917
- this.#listeners.delete(listener);
918
- };
919
- };
920
- this.getSnapshot = () => this.#snapshot;
921
- this.getAnnotationsSnapshot = () => Object.fromEntries([...this.#annotationsByImage].map(([id, annotations]) => [id, copyAnnotations(annotations)]));
922
- }
923
- setAnnotations(imageId, annotations) {
924
- if (typeof imageId !== "string" || imageId === "" || !Array.isArray(annotations)) return;
925
- if (annotations.length === 0) this.#annotationsByImage.delete(imageId);
926
- else this.#annotationsByImage.set(imageId, copyAnnotations(annotations));
927
- }
928
- open(request) {
929
- const normalized = normalizeSubscriptionViewerRequest(request);
930
- if (normalized === void 0) return false;
931
- this.#revision += 1;
932
- this.#snapshot = {
933
- ...normalized,
934
- revision: this.#revision
935
- };
936
- this.#emit();
937
- return true;
938
- }
939
- close() {
940
- if (this.#snapshot === void 0) return;
941
- const opener = this.#snapshot.opener;
942
- this.#snapshot = void 0;
943
- this.#emit();
944
- if (typeof window === "undefined") opener?.focus();
945
- else {
946
- const focus = () => {
947
- opener?.focus();
948
- };
949
- if (typeof window.requestAnimationFrame === "function") window.requestAnimationFrame(focus);
950
- else focus();
951
- }
952
- }
953
- #emit() {
954
- for (const listener of this.#listeners) listener();
955
- }
956
- };
957
- //#endregion
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}.codexSubscriptionSearchScope{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
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}.codexSubscriptionHead{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
1652
- .codexSubscription h2{font-size:16px;line-height:24px;font-weight:500}.codexSubscription h3{font-size:14px;line-height:22px;font-weight:500}
1653
- .codexSubscriptionTag{border:1px solid var(--dsw-alias-border-l3);border-radius:4px;padding:1px 6px;font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary)}
1654
- .codexSubscriptionNote{font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
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}.codexSubscriptionPreferencesCard{padding:12px 14px;gap:10px}.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}.codexSubscriptionPreferenceCopy{display:flex;min-width:0;flex-direction:column;gap:2px}.codexSubscriptionPreferenceLabel{display:flex;align-items:center;gap:6px}.codexSubscriptionPreferenceHint{max-width:300px;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
1657
- .codexSubscriptionQuotaModes{display:flex;align-items:center;gap:3px;padding:2px;border-radius:9px;background:var(--dsw-alias-bg-module-platform)}.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}.codexSubscriptionQuotaMode small{margin-left:3px;font-size:9px;line-height:1;color:var(--dsw-alias-label-tertiary)}.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)}.codexSubscriptionQuotaMode:has(input:focus-visible){outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}.codexSubscriptionQuotaMode:has(input:disabled){cursor:not-allowed;opacity:.5}.codexSubscriptionQuotaMode input{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
1658
- .codexSubscriptionContext{display:flex;flex-direction:column;gap:8px}.codexSubscriptionContextHead{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.codexSubscriptionContextCopy{display:flex;min-width:0;flex:1;flex-direction:column;gap:2px}.codexSubscriptionContextHint{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}.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}.codexSubscriptionContextTrigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.codexSubscriptionContextTrigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.codexSubscriptionContextTrigger:disabled{color:var(--dsw-alias-label-dimmed);cursor:not-allowed}.codexSubscriptionContextTrigger svg{color:var(--dsw-alias-label-tertiary);transition:transform 120ms var(--ds-ease-in-out)}.codexSubscriptionContextTrigger[aria-expanded=true] svg{transform:rotate(180deg)}.codexSubscriptionContextModels{display:flex;flex-direction:column;border-top:1px solid var(--dsw-alias-border-l2)}.codexSubscriptionContextModel{min-height:42px;display:flex;align-items:center;justify-content:space-between;gap:12px;border-bottom:1px solid var(--dsw-alias-border-l2)}.codexSubscriptionContextModel:last-child{border-bottom:0}.codexSubscriptionContextModelCopy{display:flex;min-width:0;flex-direction:column}.codexSubscriptionContextModelCopy strong{font-size:12px;line-height:18px;font-weight:500}.codexSubscriptionContextModelCopy span{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionContextInput{width:116px}
1659
- .codexSubscriptionSwitch{position:relative;flex:0 0 auto;width:32px;height:18px;padding:0;border:1px solid var(--dsw-alias-border-l3);border-radius:999px;background:var(--dsw-alias-bg-module-platform);cursor:pointer}.codexSubscriptionSwitch:disabled{cursor:not-allowed;opacity:.5}.codexSubscriptionSwitch[aria-checked=true]{background:var(--dsw-alias-label-secondary);border-color:var(--dsw-alias-label-secondary)}.codexSubscriptionSwitchKnob{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:var(--dsw-alias-bg-layer-1);transition:transform 120ms var(--ds-ease-in-out)}.codexSubscriptionSwitch[aria-checked=true] .codexSubscriptionSwitchKnob{transform:translateX(14px)}
1660
- .codexSubscriptionSearch{display:flex;flex-direction:column;gap:7px}.codexSubscriptionSearchChoices{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:6px}.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}.codexSubscriptionSearchChoice:has(input:disabled){cursor:not-allowed;opacity:.5}.codexSubscriptionSearchChoice:has(input:checked){border-color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.codexSubscriptionSearchChoice:has(input:focus-visible){outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}.codexSubscriptionSearchInput{width:14px;height:14px;margin:0;accent-color:var(--dsw-alias-label-primary);cursor:inherit}.codexSubscriptionSearchCopy{display:block;min-width:0;pointer-events:none}.codexSubscriptionSearchCopy strong,.codexSubscriptionSearchCopy span{display:block}.codexSubscriptionSearchCopy strong{font-size:12px;line-height:18px;font-weight:500;color:var(--dsw-alias-label-secondary)}.codexSubscriptionSearchChoice:has(input:checked) strong{color:var(--dsw-alias-label-primary)}.codexSubscriptionSearchCopy span{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionDivider{height:1px;background:var(--dsw-alias-border-l2)}
1661
- .codexSubscriptionQuotaModes[data-saving=true] .codexSubscriptionQuotaMode:has(input:disabled){cursor:wait;opacity:1}.codexSubscriptionSearchChoices[data-saving=true] .codexSubscriptionSearchChoice:has(input:disabled){cursor:wait;opacity:1}
1662
- .codexSubscriptionAccountRow,.codexSubscriptionSectionHead{display:flex;align-items:center;justify-content:space-between;gap:12px}.codexSubscriptionStatus{display:flex;align-items:center;gap:8px;font-size:14px;line-height:22px;font-weight:500}
1663
- .codexSubscriptionAccounts{display:flex;flex-direction:column;border-top:1px solid var(--dsw-alias-border-l2)}.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}.codexSubscriptionAccount:last-child{border-bottom:0}.codexSubscriptionAccount[data-active=true] .codexSubscriptionEmail,.codexSubscriptionAccount[data-active=true]>span{font-weight:600}.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}.codexSubscriptionEmail:hover{background:var(--dsw-alias-interactive-bg-hover)}.codexSubscriptionEmail:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}.codexSubscriptionFlow label{display:flex;flex-direction:column;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary)}
1664
- .codexSubscriptionDot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-dimmed)}.codexSubscriptionDot[data-state=connected]{background:var(--dsw-alias-state-success-primary)}.codexSubscriptionDot[data-state=disconnected]{background:var(--dsw-alias-state-error-primary)}
1665
- .codexSubscriptionActions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.codexSubscriptionFlow{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border-radius:10px;background:var(--dsw-alias-bg-module-platform)}
1666
- .codexSubscriptionFlow p{font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionCode{width:max-content;max-width:100%;font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:.08em;overflow-wrap:anywhere}
1667
- .codexSubscriptionError{font-size:13px;line-height:20px;color:var(--dsw-alias-state-error-primary)}.codexSubscriptionInput{width:100%;box-sizing:border-box}
1668
- .codexSubscriptionRecover{display:flex;align-items:center;justify-content:space-between;gap:12px}.codexSubscriptionRecover .codexSubscriptionError{flex:1}.codexSubscriptionRecover button{flex:0 0 auto}
1669
- .codexSubscriptionDiagnostics{padding:8px 12px;gap:8px;background:transparent;color:var(--dsw-alias-label-secondary)}.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)}.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}.codexSubscriptionLink:hover{background:var(--dsw-alias-bg-module-platform)}.codexSubscriptionLink:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}
1670
- .codexSubscriptionSectionTitle{display:flex;flex:1;min-width:0;flex-direction:column;gap:2px}.codexSubscriptionFreshness{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
1671
- .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}.codexSubscriptionRefresh *{white-space:nowrap!important;word-break:keep-all!important;writing-mode:horizontal-tb!important}
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}.codexSubscriptionLimit{min-width:0;border-radius:10px;padding:9px 12px;background:var(--dsw-alias-bg-module-platform);display:flex;flex-direction:column;gap:6px}
1674
- .codexSubscriptionLimitTop{display:flex;align-items:baseline;justify-content:space-between;gap:12px}.codexSubscriptionLimitLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionLimit strong{font:600 18px/24px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums}
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}.codexSubscriptionLimit progress::-webkit-progress-value{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionLimit progress::-moz-progress-bar{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionLimitMeta{display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
1677
- .codexSubscriptionCreditSection{display:flex;flex-direction:column;gap:7px}.codexSubscriptionCreditNote{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionCreditRows{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px}.codexSubscriptionCreditBalance,.codexSubscriptionSpendLimit{min-width:0;border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-module-platform)}
1678
- .codexSubscriptionCreditBalance{display:flex;flex-direction:column;gap:6px}.codexSubscriptionCreditBalance span,.codexSubscriptionCreditLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionCreditBalance strong{font:600 18px/24px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
1679
- .codexSubscriptionCreditRows{display:flex;flex-direction:column;gap:6px}.codexSubscriptionResetMeta{display:flex;min-width:0;flex-direction:column;gap:1px}.codexSubscriptionResetBalance{display:flex;flex-direction:column;gap:8px}.codexSubscriptionResetCard{display:flex;align-items:center;justify-content:space-between;gap:10px;min-width:0;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-bg-module-platform)}.codexSubscriptionResetCard .codexSubscriptionResetMeta{flex:1}.codexSubscriptionResetCard strong{overflow:hidden;font-size:12px;line-height:18px;font-weight:500;text-overflow:ellipsis;white-space:nowrap}.codexSubscriptionResetCard .codexSubscriptionActions{flex:0 0 auto}.codexSubscriptionResetCard .codexSubscriptionResetUse{min-height:28px;padding:0 10px}.codexSubscriptionResetBalance .codexSubscriptionActions{justify-content:flex-start}.codexSubscriptionResetFlow{display:flex;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:10px}.codexSubscriptionResetFlow h4{margin:0;font-size:13px;line-height:20px;font-weight:500}.codexSubscriptionResetWarning{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionResetExpiry{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionResetCheck{display:flex;align-items:flex-start;gap:8px;padding:9px 10px;border-radius:8px;background:var(--dsw-alias-bg-module-platform);font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary);cursor:pointer}.codexSubscriptionResetCheck input{margin:3px 0 0;accent-color:var(--dsw-alias-label-primary)}.codexSubscriptionResetFinal{border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}.codexSubscriptionResetResult{font-size:12px;line-height:18px;color:var(--dsw-alias-state-success-primary)}
1680
- .codexSubscriptionResetUse:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.codexSubscriptionResetUse:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}
1681
- .codexSubscriptionSpendLimit{display:flex;flex-direction:column;gap:8px}.codexSubscriptionSpendTop{display:flex;align-items:baseline;justify-content:space-between;gap:12px}.codexSubscriptionSpendTop strong{font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums}.codexSubscriptionSpendLimit progress{width:100%;height:6px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-brand-primary,#3964fe);-webkit-appearance:none;appearance:none}.codexSubscriptionSpendLimit progress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexSubscriptionSpendLimit progress::-webkit-progress-value{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionSpendLimit progress::-moz-progress-bar{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}
1682
- .codexComposerQuotaWindows{display:inline-flex;align-items:center;gap:10px;flex-wrap:wrap}.codexComposerQuota{display:inline-flex;align-items:center;gap:5px;flex:0 0 auto;height:28px;box-sizing:border-box;padding:0;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12px;line-height:20px;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap;user-select:none}.codexComposerQuotaBar{display:block;width:40px;height:4px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-label-secondary);-webkit-appearance:none;appearance:none}.codexComposerQuotaBar::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexComposerQuotaBar::-webkit-progress-value{background:var(--dsw-alias-label-secondary);border-radius:999px}.codexComposerQuotaBar::-moz-progress-bar{background:var(--dsw-alias-label-secondary);border-radius:999px}
1683
- .codexModelSelect{position:relative;min-width:0}.codexModelSelectTrigger{display:flex;align-items:center;gap:4px;min-width:0;max-width:min(360px,45cqw);height:28px;padding:0 4px 0 8px;border:0;border-radius:24px;outline:0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:500;line-height:20px;cursor:pointer}.codexModelSelectTrigger:hover:not(:disabled),.codexModelSelectTrigger[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover)}.codexModelSelectTrigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.codexModelSelectTrigger:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectBolt{display:block;flex:none;width:14px;height:14px;color:var(--dsw-alias-label-primary)}.codexModelSelectLabel{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectEffort{flex:none;color:var(--dsw-alias-label-caption)}.codexModelSelectChevron{flex:none;color:var(--dsw-alias-label-caption);transition:transform 120ms}.codexModelSelectTrigger[aria-expanded=true] .codexModelSelectChevron{transform:rotate(180deg)}
1684
- .codexModelSelectMenu,.codexModelSelectSubmenu{position:absolute;z-index:30;box-sizing:border-box;width:max-content;min-width:min(240px,calc(100vw - 32px));max-width:min(420px,calc(100vw - 32px));max-height:min(360px,calc(100vh - 96px));padding:4px;border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);overflow:hidden}.codexModelSelectMenu{right:0;bottom:calc(100% + 8px)}.codexModelSelectSubmenu{right:calc(100% + 8px);bottom:0;min-width:min(230px,calc(100vw - 32px))}.codexModelSelectCell{display:flex;align-items:center;gap:8px;width:100%;min-width:100%;height:40px;box-sizing:border-box;padding:0 10px;border:0;border-radius:10px;background:transparent;color:inherit;font-size:14px;line-height:22px;text-align:left;cursor:pointer}.codexModelSelectCell:hover,.codexModelSelectCell:focus-visible,.codexModelSelectCell[data-open=true]{background:var(--dsw-alias-interactive-bg-hover);outline:0}.codexModelSelectCell:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectCellLabel{flex:none;white-space:nowrap}.codexModelSelectCellValue{flex:auto;min-width:0;overflow:hidden;color:var(--dsw-alias-label-tertiary);text-align:right;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectCellChevron{flex:none;color:var(--dsw-alias-label-tertiary)}.codexModelSelectGroups{min-height:0;max-height:352px;overflow-y:auto}.codexModelSelectGroup+.codexModelSelectGroup{margin-top:4px}.codexModelSelectGroupTitle{position:sticky;top:0;z-index:1;padding:5px 8px 3px;background:var(--dsw-specific-menu);color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:500;line-height:18px}.codexModelSelectOption{display:flex;align-items:center;gap:8px;width:100%;min-width:100%;min-height:38px;box-sizing:border-box;padding:6px 8px;border:0;border-radius:10px;outline:0;background:transparent;color:inherit;text-align:left;cursor:pointer}.codexModelSelectOption:hover:not(:disabled),.codexModelSelectOption:focus-visible{background:var(--dsw-alias-interactive-bg-hover)}.codexModelSelectOption:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectOptionCopy{display:flex;flex:1;min-width:0;flex-direction:column}.codexModelSelectOptionName{overflow:hidden;color:inherit;font-size:14px;font-weight:500;line-height:20px;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectOptionDescription{overflow:hidden;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectCheck{display:grid;place-items:center;flex:0 0 18px;color:var(--dsw-alias-label-primary)}.codexModelSelectStatus,.codexModelSelectEmpty{padding:10px;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.codexModelSelectError,.codexModelSelectWarning{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:4px;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}.codexModelSelectWarning{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-state-warn-label)}.codexModelSelectRetry{flex:none;padding:0;border:0;background:transparent;color:inherit;font:inherit;font-weight:600;cursor:pointer}
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)}.codexImageToolRow{display:flex;align-items:center;min-height:24px;gap:8px;font-size:13px;line-height:20px}.codexImageToolIcon{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;color:var(--dsw-alias-label-secondary)}.codexImageToolIcon::before{content:'';width:8px;height:8px;border:1.5px solid currentColor;border-radius:3px}.codexImageTool[data-state=running] .codexImageToolIcon::before{border-radius:50%;border-right-color:transparent;animation:codexImageSpin 800ms linear infinite}.codexImageTool[data-state=error] .codexImageToolIcon::before{border-color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-state-error-primary)}.codexImageToolTitle{font-weight:500}.codexImageToolState{color:var(--dsw-alias-label-tertiary)}.codexImageToolError{margin:0 0 0 24px;font-size:12px;line-height:18px;color:var(--dsw-alias-state-error-primary)}.codexImageToolGallery{margin-left:24px}.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}.codexGeneratedImageFrame img{display:block;width:100%;height:100%;object-fit:cover}.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}.codexGeneratedImageModal{width:min(920px,calc(100vw - 32px));max-height:calc(100vh - 32px)}.codexGeneratedImageModalContent{min-height:0;overflow:hidden}.codexGeneratedImageViewer{display:flex;min-width:0;flex-direction:column;gap:12px}.codexGeneratedImageStage{display:grid;place-items:center;min-height:280px;max-height:calc(100vh - 260px);overflow:auto;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-module-platform)}.codexGeneratedImageStage img{display:block;max-width:100%;max-height:calc(100vh - 280px);object-fit:contain;transform-origin:center;transition:transform 120ms ease}.codexGeneratedImageMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.codexGeneratedImageToolbar{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}.codexGeneratedImageZoom{display:flex;align-items:center;gap:6px}.codexGeneratedImageZoomValue{min-width:44px;color:var(--dsw-alias-label-secondary);font-size:12px;text-align:center}.codexGeneratedImageDownload{display:inline-flex;align-items:center;gap:6px;min-height:32px;box-sizing:border-box;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:transparent;color:var(--dsw-alias-label-primary);font-size:13px;text-decoration:none}.codexGeneratedImageDownload:hover{background:var(--dsw-alias-interactive-bg-hover)}.codexGeneratedImageGuidance{display:flex;flex-direction:column;gap:2px;padding-top:2px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}@keyframes codexImageSpin{to{transform:rotate(360deg)}}
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}.codexSubscriptionActions{width:100%}.codexSubscriptionSearchChoices{grid-template-columns:1fr}}
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.useEffect)(() => setDrafts(Object.fromEntries(modelRows.map((model) => [model.key, String(snapshot.customContextWindows[model.key])]))), [snapshot.customContextWindows, snapshot.contextModels]);
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: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2128
- className: "codexSubscriptionContextHead",
2129
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2130
- className: "codexSubscriptionContextCopy",
2131
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2132
- className: "codexSubscriptionPreferenceLabel",
2133
- children: t("contextTitle")
2134
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2135
- className: "codexSubscriptionContextHint",
2136
- children: hint
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
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
2139
- open: menuOpen,
2140
- items: contextModeItems,
2141
- selectedId: snapshot.contextMode,
2142
- onSelect: (value) => {
2143
- setMenuOpen(false);
2144
- preference.set({ [CONTEXT_MODE_FIELD]: value });
2145
- },
2146
- onClose: () => setMenuOpen(false),
2147
- align: "end",
2148
- side: "bottom",
2149
- portal: true,
2150
- compact: true,
2151
- anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2152
- className: "codexSubscriptionContextTrigger",
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
- "aria-label": t("contextTitle"),
2155
- "aria-haspopup": "menu",
2156
- "aria-expanded": menuOpen,
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
- onBlur: () => commit(model.key),
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
- }, model.key))]
2201
- }) : null]
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({ retry, t }) {
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("loadFailed")
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
- onClick: retry,
2949
- children: t("accountRetry")
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 [account, setAccount] = (0, react.useState)();
3411
- const [accountError, setAccountError] = (0, react.useState)();
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 accountRequest = (0, react.useRef)(0);
3414
- const loadAccount = () => {
3415
- const id = ++accountRequest.current;
3416
- setAccount(void 0);
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
- loadAccount();
3426
- return () => {
3427
- accountRequest.current += 1;
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
- accountError === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountCard, {
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: () => setResetKey((value) => value + 1)
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));