dsh-codex-subscription 3.0.0-beta.8 → 3.0.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/README.en.md +17 -17
- package/README.md +16 -16
- package/compatibility.json +4 -2
- package/lib/client.js +1678 -136
- package/lib/index.js +1501 -106
- package/locale/en.json +1 -1
- package/locale/{zh-CN.json → zh.json} +1 -1
- package/package.json +38 -38
package/lib/client.js
CHANGED
|
@@ -1090,6 +1090,11 @@ window.__ModuleLoader__.load({
|
|
|
1090
1090
|
"runtime/cancel",
|
|
1091
1091
|
"storage/status",
|
|
1092
1092
|
"storage/clear-forecast",
|
|
1093
|
+
"voice/start",
|
|
1094
|
+
"voice/stop",
|
|
1095
|
+
"voice/status",
|
|
1096
|
+
"voice/preview",
|
|
1097
|
+
"voice/catalog",
|
|
1093
1098
|
"remote/status",
|
|
1094
1099
|
"remote/enable",
|
|
1095
1100
|
"remote/disable",
|
|
@@ -1326,10 +1331,6 @@ window.__ModuleLoader__.load({
|
|
|
1326
1331
|
className: "codexImageToolTitle",
|
|
1327
1332
|
children: t("imageGenerate")
|
|
1328
1333
|
}),
|
|
1329
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1330
|
-
className: "codexImageBeta",
|
|
1331
|
-
children: t("imageBeta")
|
|
1332
|
-
}),
|
|
1333
1334
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1334
1335
|
className: "codexImageToolState",
|
|
1335
1336
|
children: status
|
|
@@ -2676,7 +2677,7 @@ window.__ModuleLoader__.load({
|
|
|
2676
2677
|
}
|
|
2677
2678
|
return matrix;
|
|
2678
2679
|
}
|
|
2679
|
-
function parseColor(value) {
|
|
2680
|
+
function parseColor$1(value) {
|
|
2680
2681
|
const text = String(value ?? "").trim().toLowerCase();
|
|
2681
2682
|
if (!text || text === "none") return "none";
|
|
2682
2683
|
if (text === "currentcolor") return "#000000";
|
|
@@ -3038,7 +3039,7 @@ window.__ModuleLoader__.load({
|
|
|
3038
3039
|
if (!target || !/Gradient$/.test(target.name)) throw Error(`SVG paint ${reference} does not reference a gradient defined in the same SVG`);
|
|
3039
3040
|
const stops = target.children.filter((child) => child.name === "stop");
|
|
3040
3041
|
if (!stops.length) throw Error(`Gradient ${target.attrs.id} has no stops`);
|
|
3041
|
-
const channels = stops.map((stop) => parseColor(stop.attrs["stop-color"] ?? /stop-color\s*:\s*([^;]+)/.exec(stop.attrs.style ?? "")?.[1] ?? "#000000")).filter((color) => color !== "none").map((color) => [
|
|
3042
|
+
const channels = stops.map((stop) => parseColor$1(stop.attrs["stop-color"] ?? /stop-color\s*:\s*([^;]+)/.exec(stop.attrs.style ?? "")?.[1] ?? "#000000")).filter((color) => color !== "none").map((color) => [
|
|
3042
3043
|
1,
|
|
3043
3044
|
3,
|
|
3044
3045
|
5
|
|
@@ -3051,7 +3052,7 @@ window.__ModuleLoader__.load({
|
|
|
3051
3052
|
warnings.add(`Gradient ${target.attrs.id} was flattened to its average color; stack several bands for a real gradient.`);
|
|
3052
3053
|
return `#${mean.map((value) => value.toString(16).padStart(2, "0")).join("")}`;
|
|
3053
3054
|
};
|
|
3054
|
-
const paint = (value) => String(value).startsWith("url(") ? gradientColor(value) : parseColor(value);
|
|
3055
|
+
const paint = (value) => String(value).startsWith("url(") ? gradientColor(value) : parseColor$1(value);
|
|
3055
3056
|
const emit = (subpath, matrix, style, id) => {
|
|
3056
3057
|
[subpath.start, ...subpath.curves.flatMap((curve) => [
|
|
3057
3058
|
[curve[0], curve[1]],
|
|
@@ -5684,10 +5685,17 @@ window.__ModuleLoader__.load({
|
|
|
5684
5685
|
inputImageDetail_high: "高",
|
|
5685
5686
|
inputImageDetail_original: "原图",
|
|
5686
5687
|
connectors: "ChatGPT 连接器",
|
|
5687
|
-
connectorsHint: "让 DSH 里的任何模型使用你在 ChatGPT
|
|
5688
|
+
connectorsHint: "让 DSH 里的任何模型使用你在 ChatGPT 里已连接的应用(GitHub、Gmail、云端硬盘、日历等)。需先在 ChatGPT 里连接应用;应用返回的内容来自第三方,模型不会照其中的指令行事。",
|
|
5689
|
+
connectorsMode_off: "连接器关闭,模型看不到这些应用。",
|
|
5690
|
+
connectorsMode_read: "模型只能读取(搜邮件、读文件、查 PR),不会修改任何数据。",
|
|
5691
|
+
connectorsMode_ask: "发邮件、改文件等写操作每次都会弹出确认,你批准后才执行。",
|
|
5692
|
+
connectorsMode_write: "写操作直接执行,不再弹出确认,误操作无法撤回,请谨慎使用。",
|
|
5693
|
+
hintMore: "展开",
|
|
5694
|
+
hintLess: "收起",
|
|
5688
5695
|
connectors_off: "关闭",
|
|
5689
5696
|
connectors_read: "只读",
|
|
5690
|
-
connectors_ask: "
|
|
5697
|
+
connectors_ask: "询问后写入",
|
|
5698
|
+
connectors_write: "直接写入",
|
|
5691
5699
|
reasoningSummary: "推理摘要",
|
|
5692
5700
|
reasoningSummaryHint: "让模型在会话的“思考”部分给出推理摘要。自动由模型决定;简洁更短;详细更完整;关闭则不要摘要。",
|
|
5693
5701
|
retryPolicy: "请求重试",
|
|
@@ -5705,12 +5713,54 @@ window.__ModuleLoader__.load({
|
|
|
5705
5713
|
subagentBackend_dsh: "DSH",
|
|
5706
5714
|
subagentBackend_codex: "Codex",
|
|
5707
5715
|
connectionTitle: "连接方式",
|
|
5716
|
+
diagnosticsLog: "运行日志",
|
|
5717
|
+
diagnosticsLogHint: "按时间记录运行中发生的事,错误和警告在最上面的筛选里。内容已去掉令牌、邮箱和路径。",
|
|
5718
|
+
logLevel_all: "全部",
|
|
5719
|
+
logLevel_warn: "警告和错误",
|
|
5720
|
+
logLevel_error: "仅错误",
|
|
5721
|
+
logEmpty: "这个级别暂时没有记录。",
|
|
5722
|
+
logCopy: "复制日志",
|
|
5723
|
+
logCopied: "已复制日志",
|
|
5724
|
+
logEarlier: "上次运行",
|
|
5708
5725
|
remoteTitle: "手机远程控制",
|
|
5709
5726
|
remote_off: "关闭",
|
|
5710
5727
|
remote_on: "开启",
|
|
5728
|
+
voiceStart: "语音对话",
|
|
5729
|
+
voiceEnd: "结束语音",
|
|
5730
|
+
voiceConnecting: "正在连接语音…",
|
|
5731
|
+
voiceListening: "在听",
|
|
5732
|
+
voiceSpeaking: "在说",
|
|
5733
|
+
voiceWorking: "DSH 正在处理…",
|
|
5734
|
+
voiceMute: "关闭麦克风",
|
|
5735
|
+
voiceUnmute: "打开麦克风",
|
|
5736
|
+
voiceMuteSpeaker: "关闭声音",
|
|
5737
|
+
voiceUnmuteSpeaker: "打开声音",
|
|
5738
|
+
voiceMicBlocked: "没有麦克风权限,请在浏览器或系统里允许后再试。",
|
|
5739
|
+
voiceFailed: "语音没有接通,请稍后再试。",
|
|
5740
|
+
voiceTitle: "语音对话",
|
|
5741
|
+
voiceHint: "输入框里没有内容时,发送键变成语音键:点一下就能和 DSH 说话,让它做事,它的回答会念出来。语音通话由 ChatGPT 订阅提供,通话内容会经过 OpenAI。",
|
|
5742
|
+
voiceVoice: "音色",
|
|
5743
|
+
voiceLanguage: "回答语言",
|
|
5744
|
+
voiceAuto: "跟你说的语言一致",
|
|
5745
|
+
voicePlay: "试听",
|
|
5746
|
+
voiceLoading: "加载中…",
|
|
5747
|
+
voiceNoSample: "这个音色暂时没有试听样本。",
|
|
5748
|
+
voiceUnavailable: "暂无试听",
|
|
5749
|
+
voiceMore: "更多音色(暂无试听)",
|
|
5750
|
+
voiceLang_zh: "中文",
|
|
5751
|
+
voiceLang_en: "English",
|
|
5752
|
+
voiceLang_ja: "日本語",
|
|
5753
|
+
voiceLang_ko: "한국어",
|
|
5754
|
+
voiceLang_es: "Español",
|
|
5755
|
+
voiceLang_fr: "Français",
|
|
5756
|
+
voiceLang_de: "Deutsch",
|
|
5757
|
+
voiceLang_pt: "Português",
|
|
5758
|
+
voiceLang_ru: "Русский",
|
|
5711
5759
|
remoteHint: "实验功能,默认关闭。开启后,这台电脑上的 DSH 会出现在 ChatGPT 手机 App 的 Codex 主机列表里,可用手机查看对话、发消息、切换模型和审批。需要订阅账号登录;电脑要保持开机和联网。",
|
|
5712
|
-
remoteConnected: "
|
|
5760
|
+
remoteConnected: "电脑已连上 ChatGPT,还没有手机连接",
|
|
5713
5761
|
remoteConnecting: "正在连接",
|
|
5762
|
+
remoteWaiting: "正在等待上一次连接释放,通常几分钟内自动连上",
|
|
5763
|
+
remoteConflictLong: "长时间连不上,通常是另一台电脑、另一份 DSH(或另一个窗口)也在用同一个 ChatGPT 账号开着手机远程,彼此在抢连接。请关闭其他地方的手机远程,再回到这里点“关闭”后重新“开启”。",
|
|
5714
5764
|
remoteError: "连接失败,会自动重试",
|
|
5715
5765
|
remoteStopped: "已停止",
|
|
5716
5766
|
remotePaired: "已配对的手机",
|
|
@@ -5718,7 +5768,7 @@ window.__ModuleLoader__.load({
|
|
|
5718
5768
|
remoteRevoke: "移除",
|
|
5719
5769
|
remoteLastSeen: "最近 {date}",
|
|
5720
5770
|
remotePhoneUnknown: "未命名手机",
|
|
5721
|
-
remotePhones: "{count}
|
|
5771
|
+
remotePhones: "手机已连接 {count} 部",
|
|
5722
5772
|
remotePair: "显示配对码",
|
|
5723
5773
|
remotePairAgain: "重新生成配对码",
|
|
5724
5774
|
remotePairFailed: "无法生成配对码,请确认已登录订阅账号。",
|
|
@@ -6020,9 +6070,9 @@ window.__ModuleLoader__.load({
|
|
|
6020
6070
|
searchTitle: "搜索来源",
|
|
6021
6071
|
searchScope: "自动按当前会话模型分流;手动选择会覆盖所有模型和会话。",
|
|
6022
6072
|
searchAuto: "自动",
|
|
6023
|
-
searchAutoHint: "Codex 模型用订阅搜索,其他模型用 DSH",
|
|
6073
|
+
searchAutoHint: "Codex 模型用订阅搜索,其他模型用 DSH;一方未登录或没有 Key 时自动改用另一方",
|
|
6024
6074
|
searchDsh: "DSH 默认",
|
|
6025
|
-
searchDshHint: "所有模型使用 DSH
|
|
6075
|
+
searchDshHint: "所有模型使用 DSH 当前搜索服务;没有 API Key 时用已登录的 DeepSeek 账号",
|
|
6026
6076
|
searchCodex: "Codex 订阅",
|
|
6027
6077
|
searchCodexHint: "所有模型通过已登录的 ChatGPT 订阅搜索",
|
|
6028
6078
|
preferenceFailed: "设置未保存。",
|
|
@@ -6216,7 +6266,6 @@ window.__ModuleLoader__.load({
|
|
|
6216
6266
|
modelFailed: "模型目录加载失败:{value}",
|
|
6217
6267
|
groupFailed: "{name}:{value}",
|
|
6218
6268
|
imageGenerate: "生成图片",
|
|
6219
|
-
imageBeta: "Beta",
|
|
6220
6269
|
imageGenerating: "正在生成…",
|
|
6221
6270
|
imageGenerated: "已生成",
|
|
6222
6271
|
imageFailed: "生成失败",
|
|
@@ -6363,10 +6412,17 @@ window.__ModuleLoader__.load({
|
|
|
6363
6412
|
inputImageDetail_high: "High",
|
|
6364
6413
|
inputImageDetail_original: "Original",
|
|
6365
6414
|
connectors: "ChatGPT connectors",
|
|
6366
|
-
connectorsHint: "Lets any model in DSH use the apps you have connected in ChatGPT (GitHub, Gmail,
|
|
6415
|
+
connectorsHint: "Lets any model in DSH use the apps you have connected in ChatGPT (GitHub, Gmail, Drive, Calendar and more). Connect the apps in ChatGPT first; what apps return is third-party content and models do not follow instructions inside it.",
|
|
6416
|
+
connectorsMode_off: "Connectors are off; models cannot see these apps.",
|
|
6417
|
+
connectorsMode_read: "Models can only read (search mail, read files, look up pull requests) and never change data.",
|
|
6418
|
+
connectorsMode_ask: "Anything that changes data, such as sending mail or editing files, asks for your approval every time.",
|
|
6419
|
+
connectorsMode_write: "Changing actions run immediately without a prompt and cannot be undone, so use this carefully.",
|
|
6420
|
+
hintMore: "More",
|
|
6421
|
+
hintLess: "Less",
|
|
6367
6422
|
connectors_off: "Off",
|
|
6368
6423
|
connectors_read: "Read-only",
|
|
6369
|
-
connectors_ask: "
|
|
6424
|
+
connectors_ask: "Write after asking",
|
|
6425
|
+
connectors_write: "Write directly",
|
|
6370
6426
|
reasoningSummary: "Reasoning summary",
|
|
6371
6427
|
reasoningSummaryHint: "Shows a summary of the reasoning in the Thinking section of the conversation. Auto lets the model decide; concise is shorter; detailed is fuller; off asks for none.",
|
|
6372
6428
|
retryPolicy: "Request retries",
|
|
@@ -6384,12 +6440,54 @@ window.__ModuleLoader__.load({
|
|
|
6384
6440
|
subagentBackend_dsh: "DSH",
|
|
6385
6441
|
subagentBackend_codex: "Codex",
|
|
6386
6442
|
connectionTitle: "Connection",
|
|
6443
|
+
diagnosticsLog: "Log",
|
|
6444
|
+
diagnosticsLogHint: "What happened while running, in order. Filter to warnings and errors above. Tokens, emails and paths are removed.",
|
|
6445
|
+
logLevel_all: "All",
|
|
6446
|
+
logLevel_warn: "Warnings and errors",
|
|
6447
|
+
logLevel_error: "Errors only",
|
|
6448
|
+
logEmpty: "Nothing recorded at this level yet.",
|
|
6449
|
+
logCopy: "Copy log",
|
|
6450
|
+
logCopied: "Log copied",
|
|
6451
|
+
logEarlier: "previous run",
|
|
6387
6452
|
remoteTitle: "Phone remote control",
|
|
6388
6453
|
remote_off: "Off",
|
|
6389
6454
|
remote_on: "On",
|
|
6455
|
+
voiceStart: "Voice conversation",
|
|
6456
|
+
voiceEnd: "End voice",
|
|
6457
|
+
voiceConnecting: "Connecting voice…",
|
|
6458
|
+
voiceListening: "Listening",
|
|
6459
|
+
voiceSpeaking: "Speaking",
|
|
6460
|
+
voiceWorking: "DSH is working on it…",
|
|
6461
|
+
voiceMute: "Turn the microphone off",
|
|
6462
|
+
voiceUnmute: "Turn the microphone on",
|
|
6463
|
+
voiceMuteSpeaker: "Turn the sound off",
|
|
6464
|
+
voiceUnmuteSpeaker: "Turn the sound on",
|
|
6465
|
+
voiceMicBlocked: "No microphone permission: allow it in the browser or system and try again.",
|
|
6466
|
+
voiceFailed: "The voice call did not connect. Try again in a moment.",
|
|
6467
|
+
voiceTitle: "Voice conversation",
|
|
6468
|
+
voiceHint: "When the message box is empty, the send button becomes a voice button: tap it to talk to DSH, ask it to do things, and hear the answer. Calls use your ChatGPT subscription and go through OpenAI.",
|
|
6469
|
+
voiceVoice: "Voice",
|
|
6470
|
+
voiceLanguage: "Answer language",
|
|
6471
|
+
voiceAuto: "Same as you speak",
|
|
6472
|
+
voicePlay: "Listen",
|
|
6473
|
+
voiceLoading: "Loading…",
|
|
6474
|
+
voiceNoSample: "No sample is available for this voice.",
|
|
6475
|
+
voiceUnavailable: "no sample yet",
|
|
6476
|
+
voiceMore: "More voices (no sample yet)",
|
|
6477
|
+
voiceLang_zh: "中文",
|
|
6478
|
+
voiceLang_en: "English",
|
|
6479
|
+
voiceLang_ja: "日本語",
|
|
6480
|
+
voiceLang_ko: "한국어",
|
|
6481
|
+
voiceLang_es: "Español",
|
|
6482
|
+
voiceLang_fr: "Français",
|
|
6483
|
+
voiceLang_de: "Deutsch",
|
|
6484
|
+
voiceLang_pt: "Português",
|
|
6485
|
+
voiceLang_ru: "Русский",
|
|
6390
6486
|
remoteHint: "Experimental, off by default. When on, this computer's DSH appears as a host in the ChatGPT mobile app's Codex list, so you can follow conversations, send messages, switch models and approve actions from your phone. Needs a signed-in subscription account; the computer must stay on and online.",
|
|
6391
|
-
remoteConnected: "
|
|
6487
|
+
remoteConnected: "This computer is online with ChatGPT; no phone connected yet",
|
|
6392
6488
|
remoteConnecting: "Connecting",
|
|
6489
|
+
remoteWaiting: "Waiting for the previous connection to be released; usually reconnects within a few minutes",
|
|
6490
|
+
remoteConflictLong: "Still not connecting? Usually another computer, another DSH copy or another window is using phone remote control with the same ChatGPT account and they keep taking the connection from each other. Turn phone remote control off elsewhere, then switch it Off and On again here.",
|
|
6393
6491
|
remoteError: "Connection failed, retrying",
|
|
6394
6492
|
remoteStopped: "Stopped",
|
|
6395
6493
|
remotePaired: "Paired phones",
|
|
@@ -6404,7 +6502,7 @@ window.__ModuleLoader__.load({
|
|
|
6404
6502
|
remotePairSteps: "Scan this in the ChatGPT app's Codex section, or type the manual code.",
|
|
6405
6503
|
remoteManualCode: "Manual pairing code",
|
|
6406
6504
|
remoteQrLabel: "Pairing QR code",
|
|
6407
|
-
remoteExpires: "
|
|
6505
|
+
remoteExpires: "Expires in about {minutes} min",
|
|
6408
6506
|
remoteExpired: "The pairing code expired. Create a new one.",
|
|
6409
6507
|
remoteLimits: "From the phone you can follow and continue DSH conversations, start new ones, send photos, switch models, choose permissions, approve actions and view changes. The phone cannot run other commands on the computer.",
|
|
6410
6508
|
reviewModelTitle: "Auto review model",
|
|
@@ -6699,9 +6797,9 @@ window.__ModuleLoader__.load({
|
|
|
6699
6797
|
searchTitle: "Search source",
|
|
6700
6798
|
searchScope: "Auto follows the current session model; an explicit choice overrides every model and session.",
|
|
6701
6799
|
searchAuto: "Auto",
|
|
6702
|
-
searchAutoHint: "Codex models use subscription search; other models use DSH",
|
|
6800
|
+
searchAutoHint: "Codex models use subscription search; other models use DSH; if one side is not signed in or has no key, the other is used",
|
|
6703
6801
|
searchDsh: "DSH default",
|
|
6704
|
-
searchDshHint: "Use DSH's current search service for every model",
|
|
6802
|
+
searchDshHint: "Use DSH's current search service for every model; without an API key the signed-in DeepSeek account is used",
|
|
6705
6803
|
searchCodex: "Codex subscription",
|
|
6706
6804
|
searchCodexHint: "Search through the signed-in ChatGPT subscription for every model",
|
|
6707
6805
|
preferenceFailed: "The setting was not saved.",
|
|
@@ -6895,7 +6993,6 @@ window.__ModuleLoader__.load({
|
|
|
6895
6993
|
modelFailed: "Could not load models: {value}",
|
|
6896
6994
|
groupFailed: "{name}: {value}",
|
|
6897
6995
|
imageGenerate: "Generate image",
|
|
6898
|
-
imageBeta: "Beta",
|
|
6899
6996
|
imageGenerating: "Generating…",
|
|
6900
6997
|
imageGenerated: "Generated",
|
|
6901
6998
|
imageFailed: "Generation failed",
|
|
@@ -6964,11 +7061,26 @@ window.__ModuleLoader__.load({
|
|
|
6964
7061
|
.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}
|
|
6965
7062
|
.codexSubscriptionUsageCard{padding:12px 14px;gap:9px}
|
|
6966
7063
|
.codexSubscriptionPreferencesCard{padding:12px 14px;gap:10px}
|
|
6967
|
-
.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}
|
|
7064
|
+
.codexSubscriptionPreference{min-height:32px;box-sizing:border-box;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 12px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}
|
|
6968
7065
|
.codexSubscriptionPreferenceCopy{display:flex;min-width:0;flex-direction:column;gap:2px}
|
|
6969
7066
|
.codexSubscriptionPreferenceLabel{display:flex;align-items:center;gap:6px}
|
|
6970
|
-
.
|
|
6971
|
-
.
|
|
7067
|
+
.codexSubscriptionPreference>.codexSubscriptionPreferenceCopy{flex:1 1 240px}
|
|
7068
|
+
.codexSubscriptionPreferenceHint{max-width:none;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
7069
|
+
.codexSubscriptionHintText{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
|
|
7070
|
+
.codexSubscriptionHintFold[data-open=true] .codexSubscriptionHintText{display:block;-webkit-line-clamp:unset}
|
|
7071
|
+
.codexSubscriptionHintToggle{display:inline-flex;align-items:center;gap:3px;margin-top:2px;padding:0;border:0;background:none;font:inherit;font-size:11px;line-height:17px;color:var(--dsw-alias-label-secondary);cursor:pointer}
|
|
7072
|
+
.codexSubscriptionHintToggle::after{content:"";width:5px;height:5px;margin-top:-2px;border:solid currentColor;border-width:0 1.5px 1.5px 0;transform:rotate(45deg);transition:transform .15s}
|
|
7073
|
+
.codexSubscriptionHintFold[data-open=true] .codexSubscriptionHintToggle::after{margin-top:2px;transform:rotate(-135deg)}
|
|
7074
|
+
.codexSubscriptionHintToggle:hover{color:var(--dsw-alias-label-primary)}
|
|
7075
|
+
.codexSubscriptionHintToggle:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px;border-radius:3px}
|
|
7076
|
+
.codexSubscriptionPreferenceStack{margin-top:6px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l2)}
|
|
7077
|
+
.codexSubscriptionPreferenceStack>.codexSubscriptionQuotaModes{flex:1 1 100%;margin-left:0;display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr)}
|
|
7078
|
+
.codexSubscriptionPreferenceStack .codexSubscriptionQuotaMode{min-height:30px;padding:4px 8px;text-align:center;white-space:normal;line-height:16px}
|
|
7079
|
+
.codexSubscriptionPreferenceLabel small{padding:0 6px;border-radius:999px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-tertiary);font-size:10px;font-weight:500;line-height:16px}
|
|
7080
|
+
@container (max-width:560px){.codexSubscriptionPreferenceStack>.codexSubscriptionQuotaModes{grid-auto-flow:row;grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
7081
|
+
.codexSubscriptionModeNote{flex:1 1 100%;padding:7px 10px;border-radius:8px;background:var(--dsw-alias-bg-module-platform);font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
|
|
7082
|
+
.codexSubscriptionModeNote[data-danger=true]{background:color-mix(in srgb,var(--dsw-alias-label-danger,#d92d20) 12%,transparent);color:var(--dsw-alias-label-danger,#d92d20)}
|
|
7083
|
+
.codexSubscriptionQuotaModes{display:flex;flex-wrap:wrap;max-width:100%;align-items:center;gap:3px;padding:2px;border-radius:9px;background:var(--dsw-alias-bg-module-platform)}
|
|
6972
7084
|
.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}
|
|
6973
7085
|
.codexSubscriptionQuotaMode small{margin-left:3px;font-size:9px;line-height:1;color:var(--dsw-alias-label-tertiary)}
|
|
6974
7086
|
.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)}
|
|
@@ -7196,7 +7308,6 @@ window.__ModuleLoader__.load({
|
|
|
7196
7308
|
.codexGeneratedImageFrame img{display:block;width:100%;height:100%;object-fit:cover}
|
|
7197
7309
|
.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}
|
|
7198
7310
|
@keyframes codexImageSpin{to{transform:rotate(360deg)}}
|
|
7199
|
-
.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}
|
|
7200
7311
|
.codexImageToolGallery{display:flex;align-items:flex-start;flex-direction:column;gap:8px}
|
|
7201
7312
|
@container (max-width:560px){.codexSubscriptionCreditRows{grid-template-columns:1fr}}
|
|
7202
7313
|
@container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}
|
|
@@ -7231,6 +7342,59 @@ window.__ModuleLoader__.load({
|
|
|
7231
7342
|
@container (max-width:480px){.codexSubscriptionPreference,.codexSubscriptionContextHead{flex-wrap:wrap}.codexSubscriptionPreferenceHint{max-width:none}.codexSubscriptionPreference>.codexSubscriptionPreferenceCopy{flex:1 1 220px}.codexSubscriptionQuotaModes{max-width:100%;flex-wrap:wrap}.codexSubscriptionQuotaMode{min-height:30px}.codexSubscriptionActions{flex-wrap:wrap}}
|
|
7232
7343
|
@container (max-width:340px){.codexSettingsTabs{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
7233
7344
|
@media(prefers-reduced-motion:reduce){.codexSubscription *{transition:none!important}}
|
|
7345
|
+
.codexSubscriptionLogHead{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;margin-top:12px}.codexSubscriptionLogHead h4{margin:0;font-size:13px}.codexSubscriptionLog{max-height:320px;overflow:auto;margin:6px 0 0;padding:8px 10px;border:1px solid var(--border,#0002);border-radius:8px;font:11.5px/1.5 ui-monospace,Consolas,monospace;white-space:pre-wrap;word-break:break-word}.codexSubscriptionLog_error{color:#c0392b}.codexSubscriptionLog_warn{color:#b9770e}.codexSubscriptionLog_info{opacity:.8}
|
|
7346
|
+
.codexSubscriptionConnectors>.codexSubscriptionQuotaModes{flex:0 0 auto;display:flex;flex-wrap:nowrap;margin-left:auto}
|
|
7347
|
+
.codexSubscriptionConnectors .codexSubscriptionQuotaMode{min-height:28px;padding:0 10px;white-space:nowrap}
|
|
7348
|
+
.codexSubscriptionConnectors>.codexSubscriptionModeNote{padding:0;background:none;font-size:12px;line-height:18px}
|
|
7349
|
+
.codexSubscriptionConnectors>.codexSubscriptionModeNote[data-danger=true]{background:none}
|
|
7350
|
+
@container (max-width:520px){.codexSubscriptionConnectors>.codexSubscriptionQuotaModes{flex:1 1 100%;margin-left:0;display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr)}.codexSubscriptionConnectors .codexSubscriptionQuotaMode{padding:0 4px;white-space:normal;text-align:center}}
|
|
7351
|
+
.codexVoiceChoice{display:flex;align-items:center;gap:8px}
|
|
7352
|
+
.codexVoiceChoice .codexSubscriptionContextTrigger{min-width:150px;justify-content:space-between}
|
|
7353
|
+
.codexVoiceChoice .codexSubscriptionContextTrigger:first-child:not(:last-child){min-width:0;justify-content:center}
|
|
7354
|
+
/* The voice button wears the send button's own classes, so its colours, shape and theme are always the send button's. */
|
|
7355
|
+
.codexVoiceCall{position:absolute;z-index:2;margin:0}
|
|
7356
|
+
.codexVoiceCall:disabled{cursor:progress}
|
|
7357
|
+
.codexVoiceCall[data-state=connecting]{animation:codexVoicePulse 1.1s ease-in-out infinite}
|
|
7358
|
+
.codexVoiceCallNote{position:absolute;bottom:calc(100% + 8px);z-index:3;width:max-content;max-width:320px;padding:6px 10px;border-radius:8px;background:var(--dsw-alias-surface-elevated,#2a2a2e);color:var(--dsw-alias-label-primary,#fff);font-size:12px;line-height:16px;box-shadow:0 4px 16px rgba(0,0,0,.3);white-space:normal}
|
|
7359
|
+
@keyframes codexVoicePulse{50%{opacity:.55}}
|
|
7360
|
+
@media (prefers-reduced-motion:reduce){.codexVoiceCall{animation:none!important}}
|
|
7361
|
+
|
|
7362
|
+
@keyframes codexVoiceStageIn{from{opacity:0;margin-top:8px}to{opacity:1;margin-top:0}}
|
|
7363
|
+
@media (prefers-reduced-motion:reduce){.codexVoiceOrb,.codexVoiceDot,.codexVoiceStage,.codexVoiceLines p{animation:none!important;transition:none!important}}
|
|
7364
|
+
.codexVoiceStage{position:fixed;z-index:60;display:flex;flex-direction:column;align-items:center;gap:6px;width:min(560px,92vw);pointer-events:none;animation:codexVoiceStageIn .28s cubic-bezier(.33,1,.68,1)}
|
|
7365
|
+
.codexVoiceStage[data-side=above]{transform:translate(-50%,calc(-100% - 16px))}
|
|
7366
|
+
.codexVoiceStage[data-side=below]{transform:translate(-50%,22px);flex-direction:column-reverse}
|
|
7367
|
+
.codexVoiceOrbBox{position:relative;width:120px;height:120px;flex:none;margin:-14px 0}
|
|
7368
|
+
.codexVoiceOrb{position:absolute;inset:0;transition:opacity .5s ease}
|
|
7369
|
+
.codexVoiceOrb[data-ready=false]{opacity:0}
|
|
7370
|
+
.codexVoiceOrb canvas{display:block;width:100%;height:100%}
|
|
7371
|
+
.codexVoiceDot{position:absolute;left:35px;top:35px;width:50px;height:50px;border-radius:50%;background:var(--dsw-alias-label-primary,#fff);animation:codexVoicePulse 1.1s ease-in-out infinite;transition:opacity .35s ease,transform .5s cubic-bezier(.33,1,.68,1)}
|
|
7372
|
+
.codexVoiceDot[data-gone=true]{opacity:0;transform:scale(1.8);animation:none}
|
|
7373
|
+
.codexVoiceLines{display:flex;flex-direction:column;align-items:center;gap:2px;max-width:100%;min-height:0;text-align:center}
|
|
7374
|
+
.codexVoiceLines p{margin:0;max-width:100%;font-size:13px;line-height:19px;color:var(--dsw-alias-label-primary);overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;animation:codexVoiceStageIn .28s cubic-bezier(.33,1,.68,1)}
|
|
7375
|
+
.codexVoiceLines p[data-role=user]{color:var(--dsw-alias-label-secondary)}
|
|
7376
|
+
.codexVoiceLines p[data-old]{opacity:.45}
|
|
7377
|
+
.codexVoiceStatus{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
|
|
7378
|
+
.codexVoiceTools{display:inline-flex;align-items:center;gap:2px}
|
|
7379
|
+
[data-voice-live]>[data-slot="conversation.input.activity"]{display:none!important}
|
|
7380
|
+
.codexVoiceTool{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}
|
|
7381
|
+
.codexVoiceTool:hover{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary)}
|
|
7382
|
+
.codexVoiceTool[aria-pressed=true]{color:var(--dsw-alias-label-tertiary)}
|
|
7383
|
+
|
|
7384
|
+
/* Every fold-out in the settings reads as pressable: a full-width row, a chevron on the right that turns when it opens, a hover state. */
|
|
7385
|
+
.codexSubscriptionHistory>summary,.codexSubscriptionSearchOptions>summary,.codexSubscriptionRuntime summary,.codexSubscriptionDiagnostics details>summary,.codexImageDetails summary{display:flex;align-items:center;gap:8px;width:auto;min-height:32px;margin:0 -10px;padding:4px 10px;border-radius:8px;list-style:none;cursor:pointer;user-select:none;font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary);transition:background .12s}
|
|
7386
|
+
.codexSubscriptionHistory>summary::-webkit-details-marker,.codexSubscriptionSearchOptions>summary::-webkit-details-marker,.codexSubscriptionRuntime summary::-webkit-details-marker,.codexSubscriptionDiagnostics details>summary::-webkit-details-marker,.codexImageDetails summary::-webkit-details-marker{display:none}
|
|
7387
|
+
.codexSubscriptionHistory>summary::after,.codexSubscriptionSearchOptions>summary::after,.codexSubscriptionRuntime summary::after,.codexSubscriptionDiagnostics details>summary::after,.codexImageDetails summary::after{content:"";flex:none;margin-left:auto;width:7px;height:7px;border:solid var(--dsw-alias-label-tertiary);border-width:0 1.6px 1.6px 0;transform:translateY(-2px) rotate(45deg);transition:transform .15s,border-color .12s}
|
|
7388
|
+
.codexSubscriptionHistory[open]>summary::after,.codexSubscriptionSearchOptions[open]>summary::after,.codexSubscriptionRuntime details[open]>summary::after,.codexSubscriptionDiagnostics details[open]>summary::after,.codexImageDetails[open]>summary::after{transform:translateY(2px) rotate(-135deg)}
|
|
7389
|
+
.codexSubscriptionHistory>summary:hover,.codexSubscriptionSearchOptions>summary:hover,.codexSubscriptionRuntime summary:hover,.codexSubscriptionDiagnostics details>summary:hover,.codexImageDetails summary:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
7390
|
+
.codexSubscriptionHistory>summary:hover::after,.codexSubscriptionSearchOptions>summary:hover::after,.codexSubscriptionRuntime summary:hover::after,.codexSubscriptionDiagnostics details>summary:hover::after,.codexImageDetails summary:hover::after{border-color:var(--dsw-alias-label-primary)}
|
|
7391
|
+
.codexSubscriptionHistory>summary:focus-visible,.codexSubscriptionSearchOptions>summary:focus-visible,.codexSubscriptionRuntime summary:focus-visible,.codexSubscriptionDiagnostics details>summary:focus-visible,.codexImageDetails summary:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:0}
|
|
7392
|
+
.codexSubscriptionSearchOptions>summary>span{float:none;margin-left:auto;font-weight:400}
|
|
7393
|
+
.codexSubscriptionSearchOptions>summary::after{margin-left:6px}
|
|
7394
|
+
.codexSubscriptionRuntime summary{font-size:12px}
|
|
7395
|
+
.codexSubscriptionHintToggle{margin:2px 0 0 -6px;padding:1px 6px;border-radius:6px;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary);transition:background .12s}
|
|
7396
|
+
.codexSubscriptionHintToggle::after{width:6px;height:6px}
|
|
7397
|
+
.codexSubscriptionHintToggle:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
7234
7398
|
`;
|
|
7235
7399
|
//#endregion
|
|
7236
7400
|
//#region src/image-edit-reference.js
|
|
@@ -7239,10 +7403,10 @@ window.__ModuleLoader__.load({
|
|
|
7239
7403
|
const PIN_OUTER_STROKE = "#111827";
|
|
7240
7404
|
const PIN_INNER_STROKE = "#ffffff";
|
|
7241
7405
|
const finiteDimension = (value) => Number.isSafeInteger(value) && value > 0;
|
|
7242
|
-
const clamp$
|
|
7406
|
+
const clamp$3 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
7243
7407
|
const clampCenter = (value, size, edge) => {
|
|
7244
7408
|
const margin = Math.min(edge, size / 2);
|
|
7245
|
-
return clamp$
|
|
7409
|
+
return clamp$3(value, margin, size - margin);
|
|
7246
7410
|
};
|
|
7247
7411
|
function getBitmapFactory(options) {
|
|
7248
7412
|
const factory = options?.createImageBitmap ?? options?.bitmapFactory ?? globalThis.createImageBitmap;
|
|
@@ -7379,7 +7543,7 @@ window.__ModuleLoader__.load({
|
|
|
7379
7543
|
`;
|
|
7380
7544
|
//#endregion
|
|
7381
7545
|
//#region src/subscription-image-transform.js
|
|
7382
|
-
const clamp$
|
|
7546
|
+
const clamp$2 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
7383
7547
|
const fitted = () => ({
|
|
7384
7548
|
zoom: 1,
|
|
7385
7549
|
x: 0,
|
|
@@ -7421,15 +7585,15 @@ window.__ModuleLoader__.load({
|
|
|
7421
7585
|
const limitX = Math.max(0, (surface.offsetWidth * zoom - stage.clientWidth) / 2);
|
|
7422
7586
|
const limitY = Math.max(0, (surface.offsetHeight * zoom - stage.clientHeight) / 2);
|
|
7423
7587
|
return {
|
|
7424
|
-
x: clamp$
|
|
7425
|
-
y: clamp$
|
|
7588
|
+
x: clamp$2(x, -limitX, limitX),
|
|
7589
|
+
y: clamp$2(y, -limitY, limitY)
|
|
7426
7590
|
};
|
|
7427
7591
|
}, []);
|
|
7428
7592
|
const setZoomAt = (0, react.useCallback)((zoom, clientX, clientY) => {
|
|
7429
7593
|
const stage = stageRef.current;
|
|
7430
7594
|
if (!stage) return;
|
|
7431
7595
|
setTransform((current) => {
|
|
7432
|
-
const next = clamp$
|
|
7596
|
+
const next = clamp$2(zoom, .5, Math.max(8, actualScale()));
|
|
7433
7597
|
const box = stage.getBoundingClientRect();
|
|
7434
7598
|
const px = clientX - box.left - box.width / 2, py = clientY - box.top - box.height / 2;
|
|
7435
7599
|
const ratio = next / current.zoom;
|
|
@@ -7570,7 +7734,7 @@ window.__ModuleLoader__.load({
|
|
|
7570
7734
|
}) : null;
|
|
7571
7735
|
};
|
|
7572
7736
|
const fill$1 = (value, variables) => Object.entries(variables).reduce((text, [key, replacement]) => text.replaceAll(`{${key}}`, String(replacement)), value);
|
|
7573
|
-
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
7737
|
+
const clamp$1 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
7574
7738
|
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`;
|
|
7575
7739
|
const downloadName = (name) => {
|
|
7576
7740
|
const cleaned = String(name || "image.png").replace(/[<>:"/\\|?*\u0000-\u001f]/gu, "-").replace(/[. ]+$/u, "").trim();
|
|
@@ -7855,7 +8019,7 @@ window.__ModuleLoader__.load({
|
|
|
7855
8019
|
}
|
|
7856
8020
|
if (event.key === "[" || event.key === "]") {
|
|
7857
8021
|
event.preventDefault();
|
|
7858
|
-
setBrush((value) => Math.round(clamp(value + (event.key === "]" ? .01 : -.01), .01, .15) * 100) / 100);
|
|
8022
|
+
setBrush((value) => Math.round(clamp$1(value + (event.key === "]" ? .01 : -.01), .01, .15) * 100) / 100);
|
|
7859
8023
|
return;
|
|
7860
8024
|
}
|
|
7861
8025
|
if (event.key === "ArrowLeft" || event.key === "ArrowRight") return;
|
|
@@ -7941,8 +8105,8 @@ window.__ModuleLoader__.load({
|
|
|
7941
8105
|
const bounds = maskRef.current?.getBoundingClientRect();
|
|
7942
8106
|
if (bounds === void 0) return void 0;
|
|
7943
8107
|
return {
|
|
7944
|
-
x: clamp((event.clientX - bounds.left) / bounds.width, 0, 1),
|
|
7945
|
-
y: clamp((event.clientY - bounds.top) / bounds.height, 0, 1)
|
|
8108
|
+
x: clamp$1((event.clientX - bounds.left) / bounds.width, 0, 1),
|
|
8109
|
+
y: clamp$1((event.clientY - bounds.top) / bounds.height, 0, 1)
|
|
7946
8110
|
};
|
|
7947
8111
|
};
|
|
7948
8112
|
const maskHandlers = {
|
|
@@ -8022,8 +8186,8 @@ window.__ModuleLoader__.load({
|
|
|
8022
8186
|
if (bounds === void 0) return;
|
|
8023
8187
|
const annotation = {
|
|
8024
8188
|
id: crypto.randomUUID(),
|
|
8025
|
-
x: clamp((event.clientX - bounds.left) / bounds.width, 0, 1),
|
|
8026
|
-
y: clamp((event.clientY - bounds.top) / bounds.height, 0, 1),
|
|
8189
|
+
x: clamp$1((event.clientX - bounds.left) / bounds.width, 0, 1),
|
|
8190
|
+
y: clamp$1((event.clientY - bounds.top) / bounds.height, 0, 1),
|
|
8027
8191
|
note: ""
|
|
8028
8192
|
};
|
|
8029
8193
|
setAnnotations((current) => [...current, annotation]);
|
|
@@ -8589,6 +8753,51 @@ window.__ModuleLoader__.load({
|
|
|
8589
8753
|
const OUTPUT_VERBOSITY_MEDIUM = "medium";
|
|
8590
8754
|
const OUTPUT_VERBOSITY_HIGH = "high";
|
|
8591
8755
|
const DEFAULT_OUTPUT_VERBOSITY = OUTPUT_VERBOSITY_DEFAULT;
|
|
8756
|
+
const VOICE_CALL_FIELD = "voiceCall";
|
|
8757
|
+
const VOICE_VOICE_FIELD = "voiceVoice";
|
|
8758
|
+
const VOICE_VOICES = Object.freeze([
|
|
8759
|
+
"cove",
|
|
8760
|
+
"juniper",
|
|
8761
|
+
"maple",
|
|
8762
|
+
"spruce",
|
|
8763
|
+
"ember",
|
|
8764
|
+
"vale",
|
|
8765
|
+
"breeze",
|
|
8766
|
+
"arbor",
|
|
8767
|
+
"sol",
|
|
8768
|
+
"aube",
|
|
8769
|
+
"bubbie",
|
|
8770
|
+
"haetsal",
|
|
8771
|
+
"hanul",
|
|
8772
|
+
"himari",
|
|
8773
|
+
"leher",
|
|
8774
|
+
"miko",
|
|
8775
|
+
"neer",
|
|
8776
|
+
"porto",
|
|
8777
|
+
"rindo",
|
|
8778
|
+
"rio",
|
|
8779
|
+
"rivage",
|
|
8780
|
+
"selva",
|
|
8781
|
+
"sonna",
|
|
8782
|
+
"tinta",
|
|
8783
|
+
"viola"
|
|
8784
|
+
]);
|
|
8785
|
+
const VOICE_LANGUAGE_FIELD = "voiceLanguage";
|
|
8786
|
+
const VOICE_LANGUAGES = Object.freeze([
|
|
8787
|
+
"auto",
|
|
8788
|
+
"zh",
|
|
8789
|
+
"en",
|
|
8790
|
+
"ja",
|
|
8791
|
+
"ko",
|
|
8792
|
+
"es",
|
|
8793
|
+
"fr",
|
|
8794
|
+
"de",
|
|
8795
|
+
"pt",
|
|
8796
|
+
"ru"
|
|
8797
|
+
]);
|
|
8798
|
+
const normalizeVoiceCall = (value) => value === "off" ? "off" : "on";
|
|
8799
|
+
const normalizeVoiceVoice = (value) => VOICE_VOICES.includes(value) ? value : "cove";
|
|
8800
|
+
const normalizeVoiceLanguage = (value) => VOICE_LANGUAGES.includes(value) ? value : "auto";
|
|
8592
8801
|
const REASONING_SUMMARY_FIELD = "reasoningSummary";
|
|
8593
8802
|
const REASONING_SUMMARIES = Object.freeze([
|
|
8594
8803
|
"auto",
|
|
@@ -8610,7 +8819,8 @@ window.__ModuleLoader__.load({
|
|
|
8610
8819
|
const CONNECTOR_MODES = Object.freeze([
|
|
8611
8820
|
"off",
|
|
8612
8821
|
"read",
|
|
8613
|
-
"ask"
|
|
8822
|
+
"ask",
|
|
8823
|
+
"write"
|
|
8614
8824
|
]);
|
|
8615
8825
|
const INPUT_IMAGE_DETAIL_FIELD = "inputImageDetail";
|
|
8616
8826
|
const INPUT_IMAGE_DETAILS = Object.freeze([
|
|
@@ -8754,6 +8964,9 @@ window.__ModuleLoader__.load({
|
|
|
8754
8964
|
outputVerbosity: normalizeOutputVerbosity(value?.[OUTPUT_VERBOSITY_FIELD]),
|
|
8755
8965
|
inputImageDetail: normalizeInputImageDetail(value?.[INPUT_IMAGE_DETAIL_FIELD]),
|
|
8756
8966
|
reasoningSummary: normalizeReasoningSummary(value?.[REASONING_SUMMARY_FIELD]),
|
|
8967
|
+
voiceCall: normalizeVoiceCall(value?.[VOICE_CALL_FIELD]),
|
|
8968
|
+
voiceVoice: normalizeVoiceVoice(value?.[VOICE_VOICE_FIELD]),
|
|
8969
|
+
voiceLanguage: normalizeVoiceLanguage(value?.[VOICE_LANGUAGE_FIELD]),
|
|
8757
8970
|
retryPolicy: normalizeRetryPolicy(value?.[RETRY_POLICY_FIELD]),
|
|
8758
8971
|
connectors: normalizeConnectorMode(value?.[CONNECTORS_FIELD]),
|
|
8759
8972
|
streamIdleTimeoutMinutes: normalizeStreamIdleTimeoutMinutes(value?.[STREAM_IDLE_TIMEOUT_MINUTES_FIELD]),
|
|
@@ -8813,6 +9026,9 @@ window.__ModuleLoader__.load({
|
|
|
8813
9026
|
[OUTPUT_VERBOSITY_FIELD]: normalizeOutputVerbosity(value?.[OUTPUT_VERBOSITY_FIELD]),
|
|
8814
9027
|
[INPUT_IMAGE_DETAIL_FIELD]: normalizeInputImageDetail(value?.[INPUT_IMAGE_DETAIL_FIELD]),
|
|
8815
9028
|
[REASONING_SUMMARY_FIELD]: normalizeReasoningSummary(value?.[REASONING_SUMMARY_FIELD]),
|
|
9029
|
+
[VOICE_CALL_FIELD]: normalizeVoiceCall(value?.[VOICE_CALL_FIELD]),
|
|
9030
|
+
[VOICE_VOICE_FIELD]: normalizeVoiceVoice(value?.[VOICE_VOICE_FIELD]),
|
|
9031
|
+
[VOICE_LANGUAGE_FIELD]: normalizeVoiceLanguage(value?.[VOICE_LANGUAGE_FIELD]),
|
|
8816
9032
|
[RETRY_POLICY_FIELD]: normalizeRetryPolicy(value?.[RETRY_POLICY_FIELD]),
|
|
8817
9033
|
[CONNECTORS_FIELD]: normalizeConnectorMode(value?.[CONNECTORS_FIELD]),
|
|
8818
9034
|
[STREAM_IDLE_TIMEOUT_MINUTES_FIELD]: normalizeStreamIdleTimeoutMinutes(value?.[STREAM_IDLE_TIMEOUT_MINUTES_FIELD]),
|
|
@@ -9300,7 +9516,7 @@ window.__ModuleLoader__.load({
|
|
|
9300
9516
|
}
|
|
9301
9517
|
//#endregion
|
|
9302
9518
|
//#region src/version.js
|
|
9303
|
-
const PACKAGE_VERSION = "3.0.
|
|
9519
|
+
const PACKAGE_VERSION = "3.0.1";
|
|
9304
9520
|
//#endregion
|
|
9305
9521
|
//#region src/client-recovery.js
|
|
9306
9522
|
async function recoveryCall(rpc, endpoint, payload = {}, timeoutMs = 1e4) {
|
|
@@ -9979,6 +10195,1234 @@ window.__ModuleLoader__.load({
|
|
|
9979
10195
|
});
|
|
9980
10196
|
}
|
|
9981
10197
|
//#endregion
|
|
10198
|
+
//#region src/client-hint.jsx
|
|
10199
|
+
/** A setting's explanation: shows up to two lines; a "More" link appears only when the text is actually cut off. */
|
|
10200
|
+
function Hint({ children, t, as: Tag = "span" }) {
|
|
10201
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
10202
|
+
const [clipped, setClipped] = (0, react.useState)(false);
|
|
10203
|
+
const text = (0, react.useRef)(null);
|
|
10204
|
+
const id = (0, react.useId)();
|
|
10205
|
+
const foldable = typeof children === "string";
|
|
10206
|
+
(0, react.useLayoutEffect)(() => {
|
|
10207
|
+
const node = text.current;
|
|
10208
|
+
if (!foldable || node === null || open) return;
|
|
10209
|
+
const measure = () => setClipped(node.scrollHeight > node.clientHeight + 1);
|
|
10210
|
+
measure();
|
|
10211
|
+
if (typeof ResizeObserver !== "function") return;
|
|
10212
|
+
const observer = new ResizeObserver(measure);
|
|
10213
|
+
observer.observe(node);
|
|
10214
|
+
return () => observer.disconnect();
|
|
10215
|
+
}, [
|
|
10216
|
+
children,
|
|
10217
|
+
foldable,
|
|
10218
|
+
open
|
|
10219
|
+
]);
|
|
10220
|
+
if (!foldable) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tag, {
|
|
10221
|
+
className: "codexSubscriptionPreferenceHint",
|
|
10222
|
+
children
|
|
10223
|
+
});
|
|
10224
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tag, {
|
|
10225
|
+
className: "codexSubscriptionPreferenceHint codexSubscriptionHintFold",
|
|
10226
|
+
"data-open": open || void 0,
|
|
10227
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10228
|
+
id,
|
|
10229
|
+
ref: text,
|
|
10230
|
+
className: "codexSubscriptionHintText",
|
|
10231
|
+
children
|
|
10232
|
+
}), clipped || open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10233
|
+
type: "button",
|
|
10234
|
+
className: "codexSubscriptionHintToggle",
|
|
10235
|
+
"aria-expanded": open,
|
|
10236
|
+
"aria-controls": id,
|
|
10237
|
+
onClick: () => setOpen((value) => !value),
|
|
10238
|
+
children: t(open ? "hintLess" : "hintMore")
|
|
10239
|
+
}) : null]
|
|
10240
|
+
});
|
|
10241
|
+
}
|
|
10242
|
+
//#endregion
|
|
10243
|
+
//#region src/voice-call.js
|
|
10244
|
+
const ICE_WAIT_MS = 3e3;
|
|
10245
|
+
const waitForCandidates = (connection) => new Promise((resolve) => {
|
|
10246
|
+
if (connection.iceGatheringState === "complete") {
|
|
10247
|
+
resolve();
|
|
10248
|
+
return;
|
|
10249
|
+
}
|
|
10250
|
+
const done = () => {
|
|
10251
|
+
clearTimeout(timer);
|
|
10252
|
+
connection.removeEventListener("icegatheringstatechange", check);
|
|
10253
|
+
resolve();
|
|
10254
|
+
};
|
|
10255
|
+
const check = () => {
|
|
10256
|
+
if (connection.iceGatheringState === "complete") done();
|
|
10257
|
+
};
|
|
10258
|
+
const timer = setTimeout(done, ICE_WAIT_MS);
|
|
10259
|
+
connection.addEventListener("icegatheringstatechange", check);
|
|
10260
|
+
});
|
|
10261
|
+
/** Two short soft tones: rising when a call starts, falling when it ends. */
|
|
10262
|
+
const chime = (context, frequencies) => {
|
|
10263
|
+
try {
|
|
10264
|
+
const now = context.currentTime;
|
|
10265
|
+
frequencies.forEach((frequency, index) => {
|
|
10266
|
+
const tone = context.createOscillator();
|
|
10267
|
+
const gain = context.createGain();
|
|
10268
|
+
const start = now + index * .11;
|
|
10269
|
+
tone.type = "sine";
|
|
10270
|
+
tone.frequency.value = frequency;
|
|
10271
|
+
gain.gain.setValueAtTime(0, start);
|
|
10272
|
+
gain.gain.linearRampToValueAtTime(.07, start + .02);
|
|
10273
|
+
gain.gain.exponentialRampToValueAtTime(1e-4, start + .16);
|
|
10274
|
+
tone.connect(gain).connect(context.destination);
|
|
10275
|
+
tone.start(start);
|
|
10276
|
+
tone.stop(start + .18);
|
|
10277
|
+
});
|
|
10278
|
+
} catch {}
|
|
10279
|
+
};
|
|
10280
|
+
/** Loudness of an analyser as 0..1 (the root mean square of the waveform, lifted so quiet speech still moves the orb). */
|
|
10281
|
+
const loudness = (analyser, buffer) => {
|
|
10282
|
+
if (!analyser) return 0;
|
|
10283
|
+
analyser.getByteTimeDomainData(buffer);
|
|
10284
|
+
let sum = 0;
|
|
10285
|
+
for (const value of buffer) {
|
|
10286
|
+
const centred = (value - 128) / 128;
|
|
10287
|
+
sum += centred * centred;
|
|
10288
|
+
}
|
|
10289
|
+
return Math.min(1, Math.sqrt(sum / buffer.length) * 3.2);
|
|
10290
|
+
};
|
|
10291
|
+
/**
|
|
10292
|
+
* A voice call from this window: the microphone and the model's voice travel over WebRTC straight to OpenAI; the
|
|
10293
|
+
* plugin's host turns the offer into a call, joins it, and runs what is asked as turns of this conversation.
|
|
10294
|
+
* Resolves with a handle once the call is connected; rejects with the reason when it could not start.
|
|
10295
|
+
*/
|
|
10296
|
+
async function openVoiceCall({ rpc, sessionId, onEnd }) {
|
|
10297
|
+
if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia || typeof RTCPeerConnection !== "function") throw new Error("unsupported");
|
|
10298
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: {
|
|
10299
|
+
echoCancellation: true,
|
|
10300
|
+
noiseSuppression: true,
|
|
10301
|
+
autoGainControl: true
|
|
10302
|
+
} });
|
|
10303
|
+
const connection = new RTCPeerConnection();
|
|
10304
|
+
const sound = new Audio();
|
|
10305
|
+
sound.autoplay = true;
|
|
10306
|
+
const AudioContextClass = window.AudioContext ?? window.webkitAudioContext;
|
|
10307
|
+
const context = AudioContextClass ? new AudioContextClass() : void 0;
|
|
10308
|
+
const analysers = {
|
|
10309
|
+
mic: void 0,
|
|
10310
|
+
assistant: void 0
|
|
10311
|
+
};
|
|
10312
|
+
const watch = (name, source) => {
|
|
10313
|
+
if (!context) return;
|
|
10314
|
+
try {
|
|
10315
|
+
const analyser = context.createAnalyser();
|
|
10316
|
+
analyser.fftSize = 512;
|
|
10317
|
+
context.createMediaStreamSource(source).connect(analyser);
|
|
10318
|
+
analysers[name] = analyser;
|
|
10319
|
+
} catch {}
|
|
10320
|
+
};
|
|
10321
|
+
watch("mic", stream);
|
|
10322
|
+
connection.ontrack = (event) => {
|
|
10323
|
+
sound.srcObject = event.streams[0];
|
|
10324
|
+
watch("assistant", event.streams[0]);
|
|
10325
|
+
};
|
|
10326
|
+
for (const track of stream.getTracks()) connection.addTrack(track, stream);
|
|
10327
|
+
connection.createDataChannel("oai-events");
|
|
10328
|
+
let ended = false;
|
|
10329
|
+
const end = async () => {
|
|
10330
|
+
if (ended) return;
|
|
10331
|
+
ended = true;
|
|
10332
|
+
if (context) chime(context, [740, 520]);
|
|
10333
|
+
for (const track of stream.getTracks()) track.stop();
|
|
10334
|
+
sound.srcObject = null;
|
|
10335
|
+
try {
|
|
10336
|
+
connection.close();
|
|
10337
|
+
} catch {}
|
|
10338
|
+
try {
|
|
10339
|
+
await rpc.call(CHANNEL, "voice/stop", { sessionId });
|
|
10340
|
+
} catch {}
|
|
10341
|
+
setTimeout(() => {
|
|
10342
|
+
context?.close().catch(() => {});
|
|
10343
|
+
}, 600);
|
|
10344
|
+
onEnd?.();
|
|
10345
|
+
};
|
|
10346
|
+
connection.addEventListener("connectionstatechange", () => {
|
|
10347
|
+
if ([
|
|
10348
|
+
"failed",
|
|
10349
|
+
"disconnected",
|
|
10350
|
+
"closed"
|
|
10351
|
+
].includes(connection.connectionState)) end();
|
|
10352
|
+
});
|
|
10353
|
+
try {
|
|
10354
|
+
await context?.resume();
|
|
10355
|
+
await connection.setLocalDescription(await connection.createOffer());
|
|
10356
|
+
await waitForCandidates(connection);
|
|
10357
|
+
const result = await rpc.call(CHANNEL, "voice/start", {
|
|
10358
|
+
sessionId,
|
|
10359
|
+
sdp: connection.localDescription.sdp
|
|
10360
|
+
});
|
|
10361
|
+
if (!result.ok) throw new Error(result.error?.message ?? "start failed");
|
|
10362
|
+
await connection.setRemoteDescription({
|
|
10363
|
+
type: "answer",
|
|
10364
|
+
sdp: result.value.answer
|
|
10365
|
+
});
|
|
10366
|
+
} catch (error) {
|
|
10367
|
+
ended = true;
|
|
10368
|
+
for (const track of stream.getTracks()) track.stop();
|
|
10369
|
+
try {
|
|
10370
|
+
connection.close();
|
|
10371
|
+
} catch {}
|
|
10372
|
+
context?.close().catch(() => {});
|
|
10373
|
+
throw error;
|
|
10374
|
+
}
|
|
10375
|
+
if (context) chime(context, [520, 740]);
|
|
10376
|
+
const buffers = {
|
|
10377
|
+
mic: /* @__PURE__ */ new Uint8Array(512),
|
|
10378
|
+
assistant: /* @__PURE__ */ new Uint8Array(512)
|
|
10379
|
+
};
|
|
10380
|
+
let muted = false;
|
|
10381
|
+
return {
|
|
10382
|
+
stop: end,
|
|
10383
|
+
isMuted: () => muted,
|
|
10384
|
+
setMuted: (value) => {
|
|
10385
|
+
muted = value;
|
|
10386
|
+
for (const track of stream.getAudioTracks()) track.enabled = !value;
|
|
10387
|
+
},
|
|
10388
|
+
isOutputMuted: () => sound.muted,
|
|
10389
|
+
setOutputMuted: (value) => {
|
|
10390
|
+
sound.muted = value;
|
|
10391
|
+
},
|
|
10392
|
+
/** How loud the person (mic) and the model (voice) are right now, 0..1 each. */
|
|
10393
|
+
levels: () => ({
|
|
10394
|
+
mic: muted ? 0 : loudness(analysers.mic, buffers.mic),
|
|
10395
|
+
assistant: loudness(analysers.assistant, buffers.assistant)
|
|
10396
|
+
})
|
|
10397
|
+
};
|
|
10398
|
+
}
|
|
10399
|
+
//#endregion
|
|
10400
|
+
//#region src/voice-orb.js
|
|
10401
|
+
const VERTEX = "attribute vec2 a;void main(){gl_Position=vec4(a,0.,1.);}";
|
|
10402
|
+
const FRAGMENT = `
|
|
10403
|
+
precision highp float;
|
|
10404
|
+
uniform vec2 uRes;
|
|
10405
|
+
uniform float uWaveFrame;
|
|
10406
|
+
uniform float uBaseFrame;
|
|
10407
|
+
uniform float uAmp;
|
|
10408
|
+
uniform float uFlowFrame;
|
|
10409
|
+
uniform float uEdge;
|
|
10410
|
+
uniform float uNoiseScale;
|
|
10411
|
+
uniform float uSurface;
|
|
10412
|
+
uniform float uMic;
|
|
10413
|
+
uniform float uUser;
|
|
10414
|
+
uniform float uFlat;
|
|
10415
|
+
uniform vec2 uOff0;
|
|
10416
|
+
uniform vec2 uOff1;
|
|
10417
|
+
uniform vec2 uOff2;
|
|
10418
|
+
uniform vec3 uAccent;
|
|
10419
|
+
|
|
10420
|
+
float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
|
|
10421
|
+
vec2 hash2(vec2 p){return vec2(hash(p),hash(p+vec2(19.19,73.73)));}
|
|
10422
|
+
float vnoise(vec2 p){
|
|
10423
|
+
vec2 i=floor(p),f=fract(p);f=f*f*(3.-2.*f);
|
|
10424
|
+
return mix(mix(hash(i),hash(i+vec2(1.,0.)),f.x),mix(hash(i+vec2(0.,1.)),hash(i+vec2(1.,1.)),f.x),f.y);
|
|
10425
|
+
}
|
|
10426
|
+
float fbm(vec2 p,float rough,float lac){
|
|
10427
|
+
float a=vnoise(p);
|
|
10428
|
+
float b=vnoise(p*lac+vec2(17.17,31.31));
|
|
10429
|
+
float c=vnoise(p*lac*lac+vec2(47.47,11.11));
|
|
10430
|
+
return (a+b*rough+c*rough*rough)/(1.+rough+rough*rough);
|
|
10431
|
+
}
|
|
10432
|
+
vec3 noise3(vec3 coord,float w,float scale,float rough,float lac,float unit){
|
|
10433
|
+
vec2 p=coord.xy*scale;
|
|
10434
|
+
float t=w*scale;
|
|
10435
|
+
vec2 d1=vec2(.17,.11),d2=vec2(-.13,.19),d3=vec2(.07,-.23);
|
|
10436
|
+
if(unit>.5){d1=normalize(d1);d2=normalize(d2);d3=normalize(d3);}
|
|
10437
|
+
return vec3(fbm(p+d1*t,rough,lac),fbm(p+vec2(41.41,17.17)+d2*t,rough,lac),fbm(p+vec2(23.23,59.59)+d3*t,rough,lac));
|
|
10438
|
+
}
|
|
10439
|
+
|
|
10440
|
+
// the painted-paper texture: soft pale ground, darker stains with ragged edges, a fine fibre. Reads like grey watercolour.
|
|
10441
|
+
float paint(vec2 uv){
|
|
10442
|
+
vec2 w=vec2(vnoise(uv*3.+vec2(4.2,1.7)),vnoise(uv*3.+vec2(9.1,6.3)))-.5;
|
|
10443
|
+
vec2 q=uv+w*.3;
|
|
10444
|
+
float body=vnoise(q*2.+vec2(3.1,8.3))*.5+vnoise(q*4.3+vec2(9.7,1.9))*.3+vnoise(q*9.+vec2(2.3,5.5))*.2;
|
|
10445
|
+
float stain=smoothstep(.5,.64,body);
|
|
10446
|
+
float wash=smoothstep(.28,.6,vnoise(q*2.6+vec2(14.2,4.4)));
|
|
10447
|
+
float mott=vnoise(q*vec2(18.,16.)+vec2(5.,5.))*.55+vnoise(q*vec2(42.,36.))*.45;
|
|
10448
|
+
float ragged=stain*(1.-stain)*4.;
|
|
10449
|
+
float v=.92-.2*wash-.5*stain+(mott-.5)*(.1+.2*ragged);
|
|
10450
|
+
return clamp(v,.16,1.);
|
|
10451
|
+
}
|
|
10452
|
+
|
|
10453
|
+
vec3 warp3(vec3 c,float frame){
|
|
10454
|
+
vec2 pt=c.xy*8.;
|
|
10455
|
+
vec2 cell=floor(pt),local=fract(pt);
|
|
10456
|
+
vec2 nearest=vec2(0.);
|
|
10457
|
+
float best=3.4e38;
|
|
10458
|
+
float phase=frame*6.2831853/240.;
|
|
10459
|
+
for(int y=-1;y<=1;y++){for(int x=-1;x<=1;x++){
|
|
10460
|
+
vec2 off=vec2(float(x),float(y));
|
|
10461
|
+
vec2 r=hash2(cell+off);
|
|
10462
|
+
vec2 pos=off+r*.7185189723968506+sin(vec2(phase)+r*6.2831853)*.04;
|
|
10463
|
+
vec2 d=pos-local;
|
|
10464
|
+
float dd=dot(d,d);
|
|
10465
|
+
if(dd<best){best=dd;nearest=pos;}
|
|
10466
|
+
}}
|
|
10467
|
+
vec3 vor=vec3((nearest+cell)/8.,c.z);
|
|
10468
|
+
return (c-vec3(.5))*.26+(vor-c)*.06;
|
|
10469
|
+
}
|
|
10470
|
+
|
|
10471
|
+
vec3 rotY(vec3 v,float a){float c=cos(a),s=sin(a);return vec3(c*v.x+s*v.z,v.y,-s*v.x+c*v.z);}
|
|
10472
|
+
float fac(vec3 v,float lift){vec3 r=rotY(v,.7853981852531433)+vec3(0.,lift,0.);return (r.x+r.y+r.z)/3.;}
|
|
10473
|
+
// 1 below a, easing to 0 at b
|
|
10474
|
+
float ramp(float f,float a,float b){float t=clamp((f-a)/max(b-a,1e-6),0.,1.);t=t*t*(3.-2.*t);return 1.-t;}
|
|
10475
|
+
|
|
10476
|
+
vec3 interior(vec3 gen){
|
|
10477
|
+
vec3 a=uAccent;
|
|
10478
|
+
vec3 soft=mix(((16.*a-12.)*a+4.)*a,sqrt(a),step(.25,a));
|
|
10479
|
+
vec3 cShadow=max(vec3(0.),a+.9465623-1.);
|
|
10480
|
+
vec3 cMidLow=a+(2.*.89882153-1.)*(soft-a);
|
|
10481
|
+
vec3 cMidHigh=vec3(1.);
|
|
10482
|
+
vec3 cHigh=1.-(1.-a)*(1.-.63704175);
|
|
10483
|
+
|
|
10484
|
+
vec3 m=gen+vec3(0.,-.14,0.);
|
|
10485
|
+
vec2 cen=gen.xy-.5;
|
|
10486
|
+
float rad=length(cen);
|
|
10487
|
+
m.xy+=(rad>1e-6?cen/rad:vec2(0.))*smoothstep(.015,.44,rad)*uEdge;
|
|
10488
|
+
vec3 wave=noise3(m,uWaveFrame/100.,1.,.4,2.,1.);
|
|
10489
|
+
vec3 m2=m+(wave-.5)*(.8*uAmp);
|
|
10490
|
+
vec3 grain=noise3(m2,uFlowFrame/10.,4000.,.5,2.,0.);
|
|
10491
|
+
vec3 g2=(grain-.5)*uNoiseScale*.06;
|
|
10492
|
+
vec3 base=m2*.8+g2;
|
|
10493
|
+
float blend=clamp(.5-.5*cos(uBaseFrame*6.2831853/120.),0.,1.);
|
|
10494
|
+
vec3 cw=warp3(m2,uBaseFrame);
|
|
10495
|
+
|
|
10496
|
+
// tone 3: highlight
|
|
10497
|
+
vec2 p0=base.xy+uOff0;
|
|
10498
|
+
vec2 f0=vec2(base.x,1.-base.y)+uOff0;
|
|
10499
|
+
float s0=mix(paint(p0)-.5,paint(f0)-.5,blend)*(.2-.06*cos((uBaseFrame-1.)*6.2831853/120.));
|
|
10500
|
+
vec3 v6=m2+vec3(s0,s0,0.)+cw;
|
|
10501
|
+
float rHigh=ramp(fac(v6,.56),.3431819975376129,.6200000047683716);
|
|
10502
|
+
|
|
10503
|
+
// tone 2: white
|
|
10504
|
+
vec3 b1=m2*.8+vec3(.04,.02,0.)+g2;
|
|
10505
|
+
vec3 b2=m2*.8+vec3(-.04,-.02,0.)+g2;
|
|
10506
|
+
float s1=mix(paint(b1.xy+uOff1)-.5,paint(vec2(b2.x,1.-b2.y)+uOff1)-.5,blend)*(.2-.06*cos((uBaseFrame-1.)*6.2831853/80.));
|
|
10507
|
+
vec3 v5=m2+vec3(s1,s1,0.)+cw;
|
|
10508
|
+
float rMidHigh=ramp(fac(v5,.26),.4113636016845703,.5586364269256592);
|
|
10509
|
+
|
|
10510
|
+
// tone 1: mid-low, with the left-to-right fade the original uses
|
|
10511
|
+
float gx=clamp(gen.y-.32,0.,1.);
|
|
10512
|
+
float gradient=gx*gx*(3.-2.*gx);
|
|
10513
|
+
vec3 c1=m2*.8+vec3(-.08,-.04,0.)+g2;
|
|
10514
|
+
vec3 c2=m2*.8+vec3(-.96,.06,0.)+g2;
|
|
10515
|
+
float t1=mix(paint(c1.xy+uOff2),1.,gradient)-.5;
|
|
10516
|
+
float t2=mix(paint(vec2(c2.x,1.-c2.y)+uOff2),1.,gradient)-.5;
|
|
10517
|
+
float s2=mix(t1,t2,blend)*(.14-.06*cos((uBaseFrame-1.)*6.2831853/100.));
|
|
10518
|
+
vec3 v9=m2+vec3(s2,s2,0.)+cw;
|
|
10519
|
+
float rMidLow=ramp(fac(v9,.12),.3522728383541107,.7400000095367432);
|
|
10520
|
+
|
|
10521
|
+
vec3 col=cShadow;
|
|
10522
|
+
col=mix(col,cMidLow,rMidLow);
|
|
10523
|
+
col=mix(col,cMidHigh,rMidHigh);
|
|
10524
|
+
col=mix(col,cHigh,rHigh);
|
|
10525
|
+
return col;
|
|
10526
|
+
}
|
|
10527
|
+
|
|
10528
|
+
void main(){
|
|
10529
|
+
if(uFlat>.5){gl_FragColor=vec4(interior(vec3(gl_FragCoord.xy/uRes,.5)),1.);return;}
|
|
10530
|
+
vec2 frame=(gl_FragCoord.xy/uRes)*2.-1.;
|
|
10531
|
+
float user=clamp(uUser,0.,1.);
|
|
10532
|
+
float voice=max(1.-user*.14+user*clamp(uMic,0.,1.)*.08,.75);
|
|
10533
|
+
float scale=voice*uSurface;
|
|
10534
|
+
vec2 sdf=frame/scale;
|
|
10535
|
+
float dist=length(sdf)-1.;
|
|
10536
|
+
float px=2./uRes.x/scale;
|
|
10537
|
+
float shape=1.-smoothstep(-px,px,dist+px*.5);
|
|
10538
|
+
if(shape<=0.){gl_FragColor=vec4(0.);return;}
|
|
10539
|
+
vec3 col=interior(vec3(sdf*.5+.5,.5));
|
|
10540
|
+
gl_FragColor=vec4(col*shape,shape);
|
|
10541
|
+
}`;
|
|
10542
|
+
const parseColor = (text) => {
|
|
10543
|
+
const match = /(\d+(?:\.\d+)?)[ ,]+(\d+(?:\.\d+)?)[ ,]+(\d+(?:\.\d+)?)/u.exec(String(text));
|
|
10544
|
+
return match ? [
|
|
10545
|
+
Number(match[1]) / 255,
|
|
10546
|
+
Number(match[2]) / 255,
|
|
10547
|
+
Number(match[3]) / 255
|
|
10548
|
+
] : [
|
|
10549
|
+
.25,
|
|
10550
|
+
.46,
|
|
10551
|
+
.9
|
|
10552
|
+
];
|
|
10553
|
+
};
|
|
10554
|
+
const clamp = (value, low = 0, high = 1) => Math.min(Math.max(value, low), high);
|
|
10555
|
+
const smooth = (low, high, value) => {
|
|
10556
|
+
const t = clamp((value - low) / (high - low));
|
|
10557
|
+
return t * t * (3 - 2 * t);
|
|
10558
|
+
};
|
|
10559
|
+
const ease = (current, target, dt, attack, release) => current + (target - current) * (1 - Math.exp(-dt / (target > current ? attack : release)));
|
|
10560
|
+
const spring = (value, velocity, target, dt, frequency, damping) => {
|
|
10561
|
+
const omega = 2 * Math.PI * frequency;
|
|
10562
|
+
const steps = Math.max(1, Math.ceil(dt * 120));
|
|
10563
|
+
const h = dt / steps;
|
|
10564
|
+
let v = value;
|
|
10565
|
+
let w = velocity;
|
|
10566
|
+
for (let i = 0; i < steps; i += 1) {
|
|
10567
|
+
w += ((target - v) * omega * omega - w * 2 * damping * omega) * h;
|
|
10568
|
+
v += w * h;
|
|
10569
|
+
}
|
|
10570
|
+
return {
|
|
10571
|
+
value: v,
|
|
10572
|
+
velocity: w
|
|
10573
|
+
};
|
|
10574
|
+
};
|
|
10575
|
+
const TUNE = {
|
|
10576
|
+
fps: 24,
|
|
10577
|
+
startFrame: 350,
|
|
10578
|
+
surface: .34,
|
|
10579
|
+
fit: 1.4,
|
|
10580
|
+
speakSpeedScale: .56,
|
|
10581
|
+
speakSpeedBase: 1.8,
|
|
10582
|
+
speakSlowEnergy: 4.6,
|
|
10583
|
+
speakAmp: 1.5,
|
|
10584
|
+
minWaveSpeed: .72,
|
|
10585
|
+
edgeDistance: .102,
|
|
10586
|
+
edgeFrequency: 7,
|
|
10587
|
+
edgeDamping: .36,
|
|
10588
|
+
listenNoise: 2.2,
|
|
10589
|
+
flowScale: .96,
|
|
10590
|
+
driftScale: .1,
|
|
10591
|
+
windScale: .3,
|
|
10592
|
+
windSpeed: .65
|
|
10593
|
+
};
|
|
10594
|
+
const WIND = [
|
|
10595
|
+
.72,
|
|
10596
|
+
1,
|
|
10597
|
+
1.28
|
|
10598
|
+
];
|
|
10599
|
+
/** Returns a renderer for the canvas, or undefined when this browser cannot draw it. */
|
|
10600
|
+
function createOrb(canvas, accent, options = {}) {
|
|
10601
|
+
const gl = canvas.getContext("webgl", {
|
|
10602
|
+
alpha: true,
|
|
10603
|
+
premultipliedAlpha: true,
|
|
10604
|
+
antialias: false
|
|
10605
|
+
});
|
|
10606
|
+
if (!gl) return void 0;
|
|
10607
|
+
const compile = (type, source) => {
|
|
10608
|
+
const shader = gl.createShader(type);
|
|
10609
|
+
gl.shaderSource(shader, source);
|
|
10610
|
+
gl.compileShader(shader);
|
|
10611
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(shader) || "shader");
|
|
10612
|
+
return shader;
|
|
10613
|
+
};
|
|
10614
|
+
let program;
|
|
10615
|
+
try {
|
|
10616
|
+
program = gl.createProgram();
|
|
10617
|
+
gl.attachShader(program, compile(gl.VERTEX_SHADER, VERTEX));
|
|
10618
|
+
gl.attachShader(program, compile(gl.FRAGMENT_SHADER, FRAGMENT));
|
|
10619
|
+
gl.linkProgram(program);
|
|
10620
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) throw new Error("link");
|
|
10621
|
+
} catch (error) {
|
|
10622
|
+
options.onError?.(error);
|
|
10623
|
+
return;
|
|
10624
|
+
}
|
|
10625
|
+
gl.useProgram(program);
|
|
10626
|
+
const buffer = gl.createBuffer();
|
|
10627
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
10628
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
10629
|
+
-1,
|
|
10630
|
+
-1,
|
|
10631
|
+
1,
|
|
10632
|
+
-1,
|
|
10633
|
+
-1,
|
|
10634
|
+
1,
|
|
10635
|
+
1,
|
|
10636
|
+
1
|
|
10637
|
+
]), gl.STATIC_DRAW);
|
|
10638
|
+
const position = gl.getAttribLocation(program, "a");
|
|
10639
|
+
gl.enableVertexAttribArray(position);
|
|
10640
|
+
gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
|
|
10641
|
+
const at = (name) => gl.getUniformLocation(program, name);
|
|
10642
|
+
const u = {};
|
|
10643
|
+
for (const name of [
|
|
10644
|
+
"uRes",
|
|
10645
|
+
"uWaveFrame",
|
|
10646
|
+
"uBaseFrame",
|
|
10647
|
+
"uAmp",
|
|
10648
|
+
"uFlowFrame",
|
|
10649
|
+
"uEdge",
|
|
10650
|
+
"uNoiseScale",
|
|
10651
|
+
"uSurface",
|
|
10652
|
+
"uMic",
|
|
10653
|
+
"uUser",
|
|
10654
|
+
"uFlat",
|
|
10655
|
+
"uOff0",
|
|
10656
|
+
"uOff1",
|
|
10657
|
+
"uOff2",
|
|
10658
|
+
"uAccent"
|
|
10659
|
+
]) u[name] = at(name);
|
|
10660
|
+
const color = parseColor(accent);
|
|
10661
|
+
const flat = options.flat ? Number(options.flat) : 0;
|
|
10662
|
+
const s = {
|
|
10663
|
+
last: void 0,
|
|
10664
|
+
base: TUNE.startFrame,
|
|
10665
|
+
wave: TUNE.startFrame,
|
|
10666
|
+
flow: TUNE.startFrame,
|
|
10667
|
+
listen: 1,
|
|
10668
|
+
speak: 0,
|
|
10669
|
+
think: 0,
|
|
10670
|
+
level: 0,
|
|
10671
|
+
motion: 0,
|
|
10672
|
+
prevOverall: 0,
|
|
10673
|
+
accent: 0,
|
|
10674
|
+
cumulative: [
|
|
10675
|
+
0,
|
|
10676
|
+
0,
|
|
10677
|
+
0
|
|
10678
|
+
],
|
|
10679
|
+
drive: 0,
|
|
10680
|
+
boost: 0,
|
|
10681
|
+
boostV: 0,
|
|
10682
|
+
amp: 1,
|
|
10683
|
+
edge: 0,
|
|
10684
|
+
edgeV: 0,
|
|
10685
|
+
flowBoost: 0,
|
|
10686
|
+
colour: [
|
|
10687
|
+
0,
|
|
10688
|
+
0,
|
|
10689
|
+
0
|
|
10690
|
+
],
|
|
10691
|
+
phase: [
|
|
10692
|
+
0,
|
|
10693
|
+
2.1,
|
|
10694
|
+
4.2
|
|
10695
|
+
],
|
|
10696
|
+
user: 0,
|
|
10697
|
+
userOn: false,
|
|
10698
|
+
userFrom: 0,
|
|
10699
|
+
userAt: 0
|
|
10700
|
+
};
|
|
10701
|
+
return {
|
|
10702
|
+
/** Draw one frame. `now` in seconds; `input` = { mic, assistant, working, reduce } with levels 0..1. */
|
|
10703
|
+
draw(now, input) {
|
|
10704
|
+
const dt = s.last === void 0 ? .016 : clamp(now - s.last, 0, .1);
|
|
10705
|
+
s.last = now;
|
|
10706
|
+
const assistant = clamp(input.assistant ?? 0);
|
|
10707
|
+
const mic = clamp((input.mic ?? 0) * 1.75);
|
|
10708
|
+
const speaking = assistant > .05;
|
|
10709
|
+
const thinking = !speaking && !!input.working;
|
|
10710
|
+
const pace = input.reduce ? .5 : 1;
|
|
10711
|
+
const target = speaking ? {
|
|
10712
|
+
listen: .2,
|
|
10713
|
+
speak: 1,
|
|
10714
|
+
think: 0
|
|
10715
|
+
} : thinking ? {
|
|
10716
|
+
listen: .65,
|
|
10717
|
+
speak: 0,
|
|
10718
|
+
think: 1
|
|
10719
|
+
} : {
|
|
10720
|
+
listen: 1,
|
|
10721
|
+
speak: 0,
|
|
10722
|
+
think: 0
|
|
10723
|
+
};
|
|
10724
|
+
const k = 1 - Math.exp(-dt / .28);
|
|
10725
|
+
s.listen += (target.listen - s.listen) * k;
|
|
10726
|
+
s.speak += (target.speak - s.speak) * k;
|
|
10727
|
+
s.think += (target.think - s.think) * k;
|
|
10728
|
+
const overall = speaking ? assistant : 0;
|
|
10729
|
+
s.motion += (overall - s.motion) * (1 - Math.exp(-dt / .2));
|
|
10730
|
+
s.accent = Math.max(overall - s.prevOverall, 0) * 3.6;
|
|
10731
|
+
s.prevOverall = overall;
|
|
10732
|
+
for (let i = 0; i < 3; i += 1) s.cumulative[i] += overall * (i === 0 ? .82 : i === 1 ? .76 : .68) * dt * 60 * 40 * (1 - Math.exp(-dt / 2));
|
|
10733
|
+
const energy = clamp(Math.max(...s.cumulative) / 12);
|
|
10734
|
+
const waveLevel = overall > .024 ? (1 - Math.exp(-overall * 4)) ** 1.15 : 0;
|
|
10735
|
+
s.drive = ease(s.drive, waveLevel, dt, .045, .14);
|
|
10736
|
+
const speedTarget = s.drive * TUNE.speakSpeedScale * (TUNE.speakSpeedBase + Math.max(s.motion, energy) * TUNE.speakSlowEnergy + s.accent * .5);
|
|
10737
|
+
const boost = spring(s.boost, s.boostV, speedTarget, dt, 6, .76);
|
|
10738
|
+
s.boost = boost.value;
|
|
10739
|
+
s.boostV = boost.velocity;
|
|
10740
|
+
const waveSpeed = Math.max(TUNE.minWaveSpeed, 1 + s.boost);
|
|
10741
|
+
s.amp = ease(s.amp, 1 - waveLevel * (1 - TUNE.speakAmp), dt, .1, .28);
|
|
10742
|
+
const userDrive = clamp(s.user) * clamp(input.mic ?? 0);
|
|
10743
|
+
const edge = spring(s.edge, s.edgeV, userDrive * TUNE.edgeDistance, dt, TUNE.edgeFrequency, TUNE.edgeDamping);
|
|
10744
|
+
s.edge = edge.value;
|
|
10745
|
+
s.edgeV = edge.velocity;
|
|
10746
|
+
const noiseScale = 1 + userDrive * (TUNE.listenNoise - 1);
|
|
10747
|
+
s.flowBoost = ease(s.flowBoost, Math.max(s.motion, energy) * TUNE.flowScale, dt, .055, .16);
|
|
10748
|
+
const talking = !speaking && !thinking && (s.userOn ? mic > .32 : mic >= .48);
|
|
10749
|
+
if (talking !== s.userOn) {
|
|
10750
|
+
s.userFrom = s.user;
|
|
10751
|
+
s.userOn = talking;
|
|
10752
|
+
s.userAt = now;
|
|
10753
|
+
}
|
|
10754
|
+
s.user = s.userFrom + ((s.userOn ? 1 : 0) - s.userFrom) * clamp((now - s.userAt) / .14);
|
|
10755
|
+
const offsets = [
|
|
10756
|
+
[0, 0],
|
|
10757
|
+
[0, 0],
|
|
10758
|
+
[0, 0]
|
|
10759
|
+
];
|
|
10760
|
+
const audio = s.cumulative.map((value) => clamp(value / 12));
|
|
10761
|
+
for (let i = 0; i < 3; i += 1) s.colour[i] = ease(s.colour[i], audio[i] * (speaking ? 1 : 0) * 1, dt, .07, .22);
|
|
10762
|
+
if (speaking) for (let i = 0; i < 3; i += 1) s.phase[i] += dt * TUNE.windSpeed * WIND[i] * (1 + s.colour[i] * 1.5);
|
|
10763
|
+
const [r, g, b] = s.colour;
|
|
10764
|
+
const [p0, p1, p2] = s.phase;
|
|
10765
|
+
const d = TUNE.driftScale;
|
|
10766
|
+
const w = TUNE.windScale;
|
|
10767
|
+
offsets[0] = [r * d + Math.sin(p0) * r * w, -g * d * .55 + Math.cos(p0 * .82) * g * w * .65];
|
|
10768
|
+
offsets[1] = [-g * d * .75 + Math.cos(p1 * .91) * g * w * .8, b * d + Math.sin(p1) * b * w];
|
|
10769
|
+
offsets[2] = [b * d * 1.1 + Math.sin(p2 * 1.07) * b * w * 1.1, r * d * .7 + Math.cos(p2) * r * w * .75];
|
|
10770
|
+
s.base += dt * pace * TUNE.fps;
|
|
10771
|
+
s.wave += dt * pace * TUNE.fps * waveSpeed;
|
|
10772
|
+
s.flow += dt * pace * TUNE.fps * (1 + s.flowBoost);
|
|
10773
|
+
s.level = ease(s.level, speaking ? assistant : 0, dt, .09, .3);
|
|
10774
|
+
const out = smooth(.04, .46, s.level);
|
|
10775
|
+
const breath = Math.sin(now * Math.PI * .34) * .5 + .5;
|
|
10776
|
+
const state = Math.max(s.listen, s.think, s.speak);
|
|
10777
|
+
let size = TUNE.surface * (.88 + .06 * s.think) * (.82 + .18 * smooth(0, .9, state));
|
|
10778
|
+
size += out * TUNE.surface * .12;
|
|
10779
|
+
size += (1 - s.think) * (1 - out) * breath * TUNE.surface * .01;
|
|
10780
|
+
const surface = Math.min(TUNE.surface, size) * 2 * TUNE.fit;
|
|
10781
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
10782
|
+
gl.clearColor(0, 0, 0, 0);
|
|
10783
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
10784
|
+
gl.uniform2f(u.uRes, canvas.width, canvas.height);
|
|
10785
|
+
gl.uniform1f(u.uWaveFrame, s.wave);
|
|
10786
|
+
gl.uniform1f(u.uBaseFrame, s.base);
|
|
10787
|
+
gl.uniform1f(u.uAmp, s.amp);
|
|
10788
|
+
gl.uniform1f(u.uFlowFrame, s.flow);
|
|
10789
|
+
gl.uniform1f(u.uEdge, s.edge);
|
|
10790
|
+
gl.uniform1f(u.uNoiseScale, noiseScale);
|
|
10791
|
+
gl.uniform1f(u.uSurface, surface);
|
|
10792
|
+
gl.uniform1f(u.uMic, mic);
|
|
10793
|
+
gl.uniform1f(u.uUser, s.user);
|
|
10794
|
+
gl.uniform1f(u.uFlat, flat);
|
|
10795
|
+
gl.uniform2f(u.uOff0, offsets[0][0], offsets[0][1]);
|
|
10796
|
+
gl.uniform2f(u.uOff1, offsets[1][0], offsets[1][1]);
|
|
10797
|
+
gl.uniform2f(u.uOff2, offsets[2][0], offsets[2][1]);
|
|
10798
|
+
gl.uniform3f(u.uAccent, color[0], color[1], color[2]);
|
|
10799
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
10800
|
+
},
|
|
10801
|
+
dispose() {
|
|
10802
|
+
try {
|
|
10803
|
+
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
|
10804
|
+
} catch {}
|
|
10805
|
+
}
|
|
10806
|
+
};
|
|
10807
|
+
}
|
|
10808
|
+
//#endregion
|
|
10809
|
+
//#region src/client-voice-call.jsx
|
|
10810
|
+
const Bars = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
10811
|
+
width: "20",
|
|
10812
|
+
height: "20",
|
|
10813
|
+
viewBox: "0 0 20 20",
|
|
10814
|
+
fill: "none",
|
|
10815
|
+
"aria-hidden": "true",
|
|
10816
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
10817
|
+
d: "M4 8.5v3M7 5.5v9M10 3v14M13 6v8M16 8.5v3",
|
|
10818
|
+
stroke: "currentColor",
|
|
10819
|
+
strokeWidth: "2.2",
|
|
10820
|
+
strokeLinecap: "round"
|
|
10821
|
+
})
|
|
10822
|
+
});
|
|
10823
|
+
const Cross = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
10824
|
+
width: "14",
|
|
10825
|
+
height: "14",
|
|
10826
|
+
viewBox: "0 0 14 14",
|
|
10827
|
+
fill: "none",
|
|
10828
|
+
"aria-hidden": "true",
|
|
10829
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
10830
|
+
d: "M3 3l8 8M11 3l-8 8",
|
|
10831
|
+
stroke: "currentColor",
|
|
10832
|
+
strokeWidth: "2",
|
|
10833
|
+
strokeLinecap: "round"
|
|
10834
|
+
})
|
|
10835
|
+
});
|
|
10836
|
+
const Mic = ({ off }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
10837
|
+
width: "18",
|
|
10838
|
+
height: "18",
|
|
10839
|
+
viewBox: "0 0 18 18",
|
|
10840
|
+
fill: "none",
|
|
10841
|
+
"aria-hidden": "true",
|
|
10842
|
+
children: [
|
|
10843
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
10844
|
+
x: "6.2",
|
|
10845
|
+
y: "1.8",
|
|
10846
|
+
width: "5.6",
|
|
10847
|
+
height: "9",
|
|
10848
|
+
rx: "2.8",
|
|
10849
|
+
stroke: "currentColor",
|
|
10850
|
+
strokeWidth: "1.5"
|
|
10851
|
+
}),
|
|
10852
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
10853
|
+
d: "M3.5 8.4a5.5 5.5 0 0 0 11 0M9 13.9v2.3",
|
|
10854
|
+
stroke: "currentColor",
|
|
10855
|
+
strokeWidth: "1.5",
|
|
10856
|
+
strokeLinecap: "round"
|
|
10857
|
+
}),
|
|
10858
|
+
off ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
10859
|
+
d: "M2.8 15.2 15.2 2.8",
|
|
10860
|
+
stroke: "currentColor",
|
|
10861
|
+
strokeWidth: "1.7",
|
|
10862
|
+
strokeLinecap: "round"
|
|
10863
|
+
}) : null
|
|
10864
|
+
]
|
|
10865
|
+
});
|
|
10866
|
+
const Speaker = ({ off }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
10867
|
+
width: "18",
|
|
10868
|
+
height: "18",
|
|
10869
|
+
viewBox: "0 0 18 18",
|
|
10870
|
+
fill: "none",
|
|
10871
|
+
"aria-hidden": "true",
|
|
10872
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
10873
|
+
d: "M3 7h2.6L9.5 3.8v10.4L5.6 11H3z",
|
|
10874
|
+
stroke: "currentColor",
|
|
10875
|
+
strokeWidth: "1.5",
|
|
10876
|
+
strokeLinejoin: "round"
|
|
10877
|
+
}), off ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
10878
|
+
d: "M12.6 6.6l3.2 4.8M15.8 6.6l-3.2 4.8",
|
|
10879
|
+
stroke: "currentColor",
|
|
10880
|
+
strokeWidth: "1.5",
|
|
10881
|
+
strokeLinecap: "round"
|
|
10882
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
10883
|
+
d: "M12.3 6.5a3.6 3.6 0 0 1 0 5M14.4 4.6a6.4 6.4 0 0 1 0 8.8",
|
|
10884
|
+
stroke: "currentColor",
|
|
10885
|
+
strokeWidth: "1.5",
|
|
10886
|
+
strokeLinecap: "round"
|
|
10887
|
+
})]
|
|
10888
|
+
});
|
|
10889
|
+
/**
|
|
10890
|
+
* What a call looks like while it runs, drawn like Codex's own voice chat: above a composer that sits at the bottom of the
|
|
10891
|
+
* conversation (below it on the empty start page) there is a small stage with the last spoken lines, what DSH is doing
|
|
10892
|
+
* and the orb, which swells with the person's voice and with the model's. While the call connects, the orb is a pulsing dot.
|
|
10893
|
+
*/
|
|
10894
|
+
function VoiceStage({ call, connecting, rpc, sessionId, anchor, accent, t }) {
|
|
10895
|
+
const orb = (0, react.useRef)(null);
|
|
10896
|
+
const canvas = (0, react.useRef)(null);
|
|
10897
|
+
const working = (0, react.useRef)(false);
|
|
10898
|
+
const closing = (0, react.useRef)(false);
|
|
10899
|
+
const frame = (0, react.useRef)(null);
|
|
10900
|
+
const [status, setStatus] = (0, react.useState)({
|
|
10901
|
+
working: false,
|
|
10902
|
+
lines: []
|
|
10903
|
+
});
|
|
10904
|
+
working.current = status.working;
|
|
10905
|
+
closing.current = status.closing === true;
|
|
10906
|
+
const [mode, setMode] = (0, react.useState)("idle");
|
|
10907
|
+
const [box, setBox] = (0, react.useState)();
|
|
10908
|
+
(0, react.useEffect)(() => {
|
|
10909
|
+
let raf;
|
|
10910
|
+
const follow = () => {
|
|
10911
|
+
const rect = anchor.getBoundingClientRect();
|
|
10912
|
+
setBox((current) => current && Math.abs(current.left - rect.left) < .5 && Math.abs(current.top - rect.top) < .5 && Math.abs(current.width - rect.width) < .5 ? current : {
|
|
10913
|
+
left: rect.left,
|
|
10914
|
+
top: rect.top,
|
|
10915
|
+
bottom: rect.bottom,
|
|
10916
|
+
width: rect.width,
|
|
10917
|
+
docked: rect.bottom > window.innerHeight - 180
|
|
10918
|
+
});
|
|
10919
|
+
raf = requestAnimationFrame(follow);
|
|
10920
|
+
};
|
|
10921
|
+
follow();
|
|
10922
|
+
return () => cancelAnimationFrame(raf);
|
|
10923
|
+
}, [anchor]);
|
|
10924
|
+
(0, react.useEffect)(() => {
|
|
10925
|
+
if (!call) return void 0;
|
|
10926
|
+
const reduce = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
10927
|
+
const painter = canvas.current ? createOrb(canvas.current, accent) : void 0;
|
|
10928
|
+
const started = performance.now();
|
|
10929
|
+
let level = 0;
|
|
10930
|
+
let last = "";
|
|
10931
|
+
let closeSince = 0;
|
|
10932
|
+
let quietSince = 0;
|
|
10933
|
+
const tick = () => {
|
|
10934
|
+
const { mic, assistant } = call.levels();
|
|
10935
|
+
if (closing.current) {
|
|
10936
|
+
const now = performance.now();
|
|
10937
|
+
closeSince ||= now;
|
|
10938
|
+
if (assistant > .03) quietSince = 0;
|
|
10939
|
+
else quietSince ||= now;
|
|
10940
|
+
if (now - closeSince > 15e3 || now - closeSince > 1800 && now - quietSince > 1300) {
|
|
10941
|
+
closing.current = false;
|
|
10942
|
+
call.stop();
|
|
10943
|
+
return;
|
|
10944
|
+
}
|
|
10945
|
+
}
|
|
10946
|
+
const target = Math.max(mic, assistant);
|
|
10947
|
+
level += (target - level) * (target > level ? .35 : .12);
|
|
10948
|
+
orb.current?.style.setProperty("--level", reduce ? "0" : level.toFixed(3));
|
|
10949
|
+
painter?.draw((performance.now() - started) / 1e3, {
|
|
10950
|
+
mic,
|
|
10951
|
+
assistant,
|
|
10952
|
+
working: working.current,
|
|
10953
|
+
reduce
|
|
10954
|
+
});
|
|
10955
|
+
const next = assistant > .05 ? "speaking" : mic > .05 ? "listening" : "idle";
|
|
10956
|
+
if (next !== last) {
|
|
10957
|
+
last = next;
|
|
10958
|
+
setMode(next);
|
|
10959
|
+
}
|
|
10960
|
+
frame.current = requestAnimationFrame(tick);
|
|
10961
|
+
};
|
|
10962
|
+
tick();
|
|
10963
|
+
return () => {
|
|
10964
|
+
cancelAnimationFrame(frame.current);
|
|
10965
|
+
painter?.dispose();
|
|
10966
|
+
};
|
|
10967
|
+
}, [call]);
|
|
10968
|
+
(0, react.useEffect)(() => {
|
|
10969
|
+
if (!call) return void 0;
|
|
10970
|
+
let stopped = false;
|
|
10971
|
+
let seen = false;
|
|
10972
|
+
const poll = async () => {
|
|
10973
|
+
try {
|
|
10974
|
+
const result = await rpc.call(CHANNEL, "voice/status", { sessionId });
|
|
10975
|
+
if (!stopped && result.ok) {
|
|
10976
|
+
setStatus(result.value);
|
|
10977
|
+
if (result.value.active) seen = true;
|
|
10978
|
+
else if (seen) {
|
|
10979
|
+
stopped = true;
|
|
10980
|
+
call.stop();
|
|
10981
|
+
}
|
|
10982
|
+
}
|
|
10983
|
+
} catch {}
|
|
10984
|
+
};
|
|
10985
|
+
poll();
|
|
10986
|
+
const timer = setInterval(poll, 900);
|
|
10987
|
+
return () => {
|
|
10988
|
+
stopped = true;
|
|
10989
|
+
clearInterval(timer);
|
|
10990
|
+
};
|
|
10991
|
+
}, [
|
|
10992
|
+
call,
|
|
10993
|
+
rpc,
|
|
10994
|
+
sessionId
|
|
10995
|
+
]);
|
|
10996
|
+
if (!box) return null;
|
|
10997
|
+
const label = connecting ? t("voiceConnecting") : status.working ? t("voiceWorking") : mode === "speaking" ? t("voiceSpeaking") : t("voiceListening");
|
|
10998
|
+
const lines = status.lines.slice(-2);
|
|
10999
|
+
return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11000
|
+
className: "codexVoiceStage",
|
|
11001
|
+
role: "status",
|
|
11002
|
+
"aria-live": "polite",
|
|
11003
|
+
"data-side": box.docked ? "above" : "below",
|
|
11004
|
+
style: {
|
|
11005
|
+
left: box.left + box.width / 2,
|
|
11006
|
+
top: box.docked ? box.top : box.bottom,
|
|
11007
|
+
"--voice-accent": accent
|
|
11008
|
+
},
|
|
11009
|
+
children: [
|
|
11010
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11011
|
+
className: "codexVoiceLines",
|
|
11012
|
+
children: lines.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
11013
|
+
"data-role": line.role,
|
|
11014
|
+
"data-old": index < lines.length - 1 || void 0,
|
|
11015
|
+
children: line.text
|
|
11016
|
+
}, `${index}-${line.text.slice(0, 12)}`))
|
|
11017
|
+
}),
|
|
11018
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11019
|
+
className: "codexVoiceStatus",
|
|
11020
|
+
children: label
|
|
11021
|
+
}),
|
|
11022
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11023
|
+
className: "codexVoiceOrbBox",
|
|
11024
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11025
|
+
className: "codexVoiceDot",
|
|
11026
|
+
"data-gone": !connecting || void 0
|
|
11027
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11028
|
+
className: "codexVoiceOrb",
|
|
11029
|
+
ref: orb,
|
|
11030
|
+
"data-ready": !connecting,
|
|
11031
|
+
"data-mode": status.working ? "working" : mode,
|
|
11032
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("canvas", {
|
|
11033
|
+
ref: canvas,
|
|
11034
|
+
width: 240,
|
|
11035
|
+
height: 240,
|
|
11036
|
+
"aria-hidden": "true"
|
|
11037
|
+
})
|
|
11038
|
+
})]
|
|
11039
|
+
})
|
|
11040
|
+
]
|
|
11041
|
+
}), document.body);
|
|
11042
|
+
}
|
|
11043
|
+
/**
|
|
11044
|
+
* The composer's voice button. It sits exactly on the send button while there is nothing to send (the send button is
|
|
11045
|
+
* disabled) and gives it back as soon as there is text, so the one place always does the likely thing. During a call
|
|
11046
|
+
* it becomes the end button and, where DSH's dictation button is, a speaker and a microphone switch appear.
|
|
11047
|
+
*/
|
|
11048
|
+
function VoiceCallButton({ sessionId, rpc, preference, t }) {
|
|
11049
|
+
const snapshot = usePreferenceSnapshot(preference);
|
|
11050
|
+
const marker = (0, react.useRef)(null);
|
|
11051
|
+
const call = (0, react.useRef)(null);
|
|
11052
|
+
const [liveCall, setLiveCall] = (0, react.useState)(null);
|
|
11053
|
+
const accent = (0, react.useRef)("");
|
|
11054
|
+
const button = (0, react.useRef)(null);
|
|
11055
|
+
const [place, setPlace] = (0, react.useState)();
|
|
11056
|
+
const [state, setState] = (0, react.useState)("idle");
|
|
11057
|
+
const [problem, setProblem] = (0, react.useState)("");
|
|
11058
|
+
const [micOff, setMicOff] = (0, react.useState)(false);
|
|
11059
|
+
const [speakerOff, setSpeakerOff] = (0, react.useState)(false);
|
|
11060
|
+
const enabled = snapshot.status === "ready" && snapshot.voiceCall === "on";
|
|
11061
|
+
(0, react.useLayoutEffect)(() => {
|
|
11062
|
+
if (!enabled) {
|
|
11063
|
+
setPlace(void 0);
|
|
11064
|
+
return;
|
|
11065
|
+
}
|
|
11066
|
+
const trailing = marker.current?.closest("[data-slot]")?.parentElement?.parentElement;
|
|
11067
|
+
if (!trailing) return void 0;
|
|
11068
|
+
const wasStatic = getComputedStyle(trailing).position === "static";
|
|
11069
|
+
if (wasStatic) trailing.style.position = "relative";
|
|
11070
|
+
const watcher = new MutationObserver(() => measure());
|
|
11071
|
+
const measure = () => {
|
|
11072
|
+
const send = [...trailing.children].filter((child) => child.tagName === "BUTTON" && !child.classList.contains("codexVoiceCall")).at(-1);
|
|
11073
|
+
if (!send) {
|
|
11074
|
+
setPlace(void 0);
|
|
11075
|
+
return;
|
|
11076
|
+
}
|
|
11077
|
+
const host = trailing.getBoundingClientRect();
|
|
11078
|
+
const box = send.getBoundingClientRect();
|
|
11079
|
+
const ratio = window.devicePixelRatio || 1;
|
|
11080
|
+
const snap = (value) => Math.round(value * ratio) / ratio;
|
|
11081
|
+
setPlace({
|
|
11082
|
+
host: trailing,
|
|
11083
|
+
activity: trailing.querySelector("[data-slot=\"conversation.input.activity\"]")?.parentElement ?? null,
|
|
11084
|
+
left: snap(box.left - host.left - trailing.clientLeft),
|
|
11085
|
+
top: snap(box.top - host.top - trailing.clientTop),
|
|
11086
|
+
width: snap(box.width),
|
|
11087
|
+
height: snap(box.width),
|
|
11088
|
+
idle: send.disabled,
|
|
11089
|
+
className: send.className
|
|
11090
|
+
});
|
|
11091
|
+
};
|
|
11092
|
+
measure();
|
|
11093
|
+
watcher.observe(trailing, {
|
|
11094
|
+
subtree: true,
|
|
11095
|
+
childList: true,
|
|
11096
|
+
attributes: true,
|
|
11097
|
+
attributeFilter: [
|
|
11098
|
+
"disabled",
|
|
11099
|
+
"aria-label",
|
|
11100
|
+
"class"
|
|
11101
|
+
]
|
|
11102
|
+
});
|
|
11103
|
+
const resizer = new ResizeObserver(measure);
|
|
11104
|
+
resizer.observe(trailing);
|
|
11105
|
+
return () => {
|
|
11106
|
+
watcher.disconnect();
|
|
11107
|
+
resizer.disconnect();
|
|
11108
|
+
if (wasStatic) trailing.style.position = "";
|
|
11109
|
+
};
|
|
11110
|
+
}, [enabled]);
|
|
11111
|
+
const live = state === "live";
|
|
11112
|
+
(0, react.useEffect)(() => {
|
|
11113
|
+
const activity = place?.activity;
|
|
11114
|
+
if (!activity || !live) return void 0;
|
|
11115
|
+
activity.setAttribute("data-voice-live", "");
|
|
11116
|
+
return () => activity.removeAttribute("data-voice-live");
|
|
11117
|
+
}, [place?.activity, live]);
|
|
11118
|
+
(0, react.useEffect)(() => () => {
|
|
11119
|
+
call.current?.stop();
|
|
11120
|
+
}, []);
|
|
11121
|
+
(0, react.useEffect)(() => {
|
|
11122
|
+
if (!problem) return void 0;
|
|
11123
|
+
const timer = setTimeout(() => setProblem(""), 6e3);
|
|
11124
|
+
return () => clearTimeout(timer);
|
|
11125
|
+
}, [problem]);
|
|
11126
|
+
const start = async () => {
|
|
11127
|
+
setProblem("");
|
|
11128
|
+
accent.current = button.current ? getComputedStyle(button.current).backgroundColor : "";
|
|
11129
|
+
setMicOff(false);
|
|
11130
|
+
setSpeakerOff(false);
|
|
11131
|
+
setState("connecting");
|
|
11132
|
+
try {
|
|
11133
|
+
call.current = await openVoiceCall({
|
|
11134
|
+
rpc,
|
|
11135
|
+
sessionId,
|
|
11136
|
+
onEnd: () => {
|
|
11137
|
+
call.current = null;
|
|
11138
|
+
setLiveCall(null);
|
|
11139
|
+
setState("idle");
|
|
11140
|
+
}
|
|
11141
|
+
});
|
|
11142
|
+
setLiveCall(call.current);
|
|
11143
|
+
setState("live");
|
|
11144
|
+
} catch (error) {
|
|
11145
|
+
call.current = null;
|
|
11146
|
+
setLiveCall(null);
|
|
11147
|
+
setState("idle");
|
|
11148
|
+
setProblem(error?.name === "NotAllowedError" ? t("voiceMicBlocked") : t("voiceFailed"));
|
|
11149
|
+
}
|
|
11150
|
+
};
|
|
11151
|
+
const click = () => {
|
|
11152
|
+
if (state === "live") call.current?.stop();
|
|
11153
|
+
else if (state === "idle") start();
|
|
11154
|
+
};
|
|
11155
|
+
const anchor = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11156
|
+
ref: marker,
|
|
11157
|
+
hidden: true
|
|
11158
|
+
});
|
|
11159
|
+
if (!enabled || !place) return anchor;
|
|
11160
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
11161
|
+
anchor,
|
|
11162
|
+
state !== "idle" || place.idle ? (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11163
|
+
ref: button,
|
|
11164
|
+
type: "button",
|
|
11165
|
+
className: `${place.className} codexVoiceCall`,
|
|
11166
|
+
"data-state": state,
|
|
11167
|
+
onClick: click,
|
|
11168
|
+
disabled: state === "connecting",
|
|
11169
|
+
"aria-label": t(live ? "voiceEnd" : "voiceStart"),
|
|
11170
|
+
title: t(live ? "voiceEnd" : "voiceStart"),
|
|
11171
|
+
style: {
|
|
11172
|
+
left: place.left,
|
|
11173
|
+
top: place.top,
|
|
11174
|
+
width: place.width,
|
|
11175
|
+
height: place.height
|
|
11176
|
+
},
|
|
11177
|
+
children: live ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Cross, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bars, {})
|
|
11178
|
+
}), problem ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11179
|
+
className: "codexVoiceCallNote",
|
|
11180
|
+
role: "alert",
|
|
11181
|
+
style: { right: 0 },
|
|
11182
|
+
children: problem
|
|
11183
|
+
}) : null] }), place.host) : null,
|
|
11184
|
+
live && place.activity && liveCall ? (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
11185
|
+
className: "codexVoiceTools",
|
|
11186
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11187
|
+
type: "button",
|
|
11188
|
+
className: "codexVoiceTool",
|
|
11189
|
+
"aria-pressed": speakerOff,
|
|
11190
|
+
"aria-label": t(speakerOff ? "voiceUnmuteSpeaker" : "voiceMuteSpeaker"),
|
|
11191
|
+
title: t(speakerOff ? "voiceUnmuteSpeaker" : "voiceMuteSpeaker"),
|
|
11192
|
+
onClick: () => {
|
|
11193
|
+
liveCall.setOutputMuted(!speakerOff);
|
|
11194
|
+
setSpeakerOff(!speakerOff);
|
|
11195
|
+
},
|
|
11196
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Speaker, { off: speakerOff })
|
|
11197
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11198
|
+
type: "button",
|
|
11199
|
+
className: "codexVoiceTool",
|
|
11200
|
+
"aria-pressed": micOff,
|
|
11201
|
+
"aria-label": t(micOff ? "voiceUnmute" : "voiceMute"),
|
|
11202
|
+
title: t(micOff ? "voiceUnmute" : "voiceMute"),
|
|
11203
|
+
onClick: () => {
|
|
11204
|
+
liveCall.setMuted(!micOff);
|
|
11205
|
+
setMicOff(!micOff);
|
|
11206
|
+
},
|
|
11207
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Mic, { off: micOff })
|
|
11208
|
+
})]
|
|
11209
|
+
}), place.activity) : null,
|
|
11210
|
+
state !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VoiceStage, {
|
|
11211
|
+
call: liveCall,
|
|
11212
|
+
connecting: state === "connecting",
|
|
11213
|
+
rpc,
|
|
11214
|
+
sessionId,
|
|
11215
|
+
anchor: place.host.parentElement?.parentElement ?? place.host,
|
|
11216
|
+
accent: accent.current,
|
|
11217
|
+
t
|
|
11218
|
+
}) : null
|
|
11219
|
+
] });
|
|
11220
|
+
}
|
|
11221
|
+
/** One settings row: the label on the left, a menu in the same style as the context selector on the right. */
|
|
11222
|
+
function ChoiceRow({ label, items, selectedId, disabled, onSelect, trailing = null }) {
|
|
11223
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
11224
|
+
const selected = items.flatMap((item) => [item, ...item.submenu ?? []]).find((item) => item.id === selectedId)?.label ?? items[0].label;
|
|
11225
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11226
|
+
className: "codexSubscriptionPreference",
|
|
11227
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11228
|
+
className: "codexSubscriptionPreferenceCopy",
|
|
11229
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11230
|
+
className: "codexSubscriptionPreferenceLabel",
|
|
11231
|
+
children: label
|
|
11232
|
+
})
|
|
11233
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11234
|
+
className: "codexVoiceChoice",
|
|
11235
|
+
children: [trailing, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Menu, {
|
|
11236
|
+
open,
|
|
11237
|
+
items,
|
|
11238
|
+
selectedId,
|
|
11239
|
+
onSelect: (value) => {
|
|
11240
|
+
setOpen(false);
|
|
11241
|
+
onSelect(value);
|
|
11242
|
+
},
|
|
11243
|
+
onClose: () => setOpen(false),
|
|
11244
|
+
align: "end",
|
|
11245
|
+
side: "bottom",
|
|
11246
|
+
portal: true,
|
|
11247
|
+
compact: true,
|
|
11248
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
11249
|
+
className: "codexSubscriptionContextTrigger",
|
|
11250
|
+
type: "button",
|
|
11251
|
+
"aria-label": label,
|
|
11252
|
+
"aria-haspopup": "menu",
|
|
11253
|
+
"aria-expanded": open,
|
|
11254
|
+
disabled,
|
|
11255
|
+
onClick: () => setOpen((value) => !value),
|
|
11256
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: selected }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, {})]
|
|
11257
|
+
})
|
|
11258
|
+
})]
|
|
11259
|
+
})]
|
|
11260
|
+
});
|
|
11261
|
+
}
|
|
11262
|
+
/** Settings for the composer's voice button: on or off, the voice (with a short sample to listen to) and the answer language. */
|
|
11263
|
+
function VoiceCallCard({ rpc, preference, snapshot, t }) {
|
|
11264
|
+
const audio = (0, react.useRef)();
|
|
11265
|
+
const alive = (0, react.useRef)(false);
|
|
11266
|
+
const [offered, setOffered] = (0, react.useState)();
|
|
11267
|
+
const [sample, setSample] = (0, react.useState)({
|
|
11268
|
+
state: "idle",
|
|
11269
|
+
note: ""
|
|
11270
|
+
});
|
|
11271
|
+
const on = snapshot.voiceCall === "on";
|
|
11272
|
+
(0, react.useEffect)(() => {
|
|
11273
|
+
alive.current = true;
|
|
11274
|
+
return () => {
|
|
11275
|
+
alive.current = false;
|
|
11276
|
+
audio.current?.pause();
|
|
11277
|
+
};
|
|
11278
|
+
}, []);
|
|
11279
|
+
(0, react.useEffect)(() => {
|
|
11280
|
+
if (!on) return;
|
|
11281
|
+
(async () => {
|
|
11282
|
+
try {
|
|
11283
|
+
const result = await rpc.call(CHANNEL, "voice/catalog", {});
|
|
11284
|
+
if (alive.current && result.ok) setOffered(result.value.voices);
|
|
11285
|
+
} catch {}
|
|
11286
|
+
})();
|
|
11287
|
+
}, [on]);
|
|
11288
|
+
/** Play a short clip of a voice; picking a voice plays it too, so it can be heard before it is used. */
|
|
11289
|
+
const playVoice = async (voice) => {
|
|
11290
|
+
audio.current?.pause();
|
|
11291
|
+
setSample({
|
|
11292
|
+
state: "loading",
|
|
11293
|
+
note: ""
|
|
11294
|
+
});
|
|
11295
|
+
try {
|
|
11296
|
+
const result = await rpc.call(CHANNEL, "voice/preview", { voice });
|
|
11297
|
+
if (!alive.current) return;
|
|
11298
|
+
if (!result.ok) {
|
|
11299
|
+
setSample({
|
|
11300
|
+
state: "missing",
|
|
11301
|
+
note: ""
|
|
11302
|
+
});
|
|
11303
|
+
return;
|
|
11304
|
+
}
|
|
11305
|
+
const clip = new Audio(result.value.audio);
|
|
11306
|
+
audio.current = clip;
|
|
11307
|
+
clip.onended = () => {
|
|
11308
|
+
if (alive.current) setSample((current) => ({
|
|
11309
|
+
...current,
|
|
11310
|
+
state: "idle"
|
|
11311
|
+
}));
|
|
11312
|
+
};
|
|
11313
|
+
setSample({
|
|
11314
|
+
state: "playing",
|
|
11315
|
+
note: [result.value.description, result.value.accent].filter(Boolean).join(" · ")
|
|
11316
|
+
});
|
|
11317
|
+
await clip.play();
|
|
11318
|
+
} catch {
|
|
11319
|
+
if (alive.current) setSample({
|
|
11320
|
+
state: "missing",
|
|
11321
|
+
note: ""
|
|
11322
|
+
});
|
|
11323
|
+
}
|
|
11324
|
+
};
|
|
11325
|
+
const listed = VOICE_VOICES.filter((value) => offered ? offered.includes(value) : VOICE_VOICES.indexOf(value) < 9);
|
|
11326
|
+
const others = VOICE_VOICES.filter((value) => !listed.includes(value));
|
|
11327
|
+
const voices = [...listed.map((value) => ({
|
|
11328
|
+
id: value,
|
|
11329
|
+
label: value
|
|
11330
|
+
})), ...others.length === 0 ? [] : [{
|
|
11331
|
+
type: "separator",
|
|
11332
|
+
id: "more-line"
|
|
11333
|
+
}, {
|
|
11334
|
+
id: "more",
|
|
11335
|
+
label: t("voiceMore"),
|
|
11336
|
+
submenu: others.map((value) => ({
|
|
11337
|
+
id: value,
|
|
11338
|
+
label: value
|
|
11339
|
+
}))
|
|
11340
|
+
}]];
|
|
11341
|
+
const languages = VOICE_LANGUAGES.map((value) => ({
|
|
11342
|
+
id: value,
|
|
11343
|
+
label: value === "auto" ? t("voiceAuto") : t(`voiceLang_${value}`)
|
|
11344
|
+
}));
|
|
11345
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
11346
|
+
className: "codexSubscriptionCard codexSubscriptionPreferencesCard",
|
|
11347
|
+
"aria-label": t("voiceTitle"),
|
|
11348
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11349
|
+
className: "codexSubscriptionPreference",
|
|
11350
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11351
|
+
className: "codexSubscriptionPreferenceCopy",
|
|
11352
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
11353
|
+
className: "codexSubscriptionPreferenceLabel",
|
|
11354
|
+
children: [
|
|
11355
|
+
t("voiceTitle"),
|
|
11356
|
+
" ",
|
|
11357
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "Beta" })
|
|
11358
|
+
]
|
|
11359
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
11360
|
+
t,
|
|
11361
|
+
children: t("voiceHint")
|
|
11362
|
+
})]
|
|
11363
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11364
|
+
className: "codexSubscriptionQuotaModes",
|
|
11365
|
+
role: "radiogroup",
|
|
11366
|
+
"aria-label": t("voiceTitle"),
|
|
11367
|
+
"aria-busy": snapshot.saving || void 0,
|
|
11368
|
+
children: ["off", "on"].map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
11369
|
+
className: "codexSubscriptionQuotaMode",
|
|
11370
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
11371
|
+
type: "radio",
|
|
11372
|
+
name: "codex-voice-call",
|
|
11373
|
+
checked: snapshot.voiceCall === value,
|
|
11374
|
+
disabled: !snapshot.writable,
|
|
11375
|
+
onChange: () => {
|
|
11376
|
+
preference.set({ [VOICE_CALL_FIELD]: value });
|
|
11377
|
+
}
|
|
11378
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`remote_${value}`) })]
|
|
11379
|
+
}, value))
|
|
11380
|
+
})]
|
|
11381
|
+
}), on ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
11382
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "codexSubscriptionDivider" }),
|
|
11383
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChoiceRow, {
|
|
11384
|
+
label: t("voiceVoice"),
|
|
11385
|
+
disabled: !snapshot.writable,
|
|
11386
|
+
selectedId: snapshot.voiceVoice,
|
|
11387
|
+
items: voices,
|
|
11388
|
+
onSelect: (value) => {
|
|
11389
|
+
if (value === "more") return;
|
|
11390
|
+
preference.set({ [VOICE_VOICE_FIELD]: value });
|
|
11391
|
+
playVoice(value);
|
|
11392
|
+
},
|
|
11393
|
+
trailing: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11394
|
+
className: "codexSubscriptionContextTrigger",
|
|
11395
|
+
type: "button",
|
|
11396
|
+
disabled: sample.state === "loading",
|
|
11397
|
+
onClick: () => {
|
|
11398
|
+
playVoice(snapshot.voiceVoice);
|
|
11399
|
+
},
|
|
11400
|
+
children: sample.state === "loading" ? t("voiceLoading") : t("voicePlay")
|
|
11401
|
+
})
|
|
11402
|
+
}),
|
|
11403
|
+
sample.state === "playing" && sample.note ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
11404
|
+
className: "codexSubscriptionHelp",
|
|
11405
|
+
role: "status",
|
|
11406
|
+
children: sample.note
|
|
11407
|
+
}) : null,
|
|
11408
|
+
sample.state === "missing" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
11409
|
+
className: "codexSubscriptionHelp",
|
|
11410
|
+
role: "status",
|
|
11411
|
+
children: t("voiceNoSample")
|
|
11412
|
+
}) : null,
|
|
11413
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChoiceRow, {
|
|
11414
|
+
label: t("voiceLanguage"),
|
|
11415
|
+
disabled: !snapshot.writable,
|
|
11416
|
+
selectedId: snapshot.voiceLanguage,
|
|
11417
|
+
items: languages,
|
|
11418
|
+
onSelect: (value) => {
|
|
11419
|
+
preference.set({ [VOICE_LANGUAGE_FIELD]: value });
|
|
11420
|
+
}
|
|
11421
|
+
})
|
|
11422
|
+
] }) : null]
|
|
11423
|
+
});
|
|
11424
|
+
}
|
|
11425
|
+
//#endregion
|
|
9982
11426
|
//#region src/image-setting-groups.js
|
|
9983
11427
|
const IMAGE_SETTING_GROUPS = Object.freeze({
|
|
9984
11428
|
imageCapability: ["imageGeneration", "imageEditing"],
|
|
@@ -9998,7 +11442,7 @@ window.__ModuleLoader__.load({
|
|
|
9998
11442
|
}
|
|
9999
11443
|
//#endregion
|
|
10000
11444
|
//#region src/image-preferences.jsx
|
|
10001
|
-
function ImageChoice({ label, hint, value, text, items, disabled, onSelect }) {
|
|
11445
|
+
function ImageChoice({ t, label, hint, value, text, items, disabled, onSelect }) {
|
|
10002
11446
|
const [open, setOpen] = (0, react.useState)(false);
|
|
10003
11447
|
const anchor = (0, react.useRef)(null);
|
|
10004
11448
|
(0, react.useEffect)(() => {
|
|
@@ -10017,8 +11461,8 @@ window.__ModuleLoader__.load({
|
|
|
10017
11461
|
className: "codexSubscriptionPreference codexImagePreference",
|
|
10018
11462
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10019
11463
|
className: "codexSubscriptionPreferenceCopy",
|
|
10020
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), hint ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
10021
|
-
|
|
11464
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), hint ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
11465
|
+
t,
|
|
10022
11466
|
children: hint
|
|
10023
11467
|
}) : null]
|
|
10024
11468
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Menu, {
|
|
@@ -10072,6 +11516,7 @@ window.__ModuleLoader__.load({
|
|
|
10072
11516
|
Object.keys(IMAGE_SETTING_GROUPS).filter((group) => group.startsWith("sketch") === sketch).map((group) => {
|
|
10073
11517
|
const value = imageGroupValue(snapshot, group);
|
|
10074
11518
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageChoice, {
|
|
11519
|
+
t,
|
|
10075
11520
|
label: t(sketch ? `${group}Setting` : group),
|
|
10076
11521
|
hint: t(`${group}Hint`),
|
|
10077
11522
|
value,
|
|
@@ -10089,6 +11534,7 @@ window.__ModuleLoader__.load({
|
|
|
10089
11534
|
!sketch ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10090
11535
|
className: "codexImageDefaultsGroup",
|
|
10091
11536
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageChoice, {
|
|
11537
|
+
t,
|
|
10092
11538
|
label: t("imageModel"),
|
|
10093
11539
|
value: snapshot.imageModel,
|
|
10094
11540
|
text: modelLabel(snapshot.imageModel),
|
|
@@ -10104,6 +11550,7 @@ window.__ModuleLoader__.load({
|
|
|
10104
11550
|
});
|
|
10105
11551
|
}
|
|
10106
11552
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageChoice, {
|
|
11553
|
+
t,
|
|
10107
11554
|
label: t("imageQuality"),
|
|
10108
11555
|
value: snapshot.imageQuality,
|
|
10109
11556
|
text: t(`imageQuality_${snapshot.imageQuality}`),
|
|
@@ -10117,8 +11564,9 @@ window.__ModuleLoader__.load({
|
|
|
10117
11564
|
}
|
|
10118
11565
|
})]
|
|
10119
11566
|
}) : null,
|
|
10120
|
-
!sketch && snapshot.imageModel.includes("2.5") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
10121
|
-
|
|
11567
|
+
!sketch && snapshot.imageModel.includes("2.5") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
11568
|
+
t,
|
|
11569
|
+
as: "p",
|
|
10122
11570
|
children: t("imageModelHint")
|
|
10123
11571
|
}) : null
|
|
10124
11572
|
]
|
|
@@ -10176,8 +11624,8 @@ window.__ModuleLoader__.load({
|
|
|
10176
11624
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10177
11625
|
className: "codexSubscriptionPreferenceLabel",
|
|
10178
11626
|
children: t("quotaAlerts")
|
|
10179
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
10180
|
-
|
|
11627
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
11628
|
+
t,
|
|
10181
11629
|
children: t("quotaAlertsHint")
|
|
10182
11630
|
})]
|
|
10183
11631
|
}), choices("quotaAlerts", QUOTA_ALERT_MODES)]
|
|
@@ -10211,8 +11659,9 @@ window.__ModuleLoader__.load({
|
|
|
10211
11659
|
children: t("searchMode")
|
|
10212
11660
|
}), choices("searchMode", SEARCH_MODES)]
|
|
10213
11661
|
}),
|
|
10214
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
10215
|
-
|
|
11662
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
11663
|
+
t,
|
|
11664
|
+
as: "p",
|
|
10216
11665
|
children: t("searchModeHint")
|
|
10217
11666
|
}),
|
|
10218
11667
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
@@ -10231,8 +11680,8 @@ window.__ModuleLoader__.load({
|
|
|
10231
11680
|
if (event.key === "Enter") event.currentTarget.blur();
|
|
10232
11681
|
}
|
|
10233
11682
|
}),
|
|
10234
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
10235
|
-
|
|
11683
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
11684
|
+
t,
|
|
10236
11685
|
children: t("searchDomainsHint")
|
|
10237
11686
|
})
|
|
10238
11687
|
]
|
|
@@ -10318,8 +11767,8 @@ window.__ModuleLoader__.load({
|
|
|
10318
11767
|
className: "codexSubscriptionPreferenceLabel",
|
|
10319
11768
|
children: t("defaultModelTitle")
|
|
10320
11769
|
}),
|
|
10321
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
10322
|
-
|
|
11770
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
11771
|
+
t,
|
|
10323
11772
|
children: hint
|
|
10324
11773
|
}),
|
|
10325
11774
|
snapshot.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -12685,6 +14134,7 @@ window.__ModuleLoader__.load({
|
|
|
12685
14134
|
const STATUS_KEYS = {
|
|
12686
14135
|
connected: "remoteConnected",
|
|
12687
14136
|
connecting: "remoteConnecting",
|
|
14137
|
+
waiting: "remoteWaiting",
|
|
12688
14138
|
error: "remoteError",
|
|
12689
14139
|
stopped: "remoteStopped"
|
|
12690
14140
|
};
|
|
@@ -12838,8 +14288,8 @@ window.__ModuleLoader__.load({
|
|
|
12838
14288
|
" ",
|
|
12839
14289
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "Beta" })
|
|
12840
14290
|
]
|
|
12841
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
12842
|
-
|
|
14291
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
14292
|
+
t,
|
|
12843
14293
|
children: t("remoteHint")
|
|
12844
14294
|
})]
|
|
12845
14295
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -12867,9 +14317,9 @@ window.__ModuleLoader__.load({
|
|
|
12867
14317
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
12868
14318
|
role: "status",
|
|
12869
14319
|
children: [
|
|
12870
|
-
t(STATUS_KEYS[status] ?? "remoteConnecting"),
|
|
14320
|
+
status === "connected" && state?.phones > 0 ? t("remotePhones").replace("{count}", String(state.phones)) : t(STATUS_KEYS[status] ?? "remoteConnecting"),
|
|
12871
14321
|
status === "error" && state?.lastError ? ` · ${state.lastError}` : "",
|
|
12872
|
-
state?.phones > 0 ? ` · ${t("remotePhones").replace("{count}", String(state.phones))}` : ""
|
|
14322
|
+
status !== "connected" && state?.phones > 0 ? ` · ${t("remotePhones").replace("{count}", String(state.phones))}` : ""
|
|
12873
14323
|
]
|
|
12874
14324
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
12875
14325
|
type: "button",
|
|
@@ -12881,6 +14331,11 @@ window.__ModuleLoader__.load({
|
|
|
12881
14331
|
children: t(pairing ? "remotePairAgain" : "remotePair")
|
|
12882
14332
|
})]
|
|
12883
14333
|
}),
|
|
14334
|
+
status === "waiting" && state?.lastError === "conflict" && state?.reconnects >= 6 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
14335
|
+
role: "status",
|
|
14336
|
+
className: "codexSubscriptionHelp",
|
|
14337
|
+
children: t("remoteConflictLong")
|
|
14338
|
+
}) : null,
|
|
12884
14339
|
failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
12885
14340
|
role: "alert",
|
|
12886
14341
|
className: "codexSubscriptionHelp",
|
|
@@ -13001,8 +14456,8 @@ window.__ModuleLoader__.load({
|
|
|
13001
14456
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13002
14457
|
className: "codexSubscriptionPreferenceLabel",
|
|
13003
14458
|
children: t("quickQuotaSetting")
|
|
13004
|
-
}), snapshot.quickQuotaMode === "forecast" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13005
|
-
|
|
14459
|
+
}), snapshot.quickQuotaMode === "forecast" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
14460
|
+
t,
|
|
13006
14461
|
children: t("quickQuotaForecastHint")
|
|
13007
14462
|
}) : null]
|
|
13008
14463
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -13034,8 +14489,8 @@ window.__ModuleLoader__.load({
|
|
|
13034
14489
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13035
14490
|
className: "codexSubscriptionPreferenceLabel",
|
|
13036
14491
|
children: t("autoQuotaRetry")
|
|
13037
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13038
|
-
|
|
14492
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
14493
|
+
t,
|
|
13039
14494
|
children: t("autoQuotaRetryHint")
|
|
13040
14495
|
})]
|
|
13041
14496
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
|
|
@@ -13073,8 +14528,8 @@ window.__ModuleLoader__.load({
|
|
|
13073
14528
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13074
14529
|
className: "codexSubscriptionPreferenceLabel",
|
|
13075
14530
|
children: t("searchTitle")
|
|
13076
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13077
|
-
|
|
14531
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
14532
|
+
t,
|
|
13078
14533
|
children: t(hint)
|
|
13079
14534
|
})]
|
|
13080
14535
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -13106,8 +14561,8 @@ window.__ModuleLoader__.load({
|
|
|
13106
14561
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13107
14562
|
className: "codexSubscriptionPreferenceLabel",
|
|
13108
14563
|
children: t("inputImageDetail")
|
|
13109
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13110
|
-
|
|
14564
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
14565
|
+
t,
|
|
13111
14566
|
children: t("inputImageDetailHint")
|
|
13112
14567
|
})]
|
|
13113
14568
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -13141,8 +14596,8 @@ window.__ModuleLoader__.load({
|
|
|
13141
14596
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13142
14597
|
className: "codexSubscriptionPreferenceLabel",
|
|
13143
14598
|
children: t("reasoningSummary")
|
|
13144
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13145
|
-
|
|
14599
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
14600
|
+
t,
|
|
13146
14601
|
children: t("reasoningSummaryHint")
|
|
13147
14602
|
})]
|
|
13148
14603
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -13176,8 +14631,8 @@ window.__ModuleLoader__.load({
|
|
|
13176
14631
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13177
14632
|
className: "codexSubscriptionPreferenceLabel",
|
|
13178
14633
|
children: t("retryPolicy")
|
|
13179
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13180
|
-
|
|
14634
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
14635
|
+
t,
|
|
13181
14636
|
children: t("retryPolicyHint")
|
|
13182
14637
|
})]
|
|
13183
14638
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -13205,39 +14660,44 @@ window.__ModuleLoader__.load({
|
|
|
13205
14660
|
const snapshot = usePreferenceSnapshot(preference);
|
|
13206
14661
|
const writable = snapshot.status === "ready" && snapshot.writable === true;
|
|
13207
14662
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
13208
|
-
className: "codexSubscriptionPreference",
|
|
13209
|
-
children: [
|
|
13210
|
-
|
|
13211
|
-
|
|
13212
|
-
|
|
13213
|
-
|
|
13214
|
-
t("connectors")
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13218
|
-
|
|
13219
|
-
|
|
13220
|
-
|
|
13221
|
-
|
|
13222
|
-
|
|
13223
|
-
|
|
13224
|
-
|
|
13225
|
-
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13232
|
-
|
|
13233
|
-
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
13240
|
-
|
|
14663
|
+
className: "codexSubscriptionPreference codexSubscriptionConnectors",
|
|
14664
|
+
children: [
|
|
14665
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14666
|
+
className: "codexSubscriptionPreferenceCopy",
|
|
14667
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14668
|
+
className: "codexSubscriptionPreferenceLabel",
|
|
14669
|
+
children: t("connectors")
|
|
14670
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
14671
|
+
t,
|
|
14672
|
+
children: t("connectorsHint")
|
|
14673
|
+
})]
|
|
14674
|
+
}),
|
|
14675
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14676
|
+
className: "codexSubscriptionQuotaModes",
|
|
14677
|
+
"data-saving": snapshot.saving || void 0,
|
|
14678
|
+
"aria-busy": snapshot.saving || void 0,
|
|
14679
|
+
role: "radiogroup",
|
|
14680
|
+
"aria-label": t("connectors"),
|
|
14681
|
+
children: CONNECTOR_MODES.map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
14682
|
+
className: "codexSubscriptionQuotaMode",
|
|
14683
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
14684
|
+
type: "radio",
|
|
14685
|
+
name: "codex-connectors",
|
|
14686
|
+
checked: snapshot.connectors === value,
|
|
14687
|
+
disabled: !writable,
|
|
14688
|
+
onChange: () => {
|
|
14689
|
+
preference.set({ [CONNECTORS_FIELD]: value });
|
|
14690
|
+
}
|
|
14691
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`connectors_${value}`) })]
|
|
14692
|
+
}, value))
|
|
14693
|
+
}),
|
|
14694
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14695
|
+
className: "codexSubscriptionModeNote",
|
|
14696
|
+
"data-danger": snapshot.connectors === "write" || void 0,
|
|
14697
|
+
role: "status",
|
|
14698
|
+
children: t(`connectorsMode_${snapshot.connectors}`)
|
|
14699
|
+
})
|
|
14700
|
+
]
|
|
13241
14701
|
});
|
|
13242
14702
|
}
|
|
13243
14703
|
function StreamIdleTimeoutPreference({ preference, t }) {
|
|
@@ -13250,8 +14710,8 @@ window.__ModuleLoader__.load({
|
|
|
13250
14710
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13251
14711
|
className: "codexSubscriptionPreferenceLabel",
|
|
13252
14712
|
children: t("streamIdleTimeoutTitle")
|
|
13253
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13254
|
-
|
|
14713
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
14714
|
+
t,
|
|
13255
14715
|
children: t("streamIdleTimeoutHint")
|
|
13256
14716
|
})]
|
|
13257
14717
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -13317,8 +14777,8 @@ window.__ModuleLoader__.load({
|
|
|
13317
14777
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13318
14778
|
className: "codexSubscriptionPreferenceLabel",
|
|
13319
14779
|
children: t("pickerModelsTitle")
|
|
13320
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13321
|
-
|
|
14780
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
14781
|
+
t,
|
|
13322
14782
|
children: t("pickerModelsHint")
|
|
13323
14783
|
})]
|
|
13324
14784
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -13589,8 +15049,8 @@ window.__ModuleLoader__.load({
|
|
|
13589
15049
|
" ",
|
|
13590
15050
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "Beta" })
|
|
13591
15051
|
]
|
|
13592
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13593
|
-
|
|
15052
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
15053
|
+
t,
|
|
13594
15054
|
children: t("connectionHint")
|
|
13595
15055
|
})]
|
|
13596
15056
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -13633,8 +15093,8 @@ window.__ModuleLoader__.load({
|
|
|
13633
15093
|
" ",
|
|
13634
15094
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "Beta" })
|
|
13635
15095
|
]
|
|
13636
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13637
|
-
|
|
15096
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
15097
|
+
t,
|
|
13638
15098
|
children: t("reviewModelHint")
|
|
13639
15099
|
})]
|
|
13640
15100
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -13663,6 +15123,12 @@ window.__ModuleLoader__.load({
|
|
|
13663
15123
|
snapshot,
|
|
13664
15124
|
t
|
|
13665
15125
|
}),
|
|
15126
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(VoiceCallCard, {
|
|
15127
|
+
rpc,
|
|
15128
|
+
preference,
|
|
15129
|
+
snapshot,
|
|
15130
|
+
t
|
|
15131
|
+
}),
|
|
13666
15132
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
13667
15133
|
className: "codexSubscriptionCard codexSubscriptionPreferencesCard",
|
|
13668
15134
|
"aria-label": t("compactionTitle"),
|
|
@@ -13677,8 +15143,8 @@ window.__ModuleLoader__.load({
|
|
|
13677
15143
|
" ",
|
|
13678
15144
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "Beta" })
|
|
13679
15145
|
]
|
|
13680
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13681
|
-
|
|
15146
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
15147
|
+
t,
|
|
13682
15148
|
children: t("compactionHint")
|
|
13683
15149
|
})]
|
|
13684
15150
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -13715,8 +15181,8 @@ window.__ModuleLoader__.load({
|
|
|
13715
15181
|
" ",
|
|
13716
15182
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "Beta" })
|
|
13717
15183
|
]
|
|
13718
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
13719
|
-
|
|
15184
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
15185
|
+
t,
|
|
13720
15186
|
children: t(snapshot.subagentBackendAvailable ? "subagentBackendHint" : "subagentBackendUnavailable")
|
|
13721
15187
|
})]
|
|
13722
15188
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -14327,8 +15793,9 @@ window.__ModuleLoader__.load({
|
|
|
14327
15793
|
}) : null
|
|
14328
15794
|
]
|
|
14329
15795
|
}),
|
|
14330
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
14331
|
-
|
|
15796
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hint, {
|
|
15797
|
+
t,
|
|
15798
|
+
as: "p",
|
|
14332
15799
|
children: t(confirm ? "recoveryClearHint" : "recoveryHint")
|
|
14333
15800
|
}),
|
|
14334
15801
|
failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -14422,6 +15889,13 @@ window.__ModuleLoader__.load({
|
|
|
14422
15889
|
};
|
|
14423
15890
|
}
|
|
14424
15891
|
//#endregion
|
|
15892
|
+
//#region src/diagnostic-log-format.js
|
|
15893
|
+
function formatLogLine(entry) {
|
|
15894
|
+
const stamp = new Date(entry.time).toISOString().replace("T", " ").slice(0, 19);
|
|
15895
|
+
const repeat = entry.count > 1 ? ` (x${entry.count})` : "";
|
|
15896
|
+
return `${stamp} ${entry.level.toUpperCase().padEnd(5)} [${entry.area}] ${entry.message}${entry.detail ? ` — ${entry.detail}` : ""}${repeat}`;
|
|
15897
|
+
}
|
|
15898
|
+
//#endregion
|
|
14425
15899
|
//#region src/diagnostic-compact.js
|
|
14426
15900
|
function compactDiagnostic(report) {
|
|
14427
15901
|
const summary = report.summary ?? { findings: [] };
|
|
@@ -14452,17 +15926,29 @@ window.__ModuleLoader__.load({
|
|
|
14452
15926
|
reason: item.reason,
|
|
14453
15927
|
...item.latest ? { latest: `${item.latest.action} ${item.latest.status}` } : { latest: "not-verified" }
|
|
14454
15928
|
})),
|
|
15929
|
+
log: (report.log?.entries ?? []).slice(-150).map(formatLogLine),
|
|
14455
15930
|
...report.client?.status === "unknown" ? { hostCollected: false } : {}
|
|
14456
15931
|
};
|
|
14457
15932
|
}
|
|
14458
15933
|
//#endregion
|
|
14459
15934
|
//#region src/client-diagnostics.jsx
|
|
15935
|
+
const LEVELS = {
|
|
15936
|
+
all: [
|
|
15937
|
+
"info",
|
|
15938
|
+
"warn",
|
|
15939
|
+
"error"
|
|
15940
|
+
],
|
|
15941
|
+
warn: ["warn", "error"],
|
|
15942
|
+
error: ["error"]
|
|
15943
|
+
};
|
|
14460
15944
|
function DiagnosticsCard({ rpc, t, diagnostics }) {
|
|
14461
15945
|
const [report, setReport] = (0, react.useState)();
|
|
14462
15946
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
14463
15947
|
const [copied, setCopied] = (0, react.useState)(false);
|
|
14464
15948
|
const [error, setError] = (0, react.useState)(false);
|
|
14465
15949
|
const [copyError, setCopyError] = (0, react.useState)(false);
|
|
15950
|
+
const [level, setLevel] = (0, react.useState)("all");
|
|
15951
|
+
const [logCopied, setLogCopied] = (0, react.useState)(false);
|
|
14466
15952
|
const load = () => {
|
|
14467
15953
|
setBusy(true);
|
|
14468
15954
|
setError(false);
|
|
@@ -14485,6 +15971,10 @@ window.__ModuleLoader__.load({
|
|
|
14485
15971
|
setCopyError(false);
|
|
14486
15972
|
Promise.resolve().then(() => navigator.clipboard.writeText(JSON.stringify(full ? report : compactDiagnostic(report), null, 2))).then(() => setCopied(true)).catch(() => setCopyError(true));
|
|
14487
15973
|
};
|
|
15974
|
+
const lines = (report?.log?.entries ?? []).filter((entry) => LEVELS[level].includes(entry.level));
|
|
15975
|
+
const copyLog = () => {
|
|
15976
|
+
Promise.resolve().then(() => navigator.clipboard.writeText(lines.map(formatLogLine).join(String.fromCharCode(10)))).then(() => setLogCopied(true)).catch(() => setCopyError(true));
|
|
15977
|
+
};
|
|
14488
15978
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14489
15979
|
className: "codexSubscriptionCard codexSubscriptionDiagnostics",
|
|
14490
15980
|
children: [
|
|
@@ -14523,10 +16013,6 @@ window.__ModuleLoader__.load({
|
|
|
14523
16013
|
})]
|
|
14524
16014
|
}),
|
|
14525
16015
|
report === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
14526
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
14527
|
-
className: "codexSubscriptionHelp",
|
|
14528
|
-
children: t("diagnosticsCoverageHint")
|
|
14529
|
-
}),
|
|
14530
16016
|
report.generatedAt ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
14531
16017
|
className: "codexSubscriptionHelp",
|
|
14532
16018
|
children: [
|
|
@@ -14554,22 +16040,54 @@ window.__ModuleLoader__.load({
|
|
|
14554
16040
|
] }, `${event.source}:${event.action}`)) }) : null
|
|
14555
16041
|
] }, item.code)) }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("diagnosticsNoRecordedFailure") })
|
|
14556
16042
|
}),
|
|
14557
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
14558
|
-
|
|
14559
|
-
|
|
14560
|
-
|
|
14561
|
-
|
|
14562
|
-
|
|
14563
|
-
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
|
|
14572
|
-
|
|
16043
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16044
|
+
className: "codexSubscriptionLogHead",
|
|
16045
|
+
children: [
|
|
16046
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("diagnosticsLog") }),
|
|
16047
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16048
|
+
className: "codexSubscriptionQuotaModes",
|
|
16049
|
+
role: "radiogroup",
|
|
16050
|
+
"aria-label": t("diagnosticsLog"),
|
|
16051
|
+
children: Object.keys(LEVELS).map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
16052
|
+
className: "codexSubscriptionQuotaMode",
|
|
16053
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
16054
|
+
type: "radio",
|
|
16055
|
+
name: "codex-log-level",
|
|
16056
|
+
checked: level === value,
|
|
16057
|
+
onChange: () => {
|
|
16058
|
+
setLevel(value);
|
|
16059
|
+
setLogCopied(false);
|
|
16060
|
+
}
|
|
16061
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`logLevel_${value}`) })]
|
|
16062
|
+
}, value))
|
|
16063
|
+
}),
|
|
16064
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
16065
|
+
type: "button",
|
|
16066
|
+
variant: "outline",
|
|
16067
|
+
disabled: lines.length === 0,
|
|
16068
|
+
onClick: copyLog,
|
|
16069
|
+
children: logCopied ? t("logCopied") : t("logCopy")
|
|
16070
|
+
})
|
|
16071
|
+
]
|
|
16072
|
+
}),
|
|
16073
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
16074
|
+
className: "codexSubscriptionHelp",
|
|
16075
|
+
children: t("diagnosticsLogHint")
|
|
16076
|
+
}),
|
|
16077
|
+
lines.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
16078
|
+
className: "codexSubscriptionHelp",
|
|
16079
|
+
children: t("logEmpty")
|
|
16080
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
16081
|
+
className: "codexSubscriptionLog",
|
|
16082
|
+
tabIndex: 0,
|
|
16083
|
+
children: lines.slice().reverse().map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16084
|
+
className: `codexSubscriptionLogLine codexSubscriptionLog_${entry.level}`,
|
|
16085
|
+
children: [
|
|
16086
|
+
formatLogLine(entry),
|
|
16087
|
+
entry.earlier ? ` (${t("logEarlier")})` : "",
|
|
16088
|
+
String.fromCharCode(10)
|
|
16089
|
+
]
|
|
16090
|
+
}, entry.sequence))
|
|
14573
16091
|
}),
|
|
14574
16092
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
14575
16093
|
className: "codexSubscriptionHelp",
|
|
@@ -14577,6 +16095,28 @@ window.__ModuleLoader__.load({
|
|
|
14577
16095
|
}),
|
|
14578
16096
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [
|
|
14579
16097
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("diagnosticsDetails") }),
|
|
16098
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
16099
|
+
className: "codexSubscriptionHelp",
|
|
16100
|
+
children: t("diagnosticsCoverageHint")
|
|
16101
|
+
}),
|
|
16102
|
+
" ",
|
|
16103
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
16104
|
+
style: {
|
|
16105
|
+
width: "100%",
|
|
16106
|
+
fontSize: 12,
|
|
16107
|
+
textAlign: "left",
|
|
16108
|
+
borderSpacing: "0 8px"
|
|
16109
|
+
},
|
|
16110
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
16111
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("diagnosticsCapability") }),
|
|
16112
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("diagnosticsReadiness") }),
|
|
16113
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("diagnosticsEvidence") })
|
|
16114
|
+
] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: (report.inspection?.capabilities ?? []).map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
16115
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: t(`diagnosticCapability_${item.id}`) }),
|
|
16116
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [t(`diagnosticReason_${item.reason}`), item.blockedBy ? ` · ${t(`diagnosticCapability_${item.blockedBy}`)}` : ""] }),
|
|
16117
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [item.latest ? `${item.latest.action} · ${t(`diagnosticSource_${item.latest.source}`)} · ${t(`diagnosticOutcome_${item.latest.status}`)} · ${new Date(item.latest.observedAt).toLocaleTimeString(uiLocale())}` : t("diagnosticsUnverified"), item.unresolved?.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("diagnosticsUnresolved") }) : null] })
|
|
16118
|
+
] }, item.id)) })]
|
|
16119
|
+
}),
|
|
14580
16120
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
14581
16121
|
type: "button",
|
|
14582
16122
|
variant: "outline",
|
|
@@ -15769,6 +17309,7 @@ window.__ModuleLoader__.load({
|
|
|
15769
17309
|
"uiConversation",
|
|
15770
17310
|
"sessions"
|
|
15771
17311
|
];
|
|
17312
|
+
const ComposerRight = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexComposerQuota, { ...props }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VoiceCallButton, { ...props })] });
|
|
15772
17313
|
function apply(ctx) {
|
|
15773
17314
|
const diagnostics = createHostDiagnostics();
|
|
15774
17315
|
ctx.effect(() => diagnostics.register(ctx.slots), "codex-subscription: host inspection");
|
|
@@ -15846,12 +17387,13 @@ window.__ModuleLoader__.load({
|
|
|
15846
17387
|
order: 15,
|
|
15847
17388
|
locale: NS,
|
|
15848
17389
|
inject: (sessionId) => ({
|
|
17390
|
+
sessionId,
|
|
15849
17391
|
preference,
|
|
15850
17392
|
rpc,
|
|
15851
17393
|
t,
|
|
15852
17394
|
directory: modelDirectories.directoryFor(sessionId).store
|
|
15853
17395
|
})
|
|
15854
|
-
},
|
|
17396
|
+
}, ComposerRight));
|
|
15855
17397
|
scope.slots.inject("conversation.input.model", () => scope.slots.register({
|
|
15856
17398
|
name: "conversation.input.model",
|
|
15857
17399
|
priority: -10,
|