@walkhi/code-relax 0.1.0-beta.1 → 0.1.0-beta.10
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.md +14 -15
- package/dist/bin/codex-remote.mjs +81 -26
- package/dist/bin/self-relay-admin.mjs +8 -0
- package/dist/bin/self-relay-demo.mjs +1 -1
- package/dist/bin/self-relay-server.mjs +5 -2
- package/dist/shared/app-server-events.cjs +50 -22
- package/dist/shared/p2p-data-channel.cjs +50 -0
- package/dist/skills/code-relax/SKILL.md +1 -1
- package/dist/src/desktop-launcher.mjs +11 -11
- package/dist/src/lan-socket-server.mjs +8 -2
- package/dist/src/managed-app-server.mjs +27 -7
- package/dist/src/message-images.mjs +0 -18
- package/dist/src/onboarding.mjs +3 -5
- package/dist/src/platform/README.md +1 -1
- package/dist/src/platform/windows/IsolatedProcess.cs +7 -3
- package/dist/src/platform/windows/background-process.mjs +7 -2
- package/dist/src/platform/windows/codex-runtime.mjs +117 -0
- package/dist/src/platform/windows/desktop-host.mjs +16 -1
- package/dist/src/platform/windows/desktop-info.ps1 +2 -4
- package/dist/src/platform/windows/desktop-shortcut-run.ps1 +1 -1
- package/dist/src/platform/windows/desktop-shortcuts.ps1 +1 -1
- package/dist/src/platform/windows/launch-worker.mjs +42 -0
- package/dist/src/platform/windows/resident-startup.ps1 +58 -18
- package/dist/src/platform/windows/service-host.mjs +4 -7
- package/dist/src/platform/windows/start-hidden-console.ps1 +18 -8
- package/dist/src/platform/windows/stop-desktop.ps1 +7 -4
- package/dist/src/runtime-discovery.mjs +43 -0
- package/dist/src/self-relay/admin-cloud.mjs +101 -0
- package/dist/src/self-relay/admin-page.mjs +71 -0
- package/dist/src/self-relay/admin-server.mjs +61 -0
- package/dist/src/self-relay/admin-state.mjs +122 -0
- package/dist/src/self-relay/client.mjs +2 -0
- package/dist/src/self-relay/connector.mjs +23 -8
- package/dist/src/self-relay/demo.mjs +4 -5
- package/dist/src/self-relay/lifecycle.mjs +53 -49
- package/dist/src/self-relay/p2p-probe.mjs +123 -83
- package/dist/src/self-relay/server.mjs +157 -17
- package/dist/src/server.mjs +484 -1274
- package/dist/src/service-doctor.mjs +13 -25
- package/dist/src/service-lifecycle.mjs +12 -12
- package/dist/src/shared-app-server.mjs +192 -34
- package/dist/src/shared-catalog.mjs +4 -14
- package/dist/src/shared-recovery.mjs +8 -6
- package/dist/src/shared-thread-stream.mjs +1 -1
- package/dist/web/activity-view.js +283 -0
- package/dist/web/capabilities.js +2 -9
- package/dist/web/chat-transport.js +21 -21
- package/dist/web/chat.css +148 -106
- package/dist/web/chat.js +1454 -2788
- package/dist/web/community.css +82 -0
- package/dist/web/community.html +36 -0
- package/dist/web/composer-controller.js +97 -0
- package/dist/web/conversation-controller.js +99 -0
- package/dist/web/disclosure-state-controller.js +95 -0
- package/dist/web/draft-controller.js +103 -0
- package/dist/web/harmony-platform.js +3 -2
- package/dist/web/history-cache.js +112 -18
- package/dist/web/history-controller.js +167 -0
- package/dist/web/index.html +113 -47
- package/dist/web/link-action-controller.js +212 -0
- package/dist/web/message-send-controller.js +177 -0
- package/dist/web/message-view.js +98 -0
- package/dist/web/p2p-data-channel.js +50 -0
- package/dist/web/p2p-probe.js +67 -27
- package/dist/web/page-resume.js +28 -0
- package/dist/web/pending-message-store.js +108 -0
- package/dist/web/queue-controller.js +82 -0
- package/dist/web/resources.json +1 -1
- package/dist/web/self-relay-session.js +28 -18
- package/dist/web/station-connection-controller.js +75 -0
- package/dist/web/task-list-view.js +289 -0
- package/dist/web/thread-attention-controller.js +124 -0
- package/dist/web/thread-context-controller.js +30 -0
- package/dist/web/thread-list-controller.js +61 -0
- package/dist/web/thread-list-sync.js +86 -0
- package/dist/web/thread-title-controller.js +57 -0
- package/dist/web/timeline-formatters.js +249 -0
- package/dist/web/timeline-reducer.js +77 -0
- package/dist/web/timeline-renderer.js +161 -0
- package/dist/web/timeline-scroll-controller.js +50 -0
- package/dist/web/usage-controller.js +258 -0
- package/dist/web/vendor/lucide.LICENSE.txt +17 -0
- package/package.json +16 -18
- package/tools/postinstall.mjs +140 -3
- package/dist/src/app-server-client.mjs +0 -468
- package/dist/src/app-server-tasks.mjs +0 -355
- package/dist/src/platform/windows/desktop-monitor.mjs +0 -34
- package/dist/src/platform/windows/desktop-tools.mjs +0 -48
- package/dist/src/platform/windows/launch-worker.ps1 +0 -13
- package/dist/src/thread-catalog.mjs +0 -42
- package/dist/tools/find-desktop-pipe.mjs +0 -10
package/dist/web/index.html
CHANGED
|
@@ -7,59 +7,101 @@
|
|
|
7
7
|
<style>
|
|
8
8
|
#pageLoading { display:none; }
|
|
9
9
|
body.page-loading > :not(#pageLoading) { display:none !important; }
|
|
10
|
-
body.page-loading #pageLoading {
|
|
11
|
-
body.page-loading.silent-page-loading #pageLoading { visibility:hidden; }
|
|
10
|
+
body.page-loading #pageLoading { visibility:hidden; }
|
|
12
11
|
</style>
|
|
13
12
|
<link rel="stylesheet" href="chat.css">
|
|
14
13
|
</head>
|
|
15
14
|
<body class="page-loading">
|
|
16
|
-
<div id="pageLoading"
|
|
17
|
-
<section id="connectionEntry" role="region" aria-label="连接工作站">
|
|
18
|
-
<div class="connection-entry-
|
|
19
|
-
<div class="entry-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
<div
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<
|
|
15
|
+
<div id="pageLoading" aria-hidden="true"></div>
|
|
16
|
+
<section id="connectionEntry" role="region" aria-label="连接工作站">
|
|
17
|
+
<div class="connection-entry-page">
|
|
18
|
+
<div class="connection-entry-header">
|
|
19
|
+
<button id="entryBack" class="connection-entry-back" type="button" aria-label="返回工作站详情">
|
|
20
|
+
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
|
|
21
|
+
</button>
|
|
22
|
+
<h1 id="entryTitle">连接工作站</h1>
|
|
23
|
+
<span class="connection-entry-header-spacer" aria-hidden="true"></span>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="connection-entry-content">
|
|
26
|
+
<div class="connection-entry-card">
|
|
27
|
+
<p class="entry-connection-mode"><span>连接方式</span><strong id="entryMode">局域网</strong></p>
|
|
28
|
+
<div id="entryDiagram" class="entry-diagram" data-mode="lan" role="img" aria-label="移动端通过局域网双向连接工作站">
|
|
29
|
+
<div class="entry-diagram-node">
|
|
30
|
+
<svg viewBox="0 0 40 40" aria-hidden="true"><rect x="10" y="3" width="20" height="34" rx="4"/><path d="M17 7h6M18 33h4"/></svg>
|
|
31
|
+
<span>移动端</span>
|
|
32
|
+
</div>
|
|
33
|
+
<svg class="entry-diagram-link" viewBox="0 0 40 16" aria-hidden="true"><path d="M3 8h34M8 3 3 8l5 5M32 3l5 5-5 5"/></svg>
|
|
34
|
+
<div class="entry-diagram-node entry-diagram-middle">
|
|
35
|
+
<svg data-route="lan" viewBox="0 0 40 40" aria-hidden="true"><rect x="4" y="26" width="32" height="10" rx="3"/><path d="M10 26v-7m20 7v-7M12 12a12 12 0 0 1 16 0M16 17a6 6 0 0 1 8 0M10 31h2m5 0h2"/><circle cx="20" cy="22" r="1"/></svg>
|
|
36
|
+
<svg data-route="private" viewBox="0 0 40 40" aria-hidden="true"><rect x="8" y="4" width="24" height="32" rx="3"/><path d="M8 15h24M8 25h24M13 10h1m5 0h8M13 20h1m5 0h8M13 30h1m5 0h8"/></svg>
|
|
37
|
+
<svg data-route="official" viewBox="0 0 40 40" aria-hidden="true"><path d="M11 31h19a8 8 0 0 0 1-16 11 11 0 0 0-21-1 8.5 8.5 0 0 0 1 17Z"/><path d="M15 21h10m-7-3-3 3 3 3m4-6 3 3-3 3"/></svg>
|
|
38
|
+
<span id="entryDiagramMode">同一局域网</span>
|
|
39
|
+
</div>
|
|
40
|
+
<svg class="entry-diagram-link" viewBox="0 0 40 16" aria-hidden="true"><path d="M3 8h34M8 3 3 8l5 5M32 3l5 5-5 5"/></svg>
|
|
41
|
+
<div class="entry-diagram-node">
|
|
42
|
+
<svg viewBox="0 0 40 40" aria-hidden="true"><rect x="3" y="5" width="34" height="24" rx="3"/><path d="M3 24h34M16 29v6m8-6v6M12 35h16"/></svg>
|
|
43
|
+
<span>工作站</span>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
<ol id="entrySteps" class="entry-steps" data-mode="lan" aria-label="连接阶段" aria-live="polite">
|
|
47
|
+
<li data-number="1" data-state="current" aria-current="step">正在检查局域网</li>
|
|
48
|
+
<li data-number="2" data-state="pending">确认设备授权</li>
|
|
49
|
+
<li data-number="3" data-state="pending">连接工作站</li>
|
|
50
|
+
</ol>
|
|
51
|
+
<div class="connection-entry-actions">
|
|
52
|
+
<button id="entryRetry" type="button" hidden>重新连接</button>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</section>
|
|
58
|
+
<script>
|
|
59
|
+
setTimeout(() => {
|
|
60
|
+
if (window.codexEntryStarted) return;
|
|
61
|
+
const step = document.querySelector('#entrySteps [data-state="current"]');
|
|
62
|
+
if (!step) return;
|
|
63
|
+
step.dataset.state = 'error';
|
|
64
|
+
step.removeAttribute('aria-current');
|
|
65
|
+
step.textContent = '连接页面未完成初始化';
|
|
66
|
+
const detail = document.createElement('span');
|
|
67
|
+
detail.className = 'entry-step-detail'; detail.textContent = '请返回工作站详情后重试。';
|
|
68
|
+
step.appendChild(detail);
|
|
69
|
+
}, 10000);
|
|
70
|
+
</script>
|
|
71
|
+
<header>
|
|
37
72
|
<h1>Code Relax</h1>
|
|
38
73
|
<div class="sub">直接连接当前 Codex Desktop · MVP 0.22</div>
|
|
39
74
|
<div class="status"><span id="dot" class="dot"></span><span id="status">正在连接…</span></div>
|
|
40
75
|
</header>
|
|
41
|
-
<main>
|
|
42
|
-
<div id="actionToast" role="status" hidden></div>
|
|
43
|
-
<button id="sidebarBackdrop" aria-label="关闭聊天菜单" type="button"></button>
|
|
44
|
-
<
|
|
76
|
+
<main>
|
|
77
|
+
<div id="actionToast" role="status" hidden></div>
|
|
78
|
+
<button id="sidebarBackdrop" aria-label="关闭聊天菜单" type="button"></button>
|
|
79
|
+
<div class="terminal-chat-header">
|
|
80
|
+
<button id="terminalSidebarToggle" type="button" aria-label="打开聊天菜单" title="打开聊天菜单"><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/></svg></button>
|
|
81
|
+
<span id="terminalChatTitle">Code Relax</span>
|
|
82
|
+
</div>
|
|
83
|
+
<aside>
|
|
45
84
|
<div class="sidebar-brand"><span>Code Relax</span><button id="wideSidebarToggle" type="button" aria-label="收起聊天菜单" aria-expanded="true" title="收起聊天菜单"><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 3v18"/></svg></button></div>
|
|
46
85
|
<button id="newChat" type="button" class="new-chat-entry"><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4H6a3 3 0 0 0-3 3v11a3 3 0 0 0 3 3h11a3 3 0 0 0 3-3v-6M16 3a2.12 2.12 0 0 1 3 3l-8 8-4 1 1-4Z"/></svg><span>新对话</span></button>
|
|
47
86
|
<div class="toolbar"><span class="sidebar-title">项目</span><button id="refresh" class="refresh-icon" title="刷新任务" aria-label="刷新任务">↻</button></div>
|
|
48
87
|
<div id="tasks"></div>
|
|
49
|
-
<button id="archivedThreads" class="archive-list-entry" type="button" title="查看已归档对话" aria-label="查看已归档对话" hidden><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16v12H4zM3 4h18v3H3zM9 11h6"/></svg><span id="archivedThreadsLabel">已归档对话</span><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></button>
|
|
50
|
-
<button id="usageButton" type="button" title="查看剩余额度" aria-label="查看剩余额度"><svg class="codex-icon filled" viewBox="0 0 16 16" aria-hidden="true"><path d="M7.99951 0.807861C8.82345 0.807861 9.61914 0.935296 10.3667 1.17114C10.643 1.2584 10.7964 1.553 10.7095 1.82935C10.6222 2.10586 10.3268 2.25936 10.0503 2.17212C9.40387 1.96822 8.71489 1.85864 7.99951 1.85864C4.23962 1.85879 1.19204 4.90637 1.19189 8.66626C1.19189 10.4623 1.88656 12.0948 3.02295 13.3118C3.22077 13.5236 3.20925 13.856 2.99756 14.054C2.78565 14.2519 2.45327 14.2405 2.25537 14.0286C0.9446 12.6249 0.141113 10.7387 0.141113 8.66626C0.141259 4.32647 3.65972 0.808008 7.99951 0.807861Z"/><path d="M14.8569 6.00903C15.134 5.92414 15.428 6.07972 15.5132 6.35669C15.7376 7.0875 15.8579 7.86342 15.8579 8.66626C15.8579 10.7385 15.0552 12.6249 13.7446 14.0286C13.5468 14.2404 13.2144 14.2517 13.0024 14.054C12.7909 13.8561 12.7795 13.5246 12.9771 13.3127C14.1134 12.0958 14.8081 10.4623 14.8081 8.66626C14.8081 7.9691 14.7033 7.29731 14.5093 6.66528C14.4242 6.38817 14.5799 6.09422 14.8569 6.00903Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M12.2886 3.6604C12.4936 3.45576 12.8258 3.45564 13.0308 3.6604C13.2356 3.86532 13.2354 4.19756 13.0308 4.40259L9.8833 7.54907C10.0781 7.87667 10.1919 8.25855 10.1919 8.66724C10.1916 9.87721 9.21046 10.8583 8.00049 10.8586C6.79025 10.8586 5.80841 9.8774 5.80811 8.66724C5.80811 7.45681 6.79006 6.47485 8.00049 6.47485C8.42125 6.47496 8.81322 6.59526 9.14697 6.80103L12.2886 3.6604ZM8.00049 7.52563C7.36996 7.52563 6.85889 8.03671 6.85889 8.66724C6.85919 9.2975 7.37015 9.80884 8.00049 9.80884C8.63056 9.80853 9.14178 9.29731 9.14209 8.66724C9.14209 8.0369 8.63075 7.52594 8.00049 7.52563Z"/></svg><span id="usageSummary">剩余 —%</span><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></button>
|
|
51
|
-
<
|
|
88
|
+
<button id="archivedThreads" class="archive-list-entry" type="button" title="查看已归档对话" aria-label="查看已归档对话" hidden><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16v12H4zM3 4h18v3H3zM9 11h6"/></svg><span id="archivedThreadsLabel">已归档对话</span><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></button>
|
|
89
|
+
<button id="usageButton" type="button" title="查看剩余额度" aria-label="查看剩余额度"><svg class="codex-icon filled" viewBox="0 0 16 16" aria-hidden="true"><path d="M7.99951 0.807861C8.82345 0.807861 9.61914 0.935296 10.3667 1.17114C10.643 1.2584 10.7964 1.553 10.7095 1.82935C10.6222 2.10586 10.3268 2.25936 10.0503 2.17212C9.40387 1.96822 8.71489 1.85864 7.99951 1.85864C4.23962 1.85879 1.19204 4.90637 1.19189 8.66626C1.19189 10.4623 1.88656 12.0948 3.02295 13.3118C3.22077 13.5236 3.20925 13.856 2.99756 14.054C2.78565 14.2519 2.45327 14.2405 2.25537 14.0286C0.9446 12.6249 0.141113 10.7387 0.141113 8.66626C0.141259 4.32647 3.65972 0.808008 7.99951 0.807861Z"/><path d="M14.8569 6.00903C15.134 5.92414 15.428 6.07972 15.5132 6.35669C15.7376 7.0875 15.8579 7.86342 15.8579 8.66626C15.8579 10.7385 15.0552 12.6249 13.7446 14.0286C13.5468 14.2404 13.2144 14.2517 13.0024 14.054C12.7909 13.8561 12.7795 13.5246 12.9771 13.3127C14.1134 12.0958 14.8081 10.4623 14.8081 8.66626C14.8081 7.9691 14.7033 7.29731 14.5093 6.66528C14.4242 6.38817 14.5799 6.09422 14.8569 6.00903Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M12.2886 3.6604C12.4936 3.45576 12.8258 3.45564 13.0308 3.6604C13.2356 3.86532 13.2354 4.19756 13.0308 4.40259L9.8833 7.54907C10.0781 7.87667 10.1919 8.25855 10.1919 8.66724C10.1916 9.87721 9.21046 10.8583 8.00049 10.8586C6.79025 10.8586 5.80841 9.8774 5.80811 8.66724C5.80811 7.45681 6.79006 6.47485 8.00049 6.47485C8.42125 6.47496 8.81322 6.59526 9.14697 6.80103L12.2886 3.6604ZM8.00049 7.52563C7.36996 7.52563 6.85889 8.03671 6.85889 8.66724C6.85919 9.2975 7.37015 9.80884 8.00049 9.80884C8.63056 9.80853 9.14178 9.29731 9.14209 8.66724C9.14209 8.0369 8.63075 7.52594 8.00049 7.52563Z"/></svg><span id="usageSummary">剩余 —%</span><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></button>
|
|
90
|
+
<button id="announcementButton" type="button" title="查看公告" aria-label="查看公告"><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 10v4h4l5 4V6l-5 4H4Z"/><path d="M16 9a4 4 0 0 1 0 6M18.5 6.5a8 8 0 0 1 0 11"/></svg><span id="announcementTitle">公告</span><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></button>
|
|
91
|
+
<div id="sharedServiceNotice" class="shared-service-notice" role="button" tabindex="0" aria-label="查看 Codex 状态" hidden><span class="shared-service-copy"><strong id="sharedServiceNoticeTitle">Codex 内核未就绪</strong><span id="sharedServiceNoticeText">点击查看当前状况和可执行操作</span></span><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></div>
|
|
52
92
|
<button id="connectionSettings" type="button" title="修改连接" aria-label="修改连接"><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="12" rx="2"/><path d="M8 20h8M12 16v4"/></svg><span id="connectionAddress"></span><span id="connectionStatus" role="status">连接中</span><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></button>
|
|
53
93
|
<div class="app-actions">
|
|
54
94
|
<button id="returnToComputer" type="button" title="返回工作站" aria-label="返回工作站" hidden><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><g transform="translate(24 0) scale(-1 1)"><path d="M10 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h5M14 8l4 4-4 4M8 12h10"/></g></svg><span>返回</span></button>
|
|
95
|
+
<span id="installedAppVersion" class="installed-app-version" hidden></span>
|
|
55
96
|
<details id="debugMenu" hidden>
|
|
56
97
|
<summary><span>调试</span><svg class="codex-icon debug-chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></summary>
|
|
57
98
|
<div class="debug-menu-items">
|
|
58
99
|
<button id="networkDiagnostics" type="button">网络诊断</button>
|
|
59
|
-
<button id="
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
<
|
|
100
|
+
<button id="viewAppLog" type="button">查看日志</button>
|
|
101
|
+
<button id="recoverShared" type="button">重新连接 Codex 内核</button>
|
|
102
|
+
<div id="sharedRecoveryConfirm" hidden role="group" aria-label="确认重新连接 Codex 内核">
|
|
103
|
+
<p>将重新打开工作站当前的 Codex,使其接入 Codex 内核。正在运行的任务可能中断,请先保存未发送内容。工作站连接保持运行。</p>
|
|
104
|
+
<button id="sharedRecoveryExecute" type="button">确认重新连接</button>
|
|
63
105
|
<button id="sharedRecoveryCancel" type="button">取消</button>
|
|
64
106
|
</div>
|
|
65
107
|
<p id="sharedRecoveryResult" role="status" hidden></p>
|
|
@@ -69,9 +111,9 @@
|
|
|
69
111
|
</details>
|
|
70
112
|
</div>
|
|
71
113
|
</aside>
|
|
72
|
-
<section>
|
|
73
|
-
<button id="wideSidebarExpand" type="button" aria-label="展开聊天菜单" title="展开聊天菜单"><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 3v18"/></svg></button>
|
|
74
|
-
<div id="empty"><strong id="emptyConnection">准备连接</strong
|
|
114
|
+
<section>
|
|
115
|
+
<button id="wideSidebarExpand" type="button" aria-label="展开聊天菜单" title="展开聊天菜单"><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 3v18"/></svg></button>
|
|
116
|
+
<div id="empty"><strong id="emptyConnection">准备连接</strong></div>
|
|
75
117
|
<div id="selected" hidden>
|
|
76
118
|
<div id="newThreadWelcome" class="new-thread-welcome" hidden><!-- Static home icon from Codex Desktop 26.908.9136.0 -->
|
|
77
119
|
<div id="newThreadIcon" aria-hidden="true"><svg width="716" height="716" viewBox="149 149 418 418" fill="none" xmlns="http://www.w3.org/2000/svg" class="new-thread-brand"><mask id="path-1-inside-1_24_869" fill="white"><path fill-rule="evenodd" clip-rule="evenodd" d="M247.429 247.43C257.73 208.911 292.871 180.543 334.638 180.543C359.555 180.543 382.115 190.64 398.449 206.964C405.906 204.97 413.743 203.905 421.829 203.905C471.681 203.906 512.096 244.32 512.096 294.173C512.096 302.259 511.031 310.096 509.037 317.553C525.361 333.887 535.458 356.446 535.458 381.364C535.458 423.131 507.09 458.271 468.571 468.572C458.271 507.091 423.131 535.459 381.364 535.459C356.446 535.459 333.886 525.362 317.552 509.037C310.095 511.031 302.258 512.097 294.172 512.097C244.319 512.097 203.906 471.682 203.906 421.829C203.906 413.743 204.969 405.905 206.963 398.448C190.639 382.115 180.543 359.555 180.543 334.638C180.543 292.871 208.91 257.73 247.429 247.43Z"></path></mask><path d="M247.429 247.43L252.746 267.312L264.238 264.239L267.311 252.747L247.429 247.43ZM334.638 180.543L334.638 159.962L334.638 159.962L334.638 180.543ZM398.449 206.964L383.9 221.521L392.297 229.913L403.765 226.846L398.449 206.964ZM421.829 203.905L421.829 183.325L421.829 183.325L421.829 203.905ZM512.096 294.173L532.677 294.173L532.677 294.173L512.096 294.173ZM509.037 317.553L489.155 312.236L486.087 323.705L494.48 332.102L509.037 317.553ZM535.458 381.364L556.039 381.364L556.039 381.364L535.458 381.364ZM468.571 468.572L463.255 448.69L451.762 451.763L448.689 463.255L468.571 468.572ZM381.364 535.459L381.364 556.04L381.364 556.04L381.364 535.459ZM317.552 509.037L332.101 494.481L323.704 486.088L312.235 489.155L317.552 509.037ZM294.172 512.097L294.172 532.678L294.173 532.678L294.172 512.097ZM203.906 421.829L183.325 421.829L183.325 421.829L203.906 421.829ZM206.963 398.448L226.845 403.765L229.912 392.297L221.52 383.9L206.963 398.448ZM180.543 334.638L159.962 334.638L159.962 334.639L180.543 334.638ZM247.429 247.43L267.311 252.747C275.266 223.003 302.423 201.124 334.638 201.124L334.638 180.543L334.638 159.962C283.319 159.962 240.195 194.819 227.547 242.113L247.429 247.43ZM334.638 180.543L334.638 201.124C353.88 201.124 371.268 208.896 383.9 221.521L398.449 206.964L412.997 192.407C392.962 172.383 365.231 159.962 334.638 159.962L334.638 180.543ZM398.449 206.964L403.765 226.846C409.506 225.311 415.557 224.486 421.829 224.486L421.829 203.905L421.829 183.325C411.929 183.325 402.305 184.629 393.132 187.082L398.449 206.964ZM421.829 203.905L421.828 224.486C460.315 224.486 491.516 255.687 491.516 294.173L512.096 294.173L532.677 294.173C532.677 232.953 483.048 183.325 421.829 183.325L421.829 203.905ZM512.096 294.173L491.516 294.173C491.516 300.444 490.69 306.494 489.155 312.236L509.037 317.553L528.919 322.87C531.372 313.698 532.677 304.074 532.677 294.173L512.096 294.173ZM509.037 317.553L494.48 332.102C507.105 344.735 514.877 362.122 514.877 381.364L535.458 381.364L556.039 381.364C556.039 350.771 543.618 323.04 523.594 303.005L509.037 317.553ZM535.458 381.364L514.877 381.364C514.877 413.578 492.999 440.735 463.255 448.69L468.571 468.572L473.888 488.454C521.182 475.806 556.039 432.683 556.039 381.364L535.458 381.364ZM468.571 468.572L448.689 463.255C440.735 492.999 413.578 514.878 381.364 514.878L381.364 535.459L381.364 556.04C432.683 556.04 475.806 521.183 488.454 473.889L468.571 468.572ZM381.364 535.459L381.364 514.878C362.122 514.878 344.733 507.106 332.101 494.481L317.552 509.037L303.003 523.594C323.038 543.619 350.77 556.04 381.364 556.04L381.364 535.459ZM317.552 509.037L312.235 489.155C306.493 490.691 300.443 491.516 294.172 491.516L294.172 512.097L294.173 532.678C304.073 532.678 313.698 531.372 322.869 528.919L317.552 509.037ZM294.172 512.097L294.172 491.516C255.686 491.516 224.486 460.316 224.486 421.829L203.906 421.829L183.325 421.829C183.325 483.048 232.953 532.678 294.172 532.678L294.172 512.097ZM203.906 421.829L224.486 421.829C224.486 415.555 225.311 409.504 226.845 403.765L206.963 398.448L187.081 393.131C184.627 402.307 183.325 411.932 183.325 421.829L203.906 421.829ZM206.963 398.448L221.52 383.9C208.895 371.268 201.124 353.88 201.124 334.638L180.543 334.638L159.962 334.639C159.962 365.231 172.382 392.962 192.406 412.997L206.963 398.448ZM180.543 334.638L201.124 334.638C201.124 302.423 223.002 275.266 252.746 267.312L247.429 247.43L242.112 227.547C194.818 240.195 159.962 283.319 159.962 334.638L180.543 334.638Z" fill="currentColor" mask="url(#path-1-inside-1_24_869)"></path><path d="M436.706 408.738H370.021" stroke="currentColor" stroke-width="24" stroke-linecap="round"></path><path d="M276.533 309.154L303.468 357.831C304.433 359.575 304.412 361.698 303.414 363.423L276.533 409.854" stroke="currentColor" stroke-width="24" stroke-linecap="round"></path></svg></div><h3 id="newThreadTitle" class="new-thread-title">我们要构建什么?</h3></div>
|
|
@@ -81,9 +123,6 @@
|
|
|
81
123
|
<div id="timeline"></div>
|
|
82
124
|
<button id="scrollToBottom" class="scroll-to-bottom" type="button" aria-label="滚动到最新消息" title="滚动到最新消息" hidden><svg class="codex-icon filled" viewBox="0 0 20 20" aria-hidden="true"><path d="M9.33467 16.6663V4.93978L4.6374 9.63704L4.1667 9.16634L3.69599 8.69661L9.52998 2.86263L9.63447 2.77767C9.8925 2.60753 10.2433 2.63564 10.4704 2.86263L16.3034 8.69661L16.3884 8.80111C16.5588 9.05922 16.5306 9.40982 16.3034 9.63704C16.0762 9.86414 15.7255 9.89242 15.4675 9.722L15.363 9.63704L10.6647 4.9388V16.6663C10.6647 17.0336 10.367 17.3314 9.99971 17.3314C9.63259 17.3312 9.33467 17.0335 9.33467 16.6663ZM4.6374 9.63704C4.3777 9.89674 3.95569 9.89674 3.69599 9.63704C3.43657 9.37744 3.43668 8.95628 3.69599 8.69661L4.6374 9.63704Z"/></svg></button>
|
|
83
125
|
<div id="queuePanel" class="queue-panel" hidden></div>
|
|
84
|
-
<div id="turnActions" style="display:flex;gap:8px;align-items:center;padding:4px 0;font-size:12px" hidden>
|
|
85
|
-
<button id="interruptTurn" type="button" hidden>停止</button>
|
|
86
|
-
</div>
|
|
87
126
|
<div id="connectionNotice" class="connection-notice" role="status" hidden><span class="connection-indicator" aria-hidden="true"></span><span id="connectionNoticeText">连接已断开</span><button id="reconnectConversation" type="button">重新连接</button></div>
|
|
88
127
|
<div id="newThreadProjectBar" class="new-thread-project" hidden><span id="newThreadProjectName">不在项目中工作</span><button id="clearNewThreadProject" type="button" aria-label="清空项目" title="清空项目">×</button></div>
|
|
89
128
|
<div id="approvalPanel" class="approval-panel" hidden></div>
|
|
@@ -189,8 +228,10 @@
|
|
|
189
228
|
</section>
|
|
190
229
|
</main>
|
|
191
230
|
<dialog id="imageViewer" class="image-viewer">
|
|
192
|
-
<div class="image-viewer-shell">
|
|
193
|
-
<button id="
|
|
231
|
+
<div class="image-viewer-shell">
|
|
232
|
+
<button id="imageViewerOriginal" class="image-viewer-original" type="button" aria-label="查看原图" title="查看原图"><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M16 3h5v5"/><path d="M17 21h2a2 2 0 0 0 2-2"/><path d="M21 12v3"/><path d="m21 3-5 5"/><path d="M3 7V5a2 2 0 0 1 2-2"/><path d="m5 21 4.144-4.144a1.21 1.21 0 0 1 1.712 0L13 19"/><path d="M9 3h3"/><rect x="3" y="11" width="10" height="10" rx="1"/></svg><span class="image-viewer-action-label">查看原图</span></button>
|
|
233
|
+
<button id="imageViewerDownload" class="image-viewer-download" type="button" aria-label="下载原图" title="下载原图"><svg class="codex-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg></button>
|
|
234
|
+
<button id="imageViewerClose" class="image-viewer-close" type="button" aria-label="关闭图片">×</button>
|
|
194
235
|
<img id="imageViewerImage" alt="">
|
|
195
236
|
</div>
|
|
196
237
|
</dialog>
|
|
@@ -208,15 +249,40 @@
|
|
|
208
249
|
<script src="highlight.min.js"></script>
|
|
209
250
|
<script src="highlight-powershell.min.js"></script>
|
|
210
251
|
<script src="capabilities.js"></script>
|
|
211
|
-
<script src="timeline-sync.js"></script>
|
|
212
|
-
<script src="
|
|
252
|
+
<script src="timeline-sync.js"></script>
|
|
253
|
+
<script src="timeline-reducer.js"></script>
|
|
254
|
+
<script src="harmony-platform.js"></script>
|
|
213
255
|
<script src="client-platform.js"></script>
|
|
214
256
|
<script src="lan-session.js"></script>
|
|
215
|
-
<script src="history-cache.js"></script>
|
|
257
|
+
<script src="history-cache.js"></script>
|
|
216
258
|
<script src="p2p-diagnostics.js"></script>
|
|
259
|
+
<script src="p2p-data-channel.js"></script>
|
|
217
260
|
<script src="p2p-probe.js"></script>
|
|
218
261
|
<script src="self-relay-session.js"></script>
|
|
219
|
-
<script src="chat-transport.js"></script>
|
|
220
|
-
<script src="
|
|
262
|
+
<script src="chat-transport.js"></script>
|
|
263
|
+
<script src="page-resume.js"></script>
|
|
264
|
+
<script src="conversation-controller.js"></script>
|
|
265
|
+
<script src="history-controller.js"></script>
|
|
266
|
+
<script src="usage-controller.js"></script>
|
|
267
|
+
<script src="task-list-view.js"></script>
|
|
268
|
+
<script src="draft-controller.js"></script>
|
|
269
|
+
<script src="thread-attention-controller.js"></script>
|
|
270
|
+
<script src="thread-title-controller.js"></script>
|
|
271
|
+
<script src="thread-list-controller.js"></script>
|
|
272
|
+
<script src="thread-list-sync.js"></script>
|
|
273
|
+
<script src="station-connection-controller.js"></script>
|
|
274
|
+
<script src="disclosure-state-controller.js"></script>
|
|
275
|
+
<script src="pending-message-store.js"></script>
|
|
276
|
+
<script src="queue-controller.js"></script>
|
|
277
|
+
<script src="thread-context-controller.js"></script>
|
|
278
|
+
<script src="composer-controller.js"></script>
|
|
279
|
+
<script src="message-send-controller.js"></script>
|
|
280
|
+
<script src="link-action-controller.js"></script>
|
|
281
|
+
<script src="message-view.js"></script>
|
|
282
|
+
<script src="activity-view.js"></script>
|
|
283
|
+
<script src="timeline-formatters.js"></script>
|
|
284
|
+
<script src="timeline-scroll-controller.js"></script>
|
|
285
|
+
<script src="timeline-renderer.js"></script>
|
|
286
|
+
<script src="chat.js"></script>
|
|
221
287
|
</body>
|
|
222
288
|
</html>
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
(function (root) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
function createLinkActionController(options) {
|
|
5
|
+
const { document, transport, getSelectedThreadId, showToast } = options;
|
|
6
|
+
let menu;
|
|
7
|
+
let outsideHandler;
|
|
8
|
+
let updateInfoButton;
|
|
9
|
+
|
|
10
|
+
function close() {
|
|
11
|
+
if (outsideHandler) document.removeEventListener('pointerdown', outsideHandler, true);
|
|
12
|
+
outsideHandler = null;
|
|
13
|
+
menu?.remove();
|
|
14
|
+
menu = null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function installCodeRelaxUpdate() {
|
|
18
|
+
if (!root.codexNative?.installCodeRelaxUpdate) {
|
|
19
|
+
showToast('当前客户端不支持安装更新', 3000);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
root.codexNative.installCodeRelaxUpdate();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function downloadReferencedFile(anchor) {
|
|
26
|
+
if (anchor.dataset.downloading === 'true') return;
|
|
27
|
+
anchor.dataset.downloading = 'true';
|
|
28
|
+
const label = anchor.textContent;
|
|
29
|
+
anchor.textContent = '正在下载…';
|
|
30
|
+
showToast('正在下载文件…');
|
|
31
|
+
transport.readFile(getSelectedThreadId(), anchor.dataset.downloadPath).then(file => {
|
|
32
|
+
if (typeof file?.dataBase64 !== 'string') throw new Error('工作站未返回文件内容');
|
|
33
|
+
if (!root.codexNative?.saveDownloadedFile) throw new Error('当前客户端不支持保存文件');
|
|
34
|
+
root.codexNative.saveDownloadedFile(file.name || anchor.dataset.downloadName || '下载文件', file.dataBase64);
|
|
35
|
+
}).catch(error => showToast('下载失败:' + error.message, 3000)).finally(() => {
|
|
36
|
+
anchor.dataset.downloading = 'false';
|
|
37
|
+
anchor.textContent = label;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function downloadReferencedImage(anchor) {
|
|
42
|
+
if (anchor.dataset.downloading === 'true') return;
|
|
43
|
+
anchor.dataset.downloading = 'true';
|
|
44
|
+
const label = anchor.textContent;
|
|
45
|
+
anchor.textContent = '正在下载…';
|
|
46
|
+
showToast('正在下载原图…');
|
|
47
|
+
try {
|
|
48
|
+
const url = await transport.readImage(getSelectedThreadId(), anchor.dataset.downloadImage, 'original', anchor.dataset.downloadRecovery || '');
|
|
49
|
+
const response = await fetch(url);
|
|
50
|
+
if (!response.ok) throw new Error(`图片读取失败(${response.status})`);
|
|
51
|
+
const blob = await response.blob();
|
|
52
|
+
const bytes = new Uint8Array(await blob.arrayBuffer()); let binary = '';
|
|
53
|
+
for (let offset = 0; offset < bytes.length; offset += 16384) binary += String.fromCharCode(...bytes.subarray(offset, offset + 16384));
|
|
54
|
+
if (!root.codexNative?.saveDownloadedFile) throw new Error('当前客户端不支持保存图片');
|
|
55
|
+
root.codexNative.saveDownloadedFile(anchor.dataset.downloadName || '图片', btoa(binary));
|
|
56
|
+
} catch (error) {
|
|
57
|
+
showToast('下载失败:' + error.message, 3000);
|
|
58
|
+
} finally {
|
|
59
|
+
anchor.dataset.downloading = 'false';
|
|
60
|
+
anchor.textContent = label;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isCodeRelaxInstallLink(value) {
|
|
65
|
+
try {
|
|
66
|
+
const url = new URL(value);
|
|
67
|
+
const path = url.pathname.replace(/\/$/, '');
|
|
68
|
+
return url.protocol === 'https:' && url.host === 'api.uulife.site'
|
|
69
|
+
&& (path === '/code-relax/install' || path === '/code-relax/releases/latest/manifest.json5');
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function splitFileName(name) {
|
|
76
|
+
const dot = name.lastIndexOf('.');
|
|
77
|
+
return dot > 0 ? [name.slice(0, dot), name.slice(dot)] : [name, ''];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function positionMenu(anchor, nextMenu) {
|
|
81
|
+
document.body.append(nextMenu);
|
|
82
|
+
menu = nextMenu;
|
|
83
|
+
const rect = anchor.getBoundingClientRect();
|
|
84
|
+
const width = nextMenu.offsetWidth;
|
|
85
|
+
const height = nextMenu.offsetHeight;
|
|
86
|
+
nextMenu.style.left = `${Math.max(8, Math.min(rect.left, innerWidth - width - 8))}px`;
|
|
87
|
+
nextMenu.style.top = `${Math.max(8, Math.min(rect.bottom + 6, innerHeight - height - 8))}px`;
|
|
88
|
+
requestAnimationFrame(() => {
|
|
89
|
+
outsideHandler = event => {
|
|
90
|
+
if (nextMenu.contains(event.target) || anchor.contains(event.target)) return;
|
|
91
|
+
close();
|
|
92
|
+
};
|
|
93
|
+
document.addEventListener('pointerdown', outsideHandler, true);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function showFile(anchor) {
|
|
98
|
+
close();
|
|
99
|
+
const nextMenu = document.createElement('div');
|
|
100
|
+
nextMenu.className = 'file-action-menu';
|
|
101
|
+
nextMenu.setAttribute('role', 'menu');
|
|
102
|
+
const fullName = anchor.dataset.downloadName || '下载文件';
|
|
103
|
+
const fileName = document.createElement('div');
|
|
104
|
+
fileName.className = 'file-action-name';
|
|
105
|
+
const [bodyText, extensionText] = splitFileName(fullName);
|
|
106
|
+
const body = document.createElement('span');
|
|
107
|
+
body.className = 'file-action-name-body';
|
|
108
|
+
body.textContent = bodyText;
|
|
109
|
+
const extension = document.createElement('span');
|
|
110
|
+
extension.className = 'file-action-name-extension';
|
|
111
|
+
extension.textContent = extensionText;
|
|
112
|
+
fileName.append(body, extension);
|
|
113
|
+
fileName.title = fullName;
|
|
114
|
+
nextMenu.append(fileName);
|
|
115
|
+
const addAction = (label, install) => {
|
|
116
|
+
const button = document.createElement('button');
|
|
117
|
+
button.type = 'button';
|
|
118
|
+
button.textContent = label;
|
|
119
|
+
button.setAttribute('role', 'menuitem');
|
|
120
|
+
button.onclick = () => {
|
|
121
|
+
close();
|
|
122
|
+
if (install) installCodeRelaxUpdate();
|
|
123
|
+
else downloadReferencedFile(anchor);
|
|
124
|
+
};
|
|
125
|
+
nextMenu.append(button);
|
|
126
|
+
};
|
|
127
|
+
addAction('下载', false);
|
|
128
|
+
if (/^Code[- ]Relax.*\.hap$/i.test(fullName)) addAction('下载并安装', true);
|
|
129
|
+
positionMenu(anchor, nextMenu);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function showWeb(anchor) {
|
|
133
|
+
close();
|
|
134
|
+
const target = anchor.dataset.externalLink;
|
|
135
|
+
const nextMenu = document.createElement('div');
|
|
136
|
+
nextMenu.className = 'file-action-menu';
|
|
137
|
+
nextMenu.setAttribute('role', 'menu');
|
|
138
|
+
const heading = document.createElement('div');
|
|
139
|
+
heading.className = 'file-action-name link-action-url';
|
|
140
|
+
heading.textContent = target;
|
|
141
|
+
heading.title = target;
|
|
142
|
+
nextMenu.append(heading);
|
|
143
|
+
const addAction = (label, action) => {
|
|
144
|
+
const button = document.createElement('button');
|
|
145
|
+
button.type = 'button';
|
|
146
|
+
button.textContent = label;
|
|
147
|
+
button.setAttribute('role', 'menuitem');
|
|
148
|
+
button.onclick = () => {
|
|
149
|
+
close();
|
|
150
|
+
if (action === 'copy' && root.codexNative?.copyText) root.codexNative.copyText(target);
|
|
151
|
+
else if (action === 'open' && root.codexNative?.openExternalLink) root.codexNative.openExternalLink(target);
|
|
152
|
+
else if (action === 'install') installCodeRelaxUpdate();
|
|
153
|
+
else showToast('请先更新客户端后再使用此操作', 3000);
|
|
154
|
+
};
|
|
155
|
+
nextMenu.append(button);
|
|
156
|
+
return button;
|
|
157
|
+
};
|
|
158
|
+
let shouldRequestUpdateInfo = false;
|
|
159
|
+
if (isCodeRelaxInstallLink(target)) {
|
|
160
|
+
updateInfoButton = addAction('安装更新 · 正在获取版本…', 'install');
|
|
161
|
+
shouldRequestUpdateInfo = true;
|
|
162
|
+
}
|
|
163
|
+
addAction('复制链接', 'copy');
|
|
164
|
+
addAction('在系统浏览器中打开', 'open');
|
|
165
|
+
positionMenu(anchor, nextMenu);
|
|
166
|
+
if (shouldRequestUpdateInfo) {
|
|
167
|
+
if (root.codexNative?.requestCodeRelaxUpdateInfo) root.codexNative.requestCodeRelaxUpdateInfo();
|
|
168
|
+
else updateInfoButton.textContent = '安装更新';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function handleContentClick(event, embedded) {
|
|
173
|
+
const codeReference = event.target.closest('a[data-code-reference]');
|
|
174
|
+
if (codeReference) {
|
|
175
|
+
event.preventDefault();
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
const imageDownload = event.target.closest('a[data-download-image]');
|
|
179
|
+
if (imageDownload) {
|
|
180
|
+
event.preventDefault();
|
|
181
|
+
void downloadReferencedImage(imageDownload);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
const download = event.target.closest('a[data-download-path]');
|
|
185
|
+
if (download) {
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
showFile(download);
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
const webLink = event.target.closest('a[data-external-link]');
|
|
191
|
+
if (embedded && webLink) {
|
|
192
|
+
event.preventDefault();
|
|
193
|
+
showWeb(webLink);
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
if (embedded && event.target.closest('a')) event.preventDefault();
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
root.harmonyCodeRelaxUpdateInfo = (versionName, versionCode, error) => {
|
|
201
|
+
if (!updateInfoButton?.isConnected) return;
|
|
202
|
+
updateInfoButton.textContent = versionName && versionCode
|
|
203
|
+
? `安装更新 · ${versionName}(${versionCode})`
|
|
204
|
+
: '安装更新';
|
|
205
|
+
if (error) updateInfoButton.title = `版本读取失败:${error}`;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
return { close, handleContentClick, showFile, showWeb };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
root.createLinkActionController = createLinkActionController;
|
|
212
|
+
})(globalThis);
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
(function (root) {
|
|
2
|
+
function createMessageSendController(options) {
|
|
3
|
+
const { state, transport, platform, crypto, el, pendingMessages, drafts, composer, queue, threadContext, conversation } = options;
|
|
4
|
+
|
|
5
|
+
function createBody(prompt) {
|
|
6
|
+
const body = {
|
|
7
|
+
prompt,
|
|
8
|
+
clientUserMessageId: Array.from(crypto.getRandomValues(new Uint8Array(16)),
|
|
9
|
+
byte => byte.toString(16).padStart(2, '0')).join(''),
|
|
10
|
+
images: state.draftImages.map(image => image.id),
|
|
11
|
+
hostId: state.threads.find(thread => thread.id === state.selectedId)?.hostId,
|
|
12
|
+
};
|
|
13
|
+
const pending = state.pendingSettingsByThread.get(state.selectedId);
|
|
14
|
+
const selectedModel = pending?.model || state.currentModel;
|
|
15
|
+
const selectedThinking = pending?.thinking || state.currentThinking;
|
|
16
|
+
if (!transport.has('immediateSettings') || options.isNewThread()) {
|
|
17
|
+
if (selectedModel) body.model = selectedModel;
|
|
18
|
+
if (selectedThinking) body.thinking = selectedThinking;
|
|
19
|
+
}
|
|
20
|
+
if (options.isNewThread()) {
|
|
21
|
+
body.hostId = state.newThreadProject?.hostId;
|
|
22
|
+
if (transport.has('immediateSettings')) body.serviceTier = state.currentServiceTier;
|
|
23
|
+
}
|
|
24
|
+
return body;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function send(mode = 'direct') {
|
|
28
|
+
if (options.isConnectionBlocked()) return;
|
|
29
|
+
if (options.isThreadReadOnly()) {
|
|
30
|
+
options.setStatus(state.timeline.thread.readOnlyReason || '该对话当前只能查看');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (state.historyLoading || state.timeline?.cached) {
|
|
34
|
+
options.setStatus('请等待对话同步完成后再发送');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (options.isApplyingSettings()) {
|
|
38
|
+
options.setStatus('请等待工作站确认模型设置后再发送');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (platform.voice.active) {
|
|
42
|
+
platform.callbacks.sendVoice(mode);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (options.isNewThread()) return options.createNewThread();
|
|
46
|
+
const prompt = el('prompt').value.trim();
|
|
47
|
+
if (!state.selectedId || (!prompt && !state.draftImages.length) || state.uploadingImages || state.sending) return;
|
|
48
|
+
state.sending = true;
|
|
49
|
+
composer.updateMode();
|
|
50
|
+
composer.setControlsDisabled(true);
|
|
51
|
+
const threadId = state.selectedId;
|
|
52
|
+
const pendingAtSend = state.pendingSettingsByThread.get(threadId);
|
|
53
|
+
const body = createBody(prompt);
|
|
54
|
+
const optimistic = mode === 'direct' ? {
|
|
55
|
+
clientUserMessageId: body.clientUserMessageId,
|
|
56
|
+
request: { ...body },
|
|
57
|
+
text: prompt,
|
|
58
|
+
attachments: state.draftImages.map(image => ({
|
|
59
|
+
...image,
|
|
60
|
+
type: 'image',
|
|
61
|
+
available: true,
|
|
62
|
+
url: image.url || image.thumbnailUrl,
|
|
63
|
+
})),
|
|
64
|
+
baseline: pendingMessages.baseline(state.timeline),
|
|
65
|
+
turnId: composer.isActive() ? state.timeline?.turns?.at(-1)?.id : null,
|
|
66
|
+
settled: false,
|
|
67
|
+
status: '',
|
|
68
|
+
} : null;
|
|
69
|
+
if (optimistic) {
|
|
70
|
+
pendingMessages.set(threadId, [...pendingMessages.get(threadId), optimistic]);
|
|
71
|
+
el('prompt').value = '';
|
|
72
|
+
state.draftImages = [];
|
|
73
|
+
options.renderDraftImages();
|
|
74
|
+
drafts.clearConversation(threadId);
|
|
75
|
+
options.renderPendingMessages();
|
|
76
|
+
options.scrollTimelineToBottom(true);
|
|
77
|
+
composer.updateActions();
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const endpoint = mode === 'queue' ? 'queue' : 'messages';
|
|
81
|
+
const result = await options.api(`/api/threads/${encodeURIComponent(threadId)}/${endpoint}`, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
body: JSON.stringify(body),
|
|
84
|
+
});
|
|
85
|
+
if (state.pendingSettingsByThread.get(threadId) === pendingAtSend) {
|
|
86
|
+
state.pendingSettingsByThread.delete(threadId);
|
|
87
|
+
if (state.selectedId === threadId) options.renderModelControl();
|
|
88
|
+
}
|
|
89
|
+
if (optimistic) {
|
|
90
|
+
const submission = result.data || result;
|
|
91
|
+
optimistic.turnId = submission.turnId || submission.turn?.id || optimistic.turnId;
|
|
92
|
+
optimistic.status = '';
|
|
93
|
+
} else if (state.selectedId === threadId) {
|
|
94
|
+
el('prompt').value = '';
|
|
95
|
+
state.draftImages = [];
|
|
96
|
+
options.renderDraftImages();
|
|
97
|
+
drafts.clearConversation(threadId);
|
|
98
|
+
}
|
|
99
|
+
if (transport.has('immediateSettings') && state.selectedId === threadId) void threadContext.update(threadId);
|
|
100
|
+
if (mode === 'queue') queue.render(result.items || []);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (optimistic) optimistic.status = '发送未确认,收到正式消息后会自动合并';
|
|
103
|
+
options.setStatus(`发送失败:${error.message}`);
|
|
104
|
+
} finally {
|
|
105
|
+
if (optimistic) optimistic.settled = true;
|
|
106
|
+
state.sending = false;
|
|
107
|
+
composer.setControlsDisabled(false);
|
|
108
|
+
composer.updateActions();
|
|
109
|
+
if (state.selectedId === threadId) options.renderPendingMessages();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function resend(message) {
|
|
114
|
+
if (message.retrying || state.sending || options.isConnectionBlocked()
|
|
115
|
+
|| options.isThreadReadOnly() || state.historyLoading) return;
|
|
116
|
+
const threadId = state.selectedId;
|
|
117
|
+
if (!pendingMessages.has(threadId, message)) return;
|
|
118
|
+
message.retrying = true;
|
|
119
|
+
state.sending = true;
|
|
120
|
+
composer.updateActions();
|
|
121
|
+
options.renderPendingMessages();
|
|
122
|
+
const uploaded = [];
|
|
123
|
+
let selection = conversation.generation();
|
|
124
|
+
try {
|
|
125
|
+
const recovery = options.restoreConversation();
|
|
126
|
+
selection = conversation.generation();
|
|
127
|
+
await recovery;
|
|
128
|
+
if (!conversation.isCurrent(selection, threadId) || !pendingMessages.has(threadId, message)) return;
|
|
129
|
+
if (options.isConnectionBlocked()) throw new Error('连接尚未恢复,未重新发送');
|
|
130
|
+
let result;
|
|
131
|
+
if (message.queueItemId) {
|
|
132
|
+
result = await options.api(`/api/threads/${encodeURIComponent(threadId)}/queue/${encodeURIComponent(message.queueItemId)}/steer`, { method: 'POST' });
|
|
133
|
+
if (state.selectedId === threadId) queue.render(result.items || []);
|
|
134
|
+
} else {
|
|
135
|
+
const images = [];
|
|
136
|
+
for (const attachment of message.attachments) {
|
|
137
|
+
const url = attachment.url || attachment.thumbnailUrl;
|
|
138
|
+
if (!/^data:image\/(?:png|jpeg|webp);base64,/i.test(url || '')) {
|
|
139
|
+
throw new Error('原图片已不可用,未重新发送;请重新选择图片');
|
|
140
|
+
}
|
|
141
|
+
const image = await transport.addImage(await (await fetch(url)).blob());
|
|
142
|
+
images.push(image.id);
|
|
143
|
+
uploaded.push(image.id);
|
|
144
|
+
}
|
|
145
|
+
if (!conversation.isCurrent(selection, threadId) || !pendingMessages.has(threadId, message)) return;
|
|
146
|
+
result = await options.api(`/api/threads/${encodeURIComponent(threadId)}/messages`, {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
body: JSON.stringify({
|
|
149
|
+
...message.request,
|
|
150
|
+
prompt: message.text,
|
|
151
|
+
images,
|
|
152
|
+
clientUserMessageId: message.clientUserMessageId,
|
|
153
|
+
}),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const submission = result.data || result;
|
|
157
|
+
message.turnId = submission.turnId || submission.turn?.id || message.turnId;
|
|
158
|
+
message.status = '';
|
|
159
|
+
} catch (error) {
|
|
160
|
+
message.status = '发送未确认,收到正式消息后会自动合并';
|
|
161
|
+
conversation.failSync(selection);
|
|
162
|
+
options.showToast(`重新发送未完成:${error.message}`);
|
|
163
|
+
} finally {
|
|
164
|
+
for (const id of uploaded) transport.removeImage(id);
|
|
165
|
+
message.retrying = false;
|
|
166
|
+
message.settled = true;
|
|
167
|
+
state.sending = false;
|
|
168
|
+
composer.updateActions();
|
|
169
|
+
if (state.selectedId === threadId) options.renderPendingMessages();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return Object.freeze({ createBody, resend, send });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
root.createMessageSendController = createMessageSendController;
|
|
177
|
+
})(globalThis);
|