@klarkxy/dsh-session-manager 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -10
- package/README.zh-CN.md +14 -10
- package/lib/client.inner.cjs +1607 -358
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +1607 -358
- package/lib/index.d.ts +8 -0
- package/lib/index.js +3507 -2190
- package/lib/index.js.map +1 -1
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +3 -3
package/lib/client.js
CHANGED
|
@@ -21,7 +21,9 @@ const reasons = {
|
|
|
21
21
|
stale: "这个聊天已经更新,请再试一次。",
|
|
22
22
|
running: "这个聊天仍在运行。请等待当前工作结束后再试。",
|
|
23
23
|
cleanup: "清理没有完成。",
|
|
24
|
-
unsupported: "当前版本不能完成这个操作。"
|
|
24
|
+
unsupported: "当前版本不能完成这个操作。",
|
|
25
|
+
retryPending: "重发尚未完成。再次发送会继续同一次操作,不会新建另一份聊天。",
|
|
26
|
+
retryUncertain: "上次创建聊天的结果无法确认,已停止再次创建。请先检查会话列表中是否已有新聊天。"
|
|
25
27
|
},
|
|
26
28
|
en: {
|
|
27
29
|
modified: "Later operations may have made changes. Retry is unavailable.",
|
|
@@ -30,7 +32,9 @@ const reasons = {
|
|
|
30
32
|
stale: "This chat has changed. Try again.",
|
|
31
33
|
running: "This chat is still running. Wait for the current work to finish, then try again.",
|
|
32
34
|
cleanup: "Cleanup did not finish.",
|
|
33
|
-
unsupported: "This runtime cannot complete that action."
|
|
35
|
+
unsupported: "This runtime cannot complete that action.",
|
|
36
|
+
retryPending: "Resend is incomplete. Send again to continue the same operation without creating another chat.",
|
|
37
|
+
retryUncertain: "The previous chat creation could not be confirmed. Another chat will not be created; check the session list for an existing new chat."
|
|
34
38
|
}
|
|
35
39
|
};
|
|
36
40
|
const text = {
|
|
@@ -48,6 +52,12 @@ const text = {
|
|
|
48
52
|
edit: "编辑并重发",
|
|
49
53
|
send: "发送",
|
|
50
54
|
editTitle: "编辑并重发",
|
|
55
|
+
editBranchWarning: "对话将从这条消息继续,之前的版本会移入归档。已执行的文件、命令和外部修改不会撤销,模型可能再次执行相关操作。",
|
|
56
|
+
editConfirm: "确认重发",
|
|
57
|
+
editAddAttachment: "添加附件",
|
|
58
|
+
editRemoveAttachment: "删除附件",
|
|
59
|
+
editImage: "图片",
|
|
60
|
+
editPreviewFailed: "预览不可用",
|
|
51
61
|
draft: "消息文本",
|
|
52
62
|
continueDefault: "继续",
|
|
53
63
|
welcomeBack: "欢迎回来",
|
|
@@ -78,6 +88,20 @@ const text = {
|
|
|
78
88
|
compactEnabledLabel: "建议压缩上下文",
|
|
79
89
|
compactEnabledHint: "长时间离开后,建议先压缩上下文。",
|
|
80
90
|
compactMinutesLabel: "离开多久后显示建议",
|
|
91
|
+
autoContinue: "自动继续",
|
|
92
|
+
autoContinueOff: "关闭",
|
|
93
|
+
autoContinueCautious: "谨慎",
|
|
94
|
+
autoContinueBold: "奔放",
|
|
95
|
+
autoContinueTooltip: "回复提前结束时自动发送继续。谨慎:有明确依据才继续;奔放:没完成就继续,低风险的选择按推荐方案走。会话开始后锁定。",
|
|
96
|
+
autoContinueLocked: "请在会话开始前选择自动继续",
|
|
97
|
+
autoContinueStale: "状态已在别处更改,已刷新",
|
|
98
|
+
autoContinueFailed: "切换失败",
|
|
99
|
+
autoContinueGroup: "自动继续",
|
|
100
|
+
autoContinueDefaultLabel: "新会话默认形态",
|
|
101
|
+
autoContinueDefaultHint: "只影响之后新建的会话;发送第一条消息前可在输入框上方单独选择,会话开始后保持锁定。",
|
|
102
|
+
autoContinueMaxLabel: "续跑次数上限",
|
|
103
|
+
autoContinueMaxHint: "你每发一条消息之后,最多自动继续的次数(1–10)。",
|
|
104
|
+
autoContinueMaxInvalid: "请填写 1–10 的整数。",
|
|
81
105
|
minutesUnit: "分钟",
|
|
82
106
|
modelLabel: "摘要模型",
|
|
83
107
|
modelFollowOption: "当前会话 / 宿主默认模型",
|
|
@@ -104,6 +128,12 @@ const text = {
|
|
|
104
128
|
edit: "Edit and resend",
|
|
105
129
|
send: "Send",
|
|
106
130
|
editTitle: "Edit and resend",
|
|
131
|
+
editBranchWarning: "The conversation continues from this message; the previous version moves to the archive. File, command, and external changes already made are not undone — the model may perform those actions again.",
|
|
132
|
+
editConfirm: "Confirm resend",
|
|
133
|
+
editAddAttachment: "Add attachments",
|
|
134
|
+
editRemoveAttachment: "Remove attachment",
|
|
135
|
+
editImage: "Image",
|
|
136
|
+
editPreviewFailed: "Preview unavailable",
|
|
107
137
|
draft: "Message text",
|
|
108
138
|
continueDefault: "Continue",
|
|
109
139
|
welcomeBack: "Welcome back",
|
|
@@ -134,6 +164,20 @@ const text = {
|
|
|
134
164
|
compactEnabledLabel: "Suggest context compaction",
|
|
135
165
|
compactEnabledHint: "Suggest compacting the context after a long time away.",
|
|
136
166
|
compactMinutesLabel: "Show suggestion after",
|
|
167
|
+
autoContinue: "Auto-continue",
|
|
168
|
+
autoContinueOff: "Off",
|
|
169
|
+
autoContinueCautious: "Cautious",
|
|
170
|
+
autoContinueBold: "Bold",
|
|
171
|
+
autoContinueTooltip: "Sends a follow-up when a reply stops early. Cautious: continues only on clear evidence. Bold: continues unless done, taking the recommended option on low-risk choices. Locked once the session starts.",
|
|
172
|
+
autoContinueLocked: "Choose Auto-continue before the session starts",
|
|
173
|
+
autoContinueStale: "State changed elsewhere; refreshed",
|
|
174
|
+
autoContinueFailed: "Could not change Auto-continue",
|
|
175
|
+
autoContinueGroup: "Auto-continue",
|
|
176
|
+
autoContinueDefaultLabel: "Default for new sessions",
|
|
177
|
+
autoContinueDefaultHint: "Only affects sessions created later. Choose above the composer before sending the first message; the choice stays locked once the session starts.",
|
|
178
|
+
autoContinueMaxLabel: "Maximum continuations",
|
|
179
|
+
autoContinueMaxHint: "How many times it may continue automatically after each message you send (1–10).",
|
|
180
|
+
autoContinueMaxInvalid: "Enter a whole number from 1 to 10.",
|
|
137
181
|
minutesUnit: "minutes",
|
|
138
182
|
modelLabel: "Recap model",
|
|
139
183
|
modelFollowOption: "Current conversation / host default",
|
|
@@ -151,7 +195,9 @@ function uiText(locale, key) {
|
|
|
151
195
|
return text[locale][key];
|
|
152
196
|
}
|
|
153
197
|
function failureReason(failure) {
|
|
154
|
-
if (failure.code === "session/
|
|
198
|
+
if (failure.code === "session/retry-pending") return "retryPending";
|
|
199
|
+
if (failure.code === "session/retry-uncertain") return "retryUncertain";
|
|
200
|
+
if (failure.code === "session/lifecycle-unavailable" || failure.code === "session/retry-media-unavailable" || failure.code === "session-manager/unavailable") return "unsupported";
|
|
155
201
|
if (failure.code === "session-manager/busy") return "running";
|
|
156
202
|
if (failure.code === "session/retry-revision" || failure.code === "session/branch-moved" || failure.code === "session/retry-input" || failure.code === "session/retry-conflict") return "stale";
|
|
157
203
|
if (failure.code === "session/delete-pending") return "cleanup";
|
|
@@ -189,11 +235,15 @@ const DELETE_DIALOG_ID = "@klarkxy/dsh-session-manager.delete-chat";
|
|
|
189
235
|
const WELCOME_BACK_PREPARE_ENDPOINT = "welcomeback.prepare";
|
|
190
236
|
const WELCOME_BACK_COMPACT_ENDPOINT = "welcomeback.compact";
|
|
191
237
|
const CONTINUE_SUGGEST_ENDPOINT = "continue.suggest";
|
|
238
|
+
const EDIT_PREPARE_ENDPOINT = "edit.prepare";
|
|
239
|
+
const EDIT_FILE_ENDPOINT = "edit.file";
|
|
192
240
|
const CONFIG_READ_ENDPOINT = "config.read";
|
|
193
241
|
const CONFIG_WRITE_ENDPOINT = "config.write";
|
|
242
|
+
const AUTO_CONTINUE_STATUS_ENDPOINT = "autocontinue.status";
|
|
243
|
+
const AUTO_CONTINUE_UPDATE_ENDPOINT = "autocontinue.update";
|
|
194
244
|
/** Card entry id in the chat flow; the recap renders at the latest completed turn's tail. */
|
|
195
245
|
const WELCOME_BACK_CARD_ID = "@klarkxy/dsh-session-manager.welcome-back";
|
|
196
|
-
const
|
|
246
|
+
const TURN_TAIL_SLOT = "conversation.chat.turnTail";
|
|
197
247
|
const CONTINUE_GHOST_ID = "@klarkxy/dsh-session-manager.continue-ghost";
|
|
198
248
|
const CONTINUE_GHOST_SLOT = "conversation.input.overlay";
|
|
199
249
|
//#endregion
|
|
@@ -1660,10 +1710,117 @@ ${modelMenuCss}
|
|
|
1660
1710
|
.${FREE_CHAT_ROOT} .dsh-free-actions {
|
|
1661
1711
|
display: inline-flex;
|
|
1662
1712
|
align-items: center;
|
|
1663
|
-
gap:
|
|
1713
|
+
gap: 8px;
|
|
1664
1714
|
min-width: 0;
|
|
1665
1715
|
}
|
|
1666
1716
|
.${FREE_CHAT_ROOT} .dsh-free-alert { margin: 0; }
|
|
1717
|
+
.${FREE_CHAT_ROOT} .dsh-free-warning {
|
|
1718
|
+
margin: 0 0 12px;
|
|
1719
|
+
color: var(--dsw-alias-label-secondary);
|
|
1720
|
+
}
|
|
1721
|
+
.${FREE_CHAT_ROOT}.dsm-user-row {
|
|
1722
|
+
display: flex;
|
|
1723
|
+
flex-direction: column;
|
|
1724
|
+
align-items: flex-end;
|
|
1725
|
+
gap: 6px;
|
|
1726
|
+
}
|
|
1727
|
+
.${FREE_CHAT_ROOT} .dsm-user-stack {
|
|
1728
|
+
display: flex;
|
|
1729
|
+
flex-direction: column;
|
|
1730
|
+
align-items: flex-end;
|
|
1731
|
+
gap: 8px;
|
|
1732
|
+
min-width: 0;
|
|
1733
|
+
max-width: 82%;
|
|
1734
|
+
}
|
|
1735
|
+
.${FREE_CHAT_ROOT} .dsm-user-bubble {
|
|
1736
|
+
max-width: 100%;
|
|
1737
|
+
padding: 10px 16px;
|
|
1738
|
+
border-radius: var(--dsw-radius-md);
|
|
1739
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
1740
|
+
color: var(--dsw-alias-label-primary);
|
|
1741
|
+
font-size: 14px;
|
|
1742
|
+
line-height: 22px;
|
|
1743
|
+
white-space: pre-wrap;
|
|
1744
|
+
word-break: break-word;
|
|
1745
|
+
}
|
|
1746
|
+
.${FREE_CHAT_ROOT} .dsm-user-chrome {
|
|
1747
|
+
display: flex;
|
|
1748
|
+
align-items: center;
|
|
1749
|
+
gap: 8px;
|
|
1750
|
+
min-height: 28px;
|
|
1751
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1752
|
+
}
|
|
1753
|
+
.${FREE_CHAT_ROOT} .dsm-user-time {
|
|
1754
|
+
font-size: 13px;
|
|
1755
|
+
white-space: nowrap;
|
|
1756
|
+
}
|
|
1757
|
+
.${FREE_CHAT_ROOT} .dsm-icon-action {
|
|
1758
|
+
display: inline-flex;
|
|
1759
|
+
align-items: center;
|
|
1760
|
+
justify-content: center;
|
|
1761
|
+
width: 28px;
|
|
1762
|
+
height: 28px;
|
|
1763
|
+
padding: 6px;
|
|
1764
|
+
border: none;
|
|
1765
|
+
border-radius: var(--dsw-radius-sm);
|
|
1766
|
+
background: transparent;
|
|
1767
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1768
|
+
cursor: pointer;
|
|
1769
|
+
}
|
|
1770
|
+
.${FREE_CHAT_ROOT} .dsm-icon-action:hover:not(:disabled) {
|
|
1771
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
1772
|
+
color: var(--dsw-alias-label-secondary);
|
|
1773
|
+
}
|
|
1774
|
+
.${FREE_CHAT_ROOT} .dsm-icon-action svg { width: 15px; height: 15px; }
|
|
1775
|
+
.${FREE_CHAT_ROOT} .dsm-user-attachments {
|
|
1776
|
+
display: flex;
|
|
1777
|
+
flex-wrap: wrap;
|
|
1778
|
+
justify-content: flex-end;
|
|
1779
|
+
max-width: 100%;
|
|
1780
|
+
gap: 8px;
|
|
1781
|
+
}
|
|
1782
|
+
.${FREE_CHAT_ROOT} .dsm-file-card {
|
|
1783
|
+
display: inline-flex;
|
|
1784
|
+
flex: 0 0 240px;
|
|
1785
|
+
align-items: center;
|
|
1786
|
+
gap: 10px;
|
|
1787
|
+
box-sizing: border-box;
|
|
1788
|
+
width: 240px;
|
|
1789
|
+
min-height: 64px;
|
|
1790
|
+
padding: 8px 12px;
|
|
1791
|
+
border: 0.5px solid var(--dsw-alias-border-l2);
|
|
1792
|
+
border-radius: var(--dsw-radius-md);
|
|
1793
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
1794
|
+
}
|
|
1795
|
+
.${FREE_CHAT_ROOT} .dsm-file-icon { flex: none; width: 28px; height: 28px; }
|
|
1796
|
+
.${FREE_CHAT_ROOT} .dsm-file-content {
|
|
1797
|
+
display: flex;
|
|
1798
|
+
flex: 1;
|
|
1799
|
+
flex-direction: column;
|
|
1800
|
+
min-width: 0;
|
|
1801
|
+
}
|
|
1802
|
+
.${FREE_CHAT_ROOT} .dsm-file-name {
|
|
1803
|
+
overflow: hidden;
|
|
1804
|
+
color: var(--dsw-alias-label-primary);
|
|
1805
|
+
font-size: 14px;
|
|
1806
|
+
font-weight: 500;
|
|
1807
|
+
line-height: 22px;
|
|
1808
|
+
white-space: nowrap;
|
|
1809
|
+
text-overflow: ellipsis;
|
|
1810
|
+
}
|
|
1811
|
+
.${FREE_CHAT_ROOT} .dsm-file-meta {
|
|
1812
|
+
overflow: hidden;
|
|
1813
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1814
|
+
font-size: 12px;
|
|
1815
|
+
line-height: 15px;
|
|
1816
|
+
white-space: nowrap;
|
|
1817
|
+
text-overflow: ellipsis;
|
|
1818
|
+
}
|
|
1819
|
+
.${FREE_CHAT_ROOT} .dsm-reference-summary {
|
|
1820
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1821
|
+
font-size: 13px;
|
|
1822
|
+
line-height: 18px;
|
|
1823
|
+
}
|
|
1667
1824
|
/* Settings page: one quiet reading surface. Rules, not nested wells, separate
|
|
1668
1825
|
* sections; dependent controls align with the feature they belong to. */
|
|
1669
1826
|
.${FREE_CHAT_ROOT} .dsm-settings {
|
|
@@ -1803,6 +1960,66 @@ ${modelMenuCss}
|
|
|
1803
1960
|
color: var(--dsw-alias-label-secondary);
|
|
1804
1961
|
}
|
|
1805
1962
|
.${FREE_CHAT_ROOT} .dsh-welcome-done { margin: 6px 0 0; }
|
|
1963
|
+
|
|
1964
|
+
/* Hero-row Auto-continue chip: icon + mode label, tinted while a mode is on. */
|
|
1965
|
+
.${FREE_CHAT_ROOT}.smac-option {
|
|
1966
|
+
display: inline-flex;
|
|
1967
|
+
align-items: center;
|
|
1968
|
+
gap: 6px;
|
|
1969
|
+
}
|
|
1970
|
+
.${FREE_CHAT_ROOT} .smac-chip {
|
|
1971
|
+
display: inline-flex;
|
|
1972
|
+
align-items: center;
|
|
1973
|
+
gap: 4px;
|
|
1974
|
+
height: 28px;
|
|
1975
|
+
padding: 0 10px;
|
|
1976
|
+
border-radius: 999px;
|
|
1977
|
+
font-size: 13px;
|
|
1978
|
+
line-height: 20px;
|
|
1979
|
+
border: 1px solid var(--dsw-alias-border-l3);
|
|
1980
|
+
background: transparent;
|
|
1981
|
+
color: var(--dsw-alias-label-secondary);
|
|
1982
|
+
cursor: pointer;
|
|
1983
|
+
}
|
|
1984
|
+
.${FREE_CHAT_ROOT} .smac-chip svg { width: 16px; height: 16px; }
|
|
1985
|
+
.${FREE_CHAT_ROOT} .smac-chip .smac-chevron { width: 12px; height: 12px; }
|
|
1986
|
+
.${FREE_CHAT_ROOT} .smac-chip:hover:not(:disabled) {
|
|
1987
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
1988
|
+
}
|
|
1989
|
+
.${FREE_CHAT_ROOT} .smac-chip.smac-chip-on {
|
|
1990
|
+
background: var(--dsw-alias-button-primary-fill);
|
|
1991
|
+
border-color: var(--dsw-alias-button-primary-fill);
|
|
1992
|
+
color: var(--dsw-alias-label-primary-foreground);
|
|
1993
|
+
}
|
|
1994
|
+
.${FREE_CHAT_ROOT} .smac-chip.smac-chip-on:hover:not(:disabled) {
|
|
1995
|
+
background: var(--dsw-alias-button-primary-hover);
|
|
1996
|
+
border-color: var(--dsw-alias-button-primary-hover);
|
|
1997
|
+
}
|
|
1998
|
+
.${FREE_CHAT_ROOT} .smac-chip:disabled {
|
|
1999
|
+
opacity: 0.5;
|
|
2000
|
+
cursor: default;
|
|
2001
|
+
}
|
|
2002
|
+
.${FREE_CHAT_ROOT} .smac-note {
|
|
2003
|
+
font-size: 12px;
|
|
2004
|
+
line-height: 18px;
|
|
2005
|
+
color: var(--dsw-alias-label-tertiary);
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
.${FREE_CHAT_ROOT} .dsm-user-original { display: contents; }
|
|
2009
|
+
.${FREE_CHAT_ROOT} .dsm-user-original[hidden] { display: none; }
|
|
2010
|
+
.${FREE_CHAT_ROOT} .dsm-message-edit { width: min(100%, 720px); align-self: flex-end; }
|
|
2011
|
+
.${FREE_CHAT_ROOT} .dsm-message-editor { width: 100%; box-sizing: border-box; padding: 14px; border-radius: 18px; background: var(--dsw-alias-bg-layer-3); }
|
|
2012
|
+
.${FREE_CHAT_ROOT} .dsm-message-editor .dsh-free-draft { width: 100%; min-height: 80px; max-height: 360px; resize: vertical; border: none; background: transparent; box-shadow: none; padding: 4px; box-sizing: border-box; }
|
|
2013
|
+
.${FREE_CHAT_ROOT} .dsm-edit-footer { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
|
|
2014
|
+
.${FREE_CHAT_ROOT} .dsm-edit-spacer { flex: 1; }
|
|
2015
|
+
.${FREE_CHAT_ROOT} .dsm-edit-attachments { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
|
|
2016
|
+
.${FREE_CHAT_ROOT} .dsm-edit-attachment { display: flex; align-items: center; gap: 6px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; padding: 6px; max-width: 100%; }
|
|
2017
|
+
.${FREE_CHAT_ROOT} .dsm-edit-attachment img { width: 72px; height: 60px; object-fit: contain; border-radius: 6px; }
|
|
2018
|
+
.${FREE_CHAT_ROOT} .dsm-edit-attachment > span { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2019
|
+
.${FREE_CHAT_ROOT} .dsm-edit-attachment > svg { width: 24px; height: 24px; }
|
|
2020
|
+
.${FREE_CHAT_ROOT} .dsm-edit-image { position: relative; padding: 0; border: none; }
|
|
2021
|
+
.${FREE_CHAT_ROOT} .dsm-edit-image img { width: auto; height: auto; max-width: 220px; max-height: 140px; object-fit: contain; border-radius: 10px; }
|
|
2022
|
+
.${FREE_CHAT_ROOT} .dsm-edit-image > button { position: absolute; top: 4px; right: 4px; border-radius: 50%; background: var(--dsw-alias-bg-layer-3); width: 24px; height: 24px; padding: 0; }
|
|
1806
2023
|
`;
|
|
1807
2024
|
//#endregion
|
|
1808
2025
|
//#region src/free-panel.tsx
|
|
@@ -2189,11 +2406,15 @@ function bindTemporaryWorkspaceHost(ctx) {
|
|
|
2189
2406
|
//#region src/session-actions.ts
|
|
2190
2407
|
const UNSUPPORTED_CAPABILITIES = {
|
|
2191
2408
|
retry: false,
|
|
2409
|
+
retryModified: false,
|
|
2192
2410
|
freeSession: false,
|
|
2193
2411
|
deleteSession: false
|
|
2194
2412
|
};
|
|
2195
2413
|
function isRecoverable(failure) {
|
|
2196
|
-
return failure.code === "transport" || failure.code === "session/delete-pending";
|
|
2414
|
+
return failure.code === "transport" || failure.code === "session/delete-pending" || failure.code === "session/retry-pending" || failure.code === "session/retry-uncertain";
|
|
2415
|
+
}
|
|
2416
|
+
function isStaleRetryFailure(failure) {
|
|
2417
|
+
return failure?.code === "session/retry-revision" || failure?.code === "session/branch-moved" || failure?.code === "session/retry-input" || failure?.code === "session/retry-conflict";
|
|
2197
2418
|
}
|
|
2198
2419
|
function readFailure(error) {
|
|
2199
2420
|
if (isFailure(error)) return {
|
|
@@ -2254,6 +2475,8 @@ async function readCapabilities(host) {
|
|
|
2254
2475
|
const record = value;
|
|
2255
2476
|
return {
|
|
2256
2477
|
retry: record.retry === true,
|
|
2478
|
+
retryModified: record.retryModified === true,
|
|
2479
|
+
...record.retryContent === true ? { retryContent: true } : {},
|
|
2257
2480
|
freeSession: record.freeSession === true,
|
|
2258
2481
|
deleteSession: record.deleteSession === true
|
|
2259
2482
|
};
|
|
@@ -2286,19 +2509,16 @@ async function deleteOwnedSession(host, sessionId, requestId) {
|
|
|
2286
2509
|
if (phase !== "deleted" && phase !== "cleanup-pending") throw new HostOperationError("session/delete-pending", "missing-outcome");
|
|
2287
2510
|
return { phase };
|
|
2288
2511
|
}
|
|
2289
|
-
|
|
2512
|
+
/** Build the body for one edited prompt; the host keeps the original attachments when only text changes. */
|
|
2513
|
+
function editRetryRequest(input) {
|
|
2290
2514
|
return {
|
|
2291
2515
|
sessionId: input.sessionId,
|
|
2292
2516
|
messageId: input.messageId,
|
|
2293
2517
|
expectedRevision: input.expectedRevision,
|
|
2294
2518
|
expectedInputRevision: input.expectedInputRevision,
|
|
2295
|
-
requestId: input.requestId
|
|
2296
|
-
|
|
2297
|
-
}
|
|
2298
|
-
function editRetryRequest(input) {
|
|
2299
|
-
return {
|
|
2300
|
-
...directRetryRequest(input),
|
|
2301
|
-
text: input.text
|
|
2519
|
+
requestId: input.requestId,
|
|
2520
|
+
...input.content === void 0 ? { text: input.text } : { content: input.content },
|
|
2521
|
+
...input.allowModified === true ? { allowModified: true } : {}
|
|
2302
2522
|
};
|
|
2303
2523
|
}
|
|
2304
2524
|
var SubmissionLedger = class {
|
|
@@ -2483,6 +2703,8 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2483
2703
|
let ready = false;
|
|
2484
2704
|
let busy = false;
|
|
2485
2705
|
let editing = false;
|
|
2706
|
+
let modified = false;
|
|
2707
|
+
let editNeedsPreview = false;
|
|
2486
2708
|
let draft = "";
|
|
2487
2709
|
let failure;
|
|
2488
2710
|
let expectedRevision = 0;
|
|
@@ -2494,8 +2716,10 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2494
2716
|
function build() {
|
|
2495
2717
|
return {
|
|
2496
2718
|
controlsRetry: controlsFor(capabilities, ready, host).retry,
|
|
2719
|
+
controlsContent: ready && capabilities.retryContent === true,
|
|
2497
2720
|
busy,
|
|
2498
2721
|
editing,
|
|
2722
|
+
modified,
|
|
2499
2723
|
draft,
|
|
2500
2724
|
sessionId: ownerSession,
|
|
2501
2725
|
messageId: ownerMessage,
|
|
@@ -2511,7 +2735,7 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2511
2735
|
const record = value;
|
|
2512
2736
|
const inputRevision = typeof record.inputRevision === "string" ? record.inputRevision : "";
|
|
2513
2737
|
const eligible = record.eligible === true;
|
|
2514
|
-
if (eligible && inputRevision === "") throw new HostOperationError("session/lifecycle-unavailable", "input-revision");
|
|
2738
|
+
if ((eligible || record.reason === "modified") && inputRevision === "") throw new HostOperationError("session/lifecycle-unavailable", "input-revision");
|
|
2515
2739
|
return {
|
|
2516
2740
|
branchRevision: record.branchRevision,
|
|
2517
2741
|
inputRevision,
|
|
@@ -2549,7 +2773,11 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2549
2773
|
await ledger.replay(async (body) => {
|
|
2550
2774
|
await callRetry(body, signal, ticket);
|
|
2551
2775
|
});
|
|
2552
|
-
if (pending !== void 0 && ticket === call && !signal.aborted && ownerSession === pending.sessionId && ownerMessage === pending.messageId)
|
|
2776
|
+
if (pending !== void 0 && ticket === call && !signal.aborted && ownerSession === pending.sessionId && ownerMessage === pending.messageId) {
|
|
2777
|
+
editing = false;
|
|
2778
|
+
modified = false;
|
|
2779
|
+
editNeedsPreview = false;
|
|
2780
|
+
}
|
|
2553
2781
|
} catch (error) {
|
|
2554
2782
|
if (ticket === call && !signal.aborted) failure = readFailure(error);
|
|
2555
2783
|
} finally {
|
|
@@ -2568,8 +2796,9 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2568
2796
|
ready = true;
|
|
2569
2797
|
publish();
|
|
2570
2798
|
},
|
|
2571
|
-
async
|
|
2799
|
+
async beginEdit(sessionId, id, text, signal) {
|
|
2572
2800
|
if (busy || !controlsFor(capabilities, ready, host).retry || signal.aborted) return;
|
|
2801
|
+
if (editing && (ownerSession !== sessionId || ownerMessage !== id)) return;
|
|
2573
2802
|
if (ledger.pending() !== void 0) {
|
|
2574
2803
|
if (!samePending(sessionId, id)) return;
|
|
2575
2804
|
await replaySame(signal);
|
|
@@ -2578,63 +2807,25 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2578
2807
|
const ticket = ++call;
|
|
2579
2808
|
busy = true;
|
|
2580
2809
|
failure = void 0;
|
|
2581
|
-
|
|
2810
|
+
modified = false;
|
|
2811
|
+
editNeedsPreview = false;
|
|
2582
2812
|
ownerSession = sessionId;
|
|
2583
2813
|
ownerMessage = id;
|
|
2584
2814
|
publish();
|
|
2585
2815
|
try {
|
|
2586
2816
|
const seen = await preview(sessionId, id, signal, ticket);
|
|
2587
2817
|
if (seen === void 0) return;
|
|
2588
|
-
if (!seen.eligible) {
|
|
2589
|
-
failure = {
|
|
2590
|
-
code: "session/retry-blocked",
|
|
2591
|
-
reason: seen.reason ?? "unknown"
|
|
2592
|
-
};
|
|
2593
|
-
return;
|
|
2594
|
-
}
|
|
2595
2818
|
if (signal.aborted || ticket !== call) return;
|
|
2596
2819
|
expectedRevision = seen.branchRevision;
|
|
2597
2820
|
expectedInputRevision = seen.inputRevision;
|
|
2598
|
-
|
|
2599
|
-
sessionId,
|
|
2600
|
-
messageId: id,
|
|
2601
|
-
expectedRevision: seen.branchRevision,
|
|
2602
|
-
expectedInputRevision: seen.inputRevision,
|
|
2603
|
-
requestId: ids()
|
|
2604
|
-
});
|
|
2605
|
-
await ledger.submit(body, () => callRetry(body, signal, ticket));
|
|
2606
|
-
} catch (error) {
|
|
2607
|
-
if (ticket === call && !signal.aborted) failure = readFailure(error);
|
|
2608
|
-
} finally {
|
|
2609
|
-
settle();
|
|
2610
|
-
}
|
|
2611
|
-
},
|
|
2612
|
-
async beginEdit(sessionId, id, text, signal) {
|
|
2613
|
-
if (busy || !controlsFor(capabilities, ready, host).retry || signal.aborted) return;
|
|
2614
|
-
if (ledger.pending() !== void 0) {
|
|
2615
|
-
if (!samePending(sessionId, id)) return;
|
|
2616
|
-
await replaySame(signal);
|
|
2617
|
-
return;
|
|
2618
|
-
}
|
|
2619
|
-
const ticket = ++call;
|
|
2620
|
-
busy = true;
|
|
2621
|
-
failure = void 0;
|
|
2622
|
-
ownerSession = sessionId;
|
|
2623
|
-
ownerMessage = id;
|
|
2624
|
-
publish();
|
|
2625
|
-
try {
|
|
2626
|
-
const seen = await preview(sessionId, id, signal, ticket);
|
|
2627
|
-
if (seen === void 0) return;
|
|
2628
|
-
if (!seen.eligible) {
|
|
2821
|
+
if (!seen.eligible && (seen.reason !== "modified" || !capabilities.retryModified)) {
|
|
2629
2822
|
failure = {
|
|
2630
2823
|
code: "session/retry-blocked",
|
|
2631
2824
|
reason: seen.reason ?? "unknown"
|
|
2632
2825
|
};
|
|
2633
2826
|
return;
|
|
2634
2827
|
}
|
|
2635
|
-
|
|
2636
|
-
expectedRevision = seen.branchRevision;
|
|
2637
|
-
expectedInputRevision = seen.inputRevision;
|
|
2828
|
+
modified = !seen.eligible;
|
|
2638
2829
|
draft = text;
|
|
2639
2830
|
editing = true;
|
|
2640
2831
|
} catch (error) {
|
|
@@ -2651,9 +2842,11 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2651
2842
|
cancelEdit() {
|
|
2652
2843
|
if (busy) return;
|
|
2653
2844
|
editing = false;
|
|
2845
|
+
modified = false;
|
|
2846
|
+
editNeedsPreview = false;
|
|
2654
2847
|
publish();
|
|
2655
2848
|
},
|
|
2656
|
-
async sendEdit(sessionId, id, signal) {
|
|
2849
|
+
async sendEdit(sessionId, id, signal, content, confirmed) {
|
|
2657
2850
|
if (signal.aborted || busy || !controlsFor(capabilities, ready, host).retry) return;
|
|
2658
2851
|
if (!editing || ownerSession !== sessionId || ownerMessage !== id) return;
|
|
2659
2852
|
if (ledger.pending() !== void 0) {
|
|
@@ -2663,23 +2856,49 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2663
2856
|
}
|
|
2664
2857
|
const ticket = ++call;
|
|
2665
2858
|
busy = true;
|
|
2859
|
+
const refresh = editNeedsPreview || isStaleRetryFailure(failure);
|
|
2666
2860
|
failure = void 0;
|
|
2667
2861
|
const text = draft;
|
|
2668
2862
|
publish();
|
|
2669
2863
|
try {
|
|
2670
2864
|
if (signal.aborted || ticket !== call) return;
|
|
2865
|
+
if (refresh) {
|
|
2866
|
+
editNeedsPreview = true;
|
|
2867
|
+
const seen = await preview(sessionId, id, signal, ticket);
|
|
2868
|
+
if (seen === void 0) return;
|
|
2869
|
+
expectedRevision = seen.branchRevision;
|
|
2870
|
+
expectedInputRevision = seen.inputRevision;
|
|
2871
|
+
if (!seen.eligible && (seen.reason !== "modified" || !capabilities.retryModified)) {
|
|
2872
|
+
failure = {
|
|
2873
|
+
code: "session/retry-blocked",
|
|
2874
|
+
reason: seen.reason ?? "unknown"
|
|
2875
|
+
};
|
|
2876
|
+
return;
|
|
2877
|
+
}
|
|
2878
|
+
modified = !seen.eligible;
|
|
2879
|
+
editNeedsPreview = false;
|
|
2880
|
+
}
|
|
2881
|
+
if (content !== void 0 && capabilities.retryContent !== true) throw new HostOperationError("session/lifecycle-unavailable");
|
|
2671
2882
|
const body = editRetryRequest({
|
|
2672
2883
|
sessionId,
|
|
2673
2884
|
messageId: id,
|
|
2674
2885
|
expectedRevision,
|
|
2675
2886
|
expectedInputRevision,
|
|
2676
2887
|
requestId: ids(),
|
|
2677
|
-
text
|
|
2888
|
+
text,
|
|
2889
|
+
...content === void 0 ? {} : { content },
|
|
2890
|
+
allowModified: confirmed === true
|
|
2678
2891
|
});
|
|
2679
2892
|
await ledger.submit(body, () => callRetry(body, signal, ticket));
|
|
2680
|
-
if (ticket === call && !signal.aborted)
|
|
2893
|
+
if (ticket === call && !signal.aborted) {
|
|
2894
|
+
editing = false;
|
|
2895
|
+
modified = false;
|
|
2896
|
+
editNeedsPreview = false;
|
|
2897
|
+
}
|
|
2681
2898
|
} catch (error) {
|
|
2682
|
-
|
|
2899
|
+
const nextFailure = readFailure(error);
|
|
2900
|
+
if (isStaleRetryFailure(nextFailure)) editNeedsPreview = true;
|
|
2901
|
+
if (ticket === call && !signal.aborted) failure = nextFailure;
|
|
2683
2902
|
} finally {
|
|
2684
2903
|
settle();
|
|
2685
2904
|
}
|
|
@@ -2688,6 +2907,8 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2688
2907
|
if (ownerSession !== sessionId || ownerMessage !== messageId) return;
|
|
2689
2908
|
call += 1;
|
|
2690
2909
|
editing = false;
|
|
2910
|
+
modified = false;
|
|
2911
|
+
editNeedsPreview = false;
|
|
2691
2912
|
failure = void 0;
|
|
2692
2913
|
draft = "";
|
|
2693
2914
|
if (!ledger.busy) busy = false;
|
|
@@ -2696,6 +2917,8 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2696
2917
|
cancel() {
|
|
2697
2918
|
call += 1;
|
|
2698
2919
|
editing = false;
|
|
2920
|
+
modified = false;
|
|
2921
|
+
editNeedsPreview = false;
|
|
2699
2922
|
failure = void 0;
|
|
2700
2923
|
if (!ledger.busy) busy = false;
|
|
2701
2924
|
publish();
|
|
@@ -3192,6 +3415,175 @@ function createWelcomeBackModel(host, ids = () => crypto.randomUUID(), minInterv
|
|
|
3192
3415
|
};
|
|
3193
3416
|
}
|
|
3194
3417
|
//#endregion
|
|
3418
|
+
//#region src/auto-continue-option.tsx
|
|
3419
|
+
/**
|
|
3420
|
+
* The hero-row Auto-continue option: a chip that opens a three-mode menu on a
|
|
3421
|
+
* blank session and locks once the session starts. Mirrors the delegation
|
|
3422
|
+
* chip's revision discipline: stale reads never roll the chip back, a 'stale'
|
|
3423
|
+
* rejection refetches, and 'session-locked' explains itself in the tooltip.
|
|
3424
|
+
*/
|
|
3425
|
+
var RpcError = class extends Error {
|
|
3426
|
+
code;
|
|
3427
|
+
constructor(code, message) {
|
|
3428
|
+
super(message);
|
|
3429
|
+
this.name = "RpcError";
|
|
3430
|
+
this.code = code;
|
|
3431
|
+
}
|
|
3432
|
+
};
|
|
3433
|
+
async function rpc(call, endpoint, payload) {
|
|
3434
|
+
const result = await call(endpoint, payload);
|
|
3435
|
+
if (!result || result.ok !== true) {
|
|
3436
|
+
const failed = result && "error" in result ? result.error : void 0;
|
|
3437
|
+
throw new RpcError(failed?.code ?? "unknown", failed?.message ?? "");
|
|
3438
|
+
}
|
|
3439
|
+
return result.value;
|
|
3440
|
+
}
|
|
3441
|
+
const MODES = [
|
|
3442
|
+
"off",
|
|
3443
|
+
"cautious",
|
|
3444
|
+
"bold"
|
|
3445
|
+
];
|
|
3446
|
+
function AutoContinueOption({ sessionId, call, subscribeGeneration, useSession, locale }) {
|
|
3447
|
+
const [status, setStatus] = (0, react.useState)();
|
|
3448
|
+
const [saving, setSaving] = (0, react.useState)(false);
|
|
3449
|
+
const [notice, setNotice] = (0, react.useState)("");
|
|
3450
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
3451
|
+
const generationRef = (0, react.useRef)(0);
|
|
3452
|
+
const readSeqRef = (0, react.useRef)(0);
|
|
3453
|
+
const openState = useSession?.((snapshot) => snapshot.openState);
|
|
3454
|
+
const sessionRunning = useSession?.((snapshot) => snapshot.running === true);
|
|
3455
|
+
const sessionBlank = useSession?.((snapshot) => snapshot.blank);
|
|
3456
|
+
const submitting = useSession?.((snapshot) => snapshot.awaitingFirstTurn === true || (snapshot.pendingSubmissions?.length ?? 0) > 0);
|
|
3457
|
+
const load = (0, react.useCallback)(async () => {
|
|
3458
|
+
const generation = generationRef.current;
|
|
3459
|
+
const seq = ++readSeqRef.current;
|
|
3460
|
+
try {
|
|
3461
|
+
const next = await rpc(call, AUTO_CONTINUE_STATUS_ENDPOINT, { sessionId });
|
|
3462
|
+
if (generationRef.current !== generation || seq !== readSeqRef.current) return;
|
|
3463
|
+
setStatus((prev) => prev && prev.sessionId === next.sessionId && next.revision < prev.revision ? prev : next);
|
|
3464
|
+
} catch {}
|
|
3465
|
+
}, [call, sessionId]);
|
|
3466
|
+
(0, react.useEffect)(() => {
|
|
3467
|
+
generationRef.current += 1;
|
|
3468
|
+
readSeqRef.current += 1;
|
|
3469
|
+
setStatus(void 0);
|
|
3470
|
+
setSaving(false);
|
|
3471
|
+
setNotice("");
|
|
3472
|
+
setOpen(false);
|
|
3473
|
+
}, [sessionId]);
|
|
3474
|
+
(0, react.useEffect)(() => {
|
|
3475
|
+
load();
|
|
3476
|
+
}, [
|
|
3477
|
+
load,
|
|
3478
|
+
openState,
|
|
3479
|
+
sessionRunning,
|
|
3480
|
+
sessionBlank
|
|
3481
|
+
]);
|
|
3482
|
+
(0, react.useEffect)(() => {
|
|
3483
|
+
const off = subscribeGeneration?.(() => {
|
|
3484
|
+
load();
|
|
3485
|
+
});
|
|
3486
|
+
const win = typeof window === "undefined" ? void 0 : window;
|
|
3487
|
+
const onFocus = () => {
|
|
3488
|
+
load();
|
|
3489
|
+
};
|
|
3490
|
+
win?.addEventListener("focus", onFocus);
|
|
3491
|
+
return () => {
|
|
3492
|
+
off?.();
|
|
3493
|
+
win?.removeEventListener("focus", onFocus);
|
|
3494
|
+
};
|
|
3495
|
+
}, [load, subscribeGeneration]);
|
|
3496
|
+
(0, react.useEffect)(() => {
|
|
3497
|
+
if (!notice) return;
|
|
3498
|
+
const timer = setTimeout(() => setNotice(""), 4e3);
|
|
3499
|
+
return () => clearTimeout(timer);
|
|
3500
|
+
}, [notice]);
|
|
3501
|
+
const choose = (0, react.useCallback)(async (mode) => {
|
|
3502
|
+
if (!status || saving || status.locked || sessionBlank === false || sessionRunning || submitting || mode === status.mode) return;
|
|
3503
|
+
const generation = generationRef.current;
|
|
3504
|
+
readSeqRef.current += 1;
|
|
3505
|
+
setSaving(true);
|
|
3506
|
+
setNotice("");
|
|
3507
|
+
try {
|
|
3508
|
+
const next = await rpc(call, AUTO_CONTINUE_UPDATE_ENDPOINT, {
|
|
3509
|
+
sessionId,
|
|
3510
|
+
mode,
|
|
3511
|
+
expectedRevision: status.revision
|
|
3512
|
+
});
|
|
3513
|
+
if (generationRef.current !== generation) return;
|
|
3514
|
+
setStatus(next);
|
|
3515
|
+
} catch (reason) {
|
|
3516
|
+
if (generationRef.current !== generation) return;
|
|
3517
|
+
if (reason instanceof RpcError && (reason.code === "stale" || reason.code === "session-locked")) {
|
|
3518
|
+
setNotice(reason.code === "stale" ? "autoContinueStale" : "autoContinueLocked");
|
|
3519
|
+
load();
|
|
3520
|
+
} else setNotice("autoContinueFailed");
|
|
3521
|
+
} finally {
|
|
3522
|
+
if (generationRef.current === generation) setSaving(false);
|
|
3523
|
+
}
|
|
3524
|
+
}, [
|
|
3525
|
+
call,
|
|
3526
|
+
load,
|
|
3527
|
+
saving,
|
|
3528
|
+
sessionId,
|
|
3529
|
+
status,
|
|
3530
|
+
sessionBlank,
|
|
3531
|
+
sessionRunning,
|
|
3532
|
+
submitting
|
|
3533
|
+
]);
|
|
3534
|
+
if (!status || status.role === "executor" || sessionBlank === false) return null;
|
|
3535
|
+
const mode = status.mode;
|
|
3536
|
+
const locked = status.locked || sessionRunning === true || submitting === true;
|
|
3537
|
+
const tooltip = locked ? uiText(locale, "autoContinueLocked") : notice ? uiText(locale, notice) : uiText(locale, "autoContinueTooltip");
|
|
3538
|
+
const modeKey = mode === "cautious" ? "autoContinueCautious" : mode === "bold" ? "autoContinueBold" : "autoContinueOff";
|
|
3539
|
+
const label = `${uiText(locale, "autoContinue")} · ${uiText(locale, modeKey)}`;
|
|
3540
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3541
|
+
className: `${FREE_CHAT_ROOT} smac-option`,
|
|
3542
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
3543
|
+
open,
|
|
3544
|
+
align: "end",
|
|
3545
|
+
portal: true,
|
|
3546
|
+
listClassName: FREE_CHAT_ROOT,
|
|
3547
|
+
selectedId: mode,
|
|
3548
|
+
items: MODES.map((value) => ({
|
|
3549
|
+
id: value,
|
|
3550
|
+
label: uiText(locale, value === "off" ? "autoContinueOff" : value === "cautious" ? "autoContinueCautious" : "autoContinueBold")
|
|
3551
|
+
})),
|
|
3552
|
+
onSelect: (id) => {
|
|
3553
|
+
setOpen(false);
|
|
3554
|
+
choose(id);
|
|
3555
|
+
},
|
|
3556
|
+
onClose: () => setOpen(false),
|
|
3557
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
3558
|
+
label: tooltip,
|
|
3559
|
+
side: "bottom",
|
|
3560
|
+
gap: 4,
|
|
3561
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
|
|
3562
|
+
className: `smac-chip${mode !== "off" ? " smac-chip-on" : ""}`,
|
|
3563
|
+
active: mode !== "off",
|
|
3564
|
+
"aria-haspopup": "menu",
|
|
3565
|
+
"aria-expanded": open,
|
|
3566
|
+
"aria-label": label,
|
|
3567
|
+
disabled: locked || saving,
|
|
3568
|
+
onClick: () => setOpen((current) => !current),
|
|
3569
|
+
children: [
|
|
3570
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, {}),
|
|
3571
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3572
|
+
className: "smac-chip-label",
|
|
3573
|
+
children: label
|
|
3574
|
+
}),
|
|
3575
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: "smac-chevron" })
|
|
3576
|
+
]
|
|
3577
|
+
})
|
|
3578
|
+
})
|
|
3579
|
+
}), notice ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3580
|
+
role: "status",
|
|
3581
|
+
className: "smac-note",
|
|
3582
|
+
children: uiText(locale, notice)
|
|
3583
|
+
}) : null]
|
|
3584
|
+
});
|
|
3585
|
+
}
|
|
3586
|
+
//#endregion
|
|
3195
3587
|
//#region ../dsh-plugin-kit/src/contracts.ts
|
|
3196
3588
|
/** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */
|
|
3197
3589
|
function modelSettingsChangesIdentity(packageName) {
|
|
@@ -3618,6 +4010,8 @@ function toDraft(values) {
|
|
|
3618
4010
|
compactMinutes: String(compactEnabled ? values.compactMinutes : 60),
|
|
3619
4011
|
continueText: values.continueText,
|
|
3620
4012
|
continueMode: values.continueMode,
|
|
4013
|
+
autoContinueMode: values.autoContinueMode,
|
|
4014
|
+
autoContinueMax: String(values.autoContinueMax),
|
|
3621
4015
|
model: values.model === null ? "" : modelRouteKey(values.model.provider, values.model.model),
|
|
3622
4016
|
ghostModel: values.ghostModel === null ? "" : modelRouteKey(values.ghostModel.provider, values.ghostModel.model)
|
|
3623
4017
|
};
|
|
@@ -3628,8 +4022,14 @@ function minutesOf(text) {
|
|
|
3628
4022
|
const value = Number.parseInt(text.trim(), 10);
|
|
3629
4023
|
return Number.isSafeInteger(value) && value >= 1 ? value : void 0;
|
|
3630
4024
|
}
|
|
4025
|
+
/** Automatic continuations per human message: a whole number from 1 to 10. */
|
|
4026
|
+
function autoMaxOf(text) {
|
|
4027
|
+
if (!/^\d+$/.test(text.trim())) return void 0;
|
|
4028
|
+
const value = Number.parseInt(text.trim(), 10);
|
|
4029
|
+
return Number.isSafeInteger(value) && value >= 1 && value <= 10 ? value : void 0;
|
|
4030
|
+
}
|
|
3631
4031
|
function sameDraft(left, right) {
|
|
3632
|
-
return left.recapEnabled === right.recapEnabled && left.recapMinutes === right.recapMinutes && left.compactEnabled === right.compactEnabled && left.compactMinutes === right.compactMinutes && left.continueText === right.continueText && left.continueMode === right.continueMode && left.model === right.model && left.ghostModel === right.ghostModel;
|
|
4032
|
+
return left.recapEnabled === right.recapEnabled && left.recapMinutes === right.recapMinutes && left.compactEnabled === right.compactEnabled && left.compactMinutes === right.compactMinutes && left.continueText === right.continueText && left.continueMode === right.continueMode && left.autoContinueMode === right.autoContinueMode && left.autoContinueMax === right.autoContinueMax && left.model === right.model && left.ghostModel === right.ghostModel;
|
|
3633
4033
|
}
|
|
3634
4034
|
const missingCatalog = async () => {
|
|
3635
4035
|
throw new Error("Model catalog service unavailable");
|
|
@@ -3688,9 +4088,11 @@ function SessionManagerSettings(props) {
|
|
|
3688
4088
|
const ghostLabel = selectableChoices.find((choice) => modelRouteKey(choice.provider, choice.model) === draft.ghostModel)?.label ?? copy("modelFollowOption");
|
|
3689
4089
|
const recapMinutes = draft.recapEnabled ? minutesOf(draft.recapMinutes) : 0;
|
|
3690
4090
|
const compactMinutes = draft.compactEnabled ? minutesOf(draft.compactMinutes) : 0;
|
|
4091
|
+
const autoContinueMax = autoMaxOf(draft.autoContinueMax);
|
|
3691
4092
|
const recapInvalid = recapMinutes === void 0;
|
|
3692
4093
|
const compactInvalid = compactMinutes === void 0;
|
|
3693
|
-
const
|
|
4094
|
+
const autoMaxInvalid = autoContinueMax === void 0;
|
|
4095
|
+
const invalid = recapInvalid || compactInvalid || autoMaxInvalid;
|
|
3694
4096
|
const fixedPanelId = `${tabsId}-fixed-panel`;
|
|
3695
4097
|
const autoPanelId = `${tabsId}-auto-panel`;
|
|
3696
4098
|
const save = () => {
|
|
@@ -3703,6 +4105,8 @@ function SessionManagerSettings(props) {
|
|
|
3703
4105
|
compactMinutes,
|
|
3704
4106
|
continueText: draft.continueText,
|
|
3705
4107
|
continueMode: draft.continueMode,
|
|
4108
|
+
autoContinueMode: draft.autoContinueMode,
|
|
4109
|
+
autoContinueMax,
|
|
3706
4110
|
...draft.model === baseline.current?.model ? {} : { model: route === void 0 ? null : {
|
|
3707
4111
|
provider: route.provider,
|
|
3708
4112
|
model: route.model
|
|
@@ -3717,291 +4121,374 @@ function SessionManagerSettings(props) {
|
|
|
3717
4121
|
className: `${FREE_CHAT_ROOT} dsh-ui-page`,
|
|
3718
4122
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3719
4123
|
className: "dsm-settings",
|
|
3720
|
-
children: [
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3741
|
-
className: "dsh-ui-label",
|
|
3742
|
-
children: copy("continueSourceLabel")
|
|
3743
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
3744
|
-
label: copy("continueSourceLabel"),
|
|
3745
|
-
items: [{
|
|
3746
|
-
id: `${tabsId}-fixed`,
|
|
3747
|
-
panelId: fixedPanelId,
|
|
3748
|
-
value: "fixed",
|
|
3749
|
-
label: copy("modeFixed")
|
|
3750
|
-
}, {
|
|
3751
|
-
id: `${tabsId}-auto`,
|
|
3752
|
-
panelId: autoPanelId,
|
|
3753
|
-
value: "auto",
|
|
3754
|
-
label: copy("modeAuto")
|
|
3755
|
-
}],
|
|
3756
|
-
value: draft.continueMode,
|
|
3757
|
-
onChange: (value) => edit({
|
|
3758
|
-
...draft,
|
|
3759
|
-
continueMode: value
|
|
3760
|
-
})
|
|
3761
|
-
})]
|
|
3762
|
-
}),
|
|
3763
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3764
|
-
role: "tabpanel",
|
|
3765
|
-
id: fixedPanelId,
|
|
3766
|
-
"aria-labelledby": `${tabsId}-fixed`,
|
|
3767
|
-
hidden: draft.continueMode !== "fixed",
|
|
3768
|
-
children: draft.continueMode === "fixed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4124
|
+
children: [
|
|
4125
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4126
|
+
className: "dsm-setting",
|
|
4127
|
+
"aria-labelledby": `${tabsId}-ghost-heading`,
|
|
4128
|
+
children: [
|
|
4129
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
4130
|
+
className: "dsm-setting-header",
|
|
4131
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
4132
|
+
id: `${tabsId}-ghost-heading`,
|
|
4133
|
+
className: "dsh-ui-title",
|
|
4134
|
+
children: copy("settingsMode")
|
|
4135
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4136
|
+
className: "dsh-ui-hint",
|
|
4137
|
+
children: copy("settingsModeHint")
|
|
4138
|
+
})]
|
|
4139
|
+
}),
|
|
4140
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4141
|
+
className: "dsm-ghost-controls",
|
|
4142
|
+
children: [
|
|
4143
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3769
4144
|
className: "dsh-ui-field",
|
|
3770
4145
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3771
4146
|
className: "dsh-ui-label",
|
|
3772
|
-
children: copy("
|
|
3773
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
}
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
role: "tabpanel",
|
|
3786
|
-
id: autoPanelId,
|
|
3787
|
-
"aria-labelledby": `${tabsId}-auto`,
|
|
3788
|
-
hidden: draft.continueMode !== "auto",
|
|
3789
|
-
children: draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3790
|
-
className: "dsh-ui-field dsm-model-field",
|
|
3791
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3792
|
-
className: "dsh-ui-label",
|
|
3793
|
-
children: copy("ghostModelLabel")
|
|
3794
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
3795
|
-
locale: props.locale,
|
|
3796
|
-
open: ghostMenuOpen,
|
|
3797
|
-
onOpenChange: setGhostMenuOpen,
|
|
3798
|
-
listClassName: FREE_CHAT_ROOT,
|
|
3799
|
-
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3800
|
-
type: "button",
|
|
3801
|
-
className: "dsh-model-menu-trigger",
|
|
3802
|
-
"aria-label": `${copy("ghostModelLabel")}: ${ghostLabel}`,
|
|
3803
|
-
"aria-haspopup": "menu",
|
|
3804
|
-
"aria-expanded": ghostMenuOpen,
|
|
3805
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3806
|
-
className: "dsh-model-menu-triggerLabel",
|
|
3807
|
-
children: ghostLabel
|
|
3808
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${ghostMenuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
3809
|
-
}),
|
|
3810
|
-
choices: selectableChoices,
|
|
3811
|
-
selected: ghostRoute,
|
|
3812
|
-
onPick: (route) => edit({
|
|
3813
|
-
...draft,
|
|
3814
|
-
ghostModel: modelRouteKey(route.provider, route.model)
|
|
3815
|
-
}),
|
|
3816
|
-
leading: [{
|
|
3817
|
-
id: "follow",
|
|
3818
|
-
label: copy("modelFollowOption"),
|
|
3819
|
-
selected: !ghostRoute
|
|
4147
|
+
children: copy("continueSourceLabel")
|
|
4148
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
4149
|
+
label: copy("continueSourceLabel"),
|
|
4150
|
+
items: [{
|
|
4151
|
+
id: `${tabsId}-fixed`,
|
|
4152
|
+
panelId: fixedPanelId,
|
|
4153
|
+
value: "fixed",
|
|
4154
|
+
label: copy("modeFixed")
|
|
4155
|
+
}, {
|
|
4156
|
+
id: `${tabsId}-auto`,
|
|
4157
|
+
panelId: autoPanelId,
|
|
4158
|
+
value: "auto",
|
|
4159
|
+
label: copy("modeAuto")
|
|
3820
4160
|
}],
|
|
3821
|
-
|
|
4161
|
+
value: draft.continueMode,
|
|
4162
|
+
onChange: (value) => edit({
|
|
3822
4163
|
...draft,
|
|
3823
|
-
|
|
3824
|
-
})
|
|
3825
|
-
modelValue: ghostLabel
|
|
4164
|
+
continueMode: value
|
|
4165
|
+
})
|
|
3826
4166
|
})]
|
|
3827
|
-
})
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
4167
|
+
}),
|
|
4168
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4169
|
+
role: "tabpanel",
|
|
4170
|
+
id: fixedPanelId,
|
|
4171
|
+
"aria-labelledby": `${tabsId}-fixed`,
|
|
4172
|
+
hidden: draft.continueMode !== "fixed",
|
|
4173
|
+
children: draft.continueMode === "fixed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4174
|
+
className: "dsh-ui-field",
|
|
4175
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4176
|
+
className: "dsh-ui-label",
|
|
4177
|
+
children: copy("continueTextLabel")
|
|
4178
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
4179
|
+
className: "dsm-ghost-input",
|
|
4180
|
+
value: draft.continueText,
|
|
4181
|
+
placeholder: copy("continueTextPlaceholder"),
|
|
4182
|
+
onChange: (event) => edit({
|
|
4183
|
+
...draft,
|
|
4184
|
+
continueText: event.target.value
|
|
4185
|
+
})
|
|
4186
|
+
})]
|
|
4187
|
+
}) : null
|
|
4188
|
+
}),
|
|
4189
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4190
|
+
role: "tabpanel",
|
|
4191
|
+
id: autoPanelId,
|
|
4192
|
+
"aria-labelledby": `${tabsId}-auto`,
|
|
4193
|
+
hidden: draft.continueMode !== "auto",
|
|
4194
|
+
children: draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4195
|
+
className: "dsh-ui-field dsm-model-field",
|
|
4196
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4197
|
+
className: "dsh-ui-label",
|
|
4198
|
+
children: copy("ghostModelLabel")
|
|
4199
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
4200
|
+
locale: props.locale,
|
|
4201
|
+
open: ghostMenuOpen,
|
|
4202
|
+
onOpenChange: setGhostMenuOpen,
|
|
4203
|
+
listClassName: FREE_CHAT_ROOT,
|
|
4204
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4205
|
+
type: "button",
|
|
4206
|
+
className: "dsh-model-menu-trigger",
|
|
4207
|
+
"aria-label": `${copy("ghostModelLabel")}: ${ghostLabel}`,
|
|
4208
|
+
"aria-haspopup": "menu",
|
|
4209
|
+
"aria-expanded": ghostMenuOpen,
|
|
4210
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4211
|
+
className: "dsh-model-menu-triggerLabel",
|
|
4212
|
+
children: ghostLabel
|
|
4213
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${ghostMenuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
4214
|
+
}),
|
|
4215
|
+
choices: selectableChoices,
|
|
4216
|
+
selected: ghostRoute,
|
|
4217
|
+
onPick: (route) => edit({
|
|
4218
|
+
...draft,
|
|
4219
|
+
ghostModel: modelRouteKey(route.provider, route.model)
|
|
4220
|
+
}),
|
|
4221
|
+
leading: [{
|
|
4222
|
+
id: "follow",
|
|
4223
|
+
label: copy("modelFollowOption"),
|
|
4224
|
+
selected: !ghostRoute
|
|
4225
|
+
}],
|
|
4226
|
+
onPickLeading: () => edit({
|
|
4227
|
+
...draft,
|
|
4228
|
+
ghostModel: ""
|
|
4229
|
+
}),
|
|
4230
|
+
modelValue: ghostLabel
|
|
4231
|
+
})]
|
|
4232
|
+
}) : null
|
|
4233
|
+
})
|
|
4234
|
+
]
|
|
4235
|
+
}),
|
|
4236
|
+
draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4237
|
+
className: "dsh-ui-hint",
|
|
4238
|
+
children: copy("ghostModelHint")
|
|
4239
|
+
}) : null
|
|
4240
|
+
]
|
|
4241
|
+
}),
|
|
4242
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4243
|
+
className: "dsm-setting",
|
|
4244
|
+
"aria-labelledby": `${tabsId}-auto-continue-heading`,
|
|
4245
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
3841
4246
|
className: "dsm-setting-header",
|
|
3842
4247
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3843
|
-
id: `${tabsId}-
|
|
4248
|
+
id: `${tabsId}-auto-continue-heading`,
|
|
3844
4249
|
className: "dsh-ui-title",
|
|
3845
|
-
children: copy("
|
|
4250
|
+
children: copy("autoContinueGroup")
|
|
3846
4251
|
})
|
|
3847
|
-
}),
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
4252
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4253
|
+
className: "dsm-feature-options",
|
|
4254
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4255
|
+
className: "dsh-ui-field",
|
|
4256
|
+
children: [
|
|
4257
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4258
|
+
className: "dsh-ui-label",
|
|
4259
|
+
children: copy("autoContinueDefaultLabel")
|
|
4260
|
+
}),
|
|
4261
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4262
|
+
className: "dsh-ui-select",
|
|
4263
|
+
value: draft.autoContinueMode,
|
|
4264
|
+
onChange: (event) => edit({
|
|
4265
|
+
...draft,
|
|
4266
|
+
autoContinueMode: event.target.value
|
|
4267
|
+
}),
|
|
4268
|
+
children: [
|
|
4269
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4270
|
+
value: "off",
|
|
4271
|
+
children: copy("autoContinueOff")
|
|
4272
|
+
}),
|
|
4273
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4274
|
+
value: "cautious",
|
|
4275
|
+
children: copy("autoContinueCautious")
|
|
4276
|
+
}),
|
|
4277
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4278
|
+
value: "bold",
|
|
4279
|
+
children: copy("autoContinueBold")
|
|
4280
|
+
})
|
|
4281
|
+
]
|
|
4282
|
+
}),
|
|
4283
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4284
|
+
className: "dsh-ui-hint",
|
|
4285
|
+
children: copy("autoContinueDefaultHint")
|
|
4286
|
+
})
|
|
4287
|
+
]
|
|
4288
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
4289
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4290
|
+
className: "dsh-ui-field",
|
|
3854
4291
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3855
|
-
className: "dsh-ui-
|
|
3856
|
-
children: copy("
|
|
4292
|
+
className: "dsh-ui-label",
|
|
4293
|
+
children: copy("autoContinueMaxLabel")
|
|
3857
4294
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3858
|
-
className: "
|
|
3859
|
-
children:
|
|
4295
|
+
className: "dsm-minutes-control",
|
|
4296
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
4297
|
+
className: "dsm-minutes",
|
|
4298
|
+
value: draft.autoContinueMax,
|
|
4299
|
+
inputMode: "numeric",
|
|
4300
|
+
"aria-invalid": autoMaxInvalid || void 0,
|
|
4301
|
+
"aria-describedby": autoMaxInvalid ? `${tabsId}-auto-max-error` : void 0,
|
|
4302
|
+
onChange: (event) => edit({
|
|
4303
|
+
...draft,
|
|
4304
|
+
autoContinueMax: event.target.value
|
|
4305
|
+
})
|
|
4306
|
+
})
|
|
3860
4307
|
})]
|
|
3861
|
-
}),
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
}
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
4308
|
+
}),
|
|
4309
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4310
|
+
className: "dsh-ui-hint",
|
|
4311
|
+
children: copy("autoContinueMaxHint")
|
|
4312
|
+
}),
|
|
4313
|
+
autoMaxInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4314
|
+
id: `${tabsId}-auto-max-error`,
|
|
4315
|
+
className: "dsh-ui-error",
|
|
4316
|
+
role: "alert",
|
|
4317
|
+
children: copy("autoContinueMaxInvalid")
|
|
4318
|
+
}) : null
|
|
4319
|
+
] })]
|
|
4320
|
+
})]
|
|
4321
|
+
}),
|
|
4322
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4323
|
+
className: "dsm-setting",
|
|
4324
|
+
"aria-labelledby": `${tabsId}-return-heading`,
|
|
4325
|
+
children: [
|
|
4326
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
4327
|
+
className: "dsm-setting-header",
|
|
4328
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
4329
|
+
id: `${tabsId}-return-heading`,
|
|
4330
|
+
className: "dsh-ui-title",
|
|
4331
|
+
children: copy("welcomeBackGroup")
|
|
4332
|
+
})
|
|
4333
|
+
}),
|
|
4334
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4335
|
+
className: "dsm-feature",
|
|
3872
4336
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3873
|
-
className: "dsh-ui-
|
|
3874
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
3875
|
-
className: "dsh-ui-
|
|
4337
|
+
className: "dsh-ui-toggle-row",
|
|
4338
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4339
|
+
className: "dsh-ui-toggle-text",
|
|
3876
4340
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3877
|
-
className: "dsh-ui-label",
|
|
3878
|
-
children: copy("
|
|
3879
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3880
|
-
className: "
|
|
3881
|
-
children:
|
|
4341
|
+
className: "dsh-ui-toggle-label",
|
|
4342
|
+
children: copy("recapEnabledLabel")
|
|
4343
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4344
|
+
className: "dsh-ui-hint",
|
|
4345
|
+
children: copy("recapEnabledHint")
|
|
4346
|
+
})]
|
|
4347
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
4348
|
+
label: copy("recapEnabledLabel"),
|
|
4349
|
+
checked: draft.recapEnabled,
|
|
4350
|
+
onChange: (next) => edit({
|
|
4351
|
+
...draft,
|
|
4352
|
+
recapEnabled: next
|
|
4353
|
+
})
|
|
4354
|
+
})]
|
|
4355
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4356
|
+
className: "dsm-feature-options",
|
|
4357
|
+
"data-disabled": draft.recapEnabled ? void 0 : "true",
|
|
4358
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4359
|
+
className: "dsh-ui-fields-grid",
|
|
4360
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4361
|
+
className: "dsh-ui-field",
|
|
4362
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4363
|
+
className: "dsh-ui-label",
|
|
4364
|
+
children: copy("recapMinutesLabel")
|
|
4365
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4366
|
+
className: "dsm-minutes-control",
|
|
4367
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
4368
|
+
className: "dsm-minutes",
|
|
4369
|
+
value: draft.recapMinutes,
|
|
4370
|
+
inputMode: "numeric",
|
|
4371
|
+
disabled: !draft.recapEnabled,
|
|
4372
|
+
"aria-invalid": recapInvalid || void 0,
|
|
4373
|
+
"aria-describedby": recapInvalid ? `${tabsId}-recap-error` : void 0,
|
|
4374
|
+
onChange: (event) => edit({
|
|
4375
|
+
...draft,
|
|
4376
|
+
recapMinutes: event.target.value
|
|
4377
|
+
})
|
|
4378
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4379
|
+
className: "dsh-ui-meta",
|
|
4380
|
+
children: copy("minutesUnit")
|
|
4381
|
+
})]
|
|
4382
|
+
})]
|
|
4383
|
+
}), recapInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4384
|
+
id: `${tabsId}-recap-error`,
|
|
4385
|
+
className: "dsh-ui-error",
|
|
4386
|
+
role: "alert",
|
|
4387
|
+
children: copy("minutesInvalid")
|
|
4388
|
+
}) : null] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4389
|
+
className: "dsh-ui-field dsm-model-field",
|
|
4390
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4391
|
+
className: "dsh-ui-label",
|
|
4392
|
+
children: copy("modelLabel")
|
|
4393
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
4394
|
+
locale: props.locale,
|
|
4395
|
+
open: menuOpen,
|
|
4396
|
+
onOpenChange: setMenuOpen,
|
|
4397
|
+
listClassName: FREE_CHAT_ROOT,
|
|
4398
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4399
|
+
type: "button",
|
|
4400
|
+
className: "dsh-model-menu-trigger",
|
|
4401
|
+
disabled: !draft.recapEnabled,
|
|
4402
|
+
"aria-label": `${copy("modelLabel")}: ${selectedLabel}`,
|
|
4403
|
+
"aria-haspopup": "menu",
|
|
4404
|
+
"aria-expanded": menuOpen,
|
|
4405
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4406
|
+
className: "dsh-model-menu-triggerLabel",
|
|
4407
|
+
children: selectedLabel
|
|
4408
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
4409
|
+
}),
|
|
4410
|
+
choices: selectableChoices,
|
|
4411
|
+
selected: selectedRoute,
|
|
4412
|
+
onPick: (route) => edit({
|
|
4413
|
+
...draft,
|
|
4414
|
+
model: modelRouteKey(route.provider, route.model)
|
|
4415
|
+
}),
|
|
4416
|
+
leading: [{
|
|
4417
|
+
id: "follow",
|
|
4418
|
+
label: copy("modelFollowOption"),
|
|
4419
|
+
selected: !selectedRoute
|
|
4420
|
+
}],
|
|
4421
|
+
onPickLeading: () => edit({
|
|
4422
|
+
...draft,
|
|
4423
|
+
model: ""
|
|
4424
|
+
}),
|
|
4425
|
+
modelValue: selectedLabel
|
|
4426
|
+
})]
|
|
4427
|
+
})]
|
|
4428
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
4429
|
+
state: catalog,
|
|
4430
|
+
locale: props.locale
|
|
4431
|
+
})]
|
|
4432
|
+
})]
|
|
4433
|
+
}),
|
|
4434
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4435
|
+
className: "dsm-feature",
|
|
4436
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4437
|
+
className: "dsh-ui-toggle-row",
|
|
4438
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4439
|
+
className: "dsh-ui-toggle-text",
|
|
4440
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4441
|
+
className: "dsh-ui-toggle-label",
|
|
4442
|
+
children: copy("compactEnabledLabel")
|
|
4443
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4444
|
+
className: "dsh-ui-hint",
|
|
4445
|
+
children: copy("compactEnabledHint")
|
|
4446
|
+
})]
|
|
4447
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
4448
|
+
label: copy("compactEnabledLabel"),
|
|
4449
|
+
checked: draft.compactEnabled,
|
|
4450
|
+
onChange: (next) => edit({
|
|
4451
|
+
...draft,
|
|
4452
|
+
compactEnabled: next
|
|
4453
|
+
})
|
|
4454
|
+
})]
|
|
4455
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4456
|
+
className: "dsm-feature-options dsm-compact-options",
|
|
4457
|
+
"data-disabled": draft.compactEnabled ? void 0 : "true",
|
|
4458
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4459
|
+
className: "dsh-ui-field",
|
|
4460
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4461
|
+
className: "dsh-ui-label",
|
|
4462
|
+
children: copy("compactMinutesLabel")
|
|
4463
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4464
|
+
className: "dsm-minutes-control",
|
|
4465
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
3882
4466
|
className: "dsm-minutes",
|
|
3883
|
-
value: draft.
|
|
4467
|
+
value: draft.compactMinutes,
|
|
3884
4468
|
inputMode: "numeric",
|
|
3885
|
-
disabled: !draft.
|
|
3886
|
-
"aria-invalid":
|
|
3887
|
-
"aria-describedby":
|
|
4469
|
+
disabled: !draft.compactEnabled,
|
|
4470
|
+
"aria-invalid": compactInvalid || void 0,
|
|
4471
|
+
"aria-describedby": compactInvalid ? `${tabsId}-compact-error` : void 0,
|
|
3888
4472
|
onChange: (event) => edit({
|
|
3889
4473
|
...draft,
|
|
3890
|
-
|
|
4474
|
+
compactMinutes: event.target.value
|
|
3891
4475
|
})
|
|
3892
4476
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3893
4477
|
className: "dsh-ui-meta",
|
|
3894
4478
|
children: copy("minutesUnit")
|
|
3895
4479
|
})]
|
|
3896
4480
|
})]
|
|
3897
|
-
}),
|
|
3898
|
-
id: `${tabsId}-
|
|
4481
|
+
}), compactInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4482
|
+
id: `${tabsId}-compact-error`,
|
|
3899
4483
|
className: "dsh-ui-error",
|
|
3900
4484
|
role: "alert",
|
|
3901
4485
|
children: copy("minutesInvalid")
|
|
3902
|
-
}) : null] })
|
|
3903
|
-
className: "dsh-ui-field dsm-model-field",
|
|
3904
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3905
|
-
className: "dsh-ui-label",
|
|
3906
|
-
children: copy("modelLabel")
|
|
3907
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
3908
|
-
locale: props.locale,
|
|
3909
|
-
open: menuOpen,
|
|
3910
|
-
onOpenChange: setMenuOpen,
|
|
3911
|
-
listClassName: FREE_CHAT_ROOT,
|
|
3912
|
-
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3913
|
-
type: "button",
|
|
3914
|
-
className: "dsh-model-menu-trigger",
|
|
3915
|
-
disabled: !draft.recapEnabled,
|
|
3916
|
-
"aria-label": `${copy("modelLabel")}: ${selectedLabel}`,
|
|
3917
|
-
"aria-haspopup": "menu",
|
|
3918
|
-
"aria-expanded": menuOpen,
|
|
3919
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3920
|
-
className: "dsh-model-menu-triggerLabel",
|
|
3921
|
-
children: selectedLabel
|
|
3922
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
3923
|
-
}),
|
|
3924
|
-
choices: selectableChoices,
|
|
3925
|
-
selected: selectedRoute,
|
|
3926
|
-
onPick: (route) => edit({
|
|
3927
|
-
...draft,
|
|
3928
|
-
model: modelRouteKey(route.provider, route.model)
|
|
3929
|
-
}),
|
|
3930
|
-
leading: [{
|
|
3931
|
-
id: "follow",
|
|
3932
|
-
label: copy("modelFollowOption"),
|
|
3933
|
-
selected: !selectedRoute
|
|
3934
|
-
}],
|
|
3935
|
-
onPickLeading: () => edit({
|
|
3936
|
-
...draft,
|
|
3937
|
-
model: ""
|
|
3938
|
-
}),
|
|
3939
|
-
modelValue: selectedLabel
|
|
3940
|
-
})]
|
|
3941
|
-
})]
|
|
3942
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
3943
|
-
state: catalog,
|
|
3944
|
-
locale: props.locale
|
|
4486
|
+
}) : null] })
|
|
3945
4487
|
})]
|
|
3946
|
-
})
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3951
|
-
className: "dsh-ui-toggle-row",
|
|
3952
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3953
|
-
className: "dsh-ui-toggle-text",
|
|
3954
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3955
|
-
className: "dsh-ui-toggle-label",
|
|
3956
|
-
children: copy("compactEnabledLabel")
|
|
3957
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3958
|
-
className: "dsh-ui-hint",
|
|
3959
|
-
children: copy("compactEnabledHint")
|
|
3960
|
-
})]
|
|
3961
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
3962
|
-
label: copy("compactEnabledLabel"),
|
|
3963
|
-
checked: draft.compactEnabled,
|
|
3964
|
-
onChange: (next) => edit({
|
|
3965
|
-
...draft,
|
|
3966
|
-
compactEnabled: next
|
|
3967
|
-
})
|
|
3968
|
-
})]
|
|
3969
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3970
|
-
className: "dsm-feature-options dsm-compact-options",
|
|
3971
|
-
"data-disabled": draft.compactEnabled ? void 0 : "true",
|
|
3972
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3973
|
-
className: "dsh-ui-field",
|
|
3974
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3975
|
-
className: "dsh-ui-label",
|
|
3976
|
-
children: copy("compactMinutesLabel")
|
|
3977
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3978
|
-
className: "dsm-minutes-control",
|
|
3979
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
3980
|
-
className: "dsm-minutes",
|
|
3981
|
-
value: draft.compactMinutes,
|
|
3982
|
-
inputMode: "numeric",
|
|
3983
|
-
disabled: !draft.compactEnabled,
|
|
3984
|
-
"aria-invalid": compactInvalid || void 0,
|
|
3985
|
-
"aria-describedby": compactInvalid ? `${tabsId}-compact-error` : void 0,
|
|
3986
|
-
onChange: (event) => edit({
|
|
3987
|
-
...draft,
|
|
3988
|
-
compactMinutes: event.target.value
|
|
3989
|
-
})
|
|
3990
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3991
|
-
className: "dsh-ui-meta",
|
|
3992
|
-
children: copy("minutesUnit")
|
|
3993
|
-
})]
|
|
3994
|
-
})]
|
|
3995
|
-
}), compactInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3996
|
-
id: `${tabsId}-compact-error`,
|
|
3997
|
-
className: "dsh-ui-error",
|
|
3998
|
-
role: "alert",
|
|
3999
|
-
children: copy("minutesInvalid")
|
|
4000
|
-
}) : null] })
|
|
4001
|
-
})]
|
|
4002
|
-
})
|
|
4003
|
-
]
|
|
4004
|
-
})]
|
|
4488
|
+
})
|
|
4489
|
+
]
|
|
4490
|
+
})
|
|
4491
|
+
]
|
|
4005
4492
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4006
4493
|
className: "dsh-ui-form-footer dsm-settings-footer",
|
|
4007
4494
|
children: [
|
|
@@ -4031,8 +4518,185 @@ function SessionManagerSettings(props) {
|
|
|
4031
4518
|
});
|
|
4032
4519
|
}
|
|
4033
4520
|
//#endregion
|
|
4521
|
+
//#region src/message-editor.tsx
|
|
4522
|
+
function AttachmentPreview(props) {
|
|
4523
|
+
const [src, setSrc] = (0, react.useState)();
|
|
4524
|
+
const [failed, setFailed] = (0, react.useState)(false);
|
|
4525
|
+
const item = props.item;
|
|
4526
|
+
const name = item.type === "upload" ? item.file.name : item.type === "file" ? item.name : uiText(props.locale, "editImage");
|
|
4527
|
+
(0, react.useEffect)(() => {
|
|
4528
|
+
let active = true;
|
|
4529
|
+
let url;
|
|
4530
|
+
setSrc(void 0);
|
|
4531
|
+
setFailed(false);
|
|
4532
|
+
if (item.type === "upload" && item.file.type.startsWith("image/")) {
|
|
4533
|
+
url = URL.createObjectURL(item.file);
|
|
4534
|
+
setSrc(url);
|
|
4535
|
+
} else if (item.type === "image" && props.host.attachment) unwrap(props.host.attachment({
|
|
4536
|
+
sessionId: props.sessionId,
|
|
4537
|
+
attachmentId: item.attachmentId
|
|
4538
|
+
})).then((value) => {
|
|
4539
|
+
const image = value;
|
|
4540
|
+
if (active && image.data && image.attachment?.mediaType?.startsWith("image/")) setSrc(`data:${image.attachment.mediaType};base64,${image.data}`);
|
|
4541
|
+
else if (active) setFailed(true);
|
|
4542
|
+
}).catch(() => {
|
|
4543
|
+
if (active) setFailed(true);
|
|
4544
|
+
});
|
|
4545
|
+
return () => {
|
|
4546
|
+
active = false;
|
|
4547
|
+
if (url) URL.revokeObjectURL(url);
|
|
4548
|
+
};
|
|
4549
|
+
}, [
|
|
4550
|
+
item,
|
|
4551
|
+
props.host,
|
|
4552
|
+
props.sessionId
|
|
4553
|
+
]);
|
|
4554
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4555
|
+
src ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
4556
|
+
src,
|
|
4557
|
+
alt: name
|
|
4558
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, { path: name }),
|
|
4559
|
+
item.type === "file" || item.type === "upload" && !item.file.type.startsWith("image/") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4560
|
+
title: name,
|
|
4561
|
+
children: name
|
|
4562
|
+
}) : null,
|
|
4563
|
+
failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4564
|
+
role: "status",
|
|
4565
|
+
children: uiText(props.locale, "editPreviewFailed")
|
|
4566
|
+
}) : null
|
|
4567
|
+
] });
|
|
4568
|
+
}
|
|
4569
|
+
/** Lives in the original bubble; drafts and attachments remain local until Send. */
|
|
4570
|
+
function MessageEditor(props) {
|
|
4571
|
+
const textarea = (0, react.useRef)(null);
|
|
4572
|
+
const copy = (key) => uiText(props.locale, key);
|
|
4573
|
+
const attachments = props.attachments ?? [];
|
|
4574
|
+
const disabled = props.busy || props.locked === true;
|
|
4575
|
+
(0, react.useEffect)(() => {
|
|
4576
|
+
textarea.current?.focus();
|
|
4577
|
+
textarea.current?.setSelectionRange(props.text.length, props.text.length);
|
|
4578
|
+
}, []);
|
|
4579
|
+
(0, react.useEffect)(() => {
|
|
4580
|
+
const element = textarea.current;
|
|
4581
|
+
if (!element) return;
|
|
4582
|
+
element.style.height = "auto";
|
|
4583
|
+
element.style.height = `${Math.max(80, Math.min(element.scrollHeight, 360))}px`;
|
|
4584
|
+
}, [props.text]);
|
|
4585
|
+
const add = (files) => {
|
|
4586
|
+
if (!disabled && files.length > 0) props.setAttachments?.([...attachments, ...files.map((file) => ({
|
|
4587
|
+
type: "upload",
|
|
4588
|
+
file
|
|
4589
|
+
}))]);
|
|
4590
|
+
};
|
|
4591
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4592
|
+
className: "dsm-message-edit",
|
|
4593
|
+
role: "group",
|
|
4594
|
+
"aria-label": copy("editTitle"),
|
|
4595
|
+
onDragOver: (event) => {
|
|
4596
|
+
if (!disabled && props.setAttachments && event.dataTransfer.types.includes("Files")) event.preventDefault();
|
|
4597
|
+
},
|
|
4598
|
+
onDrop: (event) => {
|
|
4599
|
+
if (props.setAttachments && event.dataTransfer.files.length) {
|
|
4600
|
+
event.preventDefault();
|
|
4601
|
+
add(Array.from(event.dataTransfer.files));
|
|
4602
|
+
}
|
|
4603
|
+
},
|
|
4604
|
+
children: [attachments.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4605
|
+
className: "dsm-edit-attachments",
|
|
4606
|
+
children: attachments.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4607
|
+
className: `dsm-edit-attachment ${item.type === "image" || item.type === "upload" && item.file.type.startsWith("image/") ? "dsm-edit-image" : ""}`,
|
|
4608
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AttachmentPreview, {
|
|
4609
|
+
item,
|
|
4610
|
+
sessionId: props.sessionId,
|
|
4611
|
+
host: props.host ?? {},
|
|
4612
|
+
locale: props.locale
|
|
4613
|
+
}), props.setAttachments ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4614
|
+
size: "sm",
|
|
4615
|
+
variant: "ghost",
|
|
4616
|
+
disabled,
|
|
4617
|
+
"aria-label": `${copy("editRemoveAttachment")} ${index + 1}`,
|
|
4618
|
+
onClick: () => {
|
|
4619
|
+
props.setAttachments?.(attachments.filter((_, position) => position !== index));
|
|
4620
|
+
},
|
|
4621
|
+
children: "×"
|
|
4622
|
+
}) : null]
|
|
4623
|
+
}, item.type === "upload" ? `${index}:${item.file.name}` : `${index}:${item.attachmentId}`))
|
|
4624
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4625
|
+
className: "dsm-message-editor",
|
|
4626
|
+
children: [
|
|
4627
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
4628
|
+
ref: textarea,
|
|
4629
|
+
className: "dsh-ui-textarea dsh-free-draft",
|
|
4630
|
+
"aria-label": copy("draft"),
|
|
4631
|
+
value: props.text,
|
|
4632
|
+
disabled,
|
|
4633
|
+
onChange: (event) => {
|
|
4634
|
+
props.setText(event.target.value);
|
|
4635
|
+
},
|
|
4636
|
+
onPaste: (event) => {
|
|
4637
|
+
const files = Array.from(event.clipboardData.files);
|
|
4638
|
+
if (files.length && props.setAttachments) {
|
|
4639
|
+
event.preventDefault();
|
|
4640
|
+
add(files);
|
|
4641
|
+
}
|
|
4642
|
+
},
|
|
4643
|
+
onKeyDown: (event) => {
|
|
4644
|
+
if (event.key === "Escape" && !props.busy) {
|
|
4645
|
+
event.stopPropagation();
|
|
4646
|
+
props.onCancel();
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
}),
|
|
4650
|
+
props.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4651
|
+
className: "dsh-ui-error",
|
|
4652
|
+
role: "alert",
|
|
4653
|
+
children: props.error
|
|
4654
|
+
}) : null,
|
|
4655
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4656
|
+
className: "dsm-edit-footer",
|
|
4657
|
+
children: [
|
|
4658
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dsm-edit-spacer" }),
|
|
4659
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4660
|
+
variant: "ghost",
|
|
4661
|
+
disabled: props.busy,
|
|
4662
|
+
onClick: props.onCancel,
|
|
4663
|
+
children: copy("cancel")
|
|
4664
|
+
}),
|
|
4665
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4666
|
+
variant: "primary",
|
|
4667
|
+
disabled: props.busy || !props.text.trim() && !attachments.length,
|
|
4668
|
+
onClick: props.onSend,
|
|
4669
|
+
children: copy("send")
|
|
4670
|
+
})
|
|
4671
|
+
]
|
|
4672
|
+
})
|
|
4673
|
+
]
|
|
4674
|
+
})]
|
|
4675
|
+
});
|
|
4676
|
+
}
|
|
4677
|
+
//#endregion
|
|
4678
|
+
//#region src/edit-icon.tsx
|
|
4679
|
+
/** The stock edit primitive includes a box; message editing uses a standalone pencil. */
|
|
4680
|
+
function EditMessageIcon() {
|
|
4681
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
4682
|
+
width: "16",
|
|
4683
|
+
height: "16",
|
|
4684
|
+
viewBox: "0 0 16 16",
|
|
4685
|
+
fill: "none",
|
|
4686
|
+
"aria-hidden": "true",
|
|
4687
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
4688
|
+
d: "m10.5 2.5 3 3M2 14l1-4L10.5 2.5a2.12 2.12 0 0 1 3 3L6 13l-4 1Z",
|
|
4689
|
+
stroke: "currentColor",
|
|
4690
|
+
strokeWidth: "1",
|
|
4691
|
+
strokeLinecap: "round",
|
|
4692
|
+
strokeLinejoin: "round"
|
|
4693
|
+
})
|
|
4694
|
+
});
|
|
4695
|
+
}
|
|
4696
|
+
//#endregion
|
|
4034
4697
|
//#region src/user-actions.tsx
|
|
4035
4698
|
function UserMessageActions(props) {
|
|
4699
|
+
const [confirming, setConfirming] = (0, react.useState)(false);
|
|
4036
4700
|
const snapshot = (0, react.useSyncExternalStore)(props.model.subscribe, props.model.getSnapshot, props.model.getSnapshot);
|
|
4037
4701
|
const signal = (0, react.useRef)(new AbortController());
|
|
4038
4702
|
(0, react.useEffect)(() => {
|
|
@@ -4056,75 +4720,624 @@ function UserMessageActions(props) {
|
|
|
4056
4720
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4057
4721
|
className: `${FREE_CHAT_ROOT} dsh-free-actions`,
|
|
4058
4722
|
children: [
|
|
4059
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
4060
|
-
label: copy("retry"),
|
|
4061
|
-
side: "bottom",
|
|
4062
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4063
|
-
size: "sm",
|
|
4064
|
-
variant: "ghost",
|
|
4065
|
-
"aria-label": copy("retry"),
|
|
4066
|
-
disabled: snapshot.busy,
|
|
4067
|
-
onClick: () => {
|
|
4068
|
-
props.model.retry(props.sessionId, props.messageId, signal.current.signal);
|
|
4069
|
-
},
|
|
4070
|
-
children: copy("retry")
|
|
4071
|
-
})
|
|
4072
|
-
}),
|
|
4073
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
4723
|
+
!props.hideEdit ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
4074
4724
|
label: copy("edit"),
|
|
4075
4725
|
side: "bottom",
|
|
4076
4726
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4077
4727
|
size: "sm",
|
|
4728
|
+
className: "dsm-icon-action",
|
|
4078
4729
|
variant: "ghost",
|
|
4079
4730
|
"aria-label": copy("edit"),
|
|
4080
|
-
disabled: snapshot.busy,
|
|
4731
|
+
disabled: snapshot.busy || snapshot.editing && !mine,
|
|
4081
4732
|
onClick: () => {
|
|
4082
4733
|
props.model.beginEdit(props.sessionId, props.messageId, props.text, signal.current.signal);
|
|
4083
4734
|
},
|
|
4084
|
-
children:
|
|
4735
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditMessageIcon, {})
|
|
4085
4736
|
})
|
|
4086
|
-
}),
|
|
4087
|
-
failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4737
|
+
}) : null,
|
|
4738
|
+
failure && !editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4088
4739
|
className: "dsh-ui-error dsh-free-alert",
|
|
4089
4740
|
role: "alert",
|
|
4090
4741
|
children: failureText(props.locale, failure)
|
|
4091
4742
|
}) : null,
|
|
4743
|
+
editing && !props.hideEdit ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageEditor, {
|
|
4744
|
+
text: draft,
|
|
4745
|
+
setText: (value) => {
|
|
4746
|
+
props.model.setDraft(value);
|
|
4747
|
+
},
|
|
4748
|
+
sessionId: props.sessionId,
|
|
4749
|
+
locale: props.locale,
|
|
4750
|
+
busy: snapshot.busy,
|
|
4751
|
+
error: failure ? failureText(props.locale, failure) : void 0,
|
|
4752
|
+
onCancel: () => {
|
|
4753
|
+
props.model.cancelEdit();
|
|
4754
|
+
},
|
|
4755
|
+
onSend: () => {
|
|
4756
|
+
setConfirming(true);
|
|
4757
|
+
}
|
|
4758
|
+
}) : null,
|
|
4092
4759
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
4093
|
-
open: editing,
|
|
4760
|
+
open: confirming && editing,
|
|
4094
4761
|
className: FREE_CHAT_ROOT,
|
|
4095
4762
|
title: copy("editTitle"),
|
|
4096
4763
|
closeLabel: copy("close"),
|
|
4097
4764
|
onClose: () => {
|
|
4098
|
-
|
|
4765
|
+
setConfirming(false);
|
|
4099
4766
|
},
|
|
4100
4767
|
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4101
4768
|
variant: "ghost",
|
|
4102
|
-
|
|
4769
|
+
"data-modal-autofocus": true,
|
|
4103
4770
|
onClick: () => {
|
|
4104
|
-
|
|
4771
|
+
setConfirming(false);
|
|
4105
4772
|
},
|
|
4106
4773
|
children: copy("cancel")
|
|
4107
4774
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4108
4775
|
variant: "primary",
|
|
4109
|
-
disabled: snapshot.busy,
|
|
4110
4776
|
onClick: () => {
|
|
4111
|
-
|
|
4777
|
+
setConfirming(false);
|
|
4778
|
+
props.model.sendEdit(props.sessionId, props.messageId, signal.current.signal, void 0, true);
|
|
4112
4779
|
},
|
|
4113
|
-
children: copy("
|
|
4780
|
+
children: copy("editConfirm")
|
|
4114
4781
|
})] }),
|
|
4115
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4782
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy("editBranchWarning") })
|
|
4783
|
+
})
|
|
4784
|
+
]
|
|
4785
|
+
});
|
|
4786
|
+
}
|
|
4787
|
+
//#endregion
|
|
4788
|
+
//#region src/edit-host.ts
|
|
4789
|
+
function bindEditHost(client) {
|
|
4790
|
+
const rpc = client.connection?.rpc;
|
|
4791
|
+
const remote = client.remote?.session;
|
|
4792
|
+
return {
|
|
4793
|
+
...typeof rpc?.call === "function" ? {
|
|
4794
|
+
prepare: (request, signal) => rpc.call(TEMP_WORKSPACE_CHANNEL, EDIT_PREPARE_ENDPOINT, request, signal),
|
|
4795
|
+
stageFile: (request, signal) => rpc.call(TEMP_WORKSPACE_CHANNEL, EDIT_FILE_ENDPOINT, request, signal)
|
|
4796
|
+
} : {},
|
|
4797
|
+
uploadFile: async (sessionId, file, signal) => {
|
|
4798
|
+
const query = new URLSearchParams({
|
|
4799
|
+
sessionId,
|
|
4800
|
+
name: file.name
|
|
4801
|
+
});
|
|
4802
|
+
const response = await fetch(`/api/session/uploadFileBinary?${query}`, {
|
|
4803
|
+
method: "POST",
|
|
4804
|
+
credentials: "same-origin",
|
|
4805
|
+
headers: { "content-type": "application/octet-stream" },
|
|
4806
|
+
body: file,
|
|
4807
|
+
signal
|
|
4808
|
+
});
|
|
4809
|
+
if (!response.ok) throw new HostOperationError("session/attachment-invalid");
|
|
4810
|
+
return response.json();
|
|
4811
|
+
},
|
|
4812
|
+
...typeof remote?.attachment === "function" ? { attachment: (request) => remote.attachment(request) } : {}
|
|
4813
|
+
};
|
|
4814
|
+
}
|
|
4815
|
+
function readPreview(value) {
|
|
4816
|
+
if (typeof value !== "object" || value === null) throw new HostOperationError("session-manager/unavailable");
|
|
4817
|
+
const record = value;
|
|
4818
|
+
if (typeof record.sessionId !== "string" || typeof record.promptSeq !== "number" || typeof record.text !== "string" || typeof record.modified !== "boolean") throw new HostOperationError("session-manager/unavailable");
|
|
4819
|
+
const content = record.content;
|
|
4820
|
+
if (content !== void 0 && (!Array.isArray(content) || !content.every(isEditContent))) throw new HostOperationError("session-manager/unavailable");
|
|
4821
|
+
return {
|
|
4822
|
+
sessionId: record.sessionId,
|
|
4823
|
+
promptSeq: record.promptSeq,
|
|
4824
|
+
text: record.text,
|
|
4825
|
+
...content === void 0 ? {} : { content },
|
|
4826
|
+
modified: record.modified
|
|
4827
|
+
};
|
|
4828
|
+
}
|
|
4829
|
+
function isEditContent(value) {
|
|
4830
|
+
if (typeof value !== "object" || value === null) return false;
|
|
4831
|
+
const part = value;
|
|
4832
|
+
return part.type === "text" ? typeof part.text === "string" : (part.type === "image" || part.type === "file" && typeof part.name === "string" && typeof part.bytes === "number") && typeof part.attachmentId === "string" && part.attachmentId !== "";
|
|
4833
|
+
}
|
|
4834
|
+
/** Build the exact body for the edited prompt; images are read from the source Session. */
|
|
4835
|
+
async function buildContent(host, preview, text, signal, attachments) {
|
|
4836
|
+
const content = [];
|
|
4837
|
+
let wroteText = false;
|
|
4838
|
+
const parts = attachments === void 0 ? preview.content ?? [{
|
|
4839
|
+
type: "text",
|
|
4840
|
+
text: preview.text
|
|
4841
|
+
}] : [{
|
|
4842
|
+
type: "text",
|
|
4843
|
+
text
|
|
4844
|
+
}, ...attachments];
|
|
4845
|
+
for (const part of parts) {
|
|
4846
|
+
signal.throwIfAborted();
|
|
4847
|
+
if (part.type === "text") {
|
|
4848
|
+
if (text === preview.text) content.push(part);
|
|
4849
|
+
else if (!wroteText) content.push({
|
|
4850
|
+
type: "text",
|
|
4851
|
+
text
|
|
4852
|
+
});
|
|
4853
|
+
wroteText = true;
|
|
4854
|
+
continue;
|
|
4855
|
+
}
|
|
4856
|
+
if (part.type === "file") {
|
|
4857
|
+
content.push(part);
|
|
4858
|
+
continue;
|
|
4859
|
+
}
|
|
4860
|
+
if (part.type === "upload") {
|
|
4861
|
+
if ([
|
|
4862
|
+
"image/png",
|
|
4863
|
+
"image/jpeg",
|
|
4864
|
+
"image/webp",
|
|
4865
|
+
"image/gif"
|
|
4866
|
+
].includes(part.file.type)) {
|
|
4867
|
+
const data = await new Promise((resolve, reject) => {
|
|
4868
|
+
const reader = new FileReader();
|
|
4869
|
+
reader.onload = () => {
|
|
4870
|
+
resolve(String(reader.result).split(",")[1]);
|
|
4871
|
+
};
|
|
4872
|
+
reader.onerror = () => {
|
|
4873
|
+
reject(reader.error);
|
|
4874
|
+
};
|
|
4875
|
+
reader.readAsDataURL(part.file);
|
|
4876
|
+
});
|
|
4877
|
+
signal.throwIfAborted();
|
|
4878
|
+
content.push({
|
|
4879
|
+
type: "image",
|
|
4880
|
+
data,
|
|
4881
|
+
mediaType: part.file.type,
|
|
4882
|
+
name: part.file.name
|
|
4883
|
+
});
|
|
4884
|
+
} else content.push(part);
|
|
4885
|
+
continue;
|
|
4886
|
+
}
|
|
4887
|
+
if (!host.attachment) throw new HostOperationError("session/lifecycle-unavailable");
|
|
4888
|
+
const image = await unwrap(host.attachment({
|
|
4889
|
+
sessionId: preview.sessionId,
|
|
4890
|
+
attachmentId: part.attachmentId
|
|
4891
|
+
}));
|
|
4892
|
+
if (image?.attachment?.attachmentId !== part.attachmentId || typeof image.data !== "string" || image.data === "" || typeof image.attachment.mediaType !== "string" || !image.attachment.mediaType.startsWith("image/")) throw new HostOperationError("session/attachment-invalid");
|
|
4893
|
+
content.push({
|
|
4894
|
+
type: "image",
|
|
4895
|
+
data: image.data,
|
|
4896
|
+
mediaType: image.attachment.mediaType,
|
|
4897
|
+
...typeof image.attachment.name === "string" ? { name: image.attachment.name } : {}
|
|
4898
|
+
});
|
|
4899
|
+
}
|
|
4900
|
+
if (!wroteText && text !== "") content.push({
|
|
4901
|
+
type: "text",
|
|
4902
|
+
text
|
|
4903
|
+
});
|
|
4904
|
+
signal.throwIfAborted();
|
|
4905
|
+
return content;
|
|
4906
|
+
}
|
|
4907
|
+
/** Prepare the exact edit body for the existing Session; the host sends it on submit. */
|
|
4908
|
+
async function prepareEditContent(host, preview, text, submission, signal, attachments) {
|
|
4909
|
+
submission.prepared ??= await buildContent(host, preview, text, signal, attachments);
|
|
4910
|
+
await stageContent(host, preview, submission, preview.sessionId, signal);
|
|
4911
|
+
return submission.content;
|
|
4912
|
+
}
|
|
4913
|
+
async function stageContent(host, preview, submission, sessionId, signal) {
|
|
4914
|
+
const prepared = submission.prepared;
|
|
4915
|
+
if (submission.content !== void 0) return;
|
|
4916
|
+
const content = [];
|
|
4917
|
+
const receipts = submission.receipts ??= /* @__PURE__ */ new Map();
|
|
4918
|
+
for (const [index, part] of prepared.entries()) {
|
|
4919
|
+
signal.throwIfAborted();
|
|
4920
|
+
if (part.type !== "upload" && !(part.type === "file" && "attachmentId" in part)) {
|
|
4921
|
+
content.push(part);
|
|
4922
|
+
continue;
|
|
4923
|
+
}
|
|
4924
|
+
let receiptId = receipts.get(index);
|
|
4925
|
+
if (receiptId === void 0) {
|
|
4926
|
+
const result = part.type === "upload" ? host.uploadFile?.(sessionId, part.file, signal) : host.stageFile?.({
|
|
4927
|
+
sessionId: preview.sessionId,
|
|
4928
|
+
promptSeq: preview.promptSeq,
|
|
4929
|
+
attachmentId: part.attachmentId
|
|
4930
|
+
}, signal);
|
|
4931
|
+
if (!result) throw new HostOperationError("session/lifecycle-unavailable");
|
|
4932
|
+
const value = await unwrap(result);
|
|
4933
|
+
if (typeof value?.receiptId !== "string" || value.receiptId === "") throw new HostOperationError("session/attachment-invalid");
|
|
4934
|
+
receiptId = value.receiptId;
|
|
4935
|
+
receipts.set(index, receiptId);
|
|
4936
|
+
}
|
|
4937
|
+
content.push({
|
|
4938
|
+
type: "file",
|
|
4939
|
+
receiptId
|
|
4940
|
+
});
|
|
4941
|
+
}
|
|
4942
|
+
submission.content = content;
|
|
4943
|
+
}
|
|
4944
|
+
/** Read the exact human prompt that the editor edits. */
|
|
4945
|
+
async function previewEdit(host, sessionId, promptSeq, signal) {
|
|
4946
|
+
if (typeof host.prepare !== "function") throw new HostOperationError("session/lifecycle-unavailable");
|
|
4947
|
+
return readPreview(await unwrap(host.prepare({
|
|
4948
|
+
sessionId,
|
|
4949
|
+
promptSeq
|
|
4950
|
+
}, signal)));
|
|
4951
|
+
}
|
|
4952
|
+
//#endregion
|
|
4953
|
+
//#region src/edit-actions.tsx
|
|
4954
|
+
/** Edit one human prompt in its own Session; the host switches branches when the edit is sent. */
|
|
4955
|
+
function EditActions(props) {
|
|
4956
|
+
const sessionId = props.sessionId ?? "";
|
|
4957
|
+
const native = (0, react.useSyncExternalStore)(props.nativeRetry.subscribe, props.nativeRetry.getSnapshot, props.nativeRetry.getSnapshot);
|
|
4958
|
+
const [confirming, setConfirming] = (0, react.useState)(false);
|
|
4959
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
4960
|
+
const [editing, setEditing] = (0, react.useState)(false);
|
|
4961
|
+
const [draft, setDraft] = (0, react.useState)("");
|
|
4962
|
+
const [attachments, setAttachments] = (0, react.useState)([]);
|
|
4963
|
+
const [mediaChanged, setMediaChanged] = (0, react.useState)(false);
|
|
4964
|
+
const [locked, setLocked] = (0, react.useState)(false);
|
|
4965
|
+
const [failure, setFailure] = (0, react.useState)();
|
|
4966
|
+
const preview = (0, react.useRef)();
|
|
4967
|
+
const submission = (0, react.useRef)();
|
|
4968
|
+
const signal = (0, react.useRef)(new AbortController());
|
|
4969
|
+
(0, react.useEffect)(() => {
|
|
4970
|
+
const controller = new AbortController();
|
|
4971
|
+
signal.current = controller;
|
|
4972
|
+
return () => {
|
|
4973
|
+
controller.abort();
|
|
4974
|
+
};
|
|
4975
|
+
}, [sessionId, props.promptSeq]);
|
|
4976
|
+
const allowed = props.useSessionAccess?.(sessionId, "session/prompt") ?? true;
|
|
4977
|
+
const copy = (key) => uiText(props.locale, key);
|
|
4978
|
+
const mine = native.sessionId === sessionId && native.messageId === props.messageId;
|
|
4979
|
+
(0, react.useEffect)(() => {
|
|
4980
|
+
if (editing && !busy && native.controlsRetry && !mine) setEditing(false);
|
|
4981
|
+
}, [
|
|
4982
|
+
editing,
|
|
4983
|
+
busy,
|
|
4984
|
+
native.controlsRetry,
|
|
4985
|
+
mine
|
|
4986
|
+
]);
|
|
4987
|
+
if (!allowed || !sessionId || props.promptSeq === void 0 || !props.host.prepare) return props.render?.(null, null) ?? null;
|
|
4988
|
+
const run = async (action) => {
|
|
4989
|
+
if (busy || signal.current.signal.aborted) return;
|
|
4990
|
+
setBusy(true);
|
|
4991
|
+
setFailure(void 0);
|
|
4992
|
+
try {
|
|
4993
|
+
if (!native.controlsRetry || !props.messageId) throw new HostOperationError("session/lifecycle-unavailable");
|
|
4994
|
+
if (action === "edit") {
|
|
4995
|
+
const value = await previewEdit(props.host, sessionId, props.promptSeq, signal.current.signal);
|
|
4996
|
+
await props.nativeRetry.beginEdit(sessionId, props.messageId, value.text, signal.current.signal);
|
|
4997
|
+
const state = props.nativeRetry.getSnapshot();
|
|
4998
|
+
if (!state.editing) {
|
|
4999
|
+
setFailure(state.failure);
|
|
5000
|
+
return;
|
|
5001
|
+
}
|
|
5002
|
+
preview.current = value;
|
|
5003
|
+
submission.current = { requestId: (props.ids ?? (() => crypto.randomUUID()))() };
|
|
5004
|
+
setDraft(value.text);
|
|
5005
|
+
setAttachments((value.content ?? []).filter((part) => part.type !== "text"));
|
|
5006
|
+
setMediaChanged(false);
|
|
5007
|
+
setLocked(false);
|
|
5008
|
+
setEditing(true);
|
|
5009
|
+
return;
|
|
5010
|
+
}
|
|
5011
|
+
if (!preview.current || !submission.current) return;
|
|
5012
|
+
if (mediaChanged && native.controlsContent !== true) throw new HostOperationError("session/retry-media-unavailable");
|
|
5013
|
+
setLocked(true);
|
|
5014
|
+
const content = mediaChanged ? await prepareEditContent(props.host, preview.current, draft, submission.current, signal.current.signal, attachments) : void 0;
|
|
5015
|
+
props.nativeRetry.setDraft(draft);
|
|
5016
|
+
await props.nativeRetry.sendEdit(sessionId, props.messageId, signal.current.signal, content, true);
|
|
5017
|
+
const state = props.nativeRetry.getSnapshot();
|
|
5018
|
+
setFailure(state.failure);
|
|
5019
|
+
if (!state.editing) setEditing(false);
|
|
5020
|
+
else if (isStaleRetryFailure(state.failure)) setLocked(false);
|
|
5021
|
+
} catch (error) {
|
|
5022
|
+
if (!signal.current.signal.aborted) setFailure(readFailure(error));
|
|
5023
|
+
} finally {
|
|
5024
|
+
if (!signal.current.signal.aborted) setBusy(false);
|
|
5025
|
+
}
|
|
5026
|
+
};
|
|
5027
|
+
const cancel = () => {
|
|
5028
|
+
if (busy) return;
|
|
5029
|
+
props.nativeRetry.cancelEdit();
|
|
5030
|
+
setEditing(false);
|
|
5031
|
+
setFailure(void 0);
|
|
5032
|
+
};
|
|
5033
|
+
const invalidatePrepared = () => {
|
|
5034
|
+
if (submission.current) submission.current = { requestId: submission.current.requestId };
|
|
5035
|
+
};
|
|
5036
|
+
const actions = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5037
|
+
className: `${FREE_CHAT_ROOT} dsh-free-actions`,
|
|
5038
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
5039
|
+
label: copy("edit"),
|
|
5040
|
+
side: "bottom",
|
|
5041
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5042
|
+
className: "dsm-icon-action",
|
|
5043
|
+
size: "sm",
|
|
5044
|
+
variant: "ghost",
|
|
5045
|
+
"aria-label": copy("edit"),
|
|
5046
|
+
disabled: busy || native.busy || native.editing && !mine,
|
|
5047
|
+
onClick: () => {
|
|
5048
|
+
run("edit");
|
|
5049
|
+
},
|
|
5050
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditMessageIcon, {})
|
|
5051
|
+
})
|
|
5052
|
+
}), failure && !editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5053
|
+
className: "dsh-ui-error dsh-free-alert",
|
|
5054
|
+
role: "alert",
|
|
5055
|
+
children: failureText(props.locale, failure)
|
|
5056
|
+
}) : null]
|
|
5057
|
+
});
|
|
5058
|
+
const editor = editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageEditor, {
|
|
5059
|
+
text: draft,
|
|
5060
|
+
setText: (text) => {
|
|
5061
|
+
invalidatePrepared();
|
|
5062
|
+
setDraft(text);
|
|
5063
|
+
},
|
|
5064
|
+
attachments,
|
|
5065
|
+
setAttachments: native.controlsContent ? (items) => {
|
|
5066
|
+
invalidatePrepared();
|
|
5067
|
+
setAttachments(items);
|
|
5068
|
+
setMediaChanged(true);
|
|
5069
|
+
} : void 0,
|
|
5070
|
+
sessionId,
|
|
5071
|
+
host: props.host,
|
|
5072
|
+
locale: props.locale,
|
|
5073
|
+
busy,
|
|
5074
|
+
locked,
|
|
5075
|
+
onCancel: cancel,
|
|
5076
|
+
onSend: () => {
|
|
5077
|
+
setConfirming(true);
|
|
5078
|
+
},
|
|
5079
|
+
error: failure ? failureText(props.locale, failure) : void 0
|
|
5080
|
+
}) : null;
|
|
5081
|
+
const content = props.render ? props.render(actions, editor) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [actions, editor] });
|
|
5082
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [content, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
5083
|
+
open: confirming && editing,
|
|
5084
|
+
className: FREE_CHAT_ROOT,
|
|
5085
|
+
title: copy("editTitle"),
|
|
5086
|
+
closeLabel: copy("close"),
|
|
5087
|
+
onClose: () => {
|
|
5088
|
+
setConfirming(false);
|
|
5089
|
+
},
|
|
5090
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5091
|
+
variant: "ghost",
|
|
5092
|
+
"data-modal-autofocus": true,
|
|
5093
|
+
onClick: () => {
|
|
5094
|
+
setConfirming(false);
|
|
5095
|
+
},
|
|
5096
|
+
children: copy("cancel")
|
|
5097
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5098
|
+
variant: "primary",
|
|
5099
|
+
onClick: () => {
|
|
5100
|
+
setConfirming(false);
|
|
5101
|
+
run("send");
|
|
5102
|
+
},
|
|
5103
|
+
children: copy("editConfirm")
|
|
5104
|
+
})] }),
|
|
5105
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy("editBranchWarning") })
|
|
5106
|
+
})] });
|
|
5107
|
+
}
|
|
5108
|
+
//#endregion
|
|
5109
|
+
//#region src/user-node.tsx
|
|
5110
|
+
function contentParts(content) {
|
|
5111
|
+
const texts = [];
|
|
5112
|
+
const attachments = [];
|
|
5113
|
+
const rest = [];
|
|
5114
|
+
for (const block of content) if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
|
|
5115
|
+
else if (block.type === "image" && block.attachment !== void 0) attachments.push({
|
|
5116
|
+
type: "image",
|
|
5117
|
+
image: { attachment: block.attachment }
|
|
5118
|
+
});
|
|
5119
|
+
else if (block.type === "file" && block.attachment !== void 0) attachments.push({
|
|
5120
|
+
type: "file",
|
|
5121
|
+
file: block.attachment
|
|
5122
|
+
});
|
|
5123
|
+
else rest.push(block);
|
|
5124
|
+
return {
|
|
5125
|
+
text: texts.join(""),
|
|
5126
|
+
attachments,
|
|
5127
|
+
rest
|
|
5128
|
+
};
|
|
5129
|
+
}
|
|
5130
|
+
function pad2(value) {
|
|
5131
|
+
return String(value).padStart(2, "0");
|
|
5132
|
+
}
|
|
5133
|
+
function messageClock(time, t) {
|
|
5134
|
+
const date = new Date(time);
|
|
5135
|
+
const now = /* @__PURE__ */ new Date();
|
|
5136
|
+
const clock = `${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
|
|
5137
|
+
if (date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate()) return clock;
|
|
5138
|
+
const params = {
|
|
5139
|
+
y: date.getFullYear(),
|
|
5140
|
+
m: date.getMonth() + 1,
|
|
5141
|
+
d: date.getDate()
|
|
5142
|
+
};
|
|
5143
|
+
return `${date.getFullYear() === now.getFullYear() ? t("clock.md", params) : t("clock.ymd", params)} ${clock}`;
|
|
5144
|
+
}
|
|
5145
|
+
function MessageChrome(props) {
|
|
5146
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
5147
|
+
const timer = (0, react.useRef)(void 0);
|
|
5148
|
+
const pending = (0, react.useRef)(false);
|
|
5149
|
+
(0, react.useEffect)(() => () => {
|
|
5150
|
+
pending.current = false;
|
|
5151
|
+
if (timer.current !== void 0) clearTimeout(timer.current);
|
|
5152
|
+
}, []);
|
|
5153
|
+
const copy = () => {
|
|
5154
|
+
if (copied || pending.current) return;
|
|
5155
|
+
pending.current = true;
|
|
5156
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(props.text).then((ok) => {
|
|
5157
|
+
pending.current = false;
|
|
5158
|
+
if (!ok) return;
|
|
5159
|
+
setCopied(true);
|
|
5160
|
+
timer.current = setTimeout(() => {
|
|
5161
|
+
setCopied(false);
|
|
5162
|
+
}, 1e3);
|
|
5163
|
+
});
|
|
5164
|
+
};
|
|
5165
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5166
|
+
className: "dsm-user-chrome",
|
|
5167
|
+
children: [
|
|
5168
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5169
|
+
className: "dsm-user-time",
|
|
5170
|
+
children: messageClock(props.time, props.t)
|
|
5171
|
+
}),
|
|
5172
|
+
props.children,
|
|
5173
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
5174
|
+
label: copied ? props.t("copied") : props.t("copy"),
|
|
5175
|
+
side: "bottom",
|
|
5176
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5177
|
+
size: "sm",
|
|
5178
|
+
variant: "ghost",
|
|
5179
|
+
className: "dsm-icon-action",
|
|
5180
|
+
"aria-label": copied ? props.t("copied") : props.t("copy"),
|
|
5181
|
+
onClick: copy,
|
|
5182
|
+
children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutlineRegular, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular, {})
|
|
4123
5183
|
})
|
|
4124
5184
|
})
|
|
4125
5185
|
]
|
|
4126
5186
|
});
|
|
4127
5187
|
}
|
|
5188
|
+
/** User-message bubble whose own action row edits the exact prompt in place. */
|
|
5189
|
+
function UserNodeWithEdit(props) {
|
|
5190
|
+
const { text, attachments, rest } = contentParts(props.node.data.content);
|
|
5191
|
+
const compactImages = attachments.length > 1;
|
|
5192
|
+
const truncated = (total) => props.t("json.truncated", { total });
|
|
5193
|
+
const data = props.node.data;
|
|
5194
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditActions, {
|
|
5195
|
+
promptSeq: data.seq,
|
|
5196
|
+
sessionId: props.sessionId,
|
|
5197
|
+
locale: props.locale,
|
|
5198
|
+
host: props.host,
|
|
5199
|
+
nativeRetry: props.nativeRetry,
|
|
5200
|
+
useSessionAccess: props.useSessionAccess,
|
|
5201
|
+
ids: props.ids,
|
|
5202
|
+
messageId: props.node.id,
|
|
5203
|
+
render: (actions, editor) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5204
|
+
className: `${FREE_CHAT_ROOT} dsm-user-row`,
|
|
5205
|
+
children: [editor, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5206
|
+
className: "dsm-user-original",
|
|
5207
|
+
hidden: Boolean(editor),
|
|
5208
|
+
children: [!props.renderMessageImages && props.renderFactorySlot ? props.renderFactorySlot("conversation.chat.user.content", {
|
|
5209
|
+
content: {
|
|
5210
|
+
text,
|
|
5211
|
+
attachments,
|
|
5212
|
+
extraBlocks: rest,
|
|
5213
|
+
...data.referenceLabels === void 0 ? {} : { referenceLabels: data.referenceLabels }
|
|
5214
|
+
},
|
|
5215
|
+
loadImage: props.loadImage,
|
|
5216
|
+
facts: {
|
|
5217
|
+
...data.referenceLabels === void 0 ? {} : { sessionLabels: data.referenceLabels },
|
|
5218
|
+
...data.skillNames === void 0 ? {} : { skillNames: data.skillNames }
|
|
5219
|
+
},
|
|
5220
|
+
interactive: true
|
|
5221
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5222
|
+
className: "dsm-user-stack",
|
|
5223
|
+
children: [
|
|
5224
|
+
attachments.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5225
|
+
className: "dsm-user-attachments",
|
|
5226
|
+
"data-message-attachments": true,
|
|
5227
|
+
children: attachments.map((attachment, index) => attachment.type === "image" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: props.renderMessageImages?.({
|
|
5228
|
+
images: [attachment.image],
|
|
5229
|
+
align: "end",
|
|
5230
|
+
compact: compactImages
|
|
5231
|
+
}) }, `image:${index}`) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5232
|
+
className: "dsm-file-card",
|
|
5233
|
+
title: attachment.file.name,
|
|
5234
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
|
|
5235
|
+
path: attachment.file.name,
|
|
5236
|
+
className: "dsm-file-icon"
|
|
5237
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5238
|
+
className: "dsm-file-content",
|
|
5239
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5240
|
+
className: "dsm-file-name",
|
|
5241
|
+
children: attachment.file.name
|
|
5242
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5243
|
+
className: "dsm-file-meta",
|
|
5244
|
+
children: [(0, _deepseek_ai_dsh_client_ui_primitives.fileExtension)(attachment.file.name).toUpperCase().slice(0, 8), (0, _deepseek_ai_dsh_client_ui_primitives.fileSizeText)(attachment.file.bytes)].filter(Boolean).join(" ")
|
|
5245
|
+
})]
|
|
5246
|
+
})]
|
|
5247
|
+
}, `file:${index}`))
|
|
5248
|
+
}) : null,
|
|
5249
|
+
text !== "" || rest.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5250
|
+
className: "dsm-user-bubble",
|
|
5251
|
+
children: [text, rest.map((block, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.JsonBlock, {
|
|
5252
|
+
label: props.t("message.extraBlock"),
|
|
5253
|
+
payload: block,
|
|
5254
|
+
truncatedLabel: truncated
|
|
5255
|
+
}, index))]
|
|
5256
|
+
}) : null,
|
|
5257
|
+
data.referenceLabels !== void 0 && data.referenceLabels.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5258
|
+
className: "dsm-reference-summary",
|
|
5259
|
+
children: props.t("message.referenceSummary", { labels: data.referenceLabels.join(props.t("message.referenceSeparator")) })
|
|
5260
|
+
}) : null
|
|
5261
|
+
]
|
|
5262
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(MessageChrome, {
|
|
5263
|
+
text,
|
|
5264
|
+
time: data.time,
|
|
5265
|
+
t: props.t,
|
|
5266
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UserMessageActions, {
|
|
5267
|
+
messageId: props.node.id,
|
|
5268
|
+
text,
|
|
5269
|
+
sessionId: props.sessionId ?? "",
|
|
5270
|
+
locale: props.locale,
|
|
5271
|
+
model: props.nativeRetry,
|
|
5272
|
+
hideEdit: Boolean(actions)
|
|
5273
|
+
}), actions]
|
|
5274
|
+
})]
|
|
5275
|
+
})]
|
|
5276
|
+
})
|
|
5277
|
+
});
|
|
5278
|
+
}
|
|
5279
|
+
//#endregion
|
|
5280
|
+
//#region src/retry-contracts.ts
|
|
5281
|
+
const RETRY_CAPABILITIES_ENDPOINT = "retry.capabilities";
|
|
5282
|
+
const RETRY_PREVIEW_ENDPOINT = "retry.preview";
|
|
5283
|
+
const RETRY_COMMIT_ENDPOINT = "retry.commit";
|
|
5284
|
+
//#endregion
|
|
5285
|
+
//#region src/retry-host.ts
|
|
5286
|
+
function bindRetryHost(rpc) {
|
|
5287
|
+
const call = (endpoint, payload, signal) => {
|
|
5288
|
+
if (typeof rpc?.call !== "function") throw new HostOperationError("session/lifecycle-unavailable");
|
|
5289
|
+
return rpc.call(TEMP_WORKSPACE_CHANNEL, endpoint, payload, signal);
|
|
5290
|
+
};
|
|
5291
|
+
return {
|
|
5292
|
+
async capabilities() {
|
|
5293
|
+
const value = await unwrap(call(RETRY_CAPABILITIES_ENDPOINT, {}));
|
|
5294
|
+
if (!isCapabilities(value)) throw new HostOperationError("session-manager/unavailable");
|
|
5295
|
+
return value;
|
|
5296
|
+
},
|
|
5297
|
+
async preview(request, signal) {
|
|
5298
|
+
const value = await unwrap(call(RETRY_PREVIEW_ENDPOINT, request, signal));
|
|
5299
|
+
if (!isPreview(value)) throw new HostOperationError("session-manager/unavailable");
|
|
5300
|
+
return value;
|
|
5301
|
+
},
|
|
5302
|
+
async commit(request, signal) {
|
|
5303
|
+
const value = await unwrap(call(RETRY_COMMIT_ENDPOINT, request, signal));
|
|
5304
|
+
if (!isCommit(value)) throw new HostOperationError("session-manager/unavailable");
|
|
5305
|
+
return value;
|
|
5306
|
+
}
|
|
5307
|
+
};
|
|
5308
|
+
}
|
|
5309
|
+
/**
|
|
5310
|
+
* Commit then navigate: the forked child is the chat the user keeps, so the
|
|
5311
|
+
* browser follows a successful commit to the new session. `open` is absent
|
|
5312
|
+
* only when the host has no workspace navigation; the commit value returns
|
|
5313
|
+
* either way.
|
|
5314
|
+
*/
|
|
5315
|
+
function retryCommitAndOpen(commit, open) {
|
|
5316
|
+
return async (request, signal) => {
|
|
5317
|
+
const value = await commit(request, signal);
|
|
5318
|
+
try {
|
|
5319
|
+
await open?.(value.sessionId);
|
|
5320
|
+
} catch {
|
|
5321
|
+
throw new HostOperationError("session/retry-pending");
|
|
5322
|
+
}
|
|
5323
|
+
return value;
|
|
5324
|
+
};
|
|
5325
|
+
}
|
|
5326
|
+
function isCapabilities(value) {
|
|
5327
|
+
if (typeof value !== "object" || value === null) return false;
|
|
5328
|
+
const item = value;
|
|
5329
|
+
return typeof item.retry === "boolean" && typeof item.retryModified === "boolean" && typeof item.retryContent === "boolean";
|
|
5330
|
+
}
|
|
5331
|
+
function isPreview(value) {
|
|
5332
|
+
if (typeof value !== "object" || value === null) return false;
|
|
5333
|
+
const item = value;
|
|
5334
|
+
return typeof item.sessionId === "string" && typeof item.messageId === "string" && Number.isSafeInteger(item.promptSeq) && typeof item.branchRevision === "number" && typeof item.inputRevision === "string" && typeof item.eligible === "boolean" && typeof item.modified === "boolean";
|
|
5335
|
+
}
|
|
5336
|
+
function isCommit(value) {
|
|
5337
|
+
if (typeof value !== "object" || value === null) return false;
|
|
5338
|
+
const item = value;
|
|
5339
|
+
return typeof item.sessionId === "string" && typeof item.sourceSessionId === "string" && typeof item.archived === "boolean";
|
|
5340
|
+
}
|
|
4128
5341
|
//#endregion
|
|
4129
5342
|
//#region src/client.tsx
|
|
4130
5343
|
const name = "dsh-session-manager-client";
|
|
@@ -4144,7 +5357,14 @@ function apply(ctx) {
|
|
|
4144
5357
|
mountModelSettingsChanges(client, PLUGIN_NAME);
|
|
4145
5358
|
const host = bindHost(client);
|
|
4146
5359
|
const chats = createFreeChatModel(host);
|
|
4147
|
-
const
|
|
5360
|
+
const retryHost = bindRetryHost(client.connection?.rpc);
|
|
5361
|
+
const retries = createRetryModel({
|
|
5362
|
+
list: host.list,
|
|
5363
|
+
capabilities: () => retryHost.capabilities(),
|
|
5364
|
+
previewRetry: (request) => retryHost.preview(request),
|
|
5365
|
+
retry: retryCommitAndOpen(retryHost.commit, host.openSession)
|
|
5366
|
+
});
|
|
5367
|
+
const editHost = bindEditHost(client);
|
|
4148
5368
|
const temporaryHost = bindTemporaryWorkspaceHost(client);
|
|
4149
5369
|
const temporary = createTemporaryWorkspaceModel(temporaryHost, void 0, () => uiText(copyLocale(client.locale?.getSnapshot().active), "tempWorkspace"));
|
|
4150
5370
|
const presenceHost = bindPresenceHost(client);
|
|
@@ -4168,6 +5388,21 @@ function apply(ctx) {
|
|
|
4168
5388
|
...props
|
|
4169
5389
|
}))));
|
|
4170
5390
|
stops.push(installTemporaryWorkspacePicker(client.slots, temporary, temporaryHost.createWorkspace, client.locale));
|
|
5391
|
+
stops.push(client.slots.inject("conversation.hero.options", () => client.slots.register({
|
|
5392
|
+
name: "conversation.hero.options",
|
|
5393
|
+
id: "auto-continue",
|
|
5394
|
+
order: 30,
|
|
5395
|
+
inject: () => ({
|
|
5396
|
+
call: (endpoint, payload) => client.connection?.rpc?.call(TEMP_WORKSPACE_CHANNEL, endpoint, payload),
|
|
5397
|
+
subscribeGeneration: (listener) => client.connection?.generation?.subscribe(listener)
|
|
5398
|
+
})
|
|
5399
|
+
}, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoContinueOption, {
|
|
5400
|
+
sessionId: props.sessionId,
|
|
5401
|
+
call: props.call,
|
|
5402
|
+
subscribeGeneration: props.subscribeGeneration,
|
|
5403
|
+
useSession: props.useSession,
|
|
5404
|
+
locale: useLiveLocale(client.locale)
|
|
5405
|
+
}))));
|
|
4171
5406
|
stops.push(client.slots.inject(USER_ACTIONS_SLOT, () => client.slots.register({
|
|
4172
5407
|
name: USER_ACTIONS_SLOT,
|
|
4173
5408
|
id: USER_ACTIONS_ID,
|
|
@@ -4193,8 +5428,22 @@ function apply(ctx) {
|
|
|
4193
5428
|
locale: useLiveLocale(client.locale),
|
|
4194
5429
|
model: continues
|
|
4195
5430
|
}))));
|
|
4196
|
-
stops.push(client.slots.inject(
|
|
4197
|
-
name:
|
|
5431
|
+
stops.push(client.slots.inject("conversation.chat.node", () => client.slots.register({
|
|
5432
|
+
name: "conversation.chat.node",
|
|
5433
|
+
key: "user",
|
|
5434
|
+
locale: "chat",
|
|
5435
|
+
priority: -10
|
|
5436
|
+
}, (props) => {
|
|
5437
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UserNodeWithEdit, {
|
|
5438
|
+
...props,
|
|
5439
|
+
sessionId: props.sessionId,
|
|
5440
|
+
locale: useLiveLocale(client.locale),
|
|
5441
|
+
host: editHost,
|
|
5442
|
+
nativeRetry: retries
|
|
5443
|
+
});
|
|
5444
|
+
})));
|
|
5445
|
+
stops.push(client.slots.inject(TURN_TAIL_SLOT, () => client.slots.register({
|
|
5446
|
+
name: TURN_TAIL_SLOT,
|
|
4198
5447
|
id: WELCOME_BACK_CARD_ID,
|
|
4199
5448
|
order: 30
|
|
4200
5449
|
}, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WelcomeBackCard, {
|