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