@klarkxy/dsh-session-manager 0.3.1 → 0.3.3
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 +7 -4
- package/README.zh-CN.md +7 -4
- package/lib/client.inner.cjs +1338 -350
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +1338 -350
- package/lib/index.js +787 -18
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
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: "欢迎回来",
|
|
@@ -59,18 +69,20 @@ const text = {
|
|
|
59
69
|
settingsMode: "继续灰影",
|
|
60
70
|
modeFixed: "固定文本",
|
|
61
71
|
modeAuto: "模型生成",
|
|
62
|
-
settingsModeHint: "
|
|
72
|
+
settingsModeHint: "输入框为空时显示建议;按 Tab 填入,点击直接发送。",
|
|
73
|
+
continueSourceLabel: "内容来源",
|
|
63
74
|
continueTextLabel: "固定文本内容",
|
|
64
75
|
continueTextPlaceholder: "留空使用默认「继续」",
|
|
65
|
-
ghostModelLabel: "
|
|
66
|
-
ghostModelHint: "
|
|
67
|
-
welcomeBackGroup: "
|
|
68
|
-
recapEnabledLabel: "
|
|
69
|
-
recapEnabledHint: "
|
|
70
|
-
recapMinutesLabel: "
|
|
71
|
-
compactEnabledLabel: "
|
|
72
|
-
compactEnabledHint: "
|
|
73
|
-
compactMinutesLabel: "
|
|
76
|
+
ghostModelLabel: "生成模型",
|
|
77
|
+
ghostModelHint: "每轮结束后生成一次并缓存;不选择时使用当前会话或宿主的默认模型。",
|
|
78
|
+
welcomeBackGroup: "回到会话时",
|
|
79
|
+
recapEnabledLabel: "显示会话摘要",
|
|
80
|
+
recapEnabledHint: "在对话中回顾上次进展。",
|
|
81
|
+
recapMinutesLabel: "离开多久后显示摘要",
|
|
82
|
+
compactEnabledLabel: "建议压缩上下文",
|
|
83
|
+
compactEnabledHint: "长时间离开后,建议先压缩上下文。",
|
|
84
|
+
compactMinutesLabel: "离开多久后显示建议",
|
|
85
|
+
minutesUnit: "分钟",
|
|
74
86
|
modelLabel: "摘要模型",
|
|
75
87
|
modelFollowOption: "当前会话 / 宿主默认模型",
|
|
76
88
|
modelGroupHint: "只用于欢迎回来摘要;留空跟随会话或宿主默认。",
|
|
@@ -96,6 +108,12 @@ const text = {
|
|
|
96
108
|
edit: "Edit and resend",
|
|
97
109
|
send: "Send",
|
|
98
110
|
editTitle: "Edit and resend",
|
|
111
|
+
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.",
|
|
112
|
+
editConfirm: "Confirm resend",
|
|
113
|
+
editAddAttachment: "Add attachments",
|
|
114
|
+
editRemoveAttachment: "Remove attachment",
|
|
115
|
+
editImage: "Image",
|
|
116
|
+
editPreviewFailed: "Preview unavailable",
|
|
99
117
|
draft: "Message text",
|
|
100
118
|
continueDefault: "Continue",
|
|
101
119
|
welcomeBack: "Welcome back",
|
|
@@ -113,18 +131,20 @@ const text = {
|
|
|
113
131
|
settingsMode: "Continue ghost",
|
|
114
132
|
modeFixed: "Fixed text",
|
|
115
133
|
modeAuto: "Model-generated",
|
|
116
|
-
settingsModeHint: "
|
|
134
|
+
settingsModeHint: "Show a suggestion when the composer is empty. Press Tab to fill it, or click to send.",
|
|
135
|
+
continueSourceLabel: "Source",
|
|
117
136
|
continueTextLabel: "Fixed text",
|
|
118
137
|
continueTextPlaceholder: "Empty uses the default \"Continue\"",
|
|
119
|
-
ghostModelLabel: "
|
|
120
|
-
ghostModelHint: "Generated once after
|
|
121
|
-
welcomeBackGroup: "
|
|
122
|
-
recapEnabledLabel: "
|
|
123
|
-
recapEnabledHint: "
|
|
124
|
-
recapMinutesLabel: "
|
|
125
|
-
compactEnabledLabel: "
|
|
126
|
-
compactEnabledHint: "
|
|
127
|
-
compactMinutesLabel: "
|
|
138
|
+
ghostModelLabel: "Generation model",
|
|
139
|
+
ghostModelHint: "Generated once after each turn and cached. The default follows the current conversation or host.",
|
|
140
|
+
welcomeBackGroup: "When you return",
|
|
141
|
+
recapEnabledLabel: "Show session recap",
|
|
142
|
+
recapEnabledHint: "Review your previous progress in the conversation.",
|
|
143
|
+
recapMinutesLabel: "Show recap after",
|
|
144
|
+
compactEnabledLabel: "Suggest context compaction",
|
|
145
|
+
compactEnabledHint: "Suggest compacting the context after a long time away.",
|
|
146
|
+
compactMinutesLabel: "Show suggestion after",
|
|
147
|
+
minutesUnit: "minutes",
|
|
128
148
|
modelLabel: "Recap model",
|
|
129
149
|
modelFollowOption: "Current conversation / host default",
|
|
130
150
|
modelGroupHint: "Used only for the welcome-back recap; empty follows the conversation or host default.",
|
|
@@ -141,7 +161,9 @@ function uiText(locale, key) {
|
|
|
141
161
|
return text[locale][key];
|
|
142
162
|
}
|
|
143
163
|
function failureReason(failure) {
|
|
144
|
-
if (failure.code === "session/
|
|
164
|
+
if (failure.code === "session/retry-pending") return "retryPending";
|
|
165
|
+
if (failure.code === "session/retry-uncertain") return "retryUncertain";
|
|
166
|
+
if (failure.code === "session/lifecycle-unavailable" || failure.code === "session/retry-media-unavailable" || failure.code === "session-manager/unavailable") return "unsupported";
|
|
145
167
|
if (failure.code === "session-manager/busy") return "running";
|
|
146
168
|
if (failure.code === "session/retry-revision" || failure.code === "session/branch-moved" || failure.code === "session/retry-input" || failure.code === "session/retry-conflict") return "stale";
|
|
147
169
|
if (failure.code === "session/delete-pending") return "cleanup";
|
|
@@ -179,11 +201,13 @@ const DELETE_DIALOG_ID = "@klarkxy/dsh-session-manager.delete-chat";
|
|
|
179
201
|
const WELCOME_BACK_PREPARE_ENDPOINT = "welcomeback.prepare";
|
|
180
202
|
const WELCOME_BACK_COMPACT_ENDPOINT = "welcomeback.compact";
|
|
181
203
|
const CONTINUE_SUGGEST_ENDPOINT = "continue.suggest";
|
|
204
|
+
const EDIT_PREPARE_ENDPOINT = "edit.prepare";
|
|
205
|
+
const EDIT_FILE_ENDPOINT = "edit.file";
|
|
182
206
|
const CONFIG_READ_ENDPOINT = "config.read";
|
|
183
207
|
const CONFIG_WRITE_ENDPOINT = "config.write";
|
|
184
208
|
/** Card entry id in the chat flow; the recap renders at the latest completed turn's tail. */
|
|
185
209
|
const WELCOME_BACK_CARD_ID = "@klarkxy/dsh-session-manager.welcome-back";
|
|
186
|
-
const
|
|
210
|
+
const TURN_TAIL_SLOT = "conversation.chat.turnTail";
|
|
187
211
|
const CONTINUE_GHOST_ID = "@klarkxy/dsh-session-manager.continue-ghost";
|
|
188
212
|
const CONTINUE_GHOST_SLOT = "conversation.input.overlay";
|
|
189
213
|
//#endregion
|
|
@@ -1650,29 +1674,199 @@ ${modelMenuCss}
|
|
|
1650
1674
|
.${FREE_CHAT_ROOT} .dsh-free-actions {
|
|
1651
1675
|
display: inline-flex;
|
|
1652
1676
|
align-items: center;
|
|
1653
|
-
gap:
|
|
1677
|
+
gap: 8px;
|
|
1654
1678
|
min-width: 0;
|
|
1655
1679
|
}
|
|
1656
1680
|
.${FREE_CHAT_ROOT} .dsh-free-alert { margin: 0; }
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1681
|
+
.${FREE_CHAT_ROOT} .dsh-free-warning {
|
|
1682
|
+
margin: 0 0 12px;
|
|
1683
|
+
color: var(--dsw-alias-label-secondary);
|
|
1684
|
+
}
|
|
1685
|
+
.${FREE_CHAT_ROOT}.dsm-user-row {
|
|
1686
|
+
display: flex;
|
|
1687
|
+
flex-direction: column;
|
|
1688
|
+
align-items: flex-end;
|
|
1689
|
+
gap: 6px;
|
|
1690
|
+
}
|
|
1691
|
+
.${FREE_CHAT_ROOT} .dsm-user-stack {
|
|
1692
|
+
display: flex;
|
|
1693
|
+
flex-direction: column;
|
|
1694
|
+
align-items: flex-end;
|
|
1695
|
+
gap: 8px;
|
|
1696
|
+
min-width: 0;
|
|
1697
|
+
max-width: 82%;
|
|
1698
|
+
}
|
|
1699
|
+
.${FREE_CHAT_ROOT} .dsm-user-bubble {
|
|
1700
|
+
max-width: 100%;
|
|
1701
|
+
padding: 10px 16px;
|
|
1702
|
+
border-radius: var(--dsw-radius-md);
|
|
1703
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
1704
|
+
color: var(--dsw-alias-label-primary);
|
|
1705
|
+
font-size: 14px;
|
|
1706
|
+
line-height: 22px;
|
|
1707
|
+
white-space: pre-wrap;
|
|
1708
|
+
word-break: break-word;
|
|
1709
|
+
}
|
|
1710
|
+
.${FREE_CHAT_ROOT} .dsm-user-chrome {
|
|
1711
|
+
display: flex;
|
|
1712
|
+
align-items: center;
|
|
1713
|
+
gap: 8px;
|
|
1714
|
+
min-height: 28px;
|
|
1715
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1716
|
+
}
|
|
1717
|
+
.${FREE_CHAT_ROOT} .dsm-user-time {
|
|
1718
|
+
font-size: 13px;
|
|
1719
|
+
white-space: nowrap;
|
|
1720
|
+
}
|
|
1721
|
+
.${FREE_CHAT_ROOT} .dsm-icon-action {
|
|
1722
|
+
display: inline-flex;
|
|
1723
|
+
align-items: center;
|
|
1724
|
+
justify-content: center;
|
|
1725
|
+
width: 28px;
|
|
1726
|
+
height: 28px;
|
|
1727
|
+
padding: 6px;
|
|
1728
|
+
border: none;
|
|
1729
|
+
border-radius: var(--dsw-radius-sm);
|
|
1730
|
+
background: transparent;
|
|
1731
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1732
|
+
cursor: pointer;
|
|
1733
|
+
}
|
|
1734
|
+
.${FREE_CHAT_ROOT} .dsm-icon-action:hover:not(:disabled) {
|
|
1735
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
1736
|
+
color: var(--dsw-alias-label-secondary);
|
|
1737
|
+
}
|
|
1738
|
+
.${FREE_CHAT_ROOT} .dsm-icon-action svg { width: 15px; height: 15px; }
|
|
1739
|
+
.${FREE_CHAT_ROOT} .dsm-user-attachments {
|
|
1740
|
+
display: flex;
|
|
1741
|
+
flex-wrap: wrap;
|
|
1742
|
+
justify-content: flex-end;
|
|
1743
|
+
max-width: 100%;
|
|
1744
|
+
gap: 8px;
|
|
1745
|
+
}
|
|
1746
|
+
.${FREE_CHAT_ROOT} .dsm-file-card {
|
|
1747
|
+
display: inline-flex;
|
|
1748
|
+
flex: 0 0 240px;
|
|
1749
|
+
align-items: center;
|
|
1750
|
+
gap: 10px;
|
|
1751
|
+
box-sizing: border-box;
|
|
1752
|
+
width: 240px;
|
|
1753
|
+
min-height: 64px;
|
|
1754
|
+
padding: 8px 12px;
|
|
1755
|
+
border: 0.5px solid var(--dsw-alias-border-l2);
|
|
1756
|
+
border-radius: var(--dsw-radius-md);
|
|
1757
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
1758
|
+
}
|
|
1759
|
+
.${FREE_CHAT_ROOT} .dsm-file-icon { flex: none; width: 28px; height: 28px; }
|
|
1760
|
+
.${FREE_CHAT_ROOT} .dsm-file-content {
|
|
1761
|
+
display: flex;
|
|
1762
|
+
flex: 1;
|
|
1763
|
+
flex-direction: column;
|
|
1764
|
+
min-width: 0;
|
|
1765
|
+
}
|
|
1766
|
+
.${FREE_CHAT_ROOT} .dsm-file-name {
|
|
1767
|
+
overflow: hidden;
|
|
1768
|
+
color: var(--dsw-alias-label-primary);
|
|
1769
|
+
font-size: 14px;
|
|
1770
|
+
font-weight: 500;
|
|
1771
|
+
line-height: 22px;
|
|
1772
|
+
white-space: nowrap;
|
|
1773
|
+
text-overflow: ellipsis;
|
|
1774
|
+
}
|
|
1775
|
+
.${FREE_CHAT_ROOT} .dsm-file-meta {
|
|
1776
|
+
overflow: hidden;
|
|
1777
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1778
|
+
font-size: 12px;
|
|
1779
|
+
line-height: 15px;
|
|
1780
|
+
white-space: nowrap;
|
|
1781
|
+
text-overflow: ellipsis;
|
|
1782
|
+
}
|
|
1783
|
+
.${FREE_CHAT_ROOT} .dsm-reference-summary {
|
|
1784
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1785
|
+
font-size: 13px;
|
|
1786
|
+
line-height: 18px;
|
|
1787
|
+
}
|
|
1788
|
+
/* Settings page: one quiet reading surface. Rules, not nested wells, separate
|
|
1789
|
+
* sections; dependent controls align with the feature they belong to. */
|
|
1790
|
+
.${FREE_CHAT_ROOT} .dsm-settings {
|
|
1791
|
+
display: flex;
|
|
1792
|
+
flex-direction: column;
|
|
1793
|
+
gap: 20px;
|
|
1794
|
+
min-width: 0;
|
|
1795
|
+
}
|
|
1796
|
+
.${FREE_CHAT_ROOT} .dsm-setting {
|
|
1797
|
+
display: flex;
|
|
1798
|
+
flex-direction: column;
|
|
1799
|
+
gap: 12px;
|
|
1800
|
+
min-width: 0;
|
|
1801
|
+
}
|
|
1802
|
+
.${FREE_CHAT_ROOT} .dsm-setting + .dsm-setting {
|
|
1803
|
+
padding-top: 20px;
|
|
1804
|
+
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
1805
|
+
}
|
|
1806
|
+
.${FREE_CHAT_ROOT} .dsm-setting-header {
|
|
1807
|
+
display: flex;
|
|
1808
|
+
flex-direction: column;
|
|
1809
|
+
gap: 4px;
|
|
1810
|
+
}
|
|
1811
|
+
.${FREE_CHAT_ROOT} .dsm-ghost-controls {
|
|
1812
|
+
display: grid;
|
|
1813
|
+
grid-template-columns: minmax(220px, 0.8fr) minmax(0, 1.2fr);
|
|
1814
|
+
align-items: start;
|
|
1815
|
+
gap: 16px;
|
|
1816
|
+
}
|
|
1817
|
+
/* SegmentedTabs is 42px tall; its adjacent text controls share that baseline. */
|
|
1818
|
+
.${FREE_CHAT_ROOT} .dsm-ghost-controls .dsh-model-menu-trigger,
|
|
1819
|
+
.${FREE_CHAT_ROOT} .dsm-ghost-controls .dsm-ghost-input {
|
|
1820
|
+
height: 42px;
|
|
1821
|
+
}
|
|
1660
1822
|
.${FREE_CHAT_ROOT} .dsm-feature + .dsm-feature {
|
|
1823
|
+
margin-top: 4px;
|
|
1661
1824
|
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
1662
1825
|
}
|
|
1663
|
-
.${FREE_CHAT_ROOT} .dsm-
|
|
1826
|
+
.${FREE_CHAT_ROOT} .dsm-feature > .dsh-ui-toggle-row { padding: 8px 0; }
|
|
1827
|
+
.${FREE_CHAT_ROOT} .dsm-feature-options {
|
|
1664
1828
|
display: flex;
|
|
1665
1829
|
flex-direction: column;
|
|
1666
|
-
gap:
|
|
1667
|
-
|
|
1668
|
-
padding: 12px;
|
|
1669
|
-
border-radius: var(--dsw-radius-md);
|
|
1670
|
-
background: var(--dsw-alias-bg-layer-2);
|
|
1830
|
+
gap: 8px;
|
|
1831
|
+
padding: 4px 0 8px;
|
|
1671
1832
|
}
|
|
1672
|
-
.${FREE_CHAT_ROOT} .dsm-
|
|
1833
|
+
.${FREE_CHAT_ROOT} .dsm-feature-options[data-disabled='true'] .dsh-ui-label,
|
|
1834
|
+
.${FREE_CHAT_ROOT} .dsm-feature-options[data-disabled='true'] .dsh-ui-meta {
|
|
1673
1835
|
color: var(--dsw-alias-label-tertiary);
|
|
1674
1836
|
}
|
|
1675
|
-
.${FREE_CHAT_ROOT} .dsm-
|
|
1837
|
+
.${FREE_CHAT_ROOT} .dsm-compact-options { max-width: calc(50% - 10px); }
|
|
1838
|
+
.${FREE_CHAT_ROOT} .dsm-model-field .dsh-model-menu-root,
|
|
1839
|
+
.${FREE_CHAT_ROOT} .dsm-model-field .dsh-model-menu-trigger {
|
|
1840
|
+
width: 100%;
|
|
1841
|
+
max-width: none;
|
|
1842
|
+
}
|
|
1843
|
+
.${FREE_CHAT_ROOT} .dsm-model-field .dsh-model-menu-trigger {
|
|
1844
|
+
justify-content: space-between;
|
|
1845
|
+
height: 32px;
|
|
1846
|
+
padding: 0 10px;
|
|
1847
|
+
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
1848
|
+
border-radius: var(--dsw-radius-md);
|
|
1849
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
1850
|
+
color: var(--dsw-alias-label-primary);
|
|
1851
|
+
}
|
|
1852
|
+
.${FREE_CHAT_ROOT} .dsm-model-field .dsh-model-menu-triggerLabel {
|
|
1853
|
+
flex: 1;
|
|
1854
|
+
text-align: left;
|
|
1855
|
+
}
|
|
1856
|
+
.${FREE_CHAT_ROOT} .dsm-settings-footer .dsh-ui-notice--error {
|
|
1857
|
+
flex: 0 0 100%;
|
|
1858
|
+
min-width: 0;
|
|
1859
|
+
}
|
|
1860
|
+
.${FREE_CHAT_ROOT} .dsm-minutes-control {
|
|
1861
|
+
display: flex;
|
|
1862
|
+
align-items: center;
|
|
1863
|
+
gap: 8px;
|
|
1864
|
+
}
|
|
1865
|
+
.${FREE_CHAT_ROOT} .dsm-minutes { width: 88px; }
|
|
1866
|
+
@media (max-width: 560px) {
|
|
1867
|
+
.${FREE_CHAT_ROOT} .dsm-ghost-controls { grid-template-columns: minmax(0, 1fr); }
|
|
1868
|
+
.${FREE_CHAT_ROOT} .dsm-compact-options { max-width: none; }
|
|
1869
|
+
}
|
|
1676
1870
|
/* Welcome-back card: one flat full-width row at the latest turn's tail in the
|
|
1677
1871
|
* chat flow — a single bordered surface with the recap laid out inline, no
|
|
1678
1872
|
* nested box. */
|
|
@@ -1730,6 +1924,22 @@ ${modelMenuCss}
|
|
|
1730
1924
|
color: var(--dsw-alias-label-secondary);
|
|
1731
1925
|
}
|
|
1732
1926
|
.${FREE_CHAT_ROOT} .dsh-welcome-done { margin: 6px 0 0; }
|
|
1927
|
+
|
|
1928
|
+
.${FREE_CHAT_ROOT} .dsm-user-original { display: contents; }
|
|
1929
|
+
.${FREE_CHAT_ROOT} .dsm-user-original[hidden] { display: none; }
|
|
1930
|
+
.${FREE_CHAT_ROOT} .dsm-message-edit { width: min(100%, 720px); align-self: flex-end; }
|
|
1931
|
+
.${FREE_CHAT_ROOT} .dsm-message-editor { width: 100%; box-sizing: border-box; padding: 14px; border-radius: 18px; background: var(--dsw-alias-bg-layer-3); }
|
|
1932
|
+
.${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; }
|
|
1933
|
+
.${FREE_CHAT_ROOT} .dsm-edit-footer { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
|
|
1934
|
+
.${FREE_CHAT_ROOT} .dsm-edit-spacer { flex: 1; }
|
|
1935
|
+
.${FREE_CHAT_ROOT} .dsm-edit-attachments { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
|
|
1936
|
+
.${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%; }
|
|
1937
|
+
.${FREE_CHAT_ROOT} .dsm-edit-attachment img { width: 72px; height: 60px; object-fit: contain; border-radius: 6px; }
|
|
1938
|
+
.${FREE_CHAT_ROOT} .dsm-edit-attachment > span { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1939
|
+
.${FREE_CHAT_ROOT} .dsm-edit-attachment > svg { width: 24px; height: 24px; }
|
|
1940
|
+
.${FREE_CHAT_ROOT} .dsm-edit-image { position: relative; padding: 0; border: none; }
|
|
1941
|
+
.${FREE_CHAT_ROOT} .dsm-edit-image img { width: auto; height: auto; max-width: 220px; max-height: 140px; object-fit: contain; border-radius: 10px; }
|
|
1942
|
+
.${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; }
|
|
1733
1943
|
`;
|
|
1734
1944
|
//#endregion
|
|
1735
1945
|
//#region src/free-panel.tsx
|
|
@@ -2116,11 +2326,15 @@ function bindTemporaryWorkspaceHost(ctx) {
|
|
|
2116
2326
|
//#region src/session-actions.ts
|
|
2117
2327
|
const UNSUPPORTED_CAPABILITIES = {
|
|
2118
2328
|
retry: false,
|
|
2329
|
+
retryModified: false,
|
|
2119
2330
|
freeSession: false,
|
|
2120
2331
|
deleteSession: false
|
|
2121
2332
|
};
|
|
2122
2333
|
function isRecoverable(failure) {
|
|
2123
|
-
return failure.code === "transport" || failure.code === "session/delete-pending";
|
|
2334
|
+
return failure.code === "transport" || failure.code === "session/delete-pending" || failure.code === "session/retry-pending" || failure.code === "session/retry-uncertain";
|
|
2335
|
+
}
|
|
2336
|
+
function isStaleRetryFailure(failure) {
|
|
2337
|
+
return failure?.code === "session/retry-revision" || failure?.code === "session/branch-moved" || failure?.code === "session/retry-input" || failure?.code === "session/retry-conflict";
|
|
2124
2338
|
}
|
|
2125
2339
|
function readFailure(error) {
|
|
2126
2340
|
if (isFailure(error)) return {
|
|
@@ -2181,6 +2395,8 @@ async function readCapabilities(host) {
|
|
|
2181
2395
|
const record = value;
|
|
2182
2396
|
return {
|
|
2183
2397
|
retry: record.retry === true,
|
|
2398
|
+
retryModified: record.retryModified === true,
|
|
2399
|
+
...record.retryContent === true ? { retryContent: true } : {},
|
|
2184
2400
|
freeSession: record.freeSession === true,
|
|
2185
2401
|
deleteSession: record.deleteSession === true
|
|
2186
2402
|
};
|
|
@@ -2213,19 +2429,16 @@ async function deleteOwnedSession(host, sessionId, requestId) {
|
|
|
2213
2429
|
if (phase !== "deleted" && phase !== "cleanup-pending") throw new HostOperationError("session/delete-pending", "missing-outcome");
|
|
2214
2430
|
return { phase };
|
|
2215
2431
|
}
|
|
2216
|
-
|
|
2432
|
+
/** Build the body for one edited prompt; the host keeps the original attachments when only text changes. */
|
|
2433
|
+
function editRetryRequest(input) {
|
|
2217
2434
|
return {
|
|
2218
2435
|
sessionId: input.sessionId,
|
|
2219
2436
|
messageId: input.messageId,
|
|
2220
2437
|
expectedRevision: input.expectedRevision,
|
|
2221
2438
|
expectedInputRevision: input.expectedInputRevision,
|
|
2222
|
-
requestId: input.requestId
|
|
2223
|
-
|
|
2224
|
-
}
|
|
2225
|
-
function editRetryRequest(input) {
|
|
2226
|
-
return {
|
|
2227
|
-
...directRetryRequest(input),
|
|
2228
|
-
text: input.text
|
|
2439
|
+
requestId: input.requestId,
|
|
2440
|
+
...input.content === void 0 ? { text: input.text } : { content: input.content },
|
|
2441
|
+
...input.allowModified === true ? { allowModified: true } : {}
|
|
2229
2442
|
};
|
|
2230
2443
|
}
|
|
2231
2444
|
var SubmissionLedger = class {
|
|
@@ -2410,6 +2623,8 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2410
2623
|
let ready = false;
|
|
2411
2624
|
let busy = false;
|
|
2412
2625
|
let editing = false;
|
|
2626
|
+
let modified = false;
|
|
2627
|
+
let editNeedsPreview = false;
|
|
2413
2628
|
let draft = "";
|
|
2414
2629
|
let failure;
|
|
2415
2630
|
let expectedRevision = 0;
|
|
@@ -2421,8 +2636,10 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2421
2636
|
function build() {
|
|
2422
2637
|
return {
|
|
2423
2638
|
controlsRetry: controlsFor(capabilities, ready, host).retry,
|
|
2639
|
+
controlsContent: ready && capabilities.retryContent === true,
|
|
2424
2640
|
busy,
|
|
2425
2641
|
editing,
|
|
2642
|
+
modified,
|
|
2426
2643
|
draft,
|
|
2427
2644
|
sessionId: ownerSession,
|
|
2428
2645
|
messageId: ownerMessage,
|
|
@@ -2438,7 +2655,7 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2438
2655
|
const record = value;
|
|
2439
2656
|
const inputRevision = typeof record.inputRevision === "string" ? record.inputRevision : "";
|
|
2440
2657
|
const eligible = record.eligible === true;
|
|
2441
|
-
if (eligible && inputRevision === "") throw new HostOperationError("session/lifecycle-unavailable", "input-revision");
|
|
2658
|
+
if ((eligible || record.reason === "modified") && inputRevision === "") throw new HostOperationError("session/lifecycle-unavailable", "input-revision");
|
|
2442
2659
|
return {
|
|
2443
2660
|
branchRevision: record.branchRevision,
|
|
2444
2661
|
inputRevision,
|
|
@@ -2476,7 +2693,11 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2476
2693
|
await ledger.replay(async (body) => {
|
|
2477
2694
|
await callRetry(body, signal, ticket);
|
|
2478
2695
|
});
|
|
2479
|
-
if (pending !== void 0 && ticket === call && !signal.aborted && ownerSession === pending.sessionId && ownerMessage === pending.messageId)
|
|
2696
|
+
if (pending !== void 0 && ticket === call && !signal.aborted && ownerSession === pending.sessionId && ownerMessage === pending.messageId) {
|
|
2697
|
+
editing = false;
|
|
2698
|
+
modified = false;
|
|
2699
|
+
editNeedsPreview = false;
|
|
2700
|
+
}
|
|
2480
2701
|
} catch (error) {
|
|
2481
2702
|
if (ticket === call && !signal.aborted) failure = readFailure(error);
|
|
2482
2703
|
} finally {
|
|
@@ -2495,8 +2716,9 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2495
2716
|
ready = true;
|
|
2496
2717
|
publish();
|
|
2497
2718
|
},
|
|
2498
|
-
async
|
|
2719
|
+
async beginEdit(sessionId, id, text, signal) {
|
|
2499
2720
|
if (busy || !controlsFor(capabilities, ready, host).retry || signal.aborted) return;
|
|
2721
|
+
if (editing && (ownerSession !== sessionId || ownerMessage !== id)) return;
|
|
2500
2722
|
if (ledger.pending() !== void 0) {
|
|
2501
2723
|
if (!samePending(sessionId, id)) return;
|
|
2502
2724
|
await replaySame(signal);
|
|
@@ -2505,63 +2727,25 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2505
2727
|
const ticket = ++call;
|
|
2506
2728
|
busy = true;
|
|
2507
2729
|
failure = void 0;
|
|
2508
|
-
|
|
2730
|
+
modified = false;
|
|
2731
|
+
editNeedsPreview = false;
|
|
2509
2732
|
ownerSession = sessionId;
|
|
2510
2733
|
ownerMessage = id;
|
|
2511
2734
|
publish();
|
|
2512
2735
|
try {
|
|
2513
2736
|
const seen = await preview(sessionId, id, signal, ticket);
|
|
2514
2737
|
if (seen === void 0) return;
|
|
2515
|
-
if (!seen.eligible) {
|
|
2516
|
-
failure = {
|
|
2517
|
-
code: "session/retry-blocked",
|
|
2518
|
-
reason: seen.reason ?? "unknown"
|
|
2519
|
-
};
|
|
2520
|
-
return;
|
|
2521
|
-
}
|
|
2522
2738
|
if (signal.aborted || ticket !== call) return;
|
|
2523
2739
|
expectedRevision = seen.branchRevision;
|
|
2524
2740
|
expectedInputRevision = seen.inputRevision;
|
|
2525
|
-
|
|
2526
|
-
sessionId,
|
|
2527
|
-
messageId: id,
|
|
2528
|
-
expectedRevision: seen.branchRevision,
|
|
2529
|
-
expectedInputRevision: seen.inputRevision,
|
|
2530
|
-
requestId: ids()
|
|
2531
|
-
});
|
|
2532
|
-
await ledger.submit(body, () => callRetry(body, signal, ticket));
|
|
2533
|
-
} catch (error) {
|
|
2534
|
-
if (ticket === call && !signal.aborted) failure = readFailure(error);
|
|
2535
|
-
} finally {
|
|
2536
|
-
settle();
|
|
2537
|
-
}
|
|
2538
|
-
},
|
|
2539
|
-
async beginEdit(sessionId, id, text, signal) {
|
|
2540
|
-
if (busy || !controlsFor(capabilities, ready, host).retry || signal.aborted) return;
|
|
2541
|
-
if (ledger.pending() !== void 0) {
|
|
2542
|
-
if (!samePending(sessionId, id)) return;
|
|
2543
|
-
await replaySame(signal);
|
|
2544
|
-
return;
|
|
2545
|
-
}
|
|
2546
|
-
const ticket = ++call;
|
|
2547
|
-
busy = true;
|
|
2548
|
-
failure = void 0;
|
|
2549
|
-
ownerSession = sessionId;
|
|
2550
|
-
ownerMessage = id;
|
|
2551
|
-
publish();
|
|
2552
|
-
try {
|
|
2553
|
-
const seen = await preview(sessionId, id, signal, ticket);
|
|
2554
|
-
if (seen === void 0) return;
|
|
2555
|
-
if (!seen.eligible) {
|
|
2741
|
+
if (!seen.eligible && (seen.reason !== "modified" || !capabilities.retryModified)) {
|
|
2556
2742
|
failure = {
|
|
2557
2743
|
code: "session/retry-blocked",
|
|
2558
2744
|
reason: seen.reason ?? "unknown"
|
|
2559
2745
|
};
|
|
2560
2746
|
return;
|
|
2561
2747
|
}
|
|
2562
|
-
|
|
2563
|
-
expectedRevision = seen.branchRevision;
|
|
2564
|
-
expectedInputRevision = seen.inputRevision;
|
|
2748
|
+
modified = !seen.eligible;
|
|
2565
2749
|
draft = text;
|
|
2566
2750
|
editing = true;
|
|
2567
2751
|
} catch (error) {
|
|
@@ -2578,9 +2762,11 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2578
2762
|
cancelEdit() {
|
|
2579
2763
|
if (busy) return;
|
|
2580
2764
|
editing = false;
|
|
2765
|
+
modified = false;
|
|
2766
|
+
editNeedsPreview = false;
|
|
2581
2767
|
publish();
|
|
2582
2768
|
},
|
|
2583
|
-
async sendEdit(sessionId, id, signal) {
|
|
2769
|
+
async sendEdit(sessionId, id, signal, content, confirmed) {
|
|
2584
2770
|
if (signal.aborted || busy || !controlsFor(capabilities, ready, host).retry) return;
|
|
2585
2771
|
if (!editing || ownerSession !== sessionId || ownerMessage !== id) return;
|
|
2586
2772
|
if (ledger.pending() !== void 0) {
|
|
@@ -2590,23 +2776,49 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2590
2776
|
}
|
|
2591
2777
|
const ticket = ++call;
|
|
2592
2778
|
busy = true;
|
|
2779
|
+
const refresh = editNeedsPreview || isStaleRetryFailure(failure);
|
|
2593
2780
|
failure = void 0;
|
|
2594
2781
|
const text = draft;
|
|
2595
2782
|
publish();
|
|
2596
2783
|
try {
|
|
2597
2784
|
if (signal.aborted || ticket !== call) return;
|
|
2785
|
+
if (refresh) {
|
|
2786
|
+
editNeedsPreview = true;
|
|
2787
|
+
const seen = await preview(sessionId, id, signal, ticket);
|
|
2788
|
+
if (seen === void 0) return;
|
|
2789
|
+
expectedRevision = seen.branchRevision;
|
|
2790
|
+
expectedInputRevision = seen.inputRevision;
|
|
2791
|
+
if (!seen.eligible && (seen.reason !== "modified" || !capabilities.retryModified)) {
|
|
2792
|
+
failure = {
|
|
2793
|
+
code: "session/retry-blocked",
|
|
2794
|
+
reason: seen.reason ?? "unknown"
|
|
2795
|
+
};
|
|
2796
|
+
return;
|
|
2797
|
+
}
|
|
2798
|
+
modified = !seen.eligible;
|
|
2799
|
+
editNeedsPreview = false;
|
|
2800
|
+
}
|
|
2801
|
+
if (content !== void 0 && capabilities.retryContent !== true) throw new HostOperationError("session/lifecycle-unavailable");
|
|
2598
2802
|
const body = editRetryRequest({
|
|
2599
2803
|
sessionId,
|
|
2600
2804
|
messageId: id,
|
|
2601
2805
|
expectedRevision,
|
|
2602
2806
|
expectedInputRevision,
|
|
2603
2807
|
requestId: ids(),
|
|
2604
|
-
text
|
|
2808
|
+
text,
|
|
2809
|
+
...content === void 0 ? {} : { content },
|
|
2810
|
+
allowModified: confirmed === true
|
|
2605
2811
|
});
|
|
2606
2812
|
await ledger.submit(body, () => callRetry(body, signal, ticket));
|
|
2607
|
-
if (ticket === call && !signal.aborted)
|
|
2813
|
+
if (ticket === call && !signal.aborted) {
|
|
2814
|
+
editing = false;
|
|
2815
|
+
modified = false;
|
|
2816
|
+
editNeedsPreview = false;
|
|
2817
|
+
}
|
|
2608
2818
|
} catch (error) {
|
|
2609
|
-
|
|
2819
|
+
const nextFailure = readFailure(error);
|
|
2820
|
+
if (isStaleRetryFailure(nextFailure)) editNeedsPreview = true;
|
|
2821
|
+
if (ticket === call && !signal.aborted) failure = nextFailure;
|
|
2610
2822
|
} finally {
|
|
2611
2823
|
settle();
|
|
2612
2824
|
}
|
|
@@ -2615,6 +2827,8 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2615
2827
|
if (ownerSession !== sessionId || ownerMessage !== messageId) return;
|
|
2616
2828
|
call += 1;
|
|
2617
2829
|
editing = false;
|
|
2830
|
+
modified = false;
|
|
2831
|
+
editNeedsPreview = false;
|
|
2618
2832
|
failure = void 0;
|
|
2619
2833
|
draft = "";
|
|
2620
2834
|
if (!ledger.busy) busy = false;
|
|
@@ -2623,6 +2837,8 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
|
|
|
2623
2837
|
cancel() {
|
|
2624
2838
|
call += 1;
|
|
2625
2839
|
editing = false;
|
|
2840
|
+
modified = false;
|
|
2841
|
+
editNeedsPreview = false;
|
|
2626
2842
|
failure = void 0;
|
|
2627
2843
|
if (!ledger.busy) busy = false;
|
|
2628
2844
|
publish();
|
|
@@ -2814,11 +3030,11 @@ function createContinueModel(host) {
|
|
|
2814
3030
|
load(sessionId) {
|
|
2815
3031
|
if (sessionId === "" || lifetime.signal.aborted || inflight.has(sessionId)) return;
|
|
2816
3032
|
const current = read(sessionId);
|
|
2817
|
-
if (current.text !== void 0 && current.seq === void 0) return;
|
|
3033
|
+
if (current.text !== void 0 && current.seq === void 0 && current.pending !== true) return;
|
|
2818
3034
|
const ticket = new AbortController();
|
|
2819
3035
|
inflight.set(sessionId, ticket);
|
|
2820
3036
|
const signal = AbortSignal.any([lifetime.signal, ticket.signal]);
|
|
2821
|
-
callContinueSuggest(host, sessionId, current.seq, signal).then((value) => {
|
|
3037
|
+
callContinueSuggest(host, sessionId, current.text ? current.seq : void 0, signal).then((value) => {
|
|
2822
3038
|
if (signal.aborted || inflight.get(sessionId) !== ticket) return;
|
|
2823
3039
|
write(sessionId, value);
|
|
2824
3040
|
}).catch(() => {}).finally(() => {
|
|
@@ -2857,7 +3073,9 @@ async function callContinueSuggest(host, sessionId, knownSeq, signal) {
|
|
|
2857
3073
|
const seq = typeof value?.seq === "number" && Number.isSafeInteger(value.seq) && value.seq >= 0 ? value.seq : void 0;
|
|
2858
3074
|
return {
|
|
2859
3075
|
text,
|
|
2860
|
-
...seq === void 0 ? {} : { seq }
|
|
3076
|
+
...seq === void 0 ? {} : { seq },
|
|
3077
|
+
...value?.pending === true ? { pending: true } : {},
|
|
3078
|
+
...value?.fallback === true ? { fallback: true } : {}
|
|
2861
3079
|
};
|
|
2862
3080
|
}
|
|
2863
3081
|
function createPresenceConfigModel(host, delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
@@ -3314,8 +3532,10 @@ function ContinueGhost(props) {
|
|
|
3314
3532
|
const blank = props.useSession?.((state) => state.blank) ?? true;
|
|
3315
3533
|
const running = props.useSessionStatus?.((state) => state.get(sessionId)?.running === true) ?? false;
|
|
3316
3534
|
const snapshot = (0, react.useSyncExternalStore)(props.model.subscribe, () => props.model.getSnapshot(sessionId), () => props.model.getSnapshot(sessionId));
|
|
3317
|
-
const
|
|
3318
|
-
const
|
|
3535
|
+
const shown = running || snapshot.pending === true || snapshot.text === void 0 ? "" : snapshot.text ?? (snapshot.fallback === true ? uiText(props.locale, "continueDefault") : "");
|
|
3536
|
+
const idle = sessionId !== "" && !blank && bare && draft === "" && phase === "plain" && !running;
|
|
3537
|
+
const active = shown !== "" && idle;
|
|
3538
|
+
const text = shown;
|
|
3319
3539
|
const latest = (0, react.useRef)({
|
|
3320
3540
|
active,
|
|
3321
3541
|
text
|
|
@@ -3331,18 +3551,18 @@ function ContinueGhost(props) {
|
|
|
3331
3551
|
const finished = wasRunning.current && !running;
|
|
3332
3552
|
wasRunning.current = running;
|
|
3333
3553
|
if (finished && sessionId !== "") props.model.invalidate(sessionId);
|
|
3334
|
-
if (
|
|
3554
|
+
if (idle && !running) props.model.load(sessionId);
|
|
3335
3555
|
}, [
|
|
3336
3556
|
props.model,
|
|
3337
|
-
|
|
3557
|
+
idle,
|
|
3338
3558
|
running,
|
|
3339
3559
|
sessionId
|
|
3340
3560
|
]);
|
|
3341
3561
|
(0, react.useEffect)(() => {
|
|
3342
|
-
if (
|
|
3562
|
+
if (idle && !running && snapshot.text === void 0) props.model.load(sessionId);
|
|
3343
3563
|
}, [
|
|
3344
3564
|
props.model,
|
|
3345
|
-
|
|
3565
|
+
idle,
|
|
3346
3566
|
running,
|
|
3347
3567
|
sessionId,
|
|
3348
3568
|
snapshot
|
|
@@ -3638,117 +3858,135 @@ function SessionManagerSettings(props) {
|
|
|
3638
3858
|
};
|
|
3639
3859
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3640
3860
|
className: `${FREE_CHAT_ROOT} dsh-ui-page`,
|
|
3641
|
-
children: [
|
|
3642
|
-
|
|
3643
|
-
|
|
3861
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3862
|
+
className: "dsm-settings",
|
|
3863
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3864
|
+
className: "dsm-setting",
|
|
3865
|
+
"aria-labelledby": `${tabsId}-ghost-heading`,
|
|
3644
3866
|
children: [
|
|
3645
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3646
|
-
className: "
|
|
3647
|
-
children:
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
items: [{
|
|
3656
|
-
id: `${tabsId}-fixed`,
|
|
3657
|
-
panelId: fixedPanelId,
|
|
3658
|
-
value: "fixed",
|
|
3659
|
-
label: copy("modeFixed")
|
|
3660
|
-
}, {
|
|
3661
|
-
id: `${tabsId}-auto`,
|
|
3662
|
-
panelId: autoPanelId,
|
|
3663
|
-
value: "auto",
|
|
3664
|
-
label: copy("modeAuto")
|
|
3665
|
-
}],
|
|
3666
|
-
value: draft.continueMode,
|
|
3667
|
-
onChange: (value) => edit({
|
|
3668
|
-
...draft,
|
|
3669
|
-
continueMode: value
|
|
3670
|
-
})
|
|
3671
|
-
}) }),
|
|
3672
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3673
|
-
role: "tabpanel",
|
|
3674
|
-
id: fixedPanelId,
|
|
3675
|
-
"aria-labelledby": `${tabsId}-fixed`,
|
|
3676
|
-
hidden: draft.continueMode !== "fixed",
|
|
3677
|
-
children: draft.continueMode === "fixed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3678
|
-
className: "dsh-ui-field",
|
|
3679
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3680
|
-
className: "dsh-ui-label",
|
|
3681
|
-
children: copy("continueTextLabel")
|
|
3682
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
3683
|
-
value: draft.continueText,
|
|
3684
|
-
placeholder: copy("continueTextPlaceholder"),
|
|
3685
|
-
onChange: (event) => edit({
|
|
3686
|
-
...draft,
|
|
3687
|
-
continueText: event.target.value
|
|
3688
|
-
})
|
|
3689
|
-
})]
|
|
3690
|
-
}) : null
|
|
3867
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
3868
|
+
className: "dsm-setting-header",
|
|
3869
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3870
|
+
id: `${tabsId}-ghost-heading`,
|
|
3871
|
+
className: "dsh-ui-title",
|
|
3872
|
+
children: copy("settingsMode")
|
|
3873
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3874
|
+
className: "dsh-ui-hint",
|
|
3875
|
+
children: copy("settingsModeHint")
|
|
3876
|
+
})]
|
|
3691
3877
|
}),
|
|
3692
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
className: "dsh-ui-field",
|
|
3699
|
-
children: [
|
|
3700
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3878
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3879
|
+
className: "dsm-ghost-controls",
|
|
3880
|
+
children: [
|
|
3881
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3882
|
+
className: "dsh-ui-field",
|
|
3883
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3701
3884
|
className: "dsh-ui-label",
|
|
3702
|
-
children: copy("
|
|
3703
|
-
}),
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
"
|
|
3715
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3716
|
-
className: "dsh-model-menu-triggerLabel",
|
|
3717
|
-
children: ghostLabel
|
|
3718
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${ghostMenuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
3719
|
-
}),
|
|
3720
|
-
choices: selectableChoices,
|
|
3721
|
-
selected: ghostRoute,
|
|
3722
|
-
onPick: (route) => edit({
|
|
3723
|
-
...draft,
|
|
3724
|
-
ghostModel: modelRouteKey(route.provider, route.model)
|
|
3725
|
-
}),
|
|
3726
|
-
leading: [{
|
|
3727
|
-
id: "follow",
|
|
3728
|
-
label: copy("modelFollowOption"),
|
|
3729
|
-
selected: !ghostRoute
|
|
3885
|
+
children: copy("continueSourceLabel")
|
|
3886
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
3887
|
+
label: copy("continueSourceLabel"),
|
|
3888
|
+
items: [{
|
|
3889
|
+
id: `${tabsId}-fixed`,
|
|
3890
|
+
panelId: fixedPanelId,
|
|
3891
|
+
value: "fixed",
|
|
3892
|
+
label: copy("modeFixed")
|
|
3893
|
+
}, {
|
|
3894
|
+
id: `${tabsId}-auto`,
|
|
3895
|
+
panelId: autoPanelId,
|
|
3896
|
+
value: "auto",
|
|
3897
|
+
label: copy("modeAuto")
|
|
3730
3898
|
}],
|
|
3731
|
-
|
|
3899
|
+
value: draft.continueMode,
|
|
3900
|
+
onChange: (value) => edit({
|
|
3732
3901
|
...draft,
|
|
3733
|
-
|
|
3734
|
-
})
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
}
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3902
|
+
continueMode: value
|
|
3903
|
+
})
|
|
3904
|
+
})]
|
|
3905
|
+
}),
|
|
3906
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3907
|
+
role: "tabpanel",
|
|
3908
|
+
id: fixedPanelId,
|
|
3909
|
+
"aria-labelledby": `${tabsId}-fixed`,
|
|
3910
|
+
hidden: draft.continueMode !== "fixed",
|
|
3911
|
+
children: draft.continueMode === "fixed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3912
|
+
className: "dsh-ui-field",
|
|
3913
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3914
|
+
className: "dsh-ui-label",
|
|
3915
|
+
children: copy("continueTextLabel")
|
|
3916
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
3917
|
+
className: "dsm-ghost-input",
|
|
3918
|
+
value: draft.continueText,
|
|
3919
|
+
placeholder: copy("continueTextPlaceholder"),
|
|
3920
|
+
onChange: (event) => edit({
|
|
3921
|
+
...draft,
|
|
3922
|
+
continueText: event.target.value
|
|
3923
|
+
})
|
|
3924
|
+
})]
|
|
3925
|
+
}) : null
|
|
3926
|
+
}),
|
|
3927
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3928
|
+
role: "tabpanel",
|
|
3929
|
+
id: autoPanelId,
|
|
3930
|
+
"aria-labelledby": `${tabsId}-auto`,
|
|
3931
|
+
hidden: draft.continueMode !== "auto",
|
|
3932
|
+
children: draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3933
|
+
className: "dsh-ui-field dsm-model-field",
|
|
3934
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3935
|
+
className: "dsh-ui-label",
|
|
3936
|
+
children: copy("ghostModelLabel")
|
|
3937
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
3938
|
+
locale: props.locale,
|
|
3939
|
+
open: ghostMenuOpen,
|
|
3940
|
+
onOpenChange: setGhostMenuOpen,
|
|
3941
|
+
listClassName: FREE_CHAT_ROOT,
|
|
3942
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3943
|
+
type: "button",
|
|
3944
|
+
className: "dsh-model-menu-trigger",
|
|
3945
|
+
"aria-label": `${copy("ghostModelLabel")}: ${ghostLabel}`,
|
|
3946
|
+
"aria-haspopup": "menu",
|
|
3947
|
+
"aria-expanded": ghostMenuOpen,
|
|
3948
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3949
|
+
className: "dsh-model-menu-triggerLabel",
|
|
3950
|
+
children: ghostLabel
|
|
3951
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${ghostMenuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
3952
|
+
}),
|
|
3953
|
+
choices: selectableChoices,
|
|
3954
|
+
selected: ghostRoute,
|
|
3955
|
+
onPick: (route) => edit({
|
|
3956
|
+
...draft,
|
|
3957
|
+
ghostModel: modelRouteKey(route.provider, route.model)
|
|
3958
|
+
}),
|
|
3959
|
+
leading: [{
|
|
3960
|
+
id: "follow",
|
|
3961
|
+
label: copy("modelFollowOption"),
|
|
3962
|
+
selected: !ghostRoute
|
|
3963
|
+
}],
|
|
3964
|
+
onPickLeading: () => edit({
|
|
3965
|
+
...draft,
|
|
3966
|
+
ghostModel: ""
|
|
3967
|
+
}),
|
|
3968
|
+
modelValue: ghostLabel
|
|
3969
|
+
})]
|
|
3970
|
+
}) : null
|
|
3971
|
+
})
|
|
3972
|
+
]
|
|
3973
|
+
}),
|
|
3974
|
+
draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3975
|
+
className: "dsh-ui-hint",
|
|
3976
|
+
children: copy("ghostModelHint")
|
|
3977
|
+
}) : null
|
|
3744
3978
|
]
|
|
3745
|
-
}),
|
|
3746
|
-
|
|
3747
|
-
|
|
3979
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3980
|
+
className: "dsm-setting",
|
|
3981
|
+
"aria-labelledby": `${tabsId}-return-heading`,
|
|
3748
3982
|
children: [
|
|
3749
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
3750
|
-
className: "
|
|
3751
|
-
children:
|
|
3983
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
3984
|
+
className: "dsm-setting-header",
|
|
3985
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3986
|
+
id: `${tabsId}-return-heading`,
|
|
3987
|
+
className: "dsh-ui-title",
|
|
3988
|
+
children: copy("welcomeBackGroup")
|
|
3989
|
+
})
|
|
3752
3990
|
}),
|
|
3753
3991
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3754
3992
|
className: "dsm-feature",
|
|
@@ -3772,84 +4010,82 @@ function SessionManagerSettings(props) {
|
|
|
3772
4010
|
})
|
|
3773
4011
|
})]
|
|
3774
4012
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3775
|
-
className: "dsm-
|
|
4013
|
+
className: "dsm-feature-options",
|
|
3776
4014
|
"data-disabled": draft.recapEnabled ? void 0 : "true",
|
|
3777
|
-
children: [
|
|
3778
|
-
|
|
4015
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4016
|
+
className: "dsh-ui-fields-grid",
|
|
4017
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3779
4018
|
className: "dsh-ui-field",
|
|
3780
4019
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3781
4020
|
className: "dsh-ui-label",
|
|
3782
4021
|
children: copy("recapMinutesLabel")
|
|
3783
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3784
|
-
className: "dsm-minutes",
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
4022
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4023
|
+
className: "dsm-minutes-control",
|
|
4024
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
4025
|
+
className: "dsm-minutes",
|
|
4026
|
+
value: draft.recapMinutes,
|
|
4027
|
+
inputMode: "numeric",
|
|
4028
|
+
disabled: !draft.recapEnabled,
|
|
4029
|
+
"aria-invalid": recapInvalid || void 0,
|
|
4030
|
+
"aria-describedby": recapInvalid ? `${tabsId}-recap-error` : void 0,
|
|
4031
|
+
onChange: (event) => edit({
|
|
4032
|
+
...draft,
|
|
4033
|
+
recapMinutes: event.target.value
|
|
4034
|
+
})
|
|
4035
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4036
|
+
className: "dsh-ui-meta",
|
|
4037
|
+
children: copy("minutesUnit")
|
|
4038
|
+
})]
|
|
3794
4039
|
})]
|
|
3795
4040
|
}), recapInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3796
4041
|
id: `${tabsId}-recap-error`,
|
|
3797
4042
|
className: "dsh-ui-error",
|
|
3798
4043
|
role: "alert",
|
|
3799
4044
|
children: copy("minutesInvalid")
|
|
3800
|
-
}) : null] }),
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
4045
|
+
}) : null] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4046
|
+
className: "dsh-ui-field dsm-model-field",
|
|
4047
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4048
|
+
className: "dsh-ui-label",
|
|
4049
|
+
children: copy("modelLabel")
|
|
4050
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
4051
|
+
locale: props.locale,
|
|
4052
|
+
open: menuOpen,
|
|
4053
|
+
onOpenChange: setMenuOpen,
|
|
4054
|
+
listClassName: FREE_CHAT_ROOT,
|
|
4055
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4056
|
+
type: "button",
|
|
4057
|
+
className: "dsh-model-menu-trigger",
|
|
4058
|
+
disabled: !draft.recapEnabled,
|
|
4059
|
+
"aria-label": `${copy("modelLabel")}: ${selectedLabel}`,
|
|
4060
|
+
"aria-haspopup": "menu",
|
|
4061
|
+
"aria-expanded": menuOpen,
|
|
4062
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4063
|
+
className: "dsh-model-menu-triggerLabel",
|
|
4064
|
+
children: selectedLabel
|
|
4065
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
3807
4066
|
}),
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
}),
|
|
3831
|
-
leading: [{
|
|
3832
|
-
id: "follow",
|
|
3833
|
-
label: copy("modelFollowOption"),
|
|
3834
|
-
selected: !selectedRoute
|
|
3835
|
-
}],
|
|
3836
|
-
onPickLeading: () => edit({
|
|
3837
|
-
...draft,
|
|
3838
|
-
model: ""
|
|
3839
|
-
}),
|
|
3840
|
-
modelValue: selectedLabel
|
|
3841
|
-
}) }),
|
|
3842
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3843
|
-
className: "dsh-ui-hint",
|
|
3844
|
-
children: copy("modelGroupHint")
|
|
3845
|
-
})
|
|
3846
|
-
]
|
|
3847
|
-
}),
|
|
3848
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
3849
|
-
state: catalog,
|
|
3850
|
-
locale: props.locale
|
|
3851
|
-
})
|
|
3852
|
-
]
|
|
4067
|
+
choices: selectableChoices,
|
|
4068
|
+
selected: selectedRoute,
|
|
4069
|
+
onPick: (route) => edit({
|
|
4070
|
+
...draft,
|
|
4071
|
+
model: modelRouteKey(route.provider, route.model)
|
|
4072
|
+
}),
|
|
4073
|
+
leading: [{
|
|
4074
|
+
id: "follow",
|
|
4075
|
+
label: copy("modelFollowOption"),
|
|
4076
|
+
selected: !selectedRoute
|
|
4077
|
+
}],
|
|
4078
|
+
onPickLeading: () => edit({
|
|
4079
|
+
...draft,
|
|
4080
|
+
model: ""
|
|
4081
|
+
}),
|
|
4082
|
+
modelValue: selectedLabel
|
|
4083
|
+
})]
|
|
4084
|
+
})]
|
|
4085
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
4086
|
+
state: catalog,
|
|
4087
|
+
locale: props.locale
|
|
4088
|
+
})]
|
|
3853
4089
|
})]
|
|
3854
4090
|
}),
|
|
3855
4091
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3874,24 +4110,30 @@ function SessionManagerSettings(props) {
|
|
|
3874
4110
|
})
|
|
3875
4111
|
})]
|
|
3876
4112
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3877
|
-
className: "dsm-
|
|
4113
|
+
className: "dsm-feature-options dsm-compact-options",
|
|
3878
4114
|
"data-disabled": draft.compactEnabled ? void 0 : "true",
|
|
3879
4115
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3880
4116
|
className: "dsh-ui-field",
|
|
3881
4117
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3882
4118
|
className: "dsh-ui-label",
|
|
3883
4119
|
children: copy("compactMinutesLabel")
|
|
3884
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3885
|
-
className: "dsm-minutes",
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
4120
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4121
|
+
className: "dsm-minutes-control",
|
|
4122
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
4123
|
+
className: "dsm-minutes",
|
|
4124
|
+
value: draft.compactMinutes,
|
|
4125
|
+
inputMode: "numeric",
|
|
4126
|
+
disabled: !draft.compactEnabled,
|
|
4127
|
+
"aria-invalid": compactInvalid || void 0,
|
|
4128
|
+
"aria-describedby": compactInvalid ? `${tabsId}-compact-error` : void 0,
|
|
4129
|
+
onChange: (event) => edit({
|
|
4130
|
+
...draft,
|
|
4131
|
+
compactMinutes: event.target.value
|
|
4132
|
+
})
|
|
4133
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4134
|
+
className: "dsh-ui-meta",
|
|
4135
|
+
children: copy("minutesUnit")
|
|
4136
|
+
})]
|
|
3895
4137
|
})]
|
|
3896
4138
|
}), compactInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3897
4139
|
id: `${tabsId}-compact-error`,
|
|
@@ -3902,39 +4144,215 @@ function SessionManagerSettings(props) {
|
|
|
3902
4144
|
})]
|
|
3903
4145
|
})
|
|
3904
4146
|
]
|
|
3905
|
-
})
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
4147
|
+
})]
|
|
4148
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4149
|
+
className: "dsh-ui-form-footer dsm-settings-footer",
|
|
4150
|
+
children: [
|
|
4151
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4152
|
+
variant: "primary",
|
|
4153
|
+
disabled: invalid || snapshot.saving,
|
|
4154
|
+
onClick: save,
|
|
4155
|
+
children: snapshot.saving ? copy("settingsSaving") : copy("saveSettings")
|
|
4156
|
+
}),
|
|
4157
|
+
snapshot.saved ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4158
|
+
className: "dsh-ui-notice",
|
|
4159
|
+
role: "status",
|
|
4160
|
+
children: copy("settingsSaved")
|
|
4161
|
+
}) : null,
|
|
4162
|
+
externalChange ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4163
|
+
className: "dsh-ui-notice dsh-ui-notice--error",
|
|
4164
|
+
role: "alert",
|
|
4165
|
+
children: copy("externalChange")
|
|
4166
|
+
}) : null,
|
|
4167
|
+
snapshot.failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4168
|
+
className: "dsh-ui-notice dsh-ui-notice--error",
|
|
4169
|
+
role: "alert",
|
|
4170
|
+
children: failureText(props.locale, snapshot.failure)
|
|
4171
|
+
}) : null
|
|
4172
|
+
]
|
|
4173
|
+
})]
|
|
4174
|
+
});
|
|
4175
|
+
}
|
|
4176
|
+
//#endregion
|
|
4177
|
+
//#region src/message-editor.tsx
|
|
4178
|
+
function AttachmentPreview(props) {
|
|
4179
|
+
const [src, setSrc] = (0, react.useState)();
|
|
4180
|
+
const [failed, setFailed] = (0, react.useState)(false);
|
|
4181
|
+
const item = props.item;
|
|
4182
|
+
const name = item.type === "upload" ? item.file.name : item.type === "file" ? item.name : uiText(props.locale, "editImage");
|
|
4183
|
+
(0, react.useEffect)(() => {
|
|
4184
|
+
let active = true;
|
|
4185
|
+
let url;
|
|
4186
|
+
setSrc(void 0);
|
|
4187
|
+
setFailed(false);
|
|
4188
|
+
if (item.type === "upload" && item.file.type.startsWith("image/")) {
|
|
4189
|
+
url = URL.createObjectURL(item.file);
|
|
4190
|
+
setSrc(url);
|
|
4191
|
+
} else if (item.type === "image" && props.host.attachment) unwrap(props.host.attachment({
|
|
4192
|
+
sessionId: props.sessionId,
|
|
4193
|
+
attachmentId: item.attachmentId
|
|
4194
|
+
})).then((value) => {
|
|
4195
|
+
const image = value;
|
|
4196
|
+
if (active && image.data && image.attachment?.mediaType?.startsWith("image/")) setSrc(`data:${image.attachment.mediaType};base64,${image.data}`);
|
|
4197
|
+
else if (active) setFailed(true);
|
|
4198
|
+
}).catch(() => {
|
|
4199
|
+
if (active) setFailed(true);
|
|
4200
|
+
});
|
|
4201
|
+
return () => {
|
|
4202
|
+
active = false;
|
|
4203
|
+
if (url) URL.revokeObjectURL(url);
|
|
4204
|
+
};
|
|
4205
|
+
}, [
|
|
4206
|
+
item,
|
|
4207
|
+
props.host,
|
|
4208
|
+
props.sessionId
|
|
4209
|
+
]);
|
|
4210
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4211
|
+
src ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
4212
|
+
src,
|
|
4213
|
+
alt: name
|
|
4214
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, { path: name }),
|
|
4215
|
+
item.type === "file" || item.type === "upload" && !item.file.type.startsWith("image/") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4216
|
+
title: name,
|
|
4217
|
+
children: name
|
|
4218
|
+
}) : null,
|
|
4219
|
+
failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4220
|
+
role: "status",
|
|
4221
|
+
children: uiText(props.locale, "editPreviewFailed")
|
|
4222
|
+
}) : null
|
|
4223
|
+
] });
|
|
4224
|
+
}
|
|
4225
|
+
/** Lives in the original bubble; drafts and attachments remain local until Send. */
|
|
4226
|
+
function MessageEditor(props) {
|
|
4227
|
+
const textarea = (0, react.useRef)(null);
|
|
4228
|
+
const copy = (key) => uiText(props.locale, key);
|
|
4229
|
+
const attachments = props.attachments ?? [];
|
|
4230
|
+
const disabled = props.busy || props.locked === true;
|
|
4231
|
+
(0, react.useEffect)(() => {
|
|
4232
|
+
textarea.current?.focus();
|
|
4233
|
+
textarea.current?.setSelectionRange(props.text.length, props.text.length);
|
|
4234
|
+
}, []);
|
|
4235
|
+
(0, react.useEffect)(() => {
|
|
4236
|
+
const element = textarea.current;
|
|
4237
|
+
if (!element) return;
|
|
4238
|
+
element.style.height = "auto";
|
|
4239
|
+
element.style.height = `${Math.max(80, Math.min(element.scrollHeight, 360))}px`;
|
|
4240
|
+
}, [props.text]);
|
|
4241
|
+
const add = (files) => {
|
|
4242
|
+
if (!disabled && files.length > 0) props.setAttachments?.([...attachments, ...files.map((file) => ({
|
|
4243
|
+
type: "upload",
|
|
4244
|
+
file
|
|
4245
|
+
}))]);
|
|
4246
|
+
};
|
|
4247
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4248
|
+
className: "dsm-message-edit",
|
|
4249
|
+
role: "group",
|
|
4250
|
+
"aria-label": copy("editTitle"),
|
|
4251
|
+
onDragOver: (event) => {
|
|
4252
|
+
if (!disabled && props.setAttachments && event.dataTransfer.types.includes("Files")) event.preventDefault();
|
|
4253
|
+
},
|
|
4254
|
+
onDrop: (event) => {
|
|
4255
|
+
if (props.setAttachments && event.dataTransfer.files.length) {
|
|
4256
|
+
event.preventDefault();
|
|
4257
|
+
add(Array.from(event.dataTransfer.files));
|
|
4258
|
+
}
|
|
4259
|
+
},
|
|
4260
|
+
children: [attachments.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4261
|
+
className: "dsm-edit-attachments",
|
|
4262
|
+
children: attachments.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4263
|
+
className: `dsm-edit-attachment ${item.type === "image" || item.type === "upload" && item.file.type.startsWith("image/") ? "dsm-edit-image" : ""}`,
|
|
4264
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AttachmentPreview, {
|
|
4265
|
+
item,
|
|
4266
|
+
sessionId: props.sessionId,
|
|
4267
|
+
host: props.host ?? {},
|
|
4268
|
+
locale: props.locale
|
|
4269
|
+
}), props.setAttachments ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4270
|
+
size: "sm",
|
|
4271
|
+
variant: "ghost",
|
|
4272
|
+
disabled,
|
|
4273
|
+
"aria-label": `${copy("editRemoveAttachment")} ${index + 1}`,
|
|
4274
|
+
onClick: () => {
|
|
4275
|
+
props.setAttachments?.(attachments.filter((_, position) => position !== index));
|
|
4276
|
+
},
|
|
4277
|
+
children: "×"
|
|
4278
|
+
}) : null]
|
|
4279
|
+
}, item.type === "upload" ? `${index}:${item.file.name}` : `${index}:${item.attachmentId}`))
|
|
4280
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4281
|
+
className: "dsm-message-editor",
|
|
4282
|
+
children: [
|
|
4283
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
4284
|
+
ref: textarea,
|
|
4285
|
+
className: "dsh-ui-textarea dsh-free-draft",
|
|
4286
|
+
"aria-label": copy("draft"),
|
|
4287
|
+
value: props.text,
|
|
4288
|
+
disabled,
|
|
4289
|
+
onChange: (event) => {
|
|
4290
|
+
props.setText(event.target.value);
|
|
4291
|
+
},
|
|
4292
|
+
onPaste: (event) => {
|
|
4293
|
+
const files = Array.from(event.clipboardData.files);
|
|
4294
|
+
if (files.length && props.setAttachments) {
|
|
4295
|
+
event.preventDefault();
|
|
4296
|
+
add(files);
|
|
4297
|
+
}
|
|
4298
|
+
},
|
|
4299
|
+
onKeyDown: (event) => {
|
|
4300
|
+
if (event.key === "Escape" && !props.busy) {
|
|
4301
|
+
event.stopPropagation();
|
|
4302
|
+
props.onCancel();
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
}),
|
|
4306
|
+
props.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4307
|
+
className: "dsh-ui-error",
|
|
4308
|
+
role: "alert",
|
|
4309
|
+
children: props.error
|
|
4310
|
+
}) : null,
|
|
4311
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4312
|
+
className: "dsm-edit-footer",
|
|
4313
|
+
children: [
|
|
4314
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dsm-edit-spacer" }),
|
|
4315
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4316
|
+
variant: "ghost",
|
|
4317
|
+
disabled: props.busy,
|
|
4318
|
+
onClick: props.onCancel,
|
|
4319
|
+
children: copy("cancel")
|
|
4320
|
+
}),
|
|
4321
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4322
|
+
variant: "primary",
|
|
4323
|
+
disabled: props.busy || !props.text.trim() && !attachments.length,
|
|
4324
|
+
onClick: props.onSend,
|
|
4325
|
+
children: copy("send")
|
|
4326
|
+
})
|
|
4327
|
+
]
|
|
4328
|
+
})
|
|
4329
|
+
]
|
|
4330
|
+
})]
|
|
4331
|
+
});
|
|
4332
|
+
}
|
|
4333
|
+
//#endregion
|
|
4334
|
+
//#region src/edit-icon.tsx
|
|
4335
|
+
/** The stock edit primitive includes a box; message editing uses a standalone pencil. */
|
|
4336
|
+
function EditMessageIcon() {
|
|
4337
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
4338
|
+
width: "16",
|
|
4339
|
+
height: "16",
|
|
4340
|
+
viewBox: "0 0 16 16",
|
|
4341
|
+
fill: "none",
|
|
4342
|
+
"aria-hidden": "true",
|
|
4343
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
4344
|
+
d: "m10.5 2.5 3 3M2 14l1-4L10.5 2.5a2.12 2.12 0 0 1 3 3L6 13l-4 1Z",
|
|
4345
|
+
stroke: "currentColor",
|
|
4346
|
+
strokeWidth: "1",
|
|
4347
|
+
strokeLinecap: "round",
|
|
4348
|
+
strokeLinejoin: "round"
|
|
4349
|
+
})
|
|
3933
4350
|
});
|
|
3934
4351
|
}
|
|
3935
4352
|
//#endregion
|
|
3936
4353
|
//#region src/user-actions.tsx
|
|
3937
4354
|
function UserMessageActions(props) {
|
|
4355
|
+
const [confirming, setConfirming] = (0, react.useState)(false);
|
|
3938
4356
|
const snapshot = (0, react.useSyncExternalStore)(props.model.subscribe, props.model.getSnapshot, props.model.getSnapshot);
|
|
3939
4357
|
const signal = (0, react.useRef)(new AbortController());
|
|
3940
4358
|
(0, react.useEffect)(() => {
|
|
@@ -3958,75 +4376,624 @@ function UserMessageActions(props) {
|
|
|
3958
4376
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3959
4377
|
className: `${FREE_CHAT_ROOT} dsh-free-actions`,
|
|
3960
4378
|
children: [
|
|
3961
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
3962
|
-
label: copy("retry"),
|
|
3963
|
-
side: "bottom",
|
|
3964
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3965
|
-
size: "sm",
|
|
3966
|
-
variant: "ghost",
|
|
3967
|
-
"aria-label": copy("retry"),
|
|
3968
|
-
disabled: snapshot.busy,
|
|
3969
|
-
onClick: () => {
|
|
3970
|
-
props.model.retry(props.sessionId, props.messageId, signal.current.signal);
|
|
3971
|
-
},
|
|
3972
|
-
children: copy("retry")
|
|
3973
|
-
})
|
|
3974
|
-
}),
|
|
3975
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
4379
|
+
!props.hideEdit ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
3976
4380
|
label: copy("edit"),
|
|
3977
4381
|
side: "bottom",
|
|
3978
4382
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3979
4383
|
size: "sm",
|
|
4384
|
+
className: "dsm-icon-action",
|
|
3980
4385
|
variant: "ghost",
|
|
3981
4386
|
"aria-label": copy("edit"),
|
|
3982
|
-
disabled: snapshot.busy,
|
|
4387
|
+
disabled: snapshot.busy || snapshot.editing && !mine,
|
|
3983
4388
|
onClick: () => {
|
|
3984
4389
|
props.model.beginEdit(props.sessionId, props.messageId, props.text, signal.current.signal);
|
|
3985
4390
|
},
|
|
3986
|
-
children:
|
|
4391
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditMessageIcon, {})
|
|
3987
4392
|
})
|
|
3988
|
-
}),
|
|
3989
|
-
failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4393
|
+
}) : null,
|
|
4394
|
+
failure && !editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3990
4395
|
className: "dsh-ui-error dsh-free-alert",
|
|
3991
4396
|
role: "alert",
|
|
3992
4397
|
children: failureText(props.locale, failure)
|
|
3993
4398
|
}) : null,
|
|
4399
|
+
editing && !props.hideEdit ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageEditor, {
|
|
4400
|
+
text: draft,
|
|
4401
|
+
setText: (value) => {
|
|
4402
|
+
props.model.setDraft(value);
|
|
4403
|
+
},
|
|
4404
|
+
sessionId: props.sessionId,
|
|
4405
|
+
locale: props.locale,
|
|
4406
|
+
busy: snapshot.busy,
|
|
4407
|
+
error: failure ? failureText(props.locale, failure) : void 0,
|
|
4408
|
+
onCancel: () => {
|
|
4409
|
+
props.model.cancelEdit();
|
|
4410
|
+
},
|
|
4411
|
+
onSend: () => {
|
|
4412
|
+
setConfirming(true);
|
|
4413
|
+
}
|
|
4414
|
+
}) : null,
|
|
3994
4415
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
3995
|
-
open: editing,
|
|
4416
|
+
open: confirming && editing,
|
|
3996
4417
|
className: FREE_CHAT_ROOT,
|
|
3997
4418
|
title: copy("editTitle"),
|
|
3998
4419
|
closeLabel: copy("close"),
|
|
3999
4420
|
onClose: () => {
|
|
4000
|
-
|
|
4421
|
+
setConfirming(false);
|
|
4001
4422
|
},
|
|
4002
4423
|
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, {
|
|
4003
4424
|
variant: "ghost",
|
|
4004
|
-
|
|
4425
|
+
"data-modal-autofocus": true,
|
|
4005
4426
|
onClick: () => {
|
|
4006
|
-
|
|
4427
|
+
setConfirming(false);
|
|
4007
4428
|
},
|
|
4008
4429
|
children: copy("cancel")
|
|
4009
4430
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4010
4431
|
variant: "primary",
|
|
4011
|
-
disabled: snapshot.busy,
|
|
4012
4432
|
onClick: () => {
|
|
4013
|
-
|
|
4433
|
+
setConfirming(false);
|
|
4434
|
+
props.model.sendEdit(props.sessionId, props.messageId, signal.current.signal, void 0, true);
|
|
4014
4435
|
},
|
|
4015
|
-
children: copy("
|
|
4436
|
+
children: copy("editConfirm")
|
|
4016
4437
|
})] }),
|
|
4017
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4438
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy("editBranchWarning") })
|
|
4439
|
+
})
|
|
4440
|
+
]
|
|
4441
|
+
});
|
|
4442
|
+
}
|
|
4443
|
+
//#endregion
|
|
4444
|
+
//#region src/edit-host.ts
|
|
4445
|
+
function bindEditHost(client) {
|
|
4446
|
+
const rpc = client.connection?.rpc;
|
|
4447
|
+
const remote = client.remote?.session;
|
|
4448
|
+
return {
|
|
4449
|
+
...typeof rpc?.call === "function" ? {
|
|
4450
|
+
prepare: (request, signal) => rpc.call(TEMP_WORKSPACE_CHANNEL, EDIT_PREPARE_ENDPOINT, request, signal),
|
|
4451
|
+
stageFile: (request, signal) => rpc.call(TEMP_WORKSPACE_CHANNEL, EDIT_FILE_ENDPOINT, request, signal)
|
|
4452
|
+
} : {},
|
|
4453
|
+
uploadFile: async (sessionId, file, signal) => {
|
|
4454
|
+
const query = new URLSearchParams({
|
|
4455
|
+
sessionId,
|
|
4456
|
+
name: file.name
|
|
4457
|
+
});
|
|
4458
|
+
const response = await fetch(`/api/session/uploadFileBinary?${query}`, {
|
|
4459
|
+
method: "POST",
|
|
4460
|
+
credentials: "same-origin",
|
|
4461
|
+
headers: { "content-type": "application/octet-stream" },
|
|
4462
|
+
body: file,
|
|
4463
|
+
signal
|
|
4464
|
+
});
|
|
4465
|
+
if (!response.ok) throw new HostOperationError("session/attachment-invalid");
|
|
4466
|
+
return response.json();
|
|
4467
|
+
},
|
|
4468
|
+
...typeof remote?.attachment === "function" ? { attachment: (request) => remote.attachment(request) } : {}
|
|
4469
|
+
};
|
|
4470
|
+
}
|
|
4471
|
+
function readPreview(value) {
|
|
4472
|
+
if (typeof value !== "object" || value === null) throw new HostOperationError("session-manager/unavailable");
|
|
4473
|
+
const record = value;
|
|
4474
|
+
if (typeof record.sessionId !== "string" || typeof record.promptSeq !== "number" || typeof record.text !== "string" || typeof record.modified !== "boolean") throw new HostOperationError("session-manager/unavailable");
|
|
4475
|
+
const content = record.content;
|
|
4476
|
+
if (content !== void 0 && (!Array.isArray(content) || !content.every(isEditContent))) throw new HostOperationError("session-manager/unavailable");
|
|
4477
|
+
return {
|
|
4478
|
+
sessionId: record.sessionId,
|
|
4479
|
+
promptSeq: record.promptSeq,
|
|
4480
|
+
text: record.text,
|
|
4481
|
+
...content === void 0 ? {} : { content },
|
|
4482
|
+
modified: record.modified
|
|
4483
|
+
};
|
|
4484
|
+
}
|
|
4485
|
+
function isEditContent(value) {
|
|
4486
|
+
if (typeof value !== "object" || value === null) return false;
|
|
4487
|
+
const part = value;
|
|
4488
|
+
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 !== "";
|
|
4489
|
+
}
|
|
4490
|
+
/** Build the exact body for the edited prompt; images are read from the source Session. */
|
|
4491
|
+
async function buildContent(host, preview, text, signal, attachments) {
|
|
4492
|
+
const content = [];
|
|
4493
|
+
let wroteText = false;
|
|
4494
|
+
const parts = attachments === void 0 ? preview.content ?? [{
|
|
4495
|
+
type: "text",
|
|
4496
|
+
text: preview.text
|
|
4497
|
+
}] : [{
|
|
4498
|
+
type: "text",
|
|
4499
|
+
text
|
|
4500
|
+
}, ...attachments];
|
|
4501
|
+
for (const part of parts) {
|
|
4502
|
+
signal.throwIfAborted();
|
|
4503
|
+
if (part.type === "text") {
|
|
4504
|
+
if (text === preview.text) content.push(part);
|
|
4505
|
+
else if (!wroteText) content.push({
|
|
4506
|
+
type: "text",
|
|
4507
|
+
text
|
|
4508
|
+
});
|
|
4509
|
+
wroteText = true;
|
|
4510
|
+
continue;
|
|
4511
|
+
}
|
|
4512
|
+
if (part.type === "file") {
|
|
4513
|
+
content.push(part);
|
|
4514
|
+
continue;
|
|
4515
|
+
}
|
|
4516
|
+
if (part.type === "upload") {
|
|
4517
|
+
if ([
|
|
4518
|
+
"image/png",
|
|
4519
|
+
"image/jpeg",
|
|
4520
|
+
"image/webp",
|
|
4521
|
+
"image/gif"
|
|
4522
|
+
].includes(part.file.type)) {
|
|
4523
|
+
const data = await new Promise((resolve, reject) => {
|
|
4524
|
+
const reader = new FileReader();
|
|
4525
|
+
reader.onload = () => {
|
|
4526
|
+
resolve(String(reader.result).split(",")[1]);
|
|
4527
|
+
};
|
|
4528
|
+
reader.onerror = () => {
|
|
4529
|
+
reject(reader.error);
|
|
4530
|
+
};
|
|
4531
|
+
reader.readAsDataURL(part.file);
|
|
4532
|
+
});
|
|
4533
|
+
signal.throwIfAborted();
|
|
4534
|
+
content.push({
|
|
4535
|
+
type: "image",
|
|
4536
|
+
data,
|
|
4537
|
+
mediaType: part.file.type,
|
|
4538
|
+
name: part.file.name
|
|
4539
|
+
});
|
|
4540
|
+
} else content.push(part);
|
|
4541
|
+
continue;
|
|
4542
|
+
}
|
|
4543
|
+
if (!host.attachment) throw new HostOperationError("session/lifecycle-unavailable");
|
|
4544
|
+
const image = await unwrap(host.attachment({
|
|
4545
|
+
sessionId: preview.sessionId,
|
|
4546
|
+
attachmentId: part.attachmentId
|
|
4547
|
+
}));
|
|
4548
|
+
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");
|
|
4549
|
+
content.push({
|
|
4550
|
+
type: "image",
|
|
4551
|
+
data: image.data,
|
|
4552
|
+
mediaType: image.attachment.mediaType,
|
|
4553
|
+
...typeof image.attachment.name === "string" ? { name: image.attachment.name } : {}
|
|
4554
|
+
});
|
|
4555
|
+
}
|
|
4556
|
+
if (!wroteText && text !== "") content.push({
|
|
4557
|
+
type: "text",
|
|
4558
|
+
text
|
|
4559
|
+
});
|
|
4560
|
+
signal.throwIfAborted();
|
|
4561
|
+
return content;
|
|
4562
|
+
}
|
|
4563
|
+
/** Prepare the exact edit body for the existing Session; the host sends it on submit. */
|
|
4564
|
+
async function prepareEditContent(host, preview, text, submission, signal, attachments) {
|
|
4565
|
+
submission.prepared ??= await buildContent(host, preview, text, signal, attachments);
|
|
4566
|
+
await stageContent(host, preview, submission, preview.sessionId, signal);
|
|
4567
|
+
return submission.content;
|
|
4568
|
+
}
|
|
4569
|
+
async function stageContent(host, preview, submission, sessionId, signal) {
|
|
4570
|
+
const prepared = submission.prepared;
|
|
4571
|
+
if (submission.content !== void 0) return;
|
|
4572
|
+
const content = [];
|
|
4573
|
+
const receipts = submission.receipts ??= /* @__PURE__ */ new Map();
|
|
4574
|
+
for (const [index, part] of prepared.entries()) {
|
|
4575
|
+
signal.throwIfAborted();
|
|
4576
|
+
if (part.type !== "upload" && !(part.type === "file" && "attachmentId" in part)) {
|
|
4577
|
+
content.push(part);
|
|
4578
|
+
continue;
|
|
4579
|
+
}
|
|
4580
|
+
let receiptId = receipts.get(index);
|
|
4581
|
+
if (receiptId === void 0) {
|
|
4582
|
+
const result = part.type === "upload" ? host.uploadFile?.(sessionId, part.file, signal) : host.stageFile?.({
|
|
4583
|
+
sessionId: preview.sessionId,
|
|
4584
|
+
promptSeq: preview.promptSeq,
|
|
4585
|
+
attachmentId: part.attachmentId
|
|
4586
|
+
}, signal);
|
|
4587
|
+
if (!result) throw new HostOperationError("session/lifecycle-unavailable");
|
|
4588
|
+
const value = await unwrap(result);
|
|
4589
|
+
if (typeof value?.receiptId !== "string" || value.receiptId === "") throw new HostOperationError("session/attachment-invalid");
|
|
4590
|
+
receiptId = value.receiptId;
|
|
4591
|
+
receipts.set(index, receiptId);
|
|
4592
|
+
}
|
|
4593
|
+
content.push({
|
|
4594
|
+
type: "file",
|
|
4595
|
+
receiptId
|
|
4596
|
+
});
|
|
4597
|
+
}
|
|
4598
|
+
submission.content = content;
|
|
4599
|
+
}
|
|
4600
|
+
/** Read the exact human prompt that the editor edits. */
|
|
4601
|
+
async function previewEdit(host, sessionId, promptSeq, signal) {
|
|
4602
|
+
if (typeof host.prepare !== "function") throw new HostOperationError("session/lifecycle-unavailable");
|
|
4603
|
+
return readPreview(await unwrap(host.prepare({
|
|
4604
|
+
sessionId,
|
|
4605
|
+
promptSeq
|
|
4606
|
+
}, signal)));
|
|
4607
|
+
}
|
|
4608
|
+
//#endregion
|
|
4609
|
+
//#region src/edit-actions.tsx
|
|
4610
|
+
/** Edit one human prompt in its own Session; the host switches branches when the edit is sent. */
|
|
4611
|
+
function EditActions(props) {
|
|
4612
|
+
const sessionId = props.sessionId ?? "";
|
|
4613
|
+
const native = (0, react.useSyncExternalStore)(props.nativeRetry.subscribe, props.nativeRetry.getSnapshot, props.nativeRetry.getSnapshot);
|
|
4614
|
+
const [confirming, setConfirming] = (0, react.useState)(false);
|
|
4615
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
4616
|
+
const [editing, setEditing] = (0, react.useState)(false);
|
|
4617
|
+
const [draft, setDraft] = (0, react.useState)("");
|
|
4618
|
+
const [attachments, setAttachments] = (0, react.useState)([]);
|
|
4619
|
+
const [mediaChanged, setMediaChanged] = (0, react.useState)(false);
|
|
4620
|
+
const [locked, setLocked] = (0, react.useState)(false);
|
|
4621
|
+
const [failure, setFailure] = (0, react.useState)();
|
|
4622
|
+
const preview = (0, react.useRef)();
|
|
4623
|
+
const submission = (0, react.useRef)();
|
|
4624
|
+
const signal = (0, react.useRef)(new AbortController());
|
|
4625
|
+
(0, react.useEffect)(() => {
|
|
4626
|
+
const controller = new AbortController();
|
|
4627
|
+
signal.current = controller;
|
|
4628
|
+
return () => {
|
|
4629
|
+
controller.abort();
|
|
4630
|
+
};
|
|
4631
|
+
}, [sessionId, props.promptSeq]);
|
|
4632
|
+
const allowed = props.useSessionAccess?.(sessionId, "session/prompt") ?? true;
|
|
4633
|
+
const copy = (key) => uiText(props.locale, key);
|
|
4634
|
+
const mine = native.sessionId === sessionId && native.messageId === props.messageId;
|
|
4635
|
+
(0, react.useEffect)(() => {
|
|
4636
|
+
if (editing && !busy && native.controlsRetry && !mine) setEditing(false);
|
|
4637
|
+
}, [
|
|
4638
|
+
editing,
|
|
4639
|
+
busy,
|
|
4640
|
+
native.controlsRetry,
|
|
4641
|
+
mine
|
|
4642
|
+
]);
|
|
4643
|
+
if (!allowed || !sessionId || props.promptSeq === void 0 || !props.host.prepare) return props.render?.(null, null) ?? null;
|
|
4644
|
+
const run = async (action) => {
|
|
4645
|
+
if (busy || signal.current.signal.aborted) return;
|
|
4646
|
+
setBusy(true);
|
|
4647
|
+
setFailure(void 0);
|
|
4648
|
+
try {
|
|
4649
|
+
if (!native.controlsRetry || !props.messageId) throw new HostOperationError("session/lifecycle-unavailable");
|
|
4650
|
+
if (action === "edit") {
|
|
4651
|
+
const value = await previewEdit(props.host, sessionId, props.promptSeq, signal.current.signal);
|
|
4652
|
+
await props.nativeRetry.beginEdit(sessionId, props.messageId, value.text, signal.current.signal);
|
|
4653
|
+
const state = props.nativeRetry.getSnapshot();
|
|
4654
|
+
if (!state.editing) {
|
|
4655
|
+
setFailure(state.failure);
|
|
4656
|
+
return;
|
|
4657
|
+
}
|
|
4658
|
+
preview.current = value;
|
|
4659
|
+
submission.current = { requestId: (props.ids ?? (() => crypto.randomUUID()))() };
|
|
4660
|
+
setDraft(value.text);
|
|
4661
|
+
setAttachments((value.content ?? []).filter((part) => part.type !== "text"));
|
|
4662
|
+
setMediaChanged(false);
|
|
4663
|
+
setLocked(false);
|
|
4664
|
+
setEditing(true);
|
|
4665
|
+
return;
|
|
4666
|
+
}
|
|
4667
|
+
if (!preview.current || !submission.current) return;
|
|
4668
|
+
if (mediaChanged && native.controlsContent !== true) throw new HostOperationError("session/retry-media-unavailable");
|
|
4669
|
+
setLocked(true);
|
|
4670
|
+
const content = mediaChanged ? await prepareEditContent(props.host, preview.current, draft, submission.current, signal.current.signal, attachments) : void 0;
|
|
4671
|
+
props.nativeRetry.setDraft(draft);
|
|
4672
|
+
await props.nativeRetry.sendEdit(sessionId, props.messageId, signal.current.signal, content, true);
|
|
4673
|
+
const state = props.nativeRetry.getSnapshot();
|
|
4674
|
+
setFailure(state.failure);
|
|
4675
|
+
if (!state.editing) setEditing(false);
|
|
4676
|
+
else if (isStaleRetryFailure(state.failure)) setLocked(false);
|
|
4677
|
+
} catch (error) {
|
|
4678
|
+
if (!signal.current.signal.aborted) setFailure(readFailure(error));
|
|
4679
|
+
} finally {
|
|
4680
|
+
if (!signal.current.signal.aborted) setBusy(false);
|
|
4681
|
+
}
|
|
4682
|
+
};
|
|
4683
|
+
const cancel = () => {
|
|
4684
|
+
if (busy) return;
|
|
4685
|
+
props.nativeRetry.cancelEdit();
|
|
4686
|
+
setEditing(false);
|
|
4687
|
+
setFailure(void 0);
|
|
4688
|
+
};
|
|
4689
|
+
const invalidatePrepared = () => {
|
|
4690
|
+
if (submission.current) submission.current = { requestId: submission.current.requestId };
|
|
4691
|
+
};
|
|
4692
|
+
const actions = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4693
|
+
className: `${FREE_CHAT_ROOT} dsh-free-actions`,
|
|
4694
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
4695
|
+
label: copy("edit"),
|
|
4696
|
+
side: "bottom",
|
|
4697
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4698
|
+
className: "dsm-icon-action",
|
|
4699
|
+
size: "sm",
|
|
4700
|
+
variant: "ghost",
|
|
4701
|
+
"aria-label": copy("edit"),
|
|
4702
|
+
disabled: busy || native.busy || native.editing && !mine,
|
|
4703
|
+
onClick: () => {
|
|
4704
|
+
run("edit");
|
|
4705
|
+
},
|
|
4706
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditMessageIcon, {})
|
|
4707
|
+
})
|
|
4708
|
+
}), failure && !editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4709
|
+
className: "dsh-ui-error dsh-free-alert",
|
|
4710
|
+
role: "alert",
|
|
4711
|
+
children: failureText(props.locale, failure)
|
|
4712
|
+
}) : null]
|
|
4713
|
+
});
|
|
4714
|
+
const editor = editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageEditor, {
|
|
4715
|
+
text: draft,
|
|
4716
|
+
setText: (text) => {
|
|
4717
|
+
invalidatePrepared();
|
|
4718
|
+
setDraft(text);
|
|
4719
|
+
},
|
|
4720
|
+
attachments,
|
|
4721
|
+
setAttachments: native.controlsContent ? (items) => {
|
|
4722
|
+
invalidatePrepared();
|
|
4723
|
+
setAttachments(items);
|
|
4724
|
+
setMediaChanged(true);
|
|
4725
|
+
} : void 0,
|
|
4726
|
+
sessionId,
|
|
4727
|
+
host: props.host,
|
|
4728
|
+
locale: props.locale,
|
|
4729
|
+
busy,
|
|
4730
|
+
locked,
|
|
4731
|
+
onCancel: cancel,
|
|
4732
|
+
onSend: () => {
|
|
4733
|
+
setConfirming(true);
|
|
4734
|
+
},
|
|
4735
|
+
error: failure ? failureText(props.locale, failure) : void 0
|
|
4736
|
+
}) : null;
|
|
4737
|
+
const content = props.render ? props.render(actions, editor) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [actions, editor] });
|
|
4738
|
+
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, {
|
|
4739
|
+
open: confirming && editing,
|
|
4740
|
+
className: FREE_CHAT_ROOT,
|
|
4741
|
+
title: copy("editTitle"),
|
|
4742
|
+
closeLabel: copy("close"),
|
|
4743
|
+
onClose: () => {
|
|
4744
|
+
setConfirming(false);
|
|
4745
|
+
},
|
|
4746
|
+
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, {
|
|
4747
|
+
variant: "ghost",
|
|
4748
|
+
"data-modal-autofocus": true,
|
|
4749
|
+
onClick: () => {
|
|
4750
|
+
setConfirming(false);
|
|
4751
|
+
},
|
|
4752
|
+
children: copy("cancel")
|
|
4753
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4754
|
+
variant: "primary",
|
|
4755
|
+
onClick: () => {
|
|
4756
|
+
setConfirming(false);
|
|
4757
|
+
run("send");
|
|
4758
|
+
},
|
|
4759
|
+
children: copy("editConfirm")
|
|
4760
|
+
})] }),
|
|
4761
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy("editBranchWarning") })
|
|
4762
|
+
})] });
|
|
4763
|
+
}
|
|
4764
|
+
//#endregion
|
|
4765
|
+
//#region src/user-node.tsx
|
|
4766
|
+
function contentParts(content) {
|
|
4767
|
+
const texts = [];
|
|
4768
|
+
const attachments = [];
|
|
4769
|
+
const rest = [];
|
|
4770
|
+
for (const block of content) if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
|
|
4771
|
+
else if (block.type === "image" && block.attachment !== void 0) attachments.push({
|
|
4772
|
+
type: "image",
|
|
4773
|
+
image: { attachment: block.attachment }
|
|
4774
|
+
});
|
|
4775
|
+
else if (block.type === "file" && block.attachment !== void 0) attachments.push({
|
|
4776
|
+
type: "file",
|
|
4777
|
+
file: block.attachment
|
|
4778
|
+
});
|
|
4779
|
+
else rest.push(block);
|
|
4780
|
+
return {
|
|
4781
|
+
text: texts.join(""),
|
|
4782
|
+
attachments,
|
|
4783
|
+
rest
|
|
4784
|
+
};
|
|
4785
|
+
}
|
|
4786
|
+
function pad2(value) {
|
|
4787
|
+
return String(value).padStart(2, "0");
|
|
4788
|
+
}
|
|
4789
|
+
function messageClock(time, t) {
|
|
4790
|
+
const date = new Date(time);
|
|
4791
|
+
const now = /* @__PURE__ */ new Date();
|
|
4792
|
+
const clock = `${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
|
|
4793
|
+
if (date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate()) return clock;
|
|
4794
|
+
const params = {
|
|
4795
|
+
y: date.getFullYear(),
|
|
4796
|
+
m: date.getMonth() + 1,
|
|
4797
|
+
d: date.getDate()
|
|
4798
|
+
};
|
|
4799
|
+
return `${date.getFullYear() === now.getFullYear() ? t("clock.md", params) : t("clock.ymd", params)} ${clock}`;
|
|
4800
|
+
}
|
|
4801
|
+
function MessageChrome(props) {
|
|
4802
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
4803
|
+
const timer = (0, react.useRef)(void 0);
|
|
4804
|
+
const pending = (0, react.useRef)(false);
|
|
4805
|
+
(0, react.useEffect)(() => () => {
|
|
4806
|
+
pending.current = false;
|
|
4807
|
+
if (timer.current !== void 0) clearTimeout(timer.current);
|
|
4808
|
+
}, []);
|
|
4809
|
+
const copy = () => {
|
|
4810
|
+
if (copied || pending.current) return;
|
|
4811
|
+
pending.current = true;
|
|
4812
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(props.text).then((ok) => {
|
|
4813
|
+
pending.current = false;
|
|
4814
|
+
if (!ok) return;
|
|
4815
|
+
setCopied(true);
|
|
4816
|
+
timer.current = setTimeout(() => {
|
|
4817
|
+
setCopied(false);
|
|
4818
|
+
}, 1e3);
|
|
4819
|
+
});
|
|
4820
|
+
};
|
|
4821
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4822
|
+
className: "dsm-user-chrome",
|
|
4823
|
+
children: [
|
|
4824
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4825
|
+
className: "dsm-user-time",
|
|
4826
|
+
children: messageClock(props.time, props.t)
|
|
4827
|
+
}),
|
|
4828
|
+
props.children,
|
|
4829
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
4830
|
+
label: copied ? props.t("copied") : props.t("copy"),
|
|
4831
|
+
side: "bottom",
|
|
4832
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4833
|
+
size: "sm",
|
|
4834
|
+
variant: "ghost",
|
|
4835
|
+
className: "dsm-icon-action",
|
|
4836
|
+
"aria-label": copied ? props.t("copied") : props.t("copy"),
|
|
4837
|
+
onClick: copy,
|
|
4838
|
+
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, {})
|
|
4025
4839
|
})
|
|
4026
4840
|
})
|
|
4027
4841
|
]
|
|
4028
4842
|
});
|
|
4029
4843
|
}
|
|
4844
|
+
/** User-message bubble whose own action row edits the exact prompt in place. */
|
|
4845
|
+
function UserNodeWithEdit(props) {
|
|
4846
|
+
const { text, attachments, rest } = contentParts(props.node.data.content);
|
|
4847
|
+
const compactImages = attachments.length > 1;
|
|
4848
|
+
const truncated = (total) => props.t("json.truncated", { total });
|
|
4849
|
+
const data = props.node.data;
|
|
4850
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditActions, {
|
|
4851
|
+
promptSeq: data.seq,
|
|
4852
|
+
sessionId: props.sessionId,
|
|
4853
|
+
locale: props.locale,
|
|
4854
|
+
host: props.host,
|
|
4855
|
+
nativeRetry: props.nativeRetry,
|
|
4856
|
+
useSessionAccess: props.useSessionAccess,
|
|
4857
|
+
ids: props.ids,
|
|
4858
|
+
messageId: props.node.id,
|
|
4859
|
+
render: (actions, editor) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4860
|
+
className: `${FREE_CHAT_ROOT} dsm-user-row`,
|
|
4861
|
+
children: [editor, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4862
|
+
className: "dsm-user-original",
|
|
4863
|
+
hidden: Boolean(editor),
|
|
4864
|
+
children: [!props.renderMessageImages && props.renderFactorySlot ? props.renderFactorySlot("conversation.chat.user.content", {
|
|
4865
|
+
content: {
|
|
4866
|
+
text,
|
|
4867
|
+
attachments,
|
|
4868
|
+
extraBlocks: rest,
|
|
4869
|
+
...data.referenceLabels === void 0 ? {} : { referenceLabels: data.referenceLabels }
|
|
4870
|
+
},
|
|
4871
|
+
loadImage: props.loadImage,
|
|
4872
|
+
facts: {
|
|
4873
|
+
...data.referenceLabels === void 0 ? {} : { sessionLabels: data.referenceLabels },
|
|
4874
|
+
...data.skillNames === void 0 ? {} : { skillNames: data.skillNames }
|
|
4875
|
+
},
|
|
4876
|
+
interactive: true
|
|
4877
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4878
|
+
className: "dsm-user-stack",
|
|
4879
|
+
children: [
|
|
4880
|
+
attachments.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4881
|
+
className: "dsm-user-attachments",
|
|
4882
|
+
"data-message-attachments": true,
|
|
4883
|
+
children: attachments.map((attachment, index) => attachment.type === "image" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: props.renderMessageImages?.({
|
|
4884
|
+
images: [attachment.image],
|
|
4885
|
+
align: "end",
|
|
4886
|
+
compact: compactImages
|
|
4887
|
+
}) }, `image:${index}`) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4888
|
+
className: "dsm-file-card",
|
|
4889
|
+
title: attachment.file.name,
|
|
4890
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
|
|
4891
|
+
path: attachment.file.name,
|
|
4892
|
+
className: "dsm-file-icon"
|
|
4893
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4894
|
+
className: "dsm-file-content",
|
|
4895
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4896
|
+
className: "dsm-file-name",
|
|
4897
|
+
children: attachment.file.name
|
|
4898
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4899
|
+
className: "dsm-file-meta",
|
|
4900
|
+
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(" ")
|
|
4901
|
+
})]
|
|
4902
|
+
})]
|
|
4903
|
+
}, `file:${index}`))
|
|
4904
|
+
}) : null,
|
|
4905
|
+
text !== "" || rest.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4906
|
+
className: "dsm-user-bubble",
|
|
4907
|
+
children: [text, rest.map((block, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.JsonBlock, {
|
|
4908
|
+
label: props.t("message.extraBlock"),
|
|
4909
|
+
payload: block,
|
|
4910
|
+
truncatedLabel: truncated
|
|
4911
|
+
}, index))]
|
|
4912
|
+
}) : null,
|
|
4913
|
+
data.referenceLabels !== void 0 && data.referenceLabels.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4914
|
+
className: "dsm-reference-summary",
|
|
4915
|
+
children: props.t("message.referenceSummary", { labels: data.referenceLabels.join(props.t("message.referenceSeparator")) })
|
|
4916
|
+
}) : null
|
|
4917
|
+
]
|
|
4918
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(MessageChrome, {
|
|
4919
|
+
text,
|
|
4920
|
+
time: data.time,
|
|
4921
|
+
t: props.t,
|
|
4922
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UserMessageActions, {
|
|
4923
|
+
messageId: props.node.id,
|
|
4924
|
+
text,
|
|
4925
|
+
sessionId: props.sessionId ?? "",
|
|
4926
|
+
locale: props.locale,
|
|
4927
|
+
model: props.nativeRetry,
|
|
4928
|
+
hideEdit: Boolean(actions)
|
|
4929
|
+
}), actions]
|
|
4930
|
+
})]
|
|
4931
|
+
})]
|
|
4932
|
+
})
|
|
4933
|
+
});
|
|
4934
|
+
}
|
|
4935
|
+
//#endregion
|
|
4936
|
+
//#region src/retry-contracts.ts
|
|
4937
|
+
const RETRY_CAPABILITIES_ENDPOINT = "retry.capabilities";
|
|
4938
|
+
const RETRY_PREVIEW_ENDPOINT = "retry.preview";
|
|
4939
|
+
const RETRY_COMMIT_ENDPOINT = "retry.commit";
|
|
4940
|
+
//#endregion
|
|
4941
|
+
//#region src/retry-host.ts
|
|
4942
|
+
function bindRetryHost(rpc) {
|
|
4943
|
+
const call = (endpoint, payload, signal) => {
|
|
4944
|
+
if (typeof rpc?.call !== "function") throw new HostOperationError("session/lifecycle-unavailable");
|
|
4945
|
+
return rpc.call(TEMP_WORKSPACE_CHANNEL, endpoint, payload, signal);
|
|
4946
|
+
};
|
|
4947
|
+
return {
|
|
4948
|
+
async capabilities() {
|
|
4949
|
+
const value = await unwrap(call(RETRY_CAPABILITIES_ENDPOINT, {}));
|
|
4950
|
+
if (!isCapabilities(value)) throw new HostOperationError("session-manager/unavailable");
|
|
4951
|
+
return value;
|
|
4952
|
+
},
|
|
4953
|
+
async preview(request, signal) {
|
|
4954
|
+
const value = await unwrap(call(RETRY_PREVIEW_ENDPOINT, request, signal));
|
|
4955
|
+
if (!isPreview(value)) throw new HostOperationError("session-manager/unavailable");
|
|
4956
|
+
return value;
|
|
4957
|
+
},
|
|
4958
|
+
async commit(request, signal) {
|
|
4959
|
+
const value = await unwrap(call(RETRY_COMMIT_ENDPOINT, request, signal));
|
|
4960
|
+
if (!isCommit(value)) throw new HostOperationError("session-manager/unavailable");
|
|
4961
|
+
return value;
|
|
4962
|
+
}
|
|
4963
|
+
};
|
|
4964
|
+
}
|
|
4965
|
+
/**
|
|
4966
|
+
* Commit then navigate: the forked child is the chat the user keeps, so the
|
|
4967
|
+
* browser follows a successful commit to the new session. `open` is absent
|
|
4968
|
+
* only when the host has no workspace navigation; the commit value returns
|
|
4969
|
+
* either way.
|
|
4970
|
+
*/
|
|
4971
|
+
function retryCommitAndOpen(commit, open) {
|
|
4972
|
+
return async (request, signal) => {
|
|
4973
|
+
const value = await commit(request, signal);
|
|
4974
|
+
try {
|
|
4975
|
+
await open?.(value.sessionId);
|
|
4976
|
+
} catch {
|
|
4977
|
+
throw new HostOperationError("session/retry-pending");
|
|
4978
|
+
}
|
|
4979
|
+
return value;
|
|
4980
|
+
};
|
|
4981
|
+
}
|
|
4982
|
+
function isCapabilities(value) {
|
|
4983
|
+
if (typeof value !== "object" || value === null) return false;
|
|
4984
|
+
const item = value;
|
|
4985
|
+
return typeof item.retry === "boolean" && typeof item.retryModified === "boolean" && typeof item.retryContent === "boolean";
|
|
4986
|
+
}
|
|
4987
|
+
function isPreview(value) {
|
|
4988
|
+
if (typeof value !== "object" || value === null) return false;
|
|
4989
|
+
const item = value;
|
|
4990
|
+
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";
|
|
4991
|
+
}
|
|
4992
|
+
function isCommit(value) {
|
|
4993
|
+
if (typeof value !== "object" || value === null) return false;
|
|
4994
|
+
const item = value;
|
|
4995
|
+
return typeof item.sessionId === "string" && typeof item.sourceSessionId === "string" && typeof item.archived === "boolean";
|
|
4996
|
+
}
|
|
4030
4997
|
//#endregion
|
|
4031
4998
|
//#region src/client.tsx
|
|
4032
4999
|
const name = "dsh-session-manager-client";
|
|
@@ -4046,7 +5013,14 @@ function apply(ctx) {
|
|
|
4046
5013
|
mountModelSettingsChanges(client, PLUGIN_NAME);
|
|
4047
5014
|
const host = bindHost(client);
|
|
4048
5015
|
const chats = createFreeChatModel(host);
|
|
4049
|
-
const
|
|
5016
|
+
const retryHost = bindRetryHost(client.connection?.rpc);
|
|
5017
|
+
const retries = createRetryModel({
|
|
5018
|
+
list: host.list,
|
|
5019
|
+
capabilities: () => retryHost.capabilities(),
|
|
5020
|
+
previewRetry: (request) => retryHost.preview(request),
|
|
5021
|
+
retry: retryCommitAndOpen(retryHost.commit, host.openSession)
|
|
5022
|
+
});
|
|
5023
|
+
const editHost = bindEditHost(client);
|
|
4050
5024
|
const temporaryHost = bindTemporaryWorkspaceHost(client);
|
|
4051
5025
|
const temporary = createTemporaryWorkspaceModel(temporaryHost, void 0, () => uiText(copyLocale(client.locale?.getSnapshot().active), "tempWorkspace"));
|
|
4052
5026
|
const presenceHost = bindPresenceHost(client);
|
|
@@ -4095,8 +5069,22 @@ function apply(ctx) {
|
|
|
4095
5069
|
locale: useLiveLocale(client.locale),
|
|
4096
5070
|
model: continues
|
|
4097
5071
|
}))));
|
|
4098
|
-
stops.push(client.slots.inject(
|
|
4099
|
-
name:
|
|
5072
|
+
stops.push(client.slots.inject("conversation.chat.node", () => client.slots.register({
|
|
5073
|
+
name: "conversation.chat.node",
|
|
5074
|
+
key: "user",
|
|
5075
|
+
locale: "chat",
|
|
5076
|
+
priority: -10
|
|
5077
|
+
}, (props) => {
|
|
5078
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UserNodeWithEdit, {
|
|
5079
|
+
...props,
|
|
5080
|
+
sessionId: props.sessionId,
|
|
5081
|
+
locale: useLiveLocale(client.locale),
|
|
5082
|
+
host: editHost,
|
|
5083
|
+
nativeRetry: retries
|
|
5084
|
+
});
|
|
5085
|
+
})));
|
|
5086
|
+
stops.push(client.slots.inject(TURN_TAIL_SLOT, () => client.slots.register({
|
|
5087
|
+
name: TURN_TAIL_SLOT,
|
|
4100
5088
|
id: WELCOME_BACK_CARD_ID,
|
|
4101
5089
|
order: 30
|
|
4102
5090
|
}, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WelcomeBackCard, {
|