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