@roaming-ai/dsh-group-chat 0.2.2 → 0.3.0
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 +3 -2
- package/lib/client.js +984 -296
- package/lib/client.js.map +1 -1
- package/lib/index.js +524 -52
- package/lib/types/client/components/Bubble.d.ts +6 -3
- package/lib/types/client/components/ChatPanel.d.ts +1 -0
- package/lib/types/client/components/ConstraintList.d.ts +11 -0
- package/lib/types/client/components/FailCard.d.ts +9 -0
- package/lib/types/client/components/Fold.d.ts +19 -0
- package/lib/types/client/components/HoverTip.d.ts +22 -0
- package/lib/types/client/components/MessageFlow.d.ts +1 -0
- package/lib/types/client/components/MsgActions.d.ts +14 -0
- package/lib/types/client/hooks/useComposer.d.ts +6 -1
- package/lib/types/client/lib/composer-draft.d.ts +17 -0
- package/lib/types/core/constraints.d.ts +61 -0
- package/lib/types/core/errors.d.ts +54 -0
- package/lib/types/core/types.d.ts +22 -0
- package/lib/types/host/api/actions.d.ts +1 -1
- package/lib/types/host/engine/conversation.d.ts +5 -3
- package/lib/types/host/engine/fold.d.ts +17 -0
- package/lib/types/host/engine/index.d.ts +1 -0
- package/lib/types/host/engine/retitle.d.ts +5 -5
- package/lib/types/host/service.d.ts +1 -1
- package/lib/types/host/state.d.ts +2 -0
- package/lib/types/index.d.ts +2 -0
- package/package.json +1 -1
- package/src/client/GroupChatPanel.tsx +18 -3
- package/src/client/components/AsidePanel.tsx +10 -8
- package/src/client/components/Bubble.tsx +45 -18
- package/src/client/components/ChatPanel.tsx +17 -12
- package/src/client/components/Composer.tsx +26 -20
- package/src/client/components/ConstraintList.tsx +69 -0
- package/src/client/components/FailCard.tsx +49 -0
- package/src/client/components/Fold.tsx +72 -0
- package/src/client/components/HoverTip.tsx +134 -0
- package/src/client/components/MessageFlow.tsx +77 -54
- package/src/client/components/MsgActions.tsx +85 -0
- package/src/client/components/NavPanel.tsx +6 -1
- package/src/client/components/ThinkRow.tsx +7 -3
- package/src/client/components/ToolRow.tsx +7 -3
- package/src/client/hooks/useComposer.ts +40 -3
- package/src/client/hooks/useGroupChatState.ts +5 -4
- package/src/client/lib/composer-draft.ts +47 -0
- package/src/client/lib/styles.ts +73 -12
- package/src/client/react-dom-shim.d.ts +2 -0
- package/src/core/constraints.ts +210 -0
- package/src/core/errors.ts +184 -0
- package/src/core/json.ts +1 -0
- package/src/core/types.ts +28 -3
- package/src/host/api/actions.ts +35 -1
- package/src/host/broadcast.ts +3 -3
- package/src/host/engine/conversation.ts +103 -38
- package/src/host/engine/fold.ts +114 -0
- package/src/host/engine/index.ts +1 -0
- package/src/host/engine/retitle.ts +16 -11
- package/src/host/persistence/persistence.ts +18 -1
- package/src/host/service.ts +1 -1
- package/src/host/state.ts +5 -4
- package/src/index.ts +2 -0
package/lib/client.js
CHANGED
|
@@ -83,7 +83,32 @@ window.__ModuleLoader__.load({
|
|
|
83
83
|
".dsgc-seambtn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(128,128,128,.15));color:var(--dsw-alias-label-primary,inherit);border-color:var(--dsw-alias-border-l3,rgba(128,128,128,.4))}",
|
|
84
84
|
".dsgc-seambtn.left{left:0;border-left:none;border-radius:0 8px 8px 0;box-shadow:3px 0 10px rgba(0,0,0,.07)}",
|
|
85
85
|
".dsgc-seambtn.right{right:0;border-right:none;border-radius:8px 0 0 8px;box-shadow:-3px 0 10px rgba(0,0,0,.07)}",
|
|
86
|
-
".dsgc-chathead{display:flex;
|
|
86
|
+
".dsgc-chathead{display:flex;flex-direction:column;gap:6px;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(128,128,128,.2))}",
|
|
87
|
+
".dsgc-chathead-row{display:flex;gap:10px;align-items:center;min-width:0}",
|
|
88
|
+
".dsgc-constraints{align-self:center;box-sizing:border-box;width:76%;max-width:76%;display:flex;flex-direction:column;gap:8px;min-width:0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.3));background:var(--dsw-alias-bg-layer-3,rgba(128,128,128,.06));border-radius:12px;animation:dsgc-fade-in .18s ease-out}",
|
|
89
|
+
".dsgc-chead{display:flex;flex-direction:column;gap:2px;min-width:0}",
|
|
90
|
+
".dsgc-ctitle{margin:0;font:inherit;font-size:12px;font-weight:600;line-height:1.5;color:var(--dsw-alias-label-primary,inherit)}",
|
|
91
|
+
".dsgc-cdesc{margin:0;font-size:11.5px;line-height:1.5;color:var(--dsw-alias-label-tertiary,inherit)}",
|
|
92
|
+
".dsgc-fold{display:grid;grid-template-rows:0fr;transition:grid-template-rows .16s ease-in}",
|
|
93
|
+
".dsgc-fold.open{grid-template-rows:1fr;transition:grid-template-rows .22s cubic-bezier(.16,1,.3,1)}",
|
|
94
|
+
".dsgc-fold-inner{min-height:0;overflow:hidden;display:flex;flex-direction:column;gap:2px;opacity:0;transition:opacity .12s ease-in}",
|
|
95
|
+
".dsgc-fold.open .dsgc-fold-inner{opacity:1;transition:opacity .18s cubic-bezier(.16,1,.3,1)}",
|
|
96
|
+
".dsgc-clip{--dsgc-clip-from:var(--dsw-alias-bg-layer-3,rgba(128,128,128,.18));position:relative;min-width:0}",
|
|
97
|
+
".dsgc-clip::before,.dsgc-clip::after{content:\"\";position:absolute;left:0;right:0;height:14px;pointer-events:none;z-index:1;opacity:0;transition:opacity .12s}",
|
|
98
|
+
".dsgc-clip::before{top:0;background:linear-gradient(to bottom,var(--dsgc-clip-from),transparent)}",
|
|
99
|
+
".dsgc-clip::after{bottom:0;background:linear-gradient(to top,var(--dsgc-clip-from),transparent)}",
|
|
100
|
+
".dsgc-clip.can-up::before,.dsgc-clip.can-down::after{opacity:1}",
|
|
101
|
+
".dsgc-clip-scroll{min-width:0;overflow-y:auto;overscroll-behavior:contain;overflow-anchor:none;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-scrollbar-bg-l2,rgba(128,128,128,.35)) transparent}",
|
|
102
|
+
".dsgc-clist{display:flex;flex-direction:column;gap:2px;min-width:0}",
|
|
103
|
+
".dsgc-constraint{display:flex;gap:8px;align-items:flex-start;min-width:0;font-size:11.5px;line-height:1.5}",
|
|
104
|
+
".dsgc-ckind{flex:none;font-weight:500}",
|
|
105
|
+
".dsgc-ckind.decided{color:var(--dsw-alias-label-secondary,inherit)}",
|
|
106
|
+
".dsgc-ckind.rejected{color:var(--dsw-alias-state-error-primary,#e5484d)}",
|
|
107
|
+
".dsgc-ckind.open{color:var(--dsw-alias-label-tertiary,inherit)}",
|
|
108
|
+
".dsgc-ctext{min-width:0;color:var(--dsw-alias-label-primary,inherit);overflow-wrap:break-word}",
|
|
109
|
+
".dsgc-cmore{align-self:flex-start;border:none;background:none;padding:0;font:inherit;font-size:11.5px;line-height:1.5;color:var(--dsw-alias-label-tertiary,inherit);cursor:pointer}",
|
|
110
|
+
".dsgc-cmore:hover{color:var(--dsw-alias-label-secondary,inherit)}",
|
|
111
|
+
".dsgc-cmore:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,var(--dsw-alias-state-business-primary,#4f6ef7));outline-offset:1px}",
|
|
87
112
|
".dsgc-sess-title{flex:none;font-weight:600;font-size:14px;color:var(--dsw-alias-label-primary,inherit);max-width:30%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
88
113
|
".dsgc-topic{flex:1;background:transparent;border:none;border-bottom:1px solid transparent;padding:2px;min-height:20px;color:var(--dsw-alias-label-secondary,inherit);font:inherit;outline:none;min-width:0;transition:color .12s,border-color .12s}",
|
|
89
114
|
".dsgc-topic:hover{border-bottom-color:var(--dsw-alias-border-l2,rgba(128,128,128,.3))}",
|
|
@@ -113,7 +138,8 @@ window.__ModuleLoader__.load({
|
|
|
113
138
|
".dsgc-think .dsgc-thinkrow{font-size:12px;color:var(--dsw-alias-label-secondary,inherit);border-radius:8px;padding:3px 8px;transition:background-color .12s}",
|
|
114
139
|
".dsgc-think .dsgc-thinkrow:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(128,128,128,.1))}",
|
|
115
140
|
".dsgc-think .dsgc-thinksummary{color:var(--dsw-alias-label-tertiary,inherit);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:44ch;font-size:11.5px;margin-left:6px}",
|
|
116
|
-
".dsgc-think .dsgc-
|
|
141
|
+
".dsgc-think .dsgc-clip{margin:2px 0 4px;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l2,rgba(128,128,128,.25))}",
|
|
142
|
+
".dsgc-think .dsgc-thinkbody{font-size:12.5px;line-height:1.7;color:var(--dsw-alias-label-secondary,inherit);white-space:pre-wrap;word-break:break-word}",
|
|
117
143
|
".dsgc-pending{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary,inherit)}",
|
|
118
144
|
".dsgc-pending svg{flex:none}",
|
|
119
145
|
".dsgc-pendingdots{display:inline-flex;gap:2px;align-items:center}",
|
|
@@ -122,16 +148,39 @@ window.__ModuleLoader__.load({
|
|
|
122
148
|
".dsgc-pendingdots i:nth-child(3){animation-delay:.4s}",
|
|
123
149
|
"@keyframes dsgc-dot-breathe{0%,100%{opacity:.25}50%{opacity:1}}",
|
|
124
150
|
".dsgc-sysmsg{align-self:center;font-size:11.5px;color:var(--dsw-alias-label-secondary,inherit);background:var(--dsw-alias-bg-module-platform,rgba(128,128,128,.1));border-radius:999px;padding:2px 10px;text-align:center;max-width:90%;animation:dsgc-sys-in .24s ease-out}",
|
|
151
|
+
".dsgc-msgops{display:inline-flex;align-items:center;gap:2px;margin-top:2px;min-height:24px;visibility:hidden;opacity:0;transition:opacity .12s,visibility .12s}",
|
|
152
|
+
".dsgc-msg:hover .dsgc-msgops,.dsgc-msg:focus-within .dsgc-msgops,.dsgc-msgops.always{visibility:visible;opacity:1}",
|
|
153
|
+
"@media (hover:none){.dsgc-msg .dsgc-msgops{visibility:visible;opacity:1}}",
|
|
154
|
+
".dsgc-msg.mine .dsgc-msgops{justify-content:flex-end}",
|
|
155
|
+
".dsgc-msgop{border:none;background:none;color:var(--dsw-alias-label-tertiary,inherit);cursor:pointer;padding:3px 6px;border-radius:6px;display:inline-flex;align-items:center;gap:4px;font:inherit;font-size:11.5px;line-height:16px;transition:background-color .12s,color .12s}",
|
|
156
|
+
".dsgc-msgop svg{flex:none}",
|
|
157
|
+
".dsgc-msgop:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(128,128,128,.15));color:var(--dsw-alias-label-primary,inherit)}",
|
|
158
|
+
".dsgc-msgop:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,var(--dsw-alias-state-business-primary,#4f6ef7));outline-offset:1px}",
|
|
159
|
+
".dsgc-msgop.done{color:var(--dsw-alias-state-success-primary,#30a46c)}",
|
|
160
|
+
".dsgc-msgop.retry{color:var(--dsw-alias-state-error-primary,#e5484d)}",
|
|
161
|
+
".dsgc-msgop.retry:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary,#e5484d) 10%,transparent);color:var(--dsw-alias-state-error-primary,#e5484d)}",
|
|
162
|
+
".dsgc-msgop:disabled{opacity:.4;cursor:default}",
|
|
163
|
+
".dsgc-fail{background:color-mix(in srgb,var(--dsw-alias-state-error-primary,#e5484d) 8%,var(--dsw-alias-bg-layer-3,rgba(128,128,128,.06)));border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary,#e5484d) 28%,var(--dsw-alias-border-l1,rgba(128,128,128,.15)));border-radius:12px;padding:10px 12px;display:flex;flex-direction:column;gap:6px;min-width:0;animation:dsgc-fade-in .18s ease-out}",
|
|
164
|
+
".dsgc-failhead{display:flex;align-items:flex-start;gap:8px;min-width:0}",
|
|
165
|
+
".dsgc-failicon{flex:none;width:22px;height:22px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-state-error-primary,#e5484d);background:color-mix(in srgb,var(--dsw-alias-state-error-primary,#e5484d) 12%,transparent);margin-top:1px}",
|
|
166
|
+
".dsgc-failcopy{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}",
|
|
167
|
+
".dsgc-failtitle{font-size:13px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary,inherit)}",
|
|
168
|
+
".dsgc-faildetail{font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary,inherit);overflow-wrap:anywhere}",
|
|
169
|
+
".dsgc-failmore{align-self:flex-start;border:none;background:none;padding:0;font:inherit;font-size:11.5px;line-height:1.5;color:var(--dsw-alias-label-tertiary,inherit);cursor:pointer}",
|
|
170
|
+
".dsgc-failmore:hover{color:var(--dsw-alias-label-secondary,inherit)}",
|
|
171
|
+
".dsgc-failmore:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,var(--dsw-alias-state-business-primary,#4f6ef7));outline-offset:1px}",
|
|
172
|
+
".dsgc-fail .dsgc-clip{--dsgc-clip-from:var(--dsw-alias-bg-layer-2,rgba(128,128,128,.12));border-radius:8px}",
|
|
173
|
+
".dsgc-failraw{margin:0;font-size:11.5px;line-height:1.55;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,transparent);color:var(--dsw-alias-label-secondary,inherit)}",
|
|
125
174
|
".dsgc-tool{margin:2px 0}",
|
|
126
175
|
".dsgc-tool .dsgc-toolrow{font-size:12px;color:var(--dsw-alias-label-secondary,inherit);border-radius:8px;padding:3px 8px;transition:background-color .12s}",
|
|
127
176
|
".dsgc-tool .dsgc-toolrow:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(128,128,128,.1))}",
|
|
128
177
|
".dsgc-tool .dsgc-toolsummary{color:var(--dsw-alias-label-tertiary,inherit);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:40ch;font-size:11.5px;margin-left:6px}",
|
|
129
|
-
".dsgc-tool .dsgc-
|
|
178
|
+
".dsgc-tool .dsgc-clip{margin:2px 0 4px;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l2,rgba(128,128,128,.25))}",
|
|
179
|
+
".dsgc-tool .dsgc-toolbody{font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,inherit);white-space:pre-wrap;word-break:break-word}",
|
|
130
180
|
".dsgc-confirm{border:1px solid var(--dsw-alias-state-business-primary,#4f6ef7);border-radius:12px;background:var(--dsw-alias-bg-layer-3,rgba(128,128,128,.06));padding:10px 12px;display:flex;flex-direction:column;gap:8px;max-width:76%;animation:dsgc-fade-in .18s ease-out}",
|
|
131
181
|
".dsgc-confirmtitle{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,inherit);display:flex;align-items:center;gap:6px}",
|
|
132
182
|
".dsgc-confirmcmd{font-size:12px;line-height:1.6;white-space:pre-wrap;word-break:break-word;border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.3));border-radius:8px;padding:8px 10px;background:var(--dsw-alias-bg-layer-2,transparent);color:var(--dsw-alias-label-primary,inherit);max-height:240px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-scrollbar-bg-l2,rgba(128,128,128,.35)) transparent}",
|
|
133
183
|
".dsgc-confirmops{display:flex;gap:8px;justify-content:flex-end}",
|
|
134
|
-
".dsgc-sysmsg.err{color:var(--dsw-alias-state-error-primary,#e5484d)}",
|
|
135
184
|
".dsgc-clearnotes{margin:0;padding-left:18px;font-size:13px;line-height:1.8;color:var(--dsw-alias-label-secondary,inherit);display:flex;flex-direction:column;gap:2px}",
|
|
136
185
|
".dsgc-err{font-size:12px;color:var(--dsw-alias-state-error-primary,#e5484d);animation:dsgc-fade-in .18s ease-out}",
|
|
137
186
|
".dsgc-empty{margin:auto;display:flex;flex-direction:column;align-items:center;gap:10px;color:var(--dsw-alias-label-tertiary,inherit);text-align:center;max-width:40ch;padding:24px;animation:dsgc-fade-in .3s ease-out}",
|
|
@@ -150,8 +199,9 @@ window.__ModuleLoader__.load({
|
|
|
150
199
|
".dsgc-card .dsgc-sendrow{padding:0 12px 10px}",
|
|
151
200
|
".dsgc-stopbtn{background:var(--dsw-alias-state-error-primary,#e5484d);border-color:transparent;color:#fff;font-weight:600}",
|
|
152
201
|
".dsgc-stopbtn:hover:not(:disabled){filter:brightness(1.08);color:#fff}",
|
|
153
|
-
".dsgc-parts{display:flex;
|
|
154
|
-
".dsgc-partslabel{font-size:12px;color:var(--dsw-alias-label-tertiary,inherit);flex:none}",
|
|
202
|
+
".dsgc-parts{display:flex;align-items:flex-start;gap:6px;min-height:24px}",
|
|
203
|
+
".dsgc-partslabel{font-size:12px;line-height:24px;color:var(--dsw-alias-label-tertiary,inherit);flex:none}",
|
|
204
|
+
".dsgc-partlist{display:flex;flex-wrap:wrap;align-items:center;gap:6px;row-gap:4px;flex:1;min-width:0}",
|
|
155
205
|
".dsgc-partchip{display:inline-flex;gap:6px;align-items:center;border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.3));background:transparent;color:var(--dsw-alias-label-secondary,inherit);border-radius:999px;padding:2px 10px;font-size:12px;line-height:1.5;cursor:pointer;transition:background-color .12s,color .12s,border-color .12s;font-family:inherit}",
|
|
156
206
|
".dsgc-partchip:hover{color:var(--dsw-alias-label-primary,inherit)}",
|
|
157
207
|
".dsgc-partchip.on{background:var(--dsw-alias-interactive-bg-active,rgba(128,128,128,.18));color:var(--dsw-alias-label-primary,inherit);border-color:var(--dsw-alias-border-l2,rgba(128,128,128,.3))}",
|
|
@@ -173,6 +223,11 @@ window.__ModuleLoader__.load({
|
|
|
173
223
|
".dsgc-rounds .dsgc-roundbtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(128,128,128,.15));color:var(--dsw-alias-label-primary,inherit)}",
|
|
174
224
|
".dsgc-rounds .dsgc-roundbtn:disabled{opacity:.4;cursor:default}",
|
|
175
225
|
".dsgc-rounds .dsgc-roundnum{width:26px;text-align:center;font-variant-numeric:tabular-nums;font-weight:600;color:var(--dsw-alias-label-primary,inherit)}",
|
|
226
|
+
".dsgc-hovertip{position:fixed;z-index:100;width:max-content;max-width:50vw;padding:3px 7px;border-radius:8px;background:var(--dsw-alias-tooltip-bg,#1f1f1f);color:var(--dsw-static-neutral-bluish-00,#fff);font-size:13px;line-height:20px;white-space:pre-line;overflow-wrap:break-word;pointer-events:none;animation:dsgc-hovertip-in 150ms var(--ds-ease-in-out,ease)}",
|
|
227
|
+
".dsgc-hovertip[data-side=\"right\"]{transform:translateY(-50%)}",
|
|
228
|
+
".dsgc-hovertip[data-side=\"top\"]{transform:translate(-50%,-100%)}",
|
|
229
|
+
".dsgc-hovertip[data-side=\"bottom\"]{transform:translateX(-50%)}",
|
|
230
|
+
"@keyframes dsgc-hovertip-in{from{opacity:0}}",
|
|
176
231
|
".dsgc-mentionwrap{position:relative}",
|
|
177
232
|
".dsgc-mention{animation:dsgc-pop-in .16s cubic-bezier(.16,1,.3,1);position:absolute;left:0;right:0;bottom:calc(100% + 6px);background:var(--dsw-alias-bg-layer-3,#fff);border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.3));border-radius:10px;box-shadow:var(--dsw-shadow-lv3,0 8px 24px rgba(0,0,0,.18));max-height:220px;overflow-y:auto;padding:4px;z-index:30;display:flex;flex-direction:column;gap:2px;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-scrollbar-bg-l2,rgba(128,128,128,.35)) transparent}",
|
|
178
233
|
".dsgc-mentionitem{display:flex;align-items:center;gap:8px;border:none;background:none;color:var(--dsw-alias-label-secondary,inherit);font:inherit;font-size:12.5px;text-align:left;cursor:pointer;padding:6px 8px;border-radius:6px;transition:background-color .12s,color .12s}",
|
|
@@ -206,10 +261,12 @@ window.__ModuleLoader__.load({
|
|
|
206
261
|
".dsgc-rolename{font-weight:600;font-size:13px;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary,inherit)}",
|
|
207
262
|
".dsgc-role.off .dsgc-rolename{color:var(--dsw-alias-label-tertiary,inherit)}",
|
|
208
263
|
".dsgc-rolepersona{font-size:11.5px;color:var(--dsw-alias-label-tertiary,inherit);line-height:1.5;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:0}",
|
|
209
|
-
".dsgc-
|
|
210
|
-
".dsgc-
|
|
264
|
+
".dsgc-rolemeta{display:flex;align-items:center;gap:6px;min-width:0;flex:1}",
|
|
265
|
+
".dsgc-rolemodel{min-width:0;flex:0 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-module-platform,rgba(128,128,128,.12));border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px;color:var(--dsw-alias-label-secondary,inherit)}",
|
|
266
|
+
".dsgc-rolethink{display:inline-flex;align-items:center;flex:none;color:var(--dsw-alias-label-tertiary,inherit)}",
|
|
267
|
+
".dsgc-roleops{display:flex;gap:2px;flex:none;margin-left:auto;opacity:0;transition:opacity .12s}",
|
|
211
268
|
".dsgc-role:hover .dsgc-roleops,.dsgc-role:focus-within .dsgc-roleops{opacity:1}",
|
|
212
|
-
".dsgc-rolemenu{display:flex;align-items:center;
|
|
269
|
+
".dsgc-rolemenu{display:flex;align-items:center;gap:8px}",
|
|
213
270
|
".dsgc-field{display:flex;flex-direction:column;gap:5px}",
|
|
214
271
|
".dsgc-field>label{font-size:12px;font-weight:500;color:var(--dsw-alias-label-secondary,inherit)}",
|
|
215
272
|
".dsgc-input,.dsgc-select,.dsgc-textarea{background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-3,transparent));border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.3));color:var(--dsw-alias-label-primary,inherit);font:inherit;font-size:13px;border-radius:8px;padding:6px 10px;width:100%;box-sizing:border-box;outline:none;transition:border-color .16s,background-color .16s}",
|
|
@@ -229,7 +286,6 @@ window.__ModuleLoader__.load({
|
|
|
229
286
|
"@keyframes dsgc-drawer-out{to{transform:translateX(32px);opacity:0}}",
|
|
230
287
|
"@keyframes dsgc-msg-in{from{opacity:0;transform:translateY(6px)}}",
|
|
231
288
|
"@keyframes dsgc-sys-in{from{opacity:0;transform:scale(.96)}}",
|
|
232
|
-
"@keyframes dsgc-think-in{from{opacity:0;transform:translateY(-3px)}}",
|
|
233
289
|
"@keyframes dsgc-pop-in{from{opacity:0;transform:translateY(4px)}}",
|
|
234
290
|
"@keyframes dsgc-rise-in{from{opacity:0;transform:translate(-50%,6px)}}",
|
|
235
291
|
"@keyframes dsgc-fade-in{from{opacity:0}}",
|
|
@@ -256,7 +312,7 @@ window.__ModuleLoader__.load({
|
|
|
256
312
|
".dsgc-fbsize{flex:none;font-size:10.5px;color:var(--dsw-alias-label-tertiary,inherit)}",
|
|
257
313
|
".dsgc-partchip:focus-visible,.dsgc-mentionitem:focus-visible,.dsgc-dot:focus-visible,.dsgc-opbtn:focus-visible,.dsgc-twist:focus-visible,.dsgc-addsess:focus-visible,.dsgc-grow-row:focus-visible,.dsgc-sess-row:focus-visible,.dsgc-rename:focus-visible,.dsgc-fbrow:focus-visible,.dsgc-role:focus-visible,.dsgc-roundbtn:focus-visible,.dsgc-tobtn:focus-visible,.dsgc-seambtn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,var(--dsw-alias-state-business-primary,#4f6ef7));outline-offset:1px}",
|
|
258
314
|
"@container (max-width: 880px){.dsgc-aside{position:absolute;top:0;right:0;bottom:0;z-index:15;width:min(304px,88%);box-shadow:-12px 0 32px rgba(0,0,0,.16);border-left:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.3));transition:transform .3s cubic-bezier(.16,1,.3,1),opacity .2s ease}.dsgc-aside.closed{width:min(304px,88%);padding-left:12px;padding-right:12px;border-left-width:1px;transform:translateX(calc(100% + 14px));opacity:0;transition:transform .26s ease-in,opacity .18s ease-in,visibility 0s .24s}.dsgc-seambtn.right:not(.closed){right:min(304px,88%)}}",
|
|
259
|
-
"@container (max-width: 640px){.dsgc-nav{width:200px}.dsgc-msgbody{max-width:88%}}",
|
|
315
|
+
"@container (max-width: 640px){.dsgc-nav{width:200px}.dsgc-msgbody{max-width:88%}.dsgc-constraints{width:88%;max-width:88%}}",
|
|
260
316
|
"[class*=\"panelList\"]:has(.dsgc-entryOverlay){margin-top:4px}",
|
|
261
317
|
"[class*=\"panelRow\"]:has(.dsgc-entryOverlay){position:relative;box-sizing:border-box;height:36px;min-height:36px;padding:0 10px;font-size:13px;transition:background-color .12s,color .12s}",
|
|
262
318
|
"[class*=\"panelRow\"][class*=\"panelActive\"]:has(.dsgc-entryOverlay){font-weight:600}",
|
|
@@ -270,7 +326,7 @@ window.__ModuleLoader__.load({
|
|
|
270
326
|
".dsgc-entryIcon svg{width:18px;height:18px;display:block}",
|
|
271
327
|
"[data-sidebar-collapsed] .dsgc-entryOverlay,[class*=\"collapsed\"] .dsgc-entryOverlay{justify-content:center;padding:0}",
|
|
272
328
|
"[data-sidebar-collapsed] [class*=\"panelRow\"]:has(.dsgc-entryOverlay),[class*=\"collapsed\"] [class*=\"panelRow\"]:has(.dsgc-entryOverlay){margin:0 auto}",
|
|
273
|
-
"@media (prefers-reduced-motion:reduce){.dsgc-dot,.dsgc-partchip,.dsgc-mentionitem,.dsgc-grow-row,.dsgc-sess-row,.dsgc-opbtn,.dsgc-addsess,.dsgc-topic,.dsgc-role,.dsgc-rename,.dsgc-input,.dsgc-select,.dsgc-textarea,.dsgc-fbrow,.dsgc-twist svg,.dsgc-roleops,.dsgc-roundbtn,.dsgc-permtrigger,.dsgc-permchevron,.dsgc-tobtn,.dsgc-seambtn,.dsgc-aside,.dsgc-aside.closed,.dsgc-nav,.dsgc-nav.closed,.dsgc-clearbtn,[class*=\"panelRow\"]:has(.dsgc-entryOverlay){transition:none}.dsgc-dot:hover{transform:none}.dsgc-drawer,.dsgc-msg,.dsgc-sysmsg,.dsgc-mention,.dsgc-tobottom,.dsgc-err,.dsgc-empty,.dsgc-fb,.dsgc-
|
|
329
|
+
"@media (prefers-reduced-motion:reduce){.dsgc-dot,.dsgc-partchip,.dsgc-mentionitem,.dsgc-grow-row,.dsgc-sess-row,.dsgc-opbtn,.dsgc-addsess,.dsgc-topic,.dsgc-role,.dsgc-rename,.dsgc-input,.dsgc-select,.dsgc-textarea,.dsgc-fbrow,.dsgc-twist svg,.dsgc-roleops,.dsgc-roundbtn,.dsgc-permtrigger,.dsgc-permchevron,.dsgc-tobtn,.dsgc-seambtn,.dsgc-aside,.dsgc-aside.closed,.dsgc-nav,.dsgc-nav.closed,.dsgc-clearbtn,.dsgc-cmore,.dsgc-msgops,.dsgc-msgop,.dsgc-failmore,.dsgc-clip::before,.dsgc-clip::after,.dsgc-fold,.dsgc-fold-inner,.dsgc-fold.open,.dsgc-fold.open .dsgc-fold-inner,[class*=\"panelRow\"]:has(.dsgc-entryOverlay){transition:none}.dsgc-dot:hover{transform:none}.dsgc-drawer,.dsgc-msg,.dsgc-sysmsg,.dsgc-mention,.dsgc-tobottom,.dsgc-err,.dsgc-empty,.dsgc-fb,.dsgc-msg.live .dsgc-avatar,.dsgc-loading svg,.dsgc-pendingdots i,.dsgc-typing,.dsgc-constraints,.dsgc-hovertip,.dsgc-fail{animation:none}.dsgc-typing{background-position:0 0;background-size:100% 100%}}",
|
|
274
330
|
".dgcs-page{display:flex;flex-direction:column;gap:14px;padding:4px 0}",
|
|
275
331
|
".dgcs-head{display:flex;flex-direction:column;gap:6px}",
|
|
276
332
|
".dgcs-title{margin:0;font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary,inherit)}",
|
|
@@ -696,6 +752,119 @@ window.__ModuleLoader__.load({
|
|
|
696
752
|
});
|
|
697
753
|
}
|
|
698
754
|
//#endregion
|
|
755
|
+
//#region src/client/components/HoverTip.tsx
|
|
756
|
+
/**
|
|
757
|
+
* 对齐宿主 Tooltip 的 hover 气泡:portal 到 document.body,躲开
|
|
758
|
+
* `.dsgc-root` 的 container-type 把 position:fixed 按容器定位。
|
|
759
|
+
* @module dsh-group-chat/client/components
|
|
760
|
+
*/
|
|
761
|
+
/**
|
|
762
|
+
* @param props.label 气泡正文(pre-line)
|
|
763
|
+
* @param props.side 默认 right(对齐侧栏会话行);composer 轮数用 top
|
|
764
|
+
* @param props.delayMs hover 延迟,默认 500;键盘 focus 立即出
|
|
765
|
+
*/
|
|
766
|
+
function HoverTip(props) {
|
|
767
|
+
const { label, side = "right", delayMs = 500, maxWidth, className, children } = props;
|
|
768
|
+
const anchorRef = (0, react.useRef)(null);
|
|
769
|
+
const bubbleRef = (0, react.useRef)(null);
|
|
770
|
+
const showTimer = (0, react.useRef)(null);
|
|
771
|
+
const hover = (0, react.useRef)(false);
|
|
772
|
+
const [pos, setPos] = (0, react.useState)(null);
|
|
773
|
+
const [placement, setPlacement] = (0, react.useState)(side);
|
|
774
|
+
const cancel = () => {
|
|
775
|
+
if (showTimer.current === null) return;
|
|
776
|
+
clearTimeout(showTimer.current);
|
|
777
|
+
showTimer.current = null;
|
|
778
|
+
};
|
|
779
|
+
const show = () => {
|
|
780
|
+
const el = anchorRef.current;
|
|
781
|
+
if (!el || !label) return;
|
|
782
|
+
const r = el.getBoundingClientRect();
|
|
783
|
+
setPlacement(side);
|
|
784
|
+
setPos({
|
|
785
|
+
x: side === "right" ? r.right + 10 : r.left + r.width / 2,
|
|
786
|
+
top: r.top,
|
|
787
|
+
bottom: r.bottom
|
|
788
|
+
});
|
|
789
|
+
};
|
|
790
|
+
const hideIfIdle = () => {
|
|
791
|
+
if (!hover.current && !(anchorRef.current && anchorRef.current.contains(document.activeElement))) setPos(null);
|
|
792
|
+
};
|
|
793
|
+
(0, react.useEffect)(() => cancel, []);
|
|
794
|
+
(0, react.useLayoutEffect)(() => {
|
|
795
|
+
if (!pos) return;
|
|
796
|
+
const fit = () => {
|
|
797
|
+
const el = bubbleRef.current;
|
|
798
|
+
if (!el) return;
|
|
799
|
+
el.style.left = pos.x + "px";
|
|
800
|
+
const r = el.getBoundingClientRect();
|
|
801
|
+
let dx = 0;
|
|
802
|
+
if (r.right > window.innerWidth - 12) dx = window.innerWidth - 12 - r.right;
|
|
803
|
+
if (r.left + dx < 12) dx = 12 - r.left;
|
|
804
|
+
el.style.left = pos.x + dx + "px";
|
|
805
|
+
if (side === "right") return;
|
|
806
|
+
const fitsBelow = pos.bottom + 8 + r.height <= window.innerHeight - 12;
|
|
807
|
+
const fitsAbove = pos.top - 8 - r.height >= 12;
|
|
808
|
+
if (placement === "bottom" && !fitsBelow && fitsAbove) setPlacement("top");
|
|
809
|
+
if (placement === "top" && !fitsAbove && fitsBelow) setPlacement("bottom");
|
|
810
|
+
};
|
|
811
|
+
fit();
|
|
812
|
+
window.addEventListener("resize", fit);
|
|
813
|
+
return () => {
|
|
814
|
+
window.removeEventListener("resize", fit);
|
|
815
|
+
};
|
|
816
|
+
}, [
|
|
817
|
+
pos,
|
|
818
|
+
placement,
|
|
819
|
+
label,
|
|
820
|
+
side
|
|
821
|
+
]);
|
|
822
|
+
const y = pos === null ? 0 : placement === "right" ? pos.top + (pos.bottom - pos.top) / 2 : placement === "top" ? pos.top - 8 : pos.bottom + 8;
|
|
823
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
824
|
+
ref: anchorRef,
|
|
825
|
+
className,
|
|
826
|
+
onMouseEnter: () => {
|
|
827
|
+
hover.current = true;
|
|
828
|
+
cancel();
|
|
829
|
+
if (delayMs <= 0) {
|
|
830
|
+
show();
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
showTimer.current = setTimeout(() => {
|
|
834
|
+
showTimer.current = null;
|
|
835
|
+
show();
|
|
836
|
+
}, delayMs);
|
|
837
|
+
},
|
|
838
|
+
onMouseLeave: () => {
|
|
839
|
+
hover.current = false;
|
|
840
|
+
cancel();
|
|
841
|
+
hideIfIdle();
|
|
842
|
+
},
|
|
843
|
+
onFocus: () => {
|
|
844
|
+
cancel();
|
|
845
|
+
show();
|
|
846
|
+
},
|
|
847
|
+
onBlur: (e) => {
|
|
848
|
+
const next = e.relatedTarget;
|
|
849
|
+
if (next && anchorRef.current && anchorRef.current.contains(next)) return;
|
|
850
|
+
cancel();
|
|
851
|
+
hideIfIdle();
|
|
852
|
+
},
|
|
853
|
+
children
|
|
854
|
+
}), pos ? (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
855
|
+
ref: bubbleRef,
|
|
856
|
+
className: "dsgc-hovertip",
|
|
857
|
+
"data-side": placement,
|
|
858
|
+
role: "tooltip",
|
|
859
|
+
style: {
|
|
860
|
+
left: pos.x,
|
|
861
|
+
top: y,
|
|
862
|
+
...maxWidth ? { maxWidth } : {}
|
|
863
|
+
},
|
|
864
|
+
children: label
|
|
865
|
+
}), document.body) : null] });
|
|
866
|
+
}
|
|
867
|
+
//#endregion
|
|
699
868
|
//#region src/client/components/NavPanel.tsx
|
|
700
869
|
function NavPanel(props) {
|
|
701
870
|
const { snap, search, setSearch, collapsedGroups, setCollapsedGroups, gid, sid, setGid, setSid, setPartsSel, renameDraft, setRenameDraft, confirmDel, setConfirmDel, mutate, navOpen } = props;
|
|
@@ -824,7 +993,10 @@ window.__ModuleLoader__.load({
|
|
|
824
993
|
onClick: (e) => {
|
|
825
994
|
e.stopPropagation();
|
|
826
995
|
}
|
|
827
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
996
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HoverTip, {
|
|
997
|
+
label: s.name,
|
|
998
|
+
side: "right",
|
|
999
|
+
delayMs: 500,
|
|
828
1000
|
className: "dsgc-sess-name",
|
|
829
1001
|
children: s.name
|
|
830
1002
|
}),
|
|
@@ -1101,8 +1273,9 @@ window.__ModuleLoader__.load({
|
|
|
1101
1273
|
}) : null,
|
|
1102
1274
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1103
1275
|
className: "dsgc-rolemenu",
|
|
1104
|
-
children: [
|
|
1105
|
-
|
|
1276
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1277
|
+
className: "dsgc-rolemeta",
|
|
1278
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1106
1279
|
className: "dsgc-rolemodel",
|
|
1107
1280
|
title: r.provider + " / " + r.model + (r.thinking ? " · 深度思考" + (r.reasoningEffort && r.reasoningEffort !== "default" ? "(" + r.reasoningEffort + ")" : "") : ""),
|
|
1108
1281
|
children: [
|
|
@@ -1110,42 +1283,36 @@ window.__ModuleLoader__.load({
|
|
|
1110
1283
|
" / ",
|
|
1111
1284
|
r.model
|
|
1112
1285
|
]
|
|
1113
|
-
}),
|
|
1114
|
-
|
|
1286
|
+
}), r.thinking ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1287
|
+
className: "dsgc-rolethink",
|
|
1115
1288
|
title: "深度思考",
|
|
1116
|
-
style: {
|
|
1117
|
-
display: "inline-flex",
|
|
1118
|
-
alignItems: "center",
|
|
1119
|
-
color: "var(--dsw-alias-label-tertiary,inherit)"
|
|
1120
|
-
},
|
|
1121
1289
|
children: Icon(_deepseek_ai_dsh_client_ui_primitives.IconThinkOutline14, 14)
|
|
1122
|
-
}) : null
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
]
|
|
1290
|
+
}) : null]
|
|
1291
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1292
|
+
className: "dsgc-roleops",
|
|
1293
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1294
|
+
className: "dsgc-opbtn",
|
|
1295
|
+
title: "编辑角色",
|
|
1296
|
+
"aria-label": "编辑角色",
|
|
1297
|
+
onClick: (e) => {
|
|
1298
|
+
e.stopPropagation();
|
|
1299
|
+
openRoleEditor(r);
|
|
1300
|
+
},
|
|
1301
|
+
children: Icon(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, 14)
|
|
1302
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1303
|
+
className: "dsgc-opbtn danger",
|
|
1304
|
+
title: "移除角色",
|
|
1305
|
+
"aria-label": "移除角色",
|
|
1306
|
+
onClick: (e) => {
|
|
1307
|
+
e.stopPropagation();
|
|
1308
|
+
mutate({
|
|
1309
|
+
op: "deleteRole",
|
|
1310
|
+
roleId: r.id
|
|
1311
|
+
});
|
|
1312
|
+
},
|
|
1313
|
+
children: Icon(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, 14)
|
|
1314
|
+
})]
|
|
1315
|
+
})]
|
|
1149
1316
|
})
|
|
1150
1317
|
]
|
|
1151
1318
|
}, r.id);
|
|
@@ -1288,6 +1455,204 @@ window.__ModuleLoader__.load({
|
|
|
1288
1455
|
});
|
|
1289
1456
|
}
|
|
1290
1457
|
//#endregion
|
|
1458
|
+
//#region src/core/types.ts
|
|
1459
|
+
/** 全部合法档位(展示顺序)。 */
|
|
1460
|
+
const PERMISSION_TIERS = [
|
|
1461
|
+
"view_only",
|
|
1462
|
+
"workspace_write",
|
|
1463
|
+
"full_access"
|
|
1464
|
+
];
|
|
1465
|
+
/** 档位安全化:合法字符串原样,其余 undefined。 */
|
|
1466
|
+
function asPermissionTier(value) {
|
|
1467
|
+
return typeof value === "string" && PERMISSION_TIERS.includes(value) ? value : void 0;
|
|
1468
|
+
}
|
|
1469
|
+
//#endregion
|
|
1470
|
+
//#region src/core/errors.ts
|
|
1471
|
+
const PREFIX = "模型输出异常终止: ";
|
|
1472
|
+
const LEGACY_ROLE = /^角色「([^」]+)」发言失败[::]\s*/;
|
|
1473
|
+
/** 剥旧系统胶囊与引擎包装前缀,保留供应商原文。 */
|
|
1474
|
+
function unwrapSpeakFailure(raw) {
|
|
1475
|
+
const text = String(raw || "").trim();
|
|
1476
|
+
const legacy = parseLegacyRoleFailure(text);
|
|
1477
|
+
const body = legacy ? legacy.rest : text;
|
|
1478
|
+
return body.startsWith(PREFIX) ? body.slice(10).trim() : body;
|
|
1479
|
+
}
|
|
1480
|
+
/** 旧系统胶囊文案:角色「名」发言失败:原文。对不上则 null。 */
|
|
1481
|
+
function parseLegacyRoleFailure(text) {
|
|
1482
|
+
const m = LEGACY_ROLE.exec(String(text || ""));
|
|
1483
|
+
if (!m) return null;
|
|
1484
|
+
return {
|
|
1485
|
+
roleName: m[1],
|
|
1486
|
+
rest: String(text).slice(m[0].length)
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
/** 发言失败卡:error 标记,或旧系统胶囊文案。 */
|
|
1490
|
+
function isSpeakFailure(m) {
|
|
1491
|
+
return !!m.error || !!parseLegacyRoleFailure(m.text);
|
|
1492
|
+
}
|
|
1493
|
+
/** 失败回合对应角色:failedRoleId / speaker / 旧文案里的角色名(恰好一名才命中)。 */
|
|
1494
|
+
function resolveFailedRole(m, roles) {
|
|
1495
|
+
const byId = (id) => {
|
|
1496
|
+
if (!id) return null;
|
|
1497
|
+
for (const r of roles) if (r.id === id) return r;
|
|
1498
|
+
return null;
|
|
1499
|
+
};
|
|
1500
|
+
const direct = byId(m.failedRoleId) || (m.speaker !== "user" && m.speaker !== "system" ? byId(m.speaker) : null);
|
|
1501
|
+
if (direct) return direct;
|
|
1502
|
+
const parsed = parseLegacyRoleFailure(m.text);
|
|
1503
|
+
if (!parsed) return null;
|
|
1504
|
+
const hits = roles.filter((r) => r.name === parsed.roleName);
|
|
1505
|
+
return hits.length === 1 ? hits[0] : null;
|
|
1506
|
+
}
|
|
1507
|
+
function pickJsonBlob(text) {
|
|
1508
|
+
const start = text.indexOf("{");
|
|
1509
|
+
const end = text.lastIndexOf("}");
|
|
1510
|
+
if (start < 0 || end <= start) return null;
|
|
1511
|
+
try {
|
|
1512
|
+
const parsed = JSON.parse(text.slice(start, end + 1));
|
|
1513
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
1514
|
+
} catch {
|
|
1515
|
+
return null;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
function httpStatus(text) {
|
|
1519
|
+
const m = text.match(/\b([1-5]\d{2})\b/);
|
|
1520
|
+
if (!m) return null;
|
|
1521
|
+
const n = Number(m[1]);
|
|
1522
|
+
return n >= 100 && n <= 599 ? n : null;
|
|
1523
|
+
}
|
|
1524
|
+
function resetHint(message) {
|
|
1525
|
+
const m = message.match(/reset at ([^.]+\S)/i) || message.match(/将在\s*([^\s。]+)\s*重置/);
|
|
1526
|
+
return m ? "将在 " + m[1] + " 重置" : void 0;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* 把供应商错误压成可扫描的标题 + 短因。
|
|
1530
|
+
* 未知形态回退为「发言失败」,原文仍可展开。
|
|
1531
|
+
*/
|
|
1532
|
+
function classifySpeakFailure(raw) {
|
|
1533
|
+
const source = unwrapSpeakFailure(raw);
|
|
1534
|
+
const blob = pickJsonBlob(source);
|
|
1535
|
+
const code = blob && typeof blob.code === "string" ? blob.code : "";
|
|
1536
|
+
const type = blob && typeof blob.type === "string" ? blob.type : "";
|
|
1537
|
+
const message = blob && typeof blob.message === "string" ? blob.message : source;
|
|
1538
|
+
const status = httpStatus(source);
|
|
1539
|
+
const joined = (code + " " + type + " " + message + " " + source).toLowerCase();
|
|
1540
|
+
if (/quota|accountquotaexceeded|exceeded the .*quota|额度|配额/.test(joined) || code === "AccountQuotaExceeded") return {
|
|
1541
|
+
title: "额度已用尽",
|
|
1542
|
+
detail: resetHint(message),
|
|
1543
|
+
raw: source
|
|
1544
|
+
};
|
|
1545
|
+
if (status === 429 || /too.?many.?requests|rate.?limit|限流/.test(joined) || type === "TooManyRequests") return {
|
|
1546
|
+
title: "请求过于频繁",
|
|
1547
|
+
detail: "稍后再试,或降低并发",
|
|
1548
|
+
raw: source
|
|
1549
|
+
};
|
|
1550
|
+
if (status === 401 || status === 403 || /unauthorized|forbidden|invalid.?api.?key|鉴权|未授权/.test(joined)) return {
|
|
1551
|
+
title: "模型鉴权失败",
|
|
1552
|
+
detail: "检查该角色绑定的提供方密钥",
|
|
1553
|
+
raw: source
|
|
1554
|
+
};
|
|
1555
|
+
if (status === 404 || /model.?not.?found|unknown.?model|模型不存在/.test(joined)) return {
|
|
1556
|
+
title: "模型不可用",
|
|
1557
|
+
detail: "该角色绑定的模型可能已下线",
|
|
1558
|
+
raw: source
|
|
1559
|
+
};
|
|
1560
|
+
if (/timeout|timed out|etimedout|超时/.test(joined)) return {
|
|
1561
|
+
title: "模型响应超时",
|
|
1562
|
+
raw: source
|
|
1563
|
+
};
|
|
1564
|
+
if (/network|econnreset|econnrefused|enotfound|fetch failed|网络/.test(joined)) return {
|
|
1565
|
+
title: "网络异常",
|
|
1566
|
+
detail: "检查网络后重试",
|
|
1567
|
+
raw: source
|
|
1568
|
+
};
|
|
1569
|
+
if (status !== null && status >= 500) return {
|
|
1570
|
+
title: "模型服务暂时不可用",
|
|
1571
|
+
detail: "HTTP " + status,
|
|
1572
|
+
raw: source
|
|
1573
|
+
};
|
|
1574
|
+
if (status !== null) return {
|
|
1575
|
+
title: "发言失败",
|
|
1576
|
+
detail: "HTTP " + status,
|
|
1577
|
+
raw: source
|
|
1578
|
+
};
|
|
1579
|
+
return {
|
|
1580
|
+
title: "发言失败",
|
|
1581
|
+
raw: source
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
/** 失败卡默认复制内容:标题 + 短因 + 原文。 */
|
|
1585
|
+
function formatSpeakFailureCopy(view) {
|
|
1586
|
+
const lines = [view.title];
|
|
1587
|
+
if (view.detail) lines.push(view.detail);
|
|
1588
|
+
if (view.raw && view.raw !== view.title) lines.push(view.raw);
|
|
1589
|
+
return lines.join("\n");
|
|
1590
|
+
}
|
|
1591
|
+
//#endregion
|
|
1592
|
+
//#region src/client/components/Fold.tsx
|
|
1593
|
+
/**
|
|
1594
|
+
* 会话内折叠:高度 0fr→1fr + 溢出滚动遮罩。思考 / 工具 / 失败原文 / 结论备忘共用。
|
|
1595
|
+
* @module dsh-group-chat/client/components
|
|
1596
|
+
*/
|
|
1597
|
+
function Fold(props) {
|
|
1598
|
+
const { open, children, className } = props;
|
|
1599
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1600
|
+
className: "dsgc-fold" + (open ? " open" : "") + (className ? " " + className : ""),
|
|
1601
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1602
|
+
className: "dsgc-fold-inner",
|
|
1603
|
+
children
|
|
1604
|
+
})
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
function ClipWell(props) {
|
|
1608
|
+
const { children, maxHeight, className, watch } = props;
|
|
1609
|
+
const [clip, setClip] = (0, react.useState)({
|
|
1610
|
+
up: false,
|
|
1611
|
+
down: false
|
|
1612
|
+
});
|
|
1613
|
+
const ref = (0, react.useRef)(null);
|
|
1614
|
+
(0, react.useLayoutEffect)(() => {
|
|
1615
|
+
const el = ref.current;
|
|
1616
|
+
if (!el) return;
|
|
1617
|
+
const measure = () => {
|
|
1618
|
+
if (el.clientHeight < 2) {
|
|
1619
|
+
setClip((cur) => cur.up || cur.down ? {
|
|
1620
|
+
up: false,
|
|
1621
|
+
down: false
|
|
1622
|
+
} : cur);
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
const max = el.scrollHeight - el.clientHeight;
|
|
1626
|
+
const up = el.scrollTop > 1;
|
|
1627
|
+
const down = max > 1 && el.scrollTop < max - 1;
|
|
1628
|
+
setClip((cur) => cur.up === up && cur.down === down ? cur : {
|
|
1629
|
+
up,
|
|
1630
|
+
down
|
|
1631
|
+
});
|
|
1632
|
+
};
|
|
1633
|
+
measure();
|
|
1634
|
+
const ro = new ResizeObserver(measure);
|
|
1635
|
+
ro.observe(el);
|
|
1636
|
+
for (const child of el.children) ro.observe(child);
|
|
1637
|
+
el.addEventListener("scroll", measure, { passive: true });
|
|
1638
|
+
el.addEventListener("transitionend", measure);
|
|
1639
|
+
return () => {
|
|
1640
|
+
ro.disconnect();
|
|
1641
|
+
el.removeEventListener("scroll", measure);
|
|
1642
|
+
el.removeEventListener("transitionend", measure);
|
|
1643
|
+
};
|
|
1644
|
+
}, [watch]);
|
|
1645
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1646
|
+
className: "dsgc-clip" + (clip.up ? " can-up" : "") + (clip.down ? " can-down" : ""),
|
|
1647
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1648
|
+
className: "dsgc-clip-scroll" + (className ? " " + className : ""),
|
|
1649
|
+
ref,
|
|
1650
|
+
style: { maxHeight },
|
|
1651
|
+
children
|
|
1652
|
+
})
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
//#endregion
|
|
1291
1656
|
//#region src/client/components/ThinkRow.tsx
|
|
1292
1657
|
/**
|
|
1293
1658
|
* 思考折叠行(对标宿主 ReasoningRow:折叠摘要 / 展开全文)。
|
|
@@ -1299,9 +1664,9 @@ window.__ModuleLoader__.load({
|
|
|
1299
1664
|
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
1300
1665
|
const plain = (line) => line.replace(/^(\s*#{1,6}\s+|\s*[-*+]\s+|\s*>\s*)+/, "").replace(/[*`_~]/g, "").replace(/\s+/g, " ").trim();
|
|
1301
1666
|
const summary = plain(running ? latestLine(text) : firstLine(text));
|
|
1302
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1667
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1303
1668
|
className: "dsgc-think",
|
|
1304
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
|
|
1669
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
|
|
1305
1670
|
rowClassName: "dsgc-thinkrow",
|
|
1306
1671
|
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconThinkOutline14, { size: 14 }),
|
|
1307
1672
|
title: "思考",
|
|
@@ -1314,12 +1679,18 @@ window.__ModuleLoader__.load({
|
|
|
1314
1679
|
collapsedContent: text ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1315
1680
|
className: "dsgc-thinksummary",
|
|
1316
1681
|
children: summary
|
|
1317
|
-
}) : null
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1682
|
+
}) : null
|
|
1683
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fold, {
|
|
1684
|
+
open: expanded,
|
|
1685
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClipWell, {
|
|
1686
|
+
maxHeight: 320,
|
|
1687
|
+
watch: text,
|
|
1688
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1689
|
+
className: "dsgc-thinkbody",
|
|
1690
|
+
children: text
|
|
1691
|
+
})
|
|
1321
1692
|
})
|
|
1322
|
-
})
|
|
1693
|
+
})]
|
|
1323
1694
|
});
|
|
1324
1695
|
}
|
|
1325
1696
|
//#endregion
|
|
@@ -1347,9 +1718,9 @@ window.__ModuleLoader__.load({
|
|
|
1347
1718
|
const statusText = c.status === "ok" ? "成功" : c.status === "denied" ? "用户拒绝" : "失败";
|
|
1348
1719
|
const StatusIcon = c.status === "ok" ? _deepseek_ai_dsh_client_ui_primitives.IconCheckOutline14 : c.status === "denied" ? _deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16 : _deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16;
|
|
1349
1720
|
const dur = c.durationMs ? c.durationMs >= 1e3 ? (c.durationMs / 1e3).toFixed(1) + "s" : c.durationMs + "ms" : "";
|
|
1350
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1721
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1351
1722
|
className: "dsgc-tool",
|
|
1352
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
|
|
1723
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
|
|
1353
1724
|
rowClassName: "dsgc-toolrow",
|
|
1354
1725
|
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconOf, { size: 14 }),
|
|
1355
1726
|
title: c.tool + (brief ? " " + brief : ""),
|
|
@@ -1366,12 +1737,138 @@ window.__ModuleLoader__.load({
|
|
|
1366
1737
|
marginRight: 4,
|
|
1367
1738
|
color: c.status === "ok" ? "var(--dsw-alias-label-tertiary,inherit)" : "var(--dsw-alias-state-error-primary,#e5484d)"
|
|
1368
1739
|
}), statusText + (dur ? " · " + dur : "")]
|
|
1369
|
-
}),
|
|
1370
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1371
|
-
className: "dsgc-toolbody",
|
|
1372
|
-
children: c.output || "(无输出)"
|
|
1373
1740
|
})
|
|
1374
|
-
})
|
|
1741
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fold, {
|
|
1742
|
+
open: expanded,
|
|
1743
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClipWell, {
|
|
1744
|
+
maxHeight: 260,
|
|
1745
|
+
watch: c.output,
|
|
1746
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1747
|
+
className: "dsgc-toolbody",
|
|
1748
|
+
children: c.output || "(无输出)"
|
|
1749
|
+
})
|
|
1750
|
+
})
|
|
1751
|
+
})]
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
//#endregion
|
|
1755
|
+
//#region src/client/components/FailCard.tsx
|
|
1756
|
+
/**
|
|
1757
|
+
* 角色发言失败卡:人话标题 + 可展开原文。操作条由 Bubble 放在气泡外下方。
|
|
1758
|
+
* @module dsh-group-chat/client/FailCard
|
|
1759
|
+
*/
|
|
1760
|
+
function FailCard(props) {
|
|
1761
|
+
const view = classifySpeakFailure(props.raw);
|
|
1762
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
1763
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1764
|
+
className: "dsgc-fail",
|
|
1765
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1766
|
+
className: "dsgc-failhead",
|
|
1767
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1768
|
+
className: "dsgc-failicon",
|
|
1769
|
+
"aria-hidden": "true",
|
|
1770
|
+
children: Icon(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, 14)
|
|
1771
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1772
|
+
className: "dsgc-failcopy",
|
|
1773
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1774
|
+
className: "dsgc-failtitle",
|
|
1775
|
+
children: view.title
|
|
1776
|
+
}), view.detail ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1777
|
+
className: "dsgc-faildetail",
|
|
1778
|
+
children: view.detail
|
|
1779
|
+
}) : null]
|
|
1780
|
+
})]
|
|
1781
|
+
}), view.raw ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1782
|
+
type: "button",
|
|
1783
|
+
className: "dsgc-failmore" + (open ? " open" : ""),
|
|
1784
|
+
"aria-expanded": open,
|
|
1785
|
+
onClick: () => {
|
|
1786
|
+
setOpen((v) => !v);
|
|
1787
|
+
},
|
|
1788
|
+
children: open ? "收起原始错误" : "查看原始错误"
|
|
1789
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fold, {
|
|
1790
|
+
open,
|
|
1791
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClipWell, {
|
|
1792
|
+
maxHeight: 220,
|
|
1793
|
+
watch: open,
|
|
1794
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
1795
|
+
className: "dsgc-failraw",
|
|
1796
|
+
children: view.raw
|
|
1797
|
+
})
|
|
1798
|
+
})
|
|
1799
|
+
})] }) : null]
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
//#endregion
|
|
1803
|
+
//#region src/client/components/MsgActions.tsx
|
|
1804
|
+
/**
|
|
1805
|
+
* 消息操作条:贴在气泡外下方。用户/角色消息悬停出「复制」;失败卡常驻「复制 / 重试」。
|
|
1806
|
+
* @module dsh-group-chat/client/MsgActions
|
|
1807
|
+
*/
|
|
1808
|
+
async function writeClipboard(text) {
|
|
1809
|
+
try {
|
|
1810
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1811
|
+
await navigator.clipboard.writeText(text);
|
|
1812
|
+
return true;
|
|
1813
|
+
}
|
|
1814
|
+
} catch {}
|
|
1815
|
+
try {
|
|
1816
|
+
const el = document.createElement("textarea");
|
|
1817
|
+
el.value = text;
|
|
1818
|
+
el.setAttribute("readonly", "");
|
|
1819
|
+
el.style.position = "fixed";
|
|
1820
|
+
el.style.left = "-9999px";
|
|
1821
|
+
document.body.appendChild(el);
|
|
1822
|
+
el.select();
|
|
1823
|
+
const ok = document.execCommand("copy");
|
|
1824
|
+
document.body.removeChild(el);
|
|
1825
|
+
return ok;
|
|
1826
|
+
} catch {
|
|
1827
|
+
return false;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
function MsgActions(props) {
|
|
1831
|
+
const { copyText, onRetry, retryDisabled, retryTitle, always } = props;
|
|
1832
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
1833
|
+
const copiedTimer = (0, react.useRef)(null);
|
|
1834
|
+
(0, react.useEffect)(() => () => {
|
|
1835
|
+
if (copiedTimer.current) clearTimeout(copiedTimer.current);
|
|
1836
|
+
}, []);
|
|
1837
|
+
const copy = async () => {
|
|
1838
|
+
if (!copyText) return;
|
|
1839
|
+
if (!await writeClipboard(copyText)) return;
|
|
1840
|
+
setCopied(true);
|
|
1841
|
+
if (copiedTimer.current) clearTimeout(copiedTimer.current);
|
|
1842
|
+
copiedTimer.current = setTimeout(() => {
|
|
1843
|
+
setCopied(false);
|
|
1844
|
+
}, 1400);
|
|
1845
|
+
};
|
|
1846
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1847
|
+
className: "dsgc-msgops" + (always ? " always" : ""),
|
|
1848
|
+
role: "group",
|
|
1849
|
+
"aria-label": "消息操作",
|
|
1850
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1851
|
+
type: "button",
|
|
1852
|
+
className: "dsgc-msgop" + (copied ? " done" : ""),
|
|
1853
|
+
title: copied ? "已复制" : "复制",
|
|
1854
|
+
"aria-label": copied ? "已复制" : "复制",
|
|
1855
|
+
onClick: (e) => {
|
|
1856
|
+
e.stopPropagation();
|
|
1857
|
+
copy();
|
|
1858
|
+
},
|
|
1859
|
+
children: [Icon(copied ? _deepseek_ai_dsh_client_ui_primitives.IconCheckOutline14 : _deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, 14), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: copied ? "已复制" : "复制" })]
|
|
1860
|
+
}), onRetry ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1861
|
+
type: "button",
|
|
1862
|
+
className: "dsgc-msgop retry",
|
|
1863
|
+
title: retryTitle || "重试",
|
|
1864
|
+
"aria-label": retryTitle || "重试",
|
|
1865
|
+
disabled: retryDisabled,
|
|
1866
|
+
onClick: (e) => {
|
|
1867
|
+
e.stopPropagation();
|
|
1868
|
+
if (!retryDisabled) onRetry();
|
|
1869
|
+
},
|
|
1870
|
+
children: [Icon(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline14, 14), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "重试" })]
|
|
1871
|
+
}) : null]
|
|
1375
1872
|
});
|
|
1376
1873
|
}
|
|
1377
1874
|
//#endregion
|
|
@@ -1385,62 +1882,78 @@ window.__ModuleLoader__.load({
|
|
|
1385
1882
|
* MarkdownText 重新解析打满主线程导致「卡死后一次性蹦出」。
|
|
1386
1883
|
* @module dsh-group-chat/client/Bubble
|
|
1387
1884
|
*/
|
|
1388
|
-
function BubbleInner({ m, role }) {
|
|
1885
|
+
function BubbleInner({ m, role, busy, onRetry }) {
|
|
1389
1886
|
const isUser = m.speaker === "user";
|
|
1390
|
-
const
|
|
1391
|
-
const
|
|
1887
|
+
const isFail = isSpeakFailure(m);
|
|
1888
|
+
const isSys = m.speaker === "system" && !isFail && !role;
|
|
1889
|
+
const name = isUser ? "我" : role ? role.name : isSys ? "系统" : "成员";
|
|
1392
1890
|
if (isSys) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1393
|
-
className: "dsgc-sysmsg"
|
|
1891
|
+
className: "dsgc-sysmsg",
|
|
1394
1892
|
children: m.text
|
|
1395
1893
|
});
|
|
1894
|
+
const retryTitle = !role ? "失败角色已不存在,无法重试" : !role.enabled ? "该角色已停用,无法重试" : busy ? "已有对话进行中,请先停止" : "重试该角色发言";
|
|
1895
|
+
const copyText = isFail ? formatSpeakFailureCopy(classifySpeakFailure(m.text)) : m.text || "";
|
|
1396
1896
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1397
|
-
className: "dsgc-msg" + (isUser ? " mine" : ""),
|
|
1897
|
+
className: "dsgc-msg" + (isUser ? " mine" : "") + (isFail ? " fail" : ""),
|
|
1398
1898
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1399
1899
|
className: "dsgc-avatar" + (isUser ? " mine" : ""),
|
|
1400
1900
|
style: isUser || !role ? void 0 : { border: "2px solid " + (role.color || "#888") },
|
|
1401
1901
|
children: name.slice(0, 1)
|
|
1402
1902
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1403
1903
|
className: "dsgc-msgbody",
|
|
1404
|
-
children: [
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1904
|
+
children: [
|
|
1905
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1906
|
+
className: "dsgc-msghead",
|
|
1907
|
+
children: [
|
|
1908
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1909
|
+
className: "dsgc-msgname",
|
|
1910
|
+
children: name
|
|
1911
|
+
}),
|
|
1912
|
+
m.model ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1913
|
+
className: "dsgc-msgmodel",
|
|
1914
|
+
title: m.model,
|
|
1915
|
+
children: m.model
|
|
1916
|
+
}) : null,
|
|
1917
|
+
m.ts ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1918
|
+
className: "dsgc-msgtime",
|
|
1919
|
+
children: fmtTime(m.ts)
|
|
1920
|
+
}) : null
|
|
1921
|
+
]
|
|
1922
|
+
}),
|
|
1923
|
+
isFail ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailCard, { raw: m.text }) : isUser ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1924
|
+
className: "dsgc-msgtext",
|
|
1925
|
+
children: m.text
|
|
1926
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1927
|
+
className: "dsgc-msgtext",
|
|
1928
|
+
children: [
|
|
1929
|
+
m.reasoning ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThinkRow, { text: m.reasoning }) : null,
|
|
1930
|
+
(Array.isArray(m.toolCalls) ? m.toolCalls : []).map((c, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolRow, { c }, "tc" + i)),
|
|
1931
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
|
|
1932
|
+
text: m.text || "(无内容)",
|
|
1933
|
+
labels: MD_LABELS
|
|
1934
|
+
})
|
|
1935
|
+
]
|
|
1936
|
+
}),
|
|
1937
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MsgActions, {
|
|
1938
|
+
copyText,
|
|
1939
|
+
always: isFail,
|
|
1940
|
+
onRetry: isFail && onRetry ? () => {
|
|
1941
|
+
onRetry(m.id);
|
|
1942
|
+
} : void 0,
|
|
1943
|
+
retryDisabled: busy || !role || !role.enabled,
|
|
1944
|
+
retryTitle
|
|
1945
|
+
})
|
|
1946
|
+
]
|
|
1435
1947
|
})]
|
|
1436
1948
|
});
|
|
1437
1949
|
}
|
|
1438
1950
|
/** 渲染相关字段的值比较(消息不可变;角色仅名/色参与渲染)。 */
|
|
1439
1951
|
function bubblePropsEqual(a, b) {
|
|
1952
|
+
if (a.busy !== b.busy || a.onRetry == null !== (b.onRetry == null)) return false;
|
|
1440
1953
|
const x = a.m;
|
|
1441
1954
|
const y = b.m;
|
|
1442
1955
|
if (x !== y) {
|
|
1443
|
-
if (x.id !== y.id || x.speaker !== y.speaker || x.text !== y.text || x.reasoning !== y.reasoning || x.model !== y.model || x.error !== y.error || x.ts !== y.ts) return false;
|
|
1956
|
+
if (x.id !== y.id || x.speaker !== y.speaker || x.text !== y.text || x.reasoning !== y.reasoning || x.model !== y.model || x.error !== y.error || x.failedRoleId !== y.failedRoleId || x.ts !== y.ts) return false;
|
|
1444
1957
|
const ta = Array.isArray(x.toolCalls) ? x.toolCalls : [];
|
|
1445
1958
|
const tb = Array.isArray(y.toolCalls) ? y.toolCalls : [];
|
|
1446
1959
|
if (ta.length !== tb.length) return false;
|
|
@@ -1452,151 +1965,223 @@ window.__ModuleLoader__.load({
|
|
|
1452
1965
|
}
|
|
1453
1966
|
const ra = a.role;
|
|
1454
1967
|
const rb = b.role;
|
|
1455
|
-
return (ra ? ra.name + "\0" + (ra.color || "") : "") === (rb ? rb.name + "\0" + (rb.color || "") : "");
|
|
1968
|
+
return (ra ? ra.id + "\0" + ra.name + "\0" + (ra.color || "") + "\0" + (ra.enabled ? "1" : "0") : "") === (rb ? rb.id + "\0" + rb.name + "\0" + (rb.color || "") + "\0" + (rb.enabled ? "1" : "0") : "");
|
|
1456
1969
|
}
|
|
1457
1970
|
const Bubble = (0, react.memo)(BubbleInner, bubblePropsEqual);
|
|
1458
1971
|
//#endregion
|
|
1972
|
+
//#region src/client/components/ConstraintList.tsx
|
|
1973
|
+
/**
|
|
1974
|
+
* 会话流折点处:只读结论备忘卡(超过 4 条默认露 3 条)。
|
|
1975
|
+
* @module dsh-group-chat/client/components
|
|
1976
|
+
*/
|
|
1977
|
+
const KIND_LABEL = {
|
|
1978
|
+
decided: "已定",
|
|
1979
|
+
rejected: "否决",
|
|
1980
|
+
open: "未决"
|
|
1981
|
+
};
|
|
1982
|
+
function ConstraintRow(props) {
|
|
1983
|
+
const { item } = props;
|
|
1984
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1985
|
+
className: "dsgc-constraint",
|
|
1986
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1987
|
+
className: "dsgc-ckind " + item.kind,
|
|
1988
|
+
children: KIND_LABEL[item.kind]
|
|
1989
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1990
|
+
className: "dsgc-ctext",
|
|
1991
|
+
children: item.text
|
|
1992
|
+
})]
|
|
1993
|
+
});
|
|
1994
|
+
}
|
|
1995
|
+
function ConstraintList(props) {
|
|
1996
|
+
const { items } = props;
|
|
1997
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
1998
|
+
if (!items.length) return null;
|
|
1999
|
+
const overflow = items.length > 4;
|
|
2000
|
+
const head = overflow ? items.slice(0, 3) : items;
|
|
2001
|
+
const extra = overflow ? items.slice(3) : [];
|
|
2002
|
+
const rest = items.length - 3;
|
|
2003
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2004
|
+
className: "dsgc-constraints",
|
|
2005
|
+
"aria-labelledby": "dsgc-constraints-title",
|
|
2006
|
+
children: [
|
|
2007
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2008
|
+
className: "dsgc-chead",
|
|
2009
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
2010
|
+
id: "dsgc-constraints-title",
|
|
2011
|
+
className: "dsgc-ctitle",
|
|
2012
|
+
children: "结论备忘"
|
|
2013
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2014
|
+
className: "dsgc-cdesc",
|
|
2015
|
+
children: "窗口外消息折成的已定 / 否决 / 未决,供后续角色接着用。"
|
|
2016
|
+
})]
|
|
2017
|
+
}),
|
|
2018
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ClipWell, {
|
|
2019
|
+
maxHeight: 160,
|
|
2020
|
+
className: "dsgc-clist",
|
|
2021
|
+
watch: open,
|
|
2022
|
+
children: [head.map((c, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConstraintRow, { item: c }, i)), overflow ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fold, {
|
|
2023
|
+
open,
|
|
2024
|
+
children: extra.map((c, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConstraintRow, { item: c }, i + 3))
|
|
2025
|
+
}) : null]
|
|
2026
|
+
}),
|
|
2027
|
+
overflow ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2028
|
+
type: "button",
|
|
2029
|
+
className: "dsgc-cmore",
|
|
2030
|
+
onClick: () => {
|
|
2031
|
+
setOpen(!open);
|
|
2032
|
+
},
|
|
2033
|
+
"aria-expanded": open,
|
|
2034
|
+
children: open ? "收起" : "还有 " + rest + " 条约束"
|
|
2035
|
+
}) : null
|
|
2036
|
+
]
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
//#endregion
|
|
1459
2040
|
//#region src/client/components/MessageFlow.tsx
|
|
1460
2041
|
function MessageFlow(props) {
|
|
1461
|
-
const { snap, sess, busyNow, msgById, action } = props;
|
|
2042
|
+
const { snap, sess, busyNow, msgById, action, onRetrySpeak } = props;
|
|
1462
2043
|
const bubbles = [];
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
2044
|
+
const replaceId = busyNow ? snap.run.replaceMessageId : null;
|
|
2045
|
+
const replaceMsg = replaceId ? msgById[replaceId] : null;
|
|
2046
|
+
const liveRoleId = snap.run.currentRoleId || replaceMsg && (replaceMsg.failedRoleId || replaceMsg.speaker) || null;
|
|
2047
|
+
const lr = busyNow && liveRoleId ? roleById(snap, liveRoleId) : null;
|
|
2048
|
+
const live = lr ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2049
|
+
className: "dsgc-msg live",
|
|
2050
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2051
|
+
className: "dsgc-avatar",
|
|
2052
|
+
style: {
|
|
2053
|
+
border: "2px solid " + (lr.color || "#888"),
|
|
2054
|
+
"--role-color": lr.color || "#888"
|
|
2055
|
+
},
|
|
2056
|
+
children: lr.name.slice(0, 1)
|
|
2057
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2058
|
+
className: "dsgc-msgbody",
|
|
2059
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2060
|
+
className: "dsgc-msghead",
|
|
2061
|
+
children: [
|
|
2062
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2063
|
+
className: "dsgc-msgname",
|
|
2064
|
+
children: lr.name
|
|
2065
|
+
}),
|
|
2066
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2067
|
+
className: "dsgc-msgmodel",
|
|
2068
|
+
children: [
|
|
2069
|
+
lr.provider,
|
|
2070
|
+
" / ",
|
|
2071
|
+
lr.model
|
|
2072
|
+
]
|
|
2073
|
+
}),
|
|
2074
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2075
|
+
className: "dsgc-msgtime dsgc-typing",
|
|
2076
|
+
children: "深度求索..."
|
|
2077
|
+
})
|
|
2078
|
+
]
|
|
1483
2079
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1484
|
-
className: "dsgc-
|
|
1485
|
-
children: [
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
snap.run.partial ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
|
|
1513
|
-
text: snap.run.partial,
|
|
1514
|
-
streaming: true,
|
|
1515
|
-
labels: MD_LABELS
|
|
1516
|
-
}) : null,
|
|
1517
|
-
!snap.run.partial && !snap.run.partialReasoning ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1518
|
-
className: "dsgc-pending",
|
|
1519
|
-
children: [
|
|
1520
|
-
Icon(_deepseek_ai_dsh_client_ui_primitives.IconThinkOutline14, 14),
|
|
1521
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "思考中" }),
|
|
1522
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1523
|
-
className: "dsgc-pendingdots",
|
|
1524
|
-
"aria-hidden": "true",
|
|
1525
|
-
children: [
|
|
1526
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {}),
|
|
1527
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {}),
|
|
1528
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {})
|
|
1529
|
-
]
|
|
1530
|
-
})
|
|
1531
|
-
]
|
|
1532
|
-
}) : null
|
|
1533
|
-
]
|
|
1534
|
-
})]
|
|
2080
|
+
className: "dsgc-msgtext live",
|
|
2081
|
+
children: [
|
|
2082
|
+
snap.run.partialReasoning ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThinkRow, {
|
|
2083
|
+
text: snap.run.partialReasoning,
|
|
2084
|
+
running: true
|
|
2085
|
+
}) : null,
|
|
2086
|
+
snap.run.partial ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
|
|
2087
|
+
text: snap.run.partial,
|
|
2088
|
+
streaming: true,
|
|
2089
|
+
labels: MD_LABELS
|
|
2090
|
+
}) : null,
|
|
2091
|
+
!snap.run.partial && !snap.run.partialReasoning ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2092
|
+
className: "dsgc-pending",
|
|
2093
|
+
children: [
|
|
2094
|
+
Icon(_deepseek_ai_dsh_client_ui_primitives.IconThinkOutline14, 14),
|
|
2095
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "思考中" }),
|
|
2096
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2097
|
+
className: "dsgc-pendingdots",
|
|
2098
|
+
"aria-hidden": "true",
|
|
2099
|
+
children: [
|
|
2100
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {}),
|
|
2101
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {}),
|
|
2102
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {})
|
|
2103
|
+
]
|
|
2104
|
+
})
|
|
2105
|
+
]
|
|
2106
|
+
}) : null
|
|
2107
|
+
]
|
|
1535
2108
|
})]
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
2109
|
+
})]
|
|
2110
|
+
}, "__live") : null;
|
|
1538
2111
|
const pc = busyNow && snap.run.pendingConfirm && sess && snap.run.sessionId === sess.id ? snap.run.pendingConfirm : null;
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
2112
|
+
const confirm = pc ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2113
|
+
className: "dsgc-confirm",
|
|
2114
|
+
children: [
|
|
2115
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2116
|
+
className: "dsgc-confirmtitle",
|
|
2117
|
+
children: [Icon(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, 14), (lr ? lr.name : "角色") + " 请求执行命令(工作区内)"]
|
|
2118
|
+
}),
|
|
2119
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2120
|
+
className: "dsgc-confirmcmd",
|
|
2121
|
+
children: String(pc.args && pc.args.command || "")
|
|
2122
|
+
}),
|
|
2123
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2124
|
+
className: "dsgc-hint",
|
|
2125
|
+
children: "允许后将在工作区目录执行;拒绝后角色将继续纯文本讨论"
|
|
2126
|
+
}),
|
|
2127
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2128
|
+
className: "dsgc-confirmops",
|
|
2129
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2130
|
+
variant: "primary",
|
|
2131
|
+
size: "sm",
|
|
2132
|
+
onClick: () => {
|
|
2133
|
+
action({
|
|
2134
|
+
kind: "confirmCommand",
|
|
2135
|
+
toolCallId: pc.toolCallId,
|
|
2136
|
+
allow: true
|
|
2137
|
+
});
|
|
2138
|
+
},
|
|
2139
|
+
children: "允许"
|
|
2140
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2141
|
+
variant: "outline",
|
|
2142
|
+
size: "sm",
|
|
2143
|
+
onClick: () => {
|
|
2144
|
+
action({
|
|
2145
|
+
kind: "confirmCommand",
|
|
2146
|
+
toolCallId: pc.toolCallId,
|
|
2147
|
+
allow: false
|
|
2148
|
+
});
|
|
2149
|
+
},
|
|
2150
|
+
children: "拒绝"
|
|
2151
|
+
})]
|
|
2152
|
+
})
|
|
2153
|
+
]
|
|
2154
|
+
}, "__confirm") : null;
|
|
2155
|
+
let livePlaced = false;
|
|
2156
|
+
if (sess) {
|
|
2157
|
+
const ids = sess.messageIds;
|
|
2158
|
+
const memo = sess.constraints && sess.constraints.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConstraintList, { items: sess.constraints }, sess.id + "-constraints") : null;
|
|
2159
|
+
const foldAt = memo && ids.length > 40 ? ids.length - 40 : 0;
|
|
2160
|
+
if (memo && foldAt === 0) bubbles.push(memo);
|
|
2161
|
+
for (let i = 0; i < ids.length; i++) {
|
|
2162
|
+
if (memo && foldAt > 0 && i === foldAt) bubbles.push(memo);
|
|
2163
|
+
const m = msgById[ids[i]];
|
|
2164
|
+
if (!m) continue;
|
|
2165
|
+
const role = resolveFailedRole(m, snap.roles.filter((r) => r.groupId === sess.groupId)) || (m.speaker !== "user" && m.speaker !== "system" ? roleById(snap, m.speaker) : null);
|
|
2166
|
+
if (replaceId && m.id === replaceId) {
|
|
2167
|
+
if (live) bubbles.push(live);
|
|
2168
|
+
if (confirm) bubbles.push(confirm);
|
|
2169
|
+
livePlaced = true;
|
|
2170
|
+
continue;
|
|
2171
|
+
}
|
|
2172
|
+
bubbles.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bubble, {
|
|
2173
|
+
m,
|
|
2174
|
+
role,
|
|
2175
|
+
busy: !!snap.run.running,
|
|
2176
|
+
onRetry: onRetrySpeak
|
|
2177
|
+
}, m.id));
|
|
2178
|
+
}
|
|
1584
2179
|
}
|
|
2180
|
+
if (live && !livePlaced) bubbles.push(live);
|
|
2181
|
+
if (confirm && !livePlaced) bubbles.push(confirm);
|
|
1585
2182
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: bubbles });
|
|
1586
2183
|
}
|
|
1587
2184
|
//#endregion
|
|
1588
|
-
//#region src/core/types.ts
|
|
1589
|
-
/** 全部合法档位(展示顺序)。 */
|
|
1590
|
-
const PERMISSION_TIERS = [
|
|
1591
|
-
"view_only",
|
|
1592
|
-
"workspace_write",
|
|
1593
|
-
"full_access"
|
|
1594
|
-
];
|
|
1595
|
-
/** 档位安全化:合法字符串原样,其余 undefined。 */
|
|
1596
|
-
function asPermissionTier(value) {
|
|
1597
|
-
return typeof value === "string" && PERMISSION_TIERS.includes(value) ? value : void 0;
|
|
1598
|
-
}
|
|
1599
|
-
//#endregion
|
|
1600
2185
|
//#region src/client/components/PermissionSelect.tsx
|
|
1601
2186
|
/**
|
|
1602
2187
|
* 群组权限档位选择器(对齐主会话 composer 左下角的 /permission 选择器):
|
|
@@ -1790,6 +2375,8 @@ window.__ModuleLoader__.load({
|
|
|
1790
2375
|
}
|
|
1791
2376
|
//#endregion
|
|
1792
2377
|
//#region src/client/components/Composer.tsx
|
|
2378
|
+
/** 轮数控件 hover:宿主 Tooltip 三行说明(pre-line)。 */
|
|
2379
|
+
const ROUNDS_HINT = "一轮 = 参与角色各说一次。\n要他们自己互相反驳、你不插话时再加轮。\n要边看边插话,就留 1,再点发送。";
|
|
1793
2380
|
function Composer(props) {
|
|
1794
2381
|
const { snap, sess, group, enabledRoles, participants, mentionedRoles, busyNow, input, mention, mentionCandidates, mentionIdxC, fileCandidates, fileSearchError, fileSearchLoading, rounds, err, atBottom, bubblesLength, inputRef, scrollRef, setMentionIdx, setRounds, togglePart, onInputCE, onInputKeyDown, onPasteCE, onDropCE, onDragOverCE, insertChip, insertFileChip, sendMsg, stopRun, mutate, setMention } = props;
|
|
1795
2382
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1819,26 +2406,29 @@ window.__ModuleLoader__.load({
|
|
|
1819
2406
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1820
2407
|
className: "dsgc-partslabel",
|
|
1821
2408
|
children: "参与角色"
|
|
1822
|
-
}),
|
|
1823
|
-
className: "dsgc-
|
|
1824
|
-
children:
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
2409
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2410
|
+
className: "dsgc-partlist",
|
|
2411
|
+
children: mentionedRoles.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2412
|
+
className: "dsgc-partslabel",
|
|
2413
|
+
children: [
|
|
2414
|
+
"已 @ ",
|
|
2415
|
+
mentionedRoles.map((r) => r.name).join("、"),
|
|
2416
|
+
"(本轮仅被点名成员发言)"
|
|
2417
|
+
]
|
|
2418
|
+
}) : enabledRoles.length ? enabledRoles.map((r) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2419
|
+
type: "button",
|
|
2420
|
+
className: "dsgc-partchip" + (participants.includes(r.id) ? " on" : ""),
|
|
2421
|
+
onClick: () => togglePart(r.id),
|
|
2422
|
+
disabled: busyNow,
|
|
2423
|
+
title: busyNow ? "对话进行中,暂停调整" : "点击切换本轮是否参与",
|
|
2424
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2425
|
+
className: "dsgc-chipdot",
|
|
2426
|
+
style: { background: r.color || "#888" }
|
|
2427
|
+
}), r.name]
|
|
2428
|
+
}, r.id)) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2429
|
+
className: "dsgc-hint",
|
|
2430
|
+
children: "还没有启用的角色,请在右侧添加"
|
|
2431
|
+
})
|
|
1842
2432
|
})]
|
|
1843
2433
|
}),
|
|
1844
2434
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1970,9 +2560,12 @@ window.__ModuleLoader__.load({
|
|
|
1970
2560
|
}
|
|
1971
2561
|
}),
|
|
1972
2562
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
|
|
1973
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
2563
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(HoverTip, {
|
|
2564
|
+
label: ROUNDS_HINT,
|
|
2565
|
+
side: "top",
|
|
2566
|
+
delayMs: 500,
|
|
2567
|
+
maxWidth: 280,
|
|
1974
2568
|
className: "dsgc-rounds",
|
|
1975
|
-
title: "自由讨论的轮数(1–10):一轮 = 全体参与角色按顺序各发言一次",
|
|
1976
2569
|
children: [
|
|
1977
2570
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1978
2571
|
type: "button",
|
|
@@ -1986,7 +2579,6 @@ window.__ModuleLoader__.load({
|
|
|
1986
2579
|
}),
|
|
1987
2580
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1988
2581
|
className: "dsgc-roundnum",
|
|
1989
|
-
title: "轮数",
|
|
1990
2582
|
children: rounds
|
|
1991
2583
|
}),
|
|
1992
2584
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -2029,7 +2621,7 @@ window.__ModuleLoader__.load({
|
|
|
2029
2621
|
//#endregion
|
|
2030
2622
|
//#region src/client/components/ChatPanel.tsx
|
|
2031
2623
|
function ChatPanel(props) {
|
|
2032
|
-
const { snap, sess, group, enabledRoles, participants, mentionedRoles, busyNow, input, mention, mentionCandidates, mentionIdxC, fileCandidates, fileSearchError, fileSearchLoading, rounds, err, atBottom, topicDraft, msgById, navOpen, asideOpen, inputRef, scrollRef, setTopicDraft, setConfirmClear, setMentionIdx, setRounds, setNavOpen, setAsideOpen, togglePart, onMsgsScroll, onInputCE, onInputKeyDown, onPasteCE, onDropCE, onDragOverCE, insertChip, insertFileChip, sendMsg, stopRun, action, mutate, setMention } = props;
|
|
2624
|
+
const { snap, sess, group, enabledRoles, participants, mentionedRoles, busyNow, input, mention, mentionCandidates, mentionIdxC, fileCandidates, fileSearchError, fileSearchLoading, rounds, err, atBottom, topicDraft, msgById, navOpen, asideOpen, inputRef, scrollRef, setTopicDraft, setConfirmClear, setMentionIdx, setRounds, setNavOpen, setAsideOpen, togglePart, onMsgsScroll, onInputCE, onInputKeyDown, onPasteCE, onDropCE, onDragOverCE, insertChip, insertFileChip, sendMsg, stopRun, action, mutate, setMention, onRetrySpeak } = props;
|
|
2033
2625
|
const commitTopic = () => {
|
|
2034
2626
|
if (topicDraft !== null && sess && topicDraft !== sess.topic) mutate({
|
|
2035
2627
|
op: "setTopic",
|
|
@@ -2043,7 +2635,8 @@ window.__ModuleLoader__.load({
|
|
|
2043
2635
|
sess,
|
|
2044
2636
|
busyNow,
|
|
2045
2637
|
msgById,
|
|
2046
|
-
action
|
|
2638
|
+
action,
|
|
2639
|
+
onRetrySpeak
|
|
2047
2640
|
});
|
|
2048
2641
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2049
2642
|
className: "dsgc-chat",
|
|
@@ -2070,37 +2663,40 @@ window.__ModuleLoader__.load({
|
|
|
2070
2663
|
},
|
|
2071
2664
|
children: Icon(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, 16)
|
|
2072
2665
|
}),
|
|
2073
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2666
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2074
2667
|
className: "dsgc-chathead",
|
|
2075
|
-
children:
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2668
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2669
|
+
className: "dsgc-chathead-row",
|
|
2670
|
+
children: [
|
|
2671
|
+
sess ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2672
|
+
className: "dsgc-sess-title",
|
|
2673
|
+
title: "当前会话:" + sess.name,
|
|
2674
|
+
children: sess.name
|
|
2675
|
+
}) : null,
|
|
2676
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2677
|
+
className: "dsgc-topic",
|
|
2678
|
+
value: topicDraft === null ? sess ? sess.topic : "" : topicDraft,
|
|
2679
|
+
placeholder: "设置本会话主题(可选)…",
|
|
2680
|
+
onChange: (e) => {
|
|
2681
|
+
setTopicDraft(e.target.value);
|
|
2682
|
+
},
|
|
2683
|
+
onBlur: commitTopic,
|
|
2684
|
+
onKeyDown: (e) => {
|
|
2685
|
+
if (e.key === "Enter") e.target.blur();
|
|
2686
|
+
}
|
|
2687
|
+
}),
|
|
2688
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2689
|
+
variant: "ghost",
|
|
2690
|
+
size: "sm",
|
|
2691
|
+
className: "dsgc-clearbtn",
|
|
2692
|
+
title: "清空当前会话的消息记录",
|
|
2693
|
+
onClick: () => {
|
|
2694
|
+
if (sess) setConfirmClear(true);
|
|
2695
|
+
},
|
|
2696
|
+
children: "清空"
|
|
2697
|
+
})
|
|
2698
|
+
]
|
|
2699
|
+
})
|
|
2104
2700
|
}),
|
|
2105
2701
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2106
2702
|
className: "dsgc-msgs",
|
|
@@ -2161,6 +2757,41 @@ window.__ModuleLoader__.load({
|
|
|
2161
2757
|
});
|
|
2162
2758
|
}
|
|
2163
2759
|
//#endregion
|
|
2760
|
+
//#region src/client/lib/composer-draft.ts
|
|
2761
|
+
const LIMIT = 32;
|
|
2762
|
+
const drafts = /* @__PURE__ */ new Map();
|
|
2763
|
+
const empty = () => ({
|
|
2764
|
+
html: "",
|
|
2765
|
+
text: ""
|
|
2766
|
+
});
|
|
2767
|
+
/** 读指定会话草稿;命中则提到 LRU 最近端。 */
|
|
2768
|
+
function readComposerDraft(sessionId) {
|
|
2769
|
+
if (!sessionId) return empty();
|
|
2770
|
+
const hit = drafts.get(sessionId);
|
|
2771
|
+
if (!hit) return empty();
|
|
2772
|
+
drafts.delete(sessionId);
|
|
2773
|
+
drafts.set(sessionId, hit);
|
|
2774
|
+
return hit;
|
|
2775
|
+
}
|
|
2776
|
+
/** 写入;空草稿删槽。超出上限淘汰最旧槽。 */
|
|
2777
|
+
function writeComposerDraft(sessionId, html, text) {
|
|
2778
|
+
if (!sessionId) return;
|
|
2779
|
+
drafts.delete(sessionId);
|
|
2780
|
+
if (!html && !text) return;
|
|
2781
|
+
drafts.set(sessionId, {
|
|
2782
|
+
html,
|
|
2783
|
+
text
|
|
2784
|
+
});
|
|
2785
|
+
if (drafts.size > LIMIT) {
|
|
2786
|
+
const oldest = drafts.keys().next().value;
|
|
2787
|
+
if (oldest !== void 0) drafts.delete(oldest);
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
/** 发送成功或明确丢弃时摘槽。 */
|
|
2791
|
+
function clearComposerDraft(sessionId) {
|
|
2792
|
+
if (sessionId) drafts.delete(sessionId);
|
|
2793
|
+
}
|
|
2794
|
+
//#endregion
|
|
2164
2795
|
//#region src/client/hooks/useGroupChatState.ts
|
|
2165
2796
|
/**
|
|
2166
2797
|
* 群聊面板核心状态管理 hook
|
|
@@ -2168,8 +2799,8 @@ window.__ModuleLoader__.load({
|
|
|
2168
2799
|
*/
|
|
2169
2800
|
/**
|
|
2170
2801
|
* 面板切换缓存(模块级,跨挂载存活):main 为 keyed 槽,主会话⇄群聊切换会
|
|
2171
|
-
*
|
|
2172
|
-
*
|
|
2802
|
+
* 整体卸载重挂面板组件;缓存最近快照、选中项与按会话分槽的未发送草稿,
|
|
2803
|
+
* 让重挂载瞬时恢复上次视图。挂载后的 state 拉取 / SSE 首帧再行校正。
|
|
2173
2804
|
*/
|
|
2174
2805
|
let cachedSnap = null;
|
|
2175
2806
|
let cachedGid = null;
|
|
@@ -2212,7 +2843,7 @@ window.__ModuleLoader__.load({
|
|
|
2212
2843
|
const [fileBrowser, setFileBrowser] = (0, react.useState)(null);
|
|
2213
2844
|
const [partsSel, setPartsSel] = (0, react.useState)(null);
|
|
2214
2845
|
const [rounds, setRounds] = (0, react.useState)(1);
|
|
2215
|
-
const [input, setInput] = (0, react.useState)(
|
|
2846
|
+
const [input, setInput] = (0, react.useState)(() => readComposerDraft(cachedSid).text);
|
|
2216
2847
|
const [mention, setMention] = (0, react.useState)(null);
|
|
2217
2848
|
const [mentionIdx, setMentionIdx] = (0, react.useState)(0);
|
|
2218
2849
|
const [err, setErr] = (0, react.useState)("");
|
|
@@ -2460,12 +3091,16 @@ window.__ModuleLoader__.load({
|
|
|
2460
3091
|
}, [input, inputRef]);
|
|
2461
3092
|
return { onMsgsScroll };
|
|
2462
3093
|
}
|
|
2463
|
-
function useComposerInput(inputRef, setInput, setMention, setMentionIdx) {
|
|
2464
|
-
|
|
3094
|
+
function useComposerInput(inputRef, setInput, setMention, setMentionIdx, sessionId) {
|
|
3095
|
+
const sessionIdRef = (0, react.useRef)(sessionId);
|
|
3096
|
+
sessionIdRef.current = sessionId;
|
|
3097
|
+
/** 从 DOM 同步 input 状态(序列化),并写入当前会话草稿槽。 */
|
|
2465
3098
|
const syncFromDOM = (0, react.useCallback)(() => {
|
|
2466
3099
|
const el = inputRef.current;
|
|
2467
3100
|
if (!el) return;
|
|
2468
|
-
|
|
3101
|
+
const text = serializeInput(el);
|
|
3102
|
+
setInput(text);
|
|
3103
|
+
writeComposerDraft(sessionIdRef.current, el.innerHTML, text);
|
|
2469
3104
|
}, [inputRef, setInput]);
|
|
2470
3105
|
return {
|
|
2471
3106
|
syncFromDOM,
|
|
@@ -2493,6 +3128,35 @@ window.__ModuleLoader__.load({
|
|
|
2493
3128
|
}, [])
|
|
2494
3129
|
};
|
|
2495
3130
|
}
|
|
3131
|
+
/**
|
|
3132
|
+
* 按会话恢复草稿:切会话时先把当前 HTML 写入旧槽,再灌入新槽;
|
|
3133
|
+
* 面板重挂载(主会话⇄群聊)时 editor 是新节点,从模块缓存灌回。
|
|
3134
|
+
*/
|
|
3135
|
+
function useComposerDraft(sessionId, inputRef, setInput, setMention) {
|
|
3136
|
+
const prevIdRef = (0, react.useRef)(null);
|
|
3137
|
+
(0, react.useLayoutEffect)(() => {
|
|
3138
|
+
const el = inputRef.current;
|
|
3139
|
+
const prev = prevIdRef.current;
|
|
3140
|
+
const next = sessionId || null;
|
|
3141
|
+
if (el && prev && prev !== next) writeComposerDraft(prev, el.innerHTML, serializeInput(el));
|
|
3142
|
+
prevIdRef.current = next;
|
|
3143
|
+
if (!next) return;
|
|
3144
|
+
const draft = readComposerDraft(next);
|
|
3145
|
+
if (el) el.innerHTML = draft.html;
|
|
3146
|
+
setInput(draft.text);
|
|
3147
|
+
setMention(null);
|
|
3148
|
+
return () => {
|
|
3149
|
+
const node = inputRef.current;
|
|
3150
|
+
const id = prevIdRef.current;
|
|
3151
|
+
if (node && id) writeComposerDraft(id, node.innerHTML, serializeInput(node));
|
|
3152
|
+
};
|
|
3153
|
+
}, [
|
|
3154
|
+
sessionId,
|
|
3155
|
+
inputRef,
|
|
3156
|
+
setInput,
|
|
3157
|
+
setMention
|
|
3158
|
+
]);
|
|
3159
|
+
}
|
|
2496
3160
|
function useMentionChip(inputRef, setMention, setMentionIdx, syncFromDOM) {
|
|
2497
3161
|
return {
|
|
2498
3162
|
insertChip: (0, react.useCallback)((role) => {
|
|
@@ -2802,11 +3466,12 @@ window.__ModuleLoader__.load({
|
|
|
2802
3466
|
const { snap, gid, setGid, sid, setSid, search, setSearch, collapsedGroups, setCollapsedGroups, renameDraft, setRenameDraft, confirmDel, setConfirmDel, confirmClear, setConfirmClear, roleDraft, setRoleDraft, roleFormError, setRoleFormError, models, setModels, modelsError, setModelsError, fileBrowser, setFileBrowser, wsDraft, setWsDraft, partsSel, setPartsSel, rounds, setRounds, input, setInput, err, setErr, topicDraft, setTopicDraft, mention, setMention, mentionIdx, setMentionIdx, asideOpen, setAsideOpen, navOpen, setNavOpen, atBottom, setAtBottom, inputRef, sendingRef, scrollRef, action, mutate } = useGroupChatState();
|
|
2803
3467
|
const [toast, setToast] = (0, react.useState)(null);
|
|
2804
3468
|
const { onMsgsScroll } = useComposerEffects(inputRef, scrollRef, input, atBottom, snap);
|
|
2805
|
-
const { syncFromDOM, onInputCE, onPasteCE, onDragOverCE, onDropCE } = useComposerInput(inputRef, setInput, setMention, setMentionIdx);
|
|
2806
3469
|
let group = snap && gid ? groupById(snap, gid) : null;
|
|
2807
3470
|
if (!group && snap && snap.groups.length) group = snap.groups[0];
|
|
2808
3471
|
let sess = snap && sid ? sessById(snap, sid) : null;
|
|
2809
3472
|
if ((!sess || sess.groupId !== group?.id) && group && snap) sess = group.sessionIds.length ? sessById(snap, group.sessionIds[group.sessionIds.length - 1]) : null;
|
|
3473
|
+
const { syncFromDOM, onInputCE, onPasteCE, onDragOverCE, onDropCE } = useComposerInput(inputRef, setInput, setMention, setMentionIdx, sess ? sess.id : null);
|
|
3474
|
+
useComposerDraft(sess ? sess.id : null, inputRef, setInput, setMention);
|
|
2810
3475
|
const enabledRoles = group ? group.roleIds.map((id) => roleById(snap, id)).filter((r) => !!r && r.enabled) : [];
|
|
2811
3476
|
const participants = partsSel || enabledRoles.map((r) => r.id);
|
|
2812
3477
|
const busyNow = !!(sess && snap && snap.run.running && snap.run.sessionId === sess.id);
|
|
@@ -2843,6 +3508,7 @@ window.__ModuleLoader__.load({
|
|
|
2843
3508
|
setMention(null);
|
|
2844
3509
|
setErr("");
|
|
2845
3510
|
setAtBottom(true);
|
|
3511
|
+
clearComposerDraft(sess.id);
|
|
2846
3512
|
syncFromDOM();
|
|
2847
3513
|
}
|
|
2848
3514
|
} finally {
|
|
@@ -2885,6 +3551,25 @@ window.__ModuleLoader__.load({
|
|
|
2885
3551
|
const has = participants.includes(rid);
|
|
2886
3552
|
setPartsSel(has ? participants.filter((x) => x !== rid) : participants.concat([rid]));
|
|
2887
3553
|
};
|
|
3554
|
+
const retrySpeak = async (messageId) => {
|
|
3555
|
+
if (!sess) return;
|
|
3556
|
+
if (busyNow || snap && snap.run.running) {
|
|
3557
|
+
setToast({
|
|
3558
|
+
text: "已有对话进行中,请先停止",
|
|
3559
|
+
seq: Date.now()
|
|
3560
|
+
});
|
|
3561
|
+
return;
|
|
3562
|
+
}
|
|
3563
|
+
const res = await action({
|
|
3564
|
+
kind: "retrySpeak",
|
|
3565
|
+
sessionId: sess.id,
|
|
3566
|
+
messageId
|
|
3567
|
+
});
|
|
3568
|
+
if (res && !res.ok && res.error) setToast({
|
|
3569
|
+
text: res.error,
|
|
3570
|
+
seq: Date.now()
|
|
3571
|
+
});
|
|
3572
|
+
};
|
|
2888
3573
|
const doClear = async () => {
|
|
2889
3574
|
if (!sess) return;
|
|
2890
3575
|
if (busyNow) {
|
|
@@ -3063,7 +3748,10 @@ window.__ModuleLoader__.load({
|
|
|
3063
3748
|
stopRun,
|
|
3064
3749
|
action,
|
|
3065
3750
|
mutate,
|
|
3066
|
-
setMention
|
|
3751
|
+
setMention,
|
|
3752
|
+
onRetrySpeak: (messageId) => {
|
|
3753
|
+
retrySpeak(messageId);
|
|
3754
|
+
}
|
|
3067
3755
|
}),
|
|
3068
3756
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AsidePanel, {
|
|
3069
3757
|
snap,
|
|
@@ -3105,7 +3793,7 @@ window.__ModuleLoader__.load({
|
|
|
3105
3793
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
3106
3794
|
className: "dsgc-clearnotes",
|
|
3107
3795
|
children: [
|
|
3108
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: "
|
|
3796
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: "删除内容:本会话的用户消息与角色发言(含思考、工具调用记录)以及本会话结论/约束备忘,确认后立即落盘" }),
|
|
3109
3797
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: "不可恢复:此操作没有回收站,也没有撤销" }),
|
|
3110
3798
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: "不受影响:会话本身与主题、群成员角色、工作区目录、权限档位" })
|
|
3111
3799
|
]
|