dsh-codex-subscription 1.10.0 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -4
- package/README.md +34 -42
- package/README.zh-CN.md +34 -42
- package/THIRD_PARTY_NOTICES.md +1 -0
- package/lib/client.js +354 -201
- package/lib/index.js +478 -45
- package/package.json +5 -1
package/lib/client.js
CHANGED
|
@@ -59,12 +59,32 @@ window.__ModuleLoader__.load({
|
|
|
59
59
|
const base = prompt.trim() === "" ? translate("imageEditDefault") : prompt.trim();
|
|
60
60
|
return notes.length === 0 ? base : base + "\n\n" + translate("imageRegionNotes") + "\n" + notes.join("\n");
|
|
61
61
|
}
|
|
62
|
+
const ORIGINAL_IMAGE_ID_PATTERN = /^img_[0-9a-f]{32}$/u;
|
|
63
|
+
const positiveInteger = (value) => Number.isSafeInteger(value) && value > 0;
|
|
64
|
+
function decodeOriginalImageRef(value) {
|
|
65
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || typeof value.assetId !== "string" || !ORIGINAL_IMAGE_ID_PATTERN.test(value.assetId) || value.mediaType !== "image/png" || !positiveInteger(value.bytes) || value.bytes > 48 * 1024 * 1024 || !positiveInteger(value.width) || !positiveInteger(value.height) || typeof value.name !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(value.name) || typeof value.sha256 !== "string" || !/^[0-9a-f]{64}$/u.test(value.sha256)) return void 0;
|
|
66
|
+
return {
|
|
67
|
+
assetId: value.assetId,
|
|
68
|
+
mediaType: value.mediaType,
|
|
69
|
+
bytes: value.bytes,
|
|
70
|
+
width: value.width,
|
|
71
|
+
height: value.height,
|
|
72
|
+
name: value.name,
|
|
73
|
+
sha256: value.sha256
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function decodeImagePresentation(value) {
|
|
77
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || value.kind !== "codex-subscription-image" || value.schemaVersion !== 1) return void 0;
|
|
78
|
+
const original = decodeOriginalImageRef(value.original);
|
|
79
|
+
return original === void 0 ? void 0 : { original };
|
|
80
|
+
}
|
|
62
81
|
//#endregion
|
|
63
82
|
//#region src/settings-contract.js
|
|
64
83
|
const SETTINGS_NAMESPACE = "codex-subscription";
|
|
65
84
|
const QUICK_QUOTA_MODE_FIELD = "quickQuotaMode";
|
|
66
85
|
const LEGACY_QUICK_QUOTA_FIELD = "quickQuotaVisible";
|
|
67
86
|
const QUICK_QUOTA_MODE_PERCENT = "percent";
|
|
87
|
+
const QUICK_QUOTA_MODE_FORECAST = "forecast";
|
|
68
88
|
const SEARCH_PROVIDER_FIELD = "searchProvider";
|
|
69
89
|
const SEARCH_PROVIDER_AUTO = "auto";
|
|
70
90
|
const SEARCH_PROVIDER_CODEX = "codex";
|
|
@@ -135,7 +155,8 @@ window.__ModuleLoader__.load({
|
|
|
135
155
|
const normalizeQuickQuotaMode = (value, legacyVisible = false) => [
|
|
136
156
|
"off",
|
|
137
157
|
"percent",
|
|
138
|
-
"bar"
|
|
158
|
+
"bar",
|
|
159
|
+
"forecast"
|
|
139
160
|
].includes(value) ? value : legacyVisible === true ? QUICK_QUOTA_MODE_PERCENT : "off";
|
|
140
161
|
const supportsCodexFastMode = (modelId) => typeof modelId === "string" && (/^gpt-5\.(?:5|6)(?:$|-)/u.test(modelId) || modelId === "gpt-5.4");
|
|
141
162
|
//#endregion
|
|
@@ -153,7 +174,8 @@ window.__ModuleLoader__.load({
|
|
|
153
174
|
return {
|
|
154
175
|
remainingPercent: selected.remainingPercent,
|
|
155
176
|
windowSeconds: selected.windowSeconds,
|
|
156
|
-
...Number.isSafeInteger(selected.resetsAt) ? { resetsAt: selected.resetsAt } : {}
|
|
177
|
+
...Number.isSafeInteger(selected.resetsAt) ? { resetsAt: selected.resetsAt } : {},
|
|
178
|
+
...selected.forecast === void 0 ? {} : { forecast: selected.forecast }
|
|
157
179
|
};
|
|
158
180
|
}
|
|
159
181
|
//#endregion
|
|
@@ -296,6 +318,7 @@ window.__ModuleLoader__.load({
|
|
|
296
318
|
resetTooEarly: "请等待冷静期结束后再确认。",
|
|
297
319
|
resetAcknowledgeRequired: "请先确认已了解这次操作可能消耗重置次数。",
|
|
298
320
|
resetAccountChanged: "登录账号已变更,请重新开始。",
|
|
321
|
+
resetUncertain: "服务端返回结果不确定。请再次确认,插件会复用同一个请求,不会另外发起一次重置。",
|
|
299
322
|
creditsNote: "仅显示 Codex 为此账户或工作区实际返回的额外 Credits、消费上限或额度重置次数;三者不是同一项。",
|
|
300
323
|
creditsUsed: "已用 {used} / {limit} credits",
|
|
301
324
|
spendReached: "Credits 月度消费上限已用尽。",
|
|
@@ -304,6 +327,8 @@ window.__ModuleLoader__.load({
|
|
|
304
327
|
quickQuotaOff: "关闭",
|
|
305
328
|
quickQuotaPercent: "百分比",
|
|
306
329
|
quickQuotaBar: "进度条",
|
|
330
|
+
quickQuotaForecast: "续航预测",
|
|
331
|
+
quickQuotaBeta: "Beta",
|
|
307
332
|
contextTitle: "上下文窗口",
|
|
308
333
|
contextStandard: "标准",
|
|
309
334
|
contextStandardHint: "使用模型目录默认值;官方 Agent 预设会自动管理上下文。",
|
|
@@ -315,6 +340,12 @@ window.__ModuleLoader__.load({
|
|
|
315
340
|
contextFixed: "固定 {value}",
|
|
316
341
|
contextMaximum: "范围 128000–{value}",
|
|
317
342
|
quickQuotaStatus: "Codex 剩余额度 {value}%",
|
|
343
|
+
quickQuotaForecastStatus: "Codex 剩余额度 {value}%,按当前速度预计可用 {duration}",
|
|
344
|
+
quotaForecast: "按当前速度 {symbol}{duration}",
|
|
345
|
+
runwayDaysHours: "{days} 天 {hours} 小时",
|
|
346
|
+
runwayDays: "{days} 天",
|
|
347
|
+
runwayHours: "{hours} 小时",
|
|
348
|
+
runwayMinutes: "{minutes} 分钟",
|
|
318
349
|
speedTitle: "速度",
|
|
319
350
|
speedStandard: "标准",
|
|
320
351
|
speedStandardHint: "标准速度",
|
|
@@ -346,13 +377,16 @@ window.__ModuleLoader__.load({
|
|
|
346
377
|
imageGenerated: "已生成",
|
|
347
378
|
imageFailed: "生成失败",
|
|
348
379
|
imageLabel: "生成的图片",
|
|
349
|
-
imageOpen: "
|
|
380
|
+
imageOpen: "查看图片",
|
|
350
381
|
imageOpenNamed: "查看 {value}",
|
|
351
382
|
imageLoading: "正在加载图片…",
|
|
352
383
|
imageLoadFailed: "图片加载失败,点击重试",
|
|
353
384
|
imagePreview: "图片预览",
|
|
385
|
+
imagePreviewShort: "预览图",
|
|
354
386
|
imageClosePreview: "关闭预览",
|
|
355
|
-
imageDownload: "
|
|
387
|
+
imageDownload: "下载",
|
|
388
|
+
imageDownloadPreparing: "正在准备原图…",
|
|
389
|
+
imageDownloadFailed: "下载失败,重试",
|
|
356
390
|
imageZoomOut: "缩小",
|
|
357
391
|
imageZoomIn: "放大",
|
|
358
392
|
imageFit: "适合窗口",
|
|
@@ -450,6 +484,7 @@ window.__ModuleLoader__.load({
|
|
|
450
484
|
resetTooEarly: "Wait for the cooldown before confirming.",
|
|
451
485
|
resetAcknowledgeRequired: "Confirm that you understand this may consume a reset.",
|
|
452
486
|
resetAccountChanged: "The signed-in account changed. Start again.",
|
|
487
|
+
resetUncertain: "The server result is uncertain. Confirm again to check the same request; the plugin will not start a separate reset.",
|
|
453
488
|
creditsNote: "Shows only extra Credits, spending caps, or quota resets returned for this account or workspace; these are separate items.",
|
|
454
489
|
creditsUsed: "{used} / {limit} credits used",
|
|
455
490
|
spendReached: "The monthly Credits spending cap has been reached.",
|
|
@@ -458,6 +493,8 @@ window.__ModuleLoader__.load({
|
|
|
458
493
|
quickQuotaOff: "Off",
|
|
459
494
|
quickQuotaPercent: "Percent",
|
|
460
495
|
quickQuotaBar: "Progress bar",
|
|
496
|
+
quickQuotaForecast: "Runway",
|
|
497
|
+
quickQuotaBeta: "Beta",
|
|
461
498
|
contextTitle: "Context window",
|
|
462
499
|
contextStandard: "Standard",
|
|
463
500
|
contextStandardHint: "Use the model catalog default; official agent presets manage context automatically.",
|
|
@@ -469,6 +506,12 @@ window.__ModuleLoader__.load({
|
|
|
469
506
|
contextFixed: "Fixed {value}",
|
|
470
507
|
contextMaximum: "128000–{value}",
|
|
471
508
|
quickQuotaStatus: "Codex quota: {value}% remaining",
|
|
509
|
+
quickQuotaForecastStatus: "Codex quota: {value}% remaining; about {duration} at the current pace",
|
|
510
|
+
quotaForecast: "At current pace {symbol}{duration}",
|
|
511
|
+
runwayDaysHours: "{days}d {hours}h",
|
|
512
|
+
runwayDays: "{days}d",
|
|
513
|
+
runwayHours: "{hours}h",
|
|
514
|
+
runwayMinutes: "{minutes}m",
|
|
472
515
|
speedTitle: "Speed",
|
|
473
516
|
speedStandard: "Standard",
|
|
474
517
|
speedStandardHint: "Standard speed",
|
|
@@ -500,13 +543,16 @@ window.__ModuleLoader__.load({
|
|
|
500
543
|
imageGenerated: "Generated",
|
|
501
544
|
imageFailed: "Generation failed",
|
|
502
545
|
imageLabel: "Generated image",
|
|
503
|
-
imageOpen: "View
|
|
546
|
+
imageOpen: "View image",
|
|
504
547
|
imageOpenNamed: "View {value}",
|
|
505
548
|
imageLoading: "Loading image…",
|
|
506
549
|
imageLoadFailed: "Image failed to load. Click to retry",
|
|
507
550
|
imagePreview: "Image preview",
|
|
551
|
+
imagePreviewShort: "Preview",
|
|
508
552
|
imageClosePreview: "Close preview",
|
|
509
|
-
imageDownload: "Download
|
|
553
|
+
imageDownload: "Download",
|
|
554
|
+
imageDownloadPreparing: "Preparing original…",
|
|
555
|
+
imageDownloadFailed: "Download failed. Retry",
|
|
510
556
|
imageZoomOut: "Zoom out",
|
|
511
557
|
imageZoomIn: "Zoom in",
|
|
512
558
|
imageFit: "Fit to window",
|
|
@@ -531,7 +577,7 @@ window.__ModuleLoader__.load({
|
|
|
531
577
|
.codexSubscriptionNote{font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
|
|
532
578
|
.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}
|
|
533
579
|
.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}
|
|
534
|
-
.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}.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}
|
|
580
|
+
.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}
|
|
535
581
|
.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}
|
|
536
582
|
.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)}
|
|
537
583
|
.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)}
|
|
@@ -560,12 +606,13 @@ window.__ModuleLoader__.load({
|
|
|
560
606
|
.codexModelSelectMenu{overflow:visible}
|
|
561
607
|
.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)}}
|
|
562
608
|
.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}
|
|
609
|
+
.codexImageToolGallery{display:flex;align-items:flex-start;flex-direction:column;gap:8px}
|
|
563
610
|
@container (max-width:560px){.codexSubscriptionCreditRows{grid-template-columns:1fr}}
|
|
564
611
|
@container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}.codexSubscriptionActions{width:100%}.codexSubscriptionSearchChoices{grid-template-columns:1fr}}
|
|
565
612
|
@media(max-width:640px){.codexSubscriptionCard{padding:14px}}
|
|
566
613
|
`;
|
|
567
614
|
const IMAGE_STYLE = String.raw`
|
|
568
|
-
.codexGeneratedImageLightbox{position:fixed;inset:0;z-index:1000;display:grid;grid-template-rows:auto minmax(0,1fr)
|
|
615
|
+
.codexGeneratedImageLightbox{position:fixed;inset:0;z-index:1000;display:grid;grid-template-rows:auto minmax(0,1fr);background:var(--dsw-alias-bg-base,#111);color:var(--dsw-alias-label-primary);outline:0}.codexGeneratedImageTopbar{display:flex;align-items:center;justify-content:space-between;gap:20px;min-height:58px;padding:8px 14px;border-bottom:1px solid var(--dsw-alias-border-l2);background:color-mix(in srgb,var(--dsw-alias-bg-base,#111) 92%,transparent);backdrop-filter:blur(18px)}.codexGeneratedImageTopbar>div:first-child{display:flex;min-width:0;flex-direction:column}.codexGeneratedImageTopbar strong{overflow:hidden;font-size:13px;font-weight:600;line-height:20px;text-overflow:ellipsis;white-space:nowrap}.codexGeneratedImageTopbar small{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:17px}.codexGeneratedImageActions{display:flex;align-items:center;gap:6px}.codexGeneratedImageActions button,.codexGeneratedImageActions a{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:5px;height:32px;padding:0 10px;border:1px solid transparent;border-radius:16px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;text-decoration:none;cursor:pointer}.codexGeneratedImageActions button:hover,.codexGeneratedImageActions a:hover,.codexGeneratedImageActions button.is-active{background:var(--dsw-alias-interactive-bg-hover)}.codexGeneratedImageActions button:focus-visible,.codexGeneratedImageActions a:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}.codexGeneratedImageActions button:disabled{opacity:.4;cursor:default}.codexGeneratedImageActions>span{min-width:38px;color:var(--dsw-alias-label-tertiary);font-size:11px;text-align:center}.codexGeneratedImageClose{font-size:20px!important}.codexGeneratedImageCanvas{position:relative;display:grid;place-items:center;min-height:0;overflow:auto;padding:24px;background:var(--dsw-alias-bg-base,#111)}.codexGeneratedImageSurface{position:relative;display:inline-flex;max-width:calc(100vw - 48px);max-height:calc(100vh - 150px);transform-origin:center;transition:transform 120ms ease}.codexGeneratedImageSurface img{display:block;max-width:100%;max-height:calc(100vh - 150px);border-radius:12px;object-fit:contain}.codexGeneratedImageCanvas.is-annotating .codexGeneratedImageSurface{cursor:crosshair}.codexGeneratedImagePin{position:absolute;display:grid;place-items:center;width:24px;height:24px;padding:0;border:2px solid white;border-radius:50%;background:var(--dsw-alias-label-primary);box-shadow:0 1px 4px rgba(0,0,0,.35);color:var(--dsw-alias-bg-base,#111);font:inherit;font-size:11px;font-weight:700;transform:translate(-50%,-50%);cursor:pointer}.codexGeneratedImagePin.is-active{background:var(--dsw-alias-state-business-primary,#3964fe);color:white}.codexGeneratedImageAnnotateHint{position:absolute;bottom:14px;left:50%;padding:6px 10px;border-radius:14px;background:color-mix(in srgb,var(--dsw-alias-bg-layer-3) 90%,transparent);box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-secondary);font-size:11px;transform:translateX(-50%)}@media(max-width:760px){.codexGeneratedImageTopbar{align-items:flex-start;flex-direction:column}.codexGeneratedImageActions{width:100%;overflow-x:auto}.codexGeneratedImageActions>span{display:none}.codexGeneratedImageCanvas{padding:12px}.codexGeneratedImageSurface{max-width:calc(100vw - 24px);max-height:calc(100vh - 180px)}.codexGeneratedImageSurface img{max-height:calc(100vh - 180px)}}
|
|
569
616
|
`;
|
|
570
617
|
const IMAGE_LAYOUT_STYLE = String.raw`
|
|
571
618
|
.codexGeneratedImageWorkspace{display:grid;min-height:0;grid-template-columns:minmax(0,1fr)}
|
|
@@ -605,7 +652,66 @@ window.__ModuleLoader__.load({
|
|
|
605
652
|
return cleaned.toLowerCase().endsWith(".png") ? cleaned : `${cleaned}.png`;
|
|
606
653
|
};
|
|
607
654
|
const imageByteSize = (bytes) => bytes < 1024 * 1024 ? `${Math.max(.1, bytes / 1024).toLocaleString(void 0, { maximumFractionDigits: 1 })} KB` : `${(bytes / 1024 / 1024).toLocaleString(void 0, { maximumFractionDigits: 1 })} MB`;
|
|
608
|
-
|
|
655
|
+
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;
|
|
656
|
+
async function sha256Hex(data) {
|
|
657
|
+
const value = await crypto.subtle.digest("SHA-256", data);
|
|
658
|
+
return [...new Uint8Array(value)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
659
|
+
}
|
|
660
|
+
function decodeBase64Chunk(value) {
|
|
661
|
+
if (typeof value !== "string" || value.length === 0 || value.length > Math.ceil(4194304 / 3) * 4 + 8) throw new Error("Invalid original image chunk");
|
|
662
|
+
let decoded;
|
|
663
|
+
try {
|
|
664
|
+
decoded = atob(value);
|
|
665
|
+
} catch {
|
|
666
|
+
throw new Error("Invalid original image chunk");
|
|
667
|
+
}
|
|
668
|
+
const bytes = new Uint8Array(decoded.length);
|
|
669
|
+
for (let index = 0; index < decoded.length; index += 1) bytes[index] = decoded.charCodeAt(index);
|
|
670
|
+
return bytes;
|
|
671
|
+
}
|
|
672
|
+
async function readOriginalImage(rpc, sessionId, original) {
|
|
673
|
+
const parts = [];
|
|
674
|
+
let total = 0;
|
|
675
|
+
let done = false;
|
|
676
|
+
while (!done) {
|
|
677
|
+
const chunk = unwrap(await rpc.call(CHANNEL, "image/original/chunk", {
|
|
678
|
+
sessionId,
|
|
679
|
+
assetId: original.assetId,
|
|
680
|
+
offset: total
|
|
681
|
+
}));
|
|
682
|
+
const ref = decodeOriginalImageRef(chunk?.ref);
|
|
683
|
+
if (ref === void 0 || !originalRefMatches(ref, original) || chunk.offset !== total || typeof chunk.done !== "boolean") throw new Error("Original image metadata changed");
|
|
684
|
+
const bytes = decodeBase64Chunk(chunk.encoded);
|
|
685
|
+
if (bytes.byteLength === 0 || total + bytes.byteLength > original.bytes) throw new Error("Original image download is incomplete");
|
|
686
|
+
parts.push(bytes);
|
|
687
|
+
total += bytes.byteLength;
|
|
688
|
+
done = chunk.done;
|
|
689
|
+
}
|
|
690
|
+
if (total !== original.bytes) throw new Error("Original image download is incomplete");
|
|
691
|
+
const data = new Uint8Array(total);
|
|
692
|
+
let offset = 0;
|
|
693
|
+
for (const part of parts) {
|
|
694
|
+
data.set(part, offset);
|
|
695
|
+
offset += part.byteLength;
|
|
696
|
+
}
|
|
697
|
+
if (await sha256Hex(data) !== original.sha256) throw new Error("Original image integrity check failed");
|
|
698
|
+
return data;
|
|
699
|
+
}
|
|
700
|
+
function triggerBlobDownload(data, mediaType, filename) {
|
|
701
|
+
const url = URL.createObjectURL(new Blob([data], { type: mediaType }));
|
|
702
|
+
const anchor = document.createElement("a");
|
|
703
|
+
anchor.href = url;
|
|
704
|
+
anchor.download = filename;
|
|
705
|
+
anchor.rel = "noopener";
|
|
706
|
+
document.body.append(anchor);
|
|
707
|
+
try {
|
|
708
|
+
anchor.click();
|
|
709
|
+
} finally {
|
|
710
|
+
anchor.remove();
|
|
711
|
+
URL.revokeObjectURL(url);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
function CodexGeneratedImage({ attachment, original, rpc, sessionId, loadImage, attachForEdit, getImageViewer, t }) {
|
|
609
715
|
const [attempt, setAttempt] = (0, react.useState)(0);
|
|
610
716
|
const [error, setError] = (0, react.useState)(false);
|
|
611
717
|
const [open, setOpen] = (0, react.useState)(false);
|
|
@@ -614,9 +720,9 @@ window.__ModuleLoader__.load({
|
|
|
614
720
|
const [annotationMode, setAnnotationMode] = (0, react.useState)(false);
|
|
615
721
|
const [annotations, setAnnotations] = (0, react.useState)([]);
|
|
616
722
|
const [selectedAnnotation, setSelectedAnnotation] = (0, react.useState)();
|
|
617
|
-
const [prompt, setPrompt] = (0, react.useState)("");
|
|
618
723
|
const [editBusy, setEditBusy] = (0, react.useState)(false);
|
|
619
724
|
const [editError, setEditError] = (0, react.useState)(false);
|
|
725
|
+
const [downloadState, setDownloadState] = (0, react.useState)("idle");
|
|
620
726
|
const triggerRef = (0, react.useRef)(null);
|
|
621
727
|
const dialogRef = (0, react.useRef)(null);
|
|
622
728
|
(0, react.useEffect)(() => {
|
|
@@ -673,7 +779,8 @@ window.__ModuleLoader__.load({
|
|
|
673
779
|
}, [open]);
|
|
674
780
|
const label = attachment.name ?? t("imageLabel");
|
|
675
781
|
const downloadName = imageDownloadName(attachment);
|
|
676
|
-
const
|
|
782
|
+
const previewMeta = attachment.width + " × " + attachment.height + " · " + imageByteSize(attachment.bytes);
|
|
783
|
+
const imageMeta = original === void 0 ? previewMeta : original.width + " × " + original.height + " · " + imageByteSize(original.bytes) + " · " + t("imagePreviewShort") + " " + previewMeta;
|
|
677
784
|
const addAnnotation = (event) => {
|
|
678
785
|
if (!annotationMode || event.target.closest(".codexGeneratedImagePin")) return;
|
|
679
786
|
const bounds = event.currentTarget.getBoundingClientRect();
|
|
@@ -692,7 +799,6 @@ window.__ModuleLoader__.load({
|
|
|
692
799
|
setEditError(false);
|
|
693
800
|
try {
|
|
694
801
|
await attachForEdit(src, downloadName, buildImageEditDraft({
|
|
695
|
-
prompt,
|
|
696
802
|
annotations,
|
|
697
803
|
translate: t
|
|
698
804
|
}));
|
|
@@ -703,31 +809,50 @@ window.__ModuleLoader__.load({
|
|
|
703
809
|
setEditBusy(false);
|
|
704
810
|
}
|
|
705
811
|
};
|
|
812
|
+
const downloadOriginal = async () => {
|
|
813
|
+
if (downloadState === "pending" || original === void 0) return;
|
|
814
|
+
setDownloadState("pending");
|
|
815
|
+
try {
|
|
816
|
+
triggerBlobDownload(await readOriginalImage(rpc, sessionId, original), original.mediaType, original.name);
|
|
817
|
+
setDownloadState("idle");
|
|
818
|
+
} catch {
|
|
819
|
+
setDownloadState("failed");
|
|
820
|
+
throw new Error("original image download failed");
|
|
821
|
+
}
|
|
822
|
+
};
|
|
706
823
|
const openImage = () => {
|
|
707
824
|
if (src === void 0) return;
|
|
708
|
-
|
|
825
|
+
const viewer = getImageViewer?.();
|
|
826
|
+
const actions = [];
|
|
827
|
+
actions.push({
|
|
828
|
+
id: "continue-editing",
|
|
829
|
+
label: t("imageEdit"),
|
|
830
|
+
pendingLabel: t("imageEditPreparing"),
|
|
831
|
+
errorLabel: t("imageEditFailed"),
|
|
832
|
+
closeOnSuccess: true,
|
|
833
|
+
onInvoke: ({ annotations: nextAnnotations }) => attachForEdit(src, downloadName, buildImageEditDraft({
|
|
834
|
+
annotations: nextAnnotations,
|
|
835
|
+
translate: t
|
|
836
|
+
}))
|
|
837
|
+
});
|
|
838
|
+
if (viewer?.open?.({
|
|
709
839
|
items: [{
|
|
710
840
|
id: attachment.attachmentId ?? downloadName,
|
|
711
841
|
src,
|
|
712
842
|
name: label,
|
|
713
843
|
width: attachment.width,
|
|
714
844
|
height: attachment.height,
|
|
715
|
-
bytes: attachment.bytes
|
|
845
|
+
bytes: attachment.bytes,
|
|
846
|
+
download: original === void 0 ? void 0 : {
|
|
847
|
+
pendingLabel: t("imageDownloadPreparing"),
|
|
848
|
+
errorLabel: t("imageDownloadFailed"),
|
|
849
|
+
onInvoke: downloadOriginal
|
|
850
|
+
},
|
|
851
|
+
actions
|
|
716
852
|
}],
|
|
717
853
|
opener: triggerRef.current,
|
|
718
854
|
source: "codex-generated",
|
|
719
|
-
annotations: true
|
|
720
|
-
editor: {
|
|
721
|
-
label: t("imageEdit"),
|
|
722
|
-
busyLabel: t("imageEditPreparing"),
|
|
723
|
-
errorLabel: t("imageEditFailed"),
|
|
724
|
-
placeholder: t("imageEditPrompt"),
|
|
725
|
-
onSubmit: ({ prompt: nextPrompt, annotations: nextAnnotations }) => attachForEdit(src, downloadName, buildImageEditDraft({
|
|
726
|
-
prompt: nextPrompt,
|
|
727
|
-
annotations: nextAnnotations,
|
|
728
|
-
translate: t
|
|
729
|
-
}))
|
|
730
|
-
}
|
|
855
|
+
annotations: true
|
|
731
856
|
}) === true) return;
|
|
732
857
|
setZoom(1);
|
|
733
858
|
setOpen(true);
|
|
@@ -745,151 +870,142 @@ window.__ModuleLoader__.load({
|
|
|
745
870
|
"aria-modal": "true",
|
|
746
871
|
"aria-label": t("imagePreview"),
|
|
747
872
|
tabIndex: -1,
|
|
748
|
-
children: [
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
]
|
|
794
|
-
})]
|
|
795
|
-
}),
|
|
796
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
797
|
-
className: "codexGeneratedImageWorkspace " + (annotationMode || annotations.length > 0 ? "has-comments" : ""),
|
|
798
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
799
|
-
className: "codexGeneratedImageCanvas " + (annotationMode ? "is-annotating" : ""),
|
|
800
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
801
|
-
className: "codexGeneratedImageSurface",
|
|
802
|
-
onClick: addAnnotation,
|
|
803
|
-
style: { transform: "scale(" + zoom + ")" },
|
|
804
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
805
|
-
src,
|
|
806
|
-
alt: label
|
|
807
|
-
}), annotations.map((annotation, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
808
|
-
type: "button",
|
|
809
|
-
className: "codexGeneratedImagePin " + (annotation.id === selectedAnnotation ? "is-active" : ""),
|
|
810
|
-
"aria-label": fill(t("imageAnnotation"), { value: index + 1 }),
|
|
811
|
-
style: {
|
|
812
|
-
left: annotation.x * 100 + "%",
|
|
813
|
-
top: annotation.y * 100 + "%"
|
|
814
|
-
},
|
|
815
|
-
onClick: (event) => {
|
|
816
|
-
event.stopPropagation();
|
|
817
|
-
setSelectedAnnotation(annotation.id);
|
|
818
|
-
},
|
|
819
|
-
children: index + 1
|
|
820
|
-
}, annotation.id))]
|
|
821
|
-
}), annotationMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
822
|
-
className: "codexGeneratedImageAnnotateHint",
|
|
823
|
-
children: t("imageAnnotateHint")
|
|
824
|
-
}) : null]
|
|
825
|
-
}), annotationMode || annotations.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
|
|
826
|
-
className: "codexGeneratedImageComments",
|
|
827
|
-
"aria-label": t("imageAnnotate"),
|
|
828
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("imageAnnotate") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("imageAnnotateHint") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
829
|
-
className: "codexGeneratedImageCommentList",
|
|
830
|
-
children: annotations.map((annotation, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
|
|
831
|
-
className: annotation.id === selectedAnnotation ? "is-active" : "",
|
|
832
|
-
onClick: () => setSelectedAnnotation(annotation.id),
|
|
833
|
-
children: [
|
|
834
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: index + 1 }),
|
|
835
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
836
|
-
value: annotation.note,
|
|
837
|
-
rows: 3,
|
|
838
|
-
"aria-label": fill(t("imageAnnotation"), { value: index + 1 }),
|
|
839
|
-
placeholder: t("imageAnnotationPlaceholder"),
|
|
840
|
-
onFocus: () => setSelectedAnnotation(annotation.id),
|
|
841
|
-
onChange: (event) => {
|
|
842
|
-
const note = event.target.value;
|
|
843
|
-
setAnnotations((value) => value.map((item) => item.id === annotation.id ? {
|
|
844
|
-
...item,
|
|
845
|
-
note
|
|
846
|
-
} : item));
|
|
847
|
-
}
|
|
848
|
-
}),
|
|
849
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
850
|
-
type: "button",
|
|
851
|
-
"aria-label": t("imageRemoveAnnotation"),
|
|
852
|
-
onClick: (event) => {
|
|
853
|
-
event.stopPropagation();
|
|
854
|
-
setAnnotations((value) => value.filter((item) => item.id !== annotation.id));
|
|
855
|
-
if (selectedAnnotation === annotation.id) setSelectedAnnotation(void 0);
|
|
856
|
-
},
|
|
857
|
-
children: "×"
|
|
858
|
-
})
|
|
859
|
-
]
|
|
860
|
-
}, annotation.id))
|
|
861
|
-
})]
|
|
862
|
-
}) : null]
|
|
863
|
-
}),
|
|
864
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
865
|
-
className: "codexGeneratedImageEditDock",
|
|
866
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
867
|
-
className: "codexGeneratedImagePrompt",
|
|
868
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
869
|
-
value: prompt,
|
|
870
|
-
placeholder: t("imageEditPrompt"),
|
|
871
|
-
onChange: (event) => setPrompt(event.target.value),
|
|
872
|
-
onKeyDown: (event) => {
|
|
873
|
-
if (event.key === "Enter" && !event.shiftKey) {
|
|
874
|
-
event.preventDefault();
|
|
875
|
-
continueEditing();
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
873
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
874
|
+
className: "codexGeneratedImageTopbar",
|
|
875
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: imageMeta })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
876
|
+
className: "codexGeneratedImageActions",
|
|
877
|
+
children: [
|
|
878
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
879
|
+
type: "button",
|
|
880
|
+
className: annotationMode ? "is-active" : "",
|
|
881
|
+
"aria-pressed": annotationMode,
|
|
882
|
+
onClick: () => setAnnotationMode((value) => !value),
|
|
883
|
+
children: t("imageAnnotate")
|
|
884
|
+
}),
|
|
885
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
886
|
+
type: "button",
|
|
887
|
+
"aria-label": t("imageZoomOut"),
|
|
888
|
+
disabled: zoom <= .5,
|
|
889
|
+
onClick: () => setZoom((value) => Math.max(.5, value - .25)),
|
|
890
|
+
children: "−"
|
|
891
|
+
}),
|
|
892
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [Math.round(zoom * 100), "%"] }),
|
|
893
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
894
|
+
type: "button",
|
|
895
|
+
"aria-label": t("imageZoomIn"),
|
|
896
|
+
disabled: zoom >= 3,
|
|
897
|
+
onClick: () => setZoom((value) => Math.min(3, value + .25)),
|
|
898
|
+
children: "+"
|
|
899
|
+
}),
|
|
900
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
901
|
+
type: "button",
|
|
902
|
+
onClick: () => setZoom(1),
|
|
903
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, { "aria-hidden": "true" }), t("imageFit")]
|
|
904
|
+
}),
|
|
905
|
+
original === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
906
|
+
href: src,
|
|
907
|
+
download: downloadName,
|
|
908
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, { "aria-hidden": "true" }), t("imageDownload")]
|
|
909
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
910
|
+
type: "button",
|
|
911
|
+
disabled: downloadState === "pending",
|
|
912
|
+
onClick: () => {
|
|
913
|
+
downloadOriginal().catch(() => void 0);
|
|
914
|
+
},
|
|
915
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, { "aria-hidden": "true" }), downloadState === "pending" ? t("imageDownloadPreparing") : downloadState === "failed" ? t("imageDownloadFailed") : t("imageDownload")]
|
|
916
|
+
}),
|
|
917
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
879
918
|
type: "button",
|
|
880
919
|
disabled: editBusy,
|
|
881
920
|
onClick: () => {
|
|
882
921
|
continueEditing();
|
|
883
922
|
},
|
|
884
|
-
children: editBusy ? t("imageEditPreparing") : t("imageEdit")
|
|
885
|
-
})
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
923
|
+
children: editBusy ? t("imageEditPreparing") : editError ? t("imageEditFailed") : t("imageEdit")
|
|
924
|
+
}),
|
|
925
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
926
|
+
type: "button",
|
|
927
|
+
className: "codexGeneratedImageClose",
|
|
928
|
+
"aria-label": t("imageClosePreview"),
|
|
929
|
+
onClick: close,
|
|
930
|
+
children: "×"
|
|
931
|
+
})
|
|
932
|
+
]
|
|
933
|
+
})]
|
|
934
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
935
|
+
className: "codexGeneratedImageWorkspace " + (annotationMode || annotations.length > 0 ? "has-comments" : ""),
|
|
936
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
937
|
+
className: "codexGeneratedImageCanvas " + (annotationMode ? "is-annotating" : ""),
|
|
938
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
939
|
+
className: "codexGeneratedImageSurface",
|
|
940
|
+
onClick: addAnnotation,
|
|
941
|
+
style: { transform: "scale(" + zoom + ")" },
|
|
942
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
943
|
+
src,
|
|
944
|
+
alt: label
|
|
945
|
+
}), annotations.map((annotation, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
946
|
+
type: "button",
|
|
947
|
+
className: "codexGeneratedImagePin " + (annotation.id === selectedAnnotation ? "is-active" : ""),
|
|
948
|
+
"aria-label": fill(t("imageAnnotation"), { value: index + 1 }),
|
|
949
|
+
style: {
|
|
950
|
+
left: annotation.x * 100 + "%",
|
|
951
|
+
top: annotation.y * 100 + "%"
|
|
952
|
+
},
|
|
953
|
+
onClick: (event) => {
|
|
954
|
+
event.stopPropagation();
|
|
955
|
+
setSelectedAnnotation(annotation.id);
|
|
956
|
+
},
|
|
957
|
+
children: index + 1
|
|
958
|
+
}, annotation.id))]
|
|
959
|
+
}), annotationMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
960
|
+
className: "codexGeneratedImageAnnotateHint",
|
|
961
|
+
children: t("imageAnnotateHint")
|
|
890
962
|
}) : null]
|
|
891
|
-
})
|
|
892
|
-
|
|
963
|
+
}), annotationMode || annotations.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
|
|
964
|
+
className: "codexGeneratedImageComments",
|
|
965
|
+
"aria-label": t("imageAnnotate"),
|
|
966
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("imageAnnotate") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("imageAnnotateHint") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
967
|
+
className: "codexGeneratedImageCommentList",
|
|
968
|
+
children: annotations.map((annotation, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
|
|
969
|
+
className: annotation.id === selectedAnnotation ? "is-active" : "",
|
|
970
|
+
onClick: () => setSelectedAnnotation(annotation.id),
|
|
971
|
+
children: [
|
|
972
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: index + 1 }),
|
|
973
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
974
|
+
value: annotation.note,
|
|
975
|
+
rows: 3,
|
|
976
|
+
"aria-label": fill(t("imageAnnotation"), { value: index + 1 }),
|
|
977
|
+
placeholder: t("imageAnnotationPlaceholder"),
|
|
978
|
+
onFocus: () => setSelectedAnnotation(annotation.id),
|
|
979
|
+
onChange: (event) => {
|
|
980
|
+
const note = event.target.value;
|
|
981
|
+
setAnnotations((value) => value.map((item) => item.id === annotation.id ? {
|
|
982
|
+
...item,
|
|
983
|
+
note
|
|
984
|
+
} : item));
|
|
985
|
+
},
|
|
986
|
+
onKeyDown: (event) => {
|
|
987
|
+
if (event.key === "Enter" && !event.shiftKey || event.key === "Escape") {
|
|
988
|
+
event.preventDefault();
|
|
989
|
+
event.stopPropagation();
|
|
990
|
+
setSelectedAnnotation(void 0);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}),
|
|
994
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
995
|
+
type: "button",
|
|
996
|
+
"aria-label": t("imageRemoveAnnotation"),
|
|
997
|
+
onClick: (event) => {
|
|
998
|
+
event.stopPropagation();
|
|
999
|
+
setAnnotations((value) => value.filter((item) => item.id !== annotation.id));
|
|
1000
|
+
if (selectedAnnotation === annotation.id) setSelectedAnnotation(void 0);
|
|
1001
|
+
},
|
|
1002
|
+
children: "×"
|
|
1003
|
+
})
|
|
1004
|
+
]
|
|
1005
|
+
}, annotation.id))
|
|
1006
|
+
})]
|
|
1007
|
+
}) : null]
|
|
1008
|
+
})]
|
|
893
1009
|
}), document.body);
|
|
894
1010
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
895
1011
|
ref: triggerRef,
|
|
@@ -904,13 +1020,14 @@ window.__ModuleLoader__.load({
|
|
|
904
1020
|
})
|
|
905
1021
|
}), lightbox] });
|
|
906
1022
|
}
|
|
907
|
-
function CodexImageToolRow({ block, loadImage, attachForEdit, getImageViewer, t }) {
|
|
1023
|
+
function CodexImageToolRow({ block, sessionId, rpc, loadImage, attachForEdit, getImageViewer, t }) {
|
|
908
1024
|
const settled = block?.kind === "tool-result";
|
|
909
1025
|
const image = settled ? block.content.find((item) => item?.type === "image" && item.attachment !== void 0) : void 0;
|
|
910
1026
|
const failed = settled && block.isError === true;
|
|
911
1027
|
const state = !settled ? "running" : failed ? "error" : "done";
|
|
912
1028
|
const status = !settled ? t("imageGenerating") : failed ? t("imageFailed") : t("imageGenerated");
|
|
913
1029
|
const error = failed ? block.content.find((item) => item?.type === "text" && typeof item.text === "string")?.text : void 0;
|
|
1030
|
+
const original = decodeImagePresentation(block?.meta)?.original;
|
|
914
1031
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
915
1032
|
className: "codexImageTool",
|
|
916
1033
|
"data-state": state,
|
|
@@ -940,6 +1057,9 @@ window.__ModuleLoader__.load({
|
|
|
940
1057
|
className: "codexImageToolGallery",
|
|
941
1058
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexGeneratedImage, {
|
|
942
1059
|
attachment: image.attachment,
|
|
1060
|
+
original,
|
|
1061
|
+
rpc,
|
|
1062
|
+
sessionId,
|
|
943
1063
|
loadImage,
|
|
944
1064
|
attachForEdit,
|
|
945
1065
|
getImageViewer,
|
|
@@ -1080,6 +1200,18 @@ window.__ModuleLoader__.load({
|
|
|
1080
1200
|
}
|
|
1081
1201
|
const usePreferenceSnapshot = (preference) => (0, react.useSyncExternalStore)(preference.subscribe, preference.getSnapshot);
|
|
1082
1202
|
const notifyQuickQuota = () => window.dispatchEvent(new Event(QUICK_QUOTA_REFRESH_EVENT));
|
|
1203
|
+
const formatRunway = (seconds, t) => {
|
|
1204
|
+
if (!Number.isFinite(seconds) || seconds <= 0) return void 0;
|
|
1205
|
+
const minutes = Math.max(1, Math.round(seconds / 60));
|
|
1206
|
+
const days = Math.floor(minutes / 1440);
|
|
1207
|
+
const hours = Math.floor(minutes % 1440 / 60);
|
|
1208
|
+
if (days > 0) return hours > 0 ? fill(t("runwayDaysHours"), {
|
|
1209
|
+
days,
|
|
1210
|
+
hours
|
|
1211
|
+
}) : fill(t("runwayDays"), { days });
|
|
1212
|
+
if (hours > 0) return fill(t("runwayHours"), { hours });
|
|
1213
|
+
return fill(t("runwayMinutes"), { minutes });
|
|
1214
|
+
};
|
|
1083
1215
|
function useQuickQuota(rpc, enabled, model) {
|
|
1084
1216
|
const [quota, setQuota] = (0, react.useState)();
|
|
1085
1217
|
(0, react.useEffect)(() => {
|
|
@@ -1155,7 +1287,12 @@ window.__ModuleLoader__.load({
|
|
|
1155
1287
|
children: [
|
|
1156
1288
|
choice("off", t("quickQuotaOff")),
|
|
1157
1289
|
choice(QUICK_QUOTA_MODE_PERCENT, t("quickQuotaPercent")),
|
|
1158
|
-
choice("bar", t("quickQuotaBar"))
|
|
1290
|
+
choice("bar", t("quickQuotaBar")),
|
|
1291
|
+
choice(QUICK_QUOTA_MODE_FORECAST, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1292
|
+
t("quickQuotaForecast"),
|
|
1293
|
+
" ",
|
|
1294
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("quickQuotaBeta") })
|
|
1295
|
+
] }))
|
|
1159
1296
|
]
|
|
1160
1297
|
})]
|
|
1161
1298
|
});
|
|
@@ -1360,11 +1497,16 @@ window.__ModuleLoader__.load({
|
|
|
1360
1497
|
const current = (0, react.useSyncExternalStore)((listener) => directory.subscribe(listener), () => directory.getSnapshot()).current;
|
|
1361
1498
|
const codex = current?.provider === "openai-codex";
|
|
1362
1499
|
const quotaEnabled = preferenceSnapshot.status === "ready" && preferenceSnapshot.quickQuotaMode !== "off" && codex;
|
|
1500
|
+
const forecastMode = preferenceSnapshot.quickQuotaMode === QUICK_QUOTA_MODE_FORECAST;
|
|
1363
1501
|
const quota = useQuickQuota(rpc, quotaEnabled, current?.model);
|
|
1364
1502
|
if (!quotaEnabled || quota === void 0) return null;
|
|
1365
1503
|
const value = Math.round(Number(quota.remainingPercent) * 10) / 10;
|
|
1366
1504
|
const display = percent(value);
|
|
1367
|
-
const
|
|
1505
|
+
const duration = quota.forecast?.status === "ready" ? formatRunway(quota.forecast.runwaySeconds, t) : void 0;
|
|
1506
|
+
const label = duration === void 0 ? fill(t("quickQuotaStatus"), { value: display }) : fill(t("quickQuotaForecastStatus"), {
|
|
1507
|
+
value: display,
|
|
1508
|
+
duration
|
|
1509
|
+
});
|
|
1368
1510
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1369
1511
|
className: "codexComposerQuota",
|
|
1370
1512
|
role: "status",
|
|
@@ -1375,7 +1517,7 @@ window.__ModuleLoader__.load({
|
|
|
1375
1517
|
max: 100,
|
|
1376
1518
|
value,
|
|
1377
1519
|
"aria-hidden": "true"
|
|
1378
|
-
}) : `${display}%`
|
|
1520
|
+
}) : forecastMode && duration !== void 0 ? `${display}% · ${quota.forecast.survivesReset ? "≥" : "≈"}${duration}` : `${display}%`
|
|
1379
1521
|
});
|
|
1380
1522
|
}
|
|
1381
1523
|
function CodexModelSelect({ locked, available, directory, load, select, preference, t }) {
|
|
@@ -2170,7 +2312,8 @@ window.__ModuleLoader__.load({
|
|
|
2170
2312
|
["This quota reset is already in progress", "resetInProgress"],
|
|
2171
2313
|
["Wait before confirming this quota reset", "resetTooEarly"],
|
|
2172
2314
|
["You must acknowledge that this may consume one quota reset", "resetAcknowledgeRequired"],
|
|
2173
|
-
["The signed-in ChatGPT account changed", "resetAccountChanged"]
|
|
2315
|
+
["The signed-in ChatGPT account changed", "resetAccountChanged"],
|
|
2316
|
+
["Quota reset result is uncertain; retry this confirmation to check the same request", "resetUncertain"]
|
|
2174
2317
|
])).get(error instanceof Error ? error.message : "") ?? "resetFailed");
|
|
2175
2318
|
}
|
|
2176
2319
|
function UsageCard({ rpc, t, signedIn, resetKey }) {
|
|
@@ -2281,7 +2424,10 @@ window.__ModuleLoader__.load({
|
|
|
2281
2424
|
}),
|
|
2282
2425
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2283
2426
|
className: "codexSubscriptionLimitMeta",
|
|
2284
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children:
|
|
2427
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window.forecast?.status === "ready" ? fill(t("quotaForecast"), {
|
|
2428
|
+
symbol: window.forecast.survivesReset ? "≥" : "≈",
|
|
2429
|
+
duration: formatRunway(window.forecast.runwaySeconds, t)
|
|
2430
|
+
}) : windowLabel(window.windowSeconds, t) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetTime, {
|
|
2285
2431
|
resetsAt: window.resetsAt,
|
|
2286
2432
|
t
|
|
2287
2433
|
})]
|
|
@@ -2437,43 +2583,50 @@ window.__ModuleLoader__.load({
|
|
|
2437
2583
|
t
|
|
2438
2584
|
})
|
|
2439
2585
|
}, CodexSection));
|
|
2440
|
-
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
2441
|
-
name: "conversation.input.right",
|
|
2442
|
-
id: "codex-subscription-quota",
|
|
2443
|
-
order: 15,
|
|
2444
|
-
locale: NS,
|
|
2445
|
-
inject: (sessionId) => ({
|
|
2446
|
-
preference,
|
|
2447
|
-
rpc: connection.rpc,
|
|
2448
|
-
t,
|
|
2449
|
-
directory: ctx.modelDirectories.directoryFor(sessionId).store
|
|
2450
|
-
})
|
|
2451
|
-
}, CodexComposerQuota));
|
|
2452
2586
|
const sessions = ctx.get("sessions");
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2587
|
+
const installDirectorySlots = (scope) => {
|
|
2588
|
+
const modelDirectories = scope.get("modelDirectories");
|
|
2589
|
+
scope.slots.inject("conversation.input.right", () => scope.slots.register({
|
|
2590
|
+
name: "conversation.input.right",
|
|
2591
|
+
id: "codex-subscription-quota",
|
|
2592
|
+
order: 15,
|
|
2593
|
+
locale: NS,
|
|
2594
|
+
inject: (sessionId) => ({
|
|
2595
|
+
preference,
|
|
2596
|
+
rpc: connection.rpc,
|
|
2597
|
+
t,
|
|
2598
|
+
directory: modelDirectories.directoryFor(sessionId).store
|
|
2599
|
+
})
|
|
2600
|
+
}, CodexComposerQuota));
|
|
2601
|
+
scope.slots.inject("conversation.input.model", () => scope.slots.register({
|
|
2602
|
+
name: "conversation.input.model",
|
|
2603
|
+
priority: -10,
|
|
2604
|
+
locale: NS,
|
|
2605
|
+
inject: (sessionId) => {
|
|
2606
|
+
const directory = modelDirectories.directoryFor(sessionId);
|
|
2607
|
+
const available = sessions.subagentAddress(sessionId) === void 0;
|
|
2608
|
+
return {
|
|
2609
|
+
available,
|
|
2610
|
+
directory: directory.store,
|
|
2611
|
+
load: () => {
|
|
2612
|
+
if (available) directory.load();
|
|
2613
|
+
},
|
|
2614
|
+
select: (selection) => available ? directory.select(selection).then(() => true, () => false) : Promise.resolve(false),
|
|
2615
|
+
preference
|
|
2616
|
+
};
|
|
2617
|
+
}
|
|
2618
|
+
}, CodexModelSelect));
|
|
2619
|
+
};
|
|
2620
|
+
if (ctx.get("remote.session") === void 0) installDirectorySlots(ctx);
|
|
2621
|
+
else ctx.inject(["remote.session"], installDirectorySlots);
|
|
2471
2622
|
const conversation = ctx.get("conversation");
|
|
2472
2623
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
2473
2624
|
name: "tool.call.toolview",
|
|
2474
2625
|
key: "codex_image_generate",
|
|
2475
2626
|
locale: NS,
|
|
2476
2627
|
inject: (sessionId) => ({
|
|
2628
|
+
sessionId,
|
|
2629
|
+
rpc: connection.rpc,
|
|
2477
2630
|
t,
|
|
2478
2631
|
loadImage: (attachment) => conversation.resolveImage(sessionId, attachment),
|
|
2479
2632
|
getImageViewer: () => {
|