newmark-agent 0.5.14 → 0.5.15
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/dist/conversation-utility-host.bundle.cjs +3373 -1900
- package/dist/conversation-utility-host.js +1 -1
- package/dist/core/agent.d.ts +105 -14
- package/dist/core/agent.js +658 -104
- package/dist/core/agentKernel/agent.d.ts +1 -0
- package/dist/core/agentKernel/agent.js +9 -0
- package/dist/core/agentKernelDiagnostics.d.ts +4 -6
- package/dist/core/agentKernelDiagnostics.js +12 -9
- package/dist/core/agentKernelRunner.d.ts +5 -0
- package/dist/core/agentKernelRunner.js +236 -85
- package/dist/core/autoRouter.js +16 -3
- package/dist/core/conversationCommandState.d.ts +17 -0
- package/dist/core/conversationCommandState.js +140 -0
- package/dist/core/conversationKernel.d.ts +25 -4
- package/dist/core/conversationKernel.js +347 -83
- package/dist/core/conversationListEvent.d.ts +6 -0
- package/dist/core/conversationListEvent.js +14 -0
- package/dist/core/electronUtilityAgentClient.d.ts +4 -1
- package/dist/core/electronUtilityAgentClient.js +4 -4
- package/dist/core/electronUtilityRuntimePool.d.ts +8 -2
- package/dist/core/electronUtilityRuntimePool.js +11 -5
- package/dist/core/installUpdate.js +41 -29
- package/dist/core/providerUsageAccounting.d.ts +47 -0
- package/dist/core/providerUsageAccounting.js +71 -0
- package/dist/core/requestContextEstimate.d.ts +18 -0
- package/dist/core/requestContextEstimate.js +54 -0
- package/dist/core/subagent.d.ts +89 -5
- package/dist/core/subagent.js +264 -41
- package/dist/core/subagentCommunication.d.ts +43 -0
- package/dist/core/subagentCommunication.js +167 -0
- package/dist/core/types.d.ts +32 -1
- package/dist/core/utilityAgentProtocol.d.ts +4 -0
- package/dist/core/workEventCoalescer.js +4 -1
- package/dist/core/wslAgentClient.d.ts +18 -5
- package/dist/core/wslAgentClient.js +79 -27
- package/dist/core/wslAgentProtocol.d.ts +7 -0
- package/dist/core/wslAgentRuntimePool.d.ts +8 -2
- package/dist/core/wslAgentRuntimePool.js +19 -6
- package/dist/core/wslRuntimeProcessTree.d.ts +10 -0
- package/dist/core/wslRuntimeProcessTree.js +104 -0
- package/dist/llm/provider.d.ts +5 -12
- package/dist/llm/provider.js +148 -267
- package/dist/main.js +582 -356
- package/dist/preload.js +3 -3
- package/dist/providers/chat-completions.adapter.d.ts +2 -0
- package/dist/providers/chat-completions.adapter.js +84 -36
- package/dist/providers/provider-adapter.d.ts +2 -0
- package/dist/providers/provider-events.d.ts +23 -14
- package/dist/providers/provider-events.js +148 -43
- package/dist/providers/provider-headers.d.ts +7 -3
- package/dist/providers/provider-headers.js +109 -39
- package/dist/providers/provider-request-compat.d.ts +6 -0
- package/dist/providers/provider-request-compat.js +37 -0
- package/dist/providers/responses.adapter.d.ts +2 -0
- package/dist/providers/responses.adapter.js +192 -126
- package/dist/server.d.ts +9 -2
- package/dist/server.js +100 -24
- package/dist/tools/index.js +17 -1
- package/dist/ui/index.html +2374 -616
- package/dist/ui/lucide-sprite.svg +7 -0
- package/dist/ui/startup.html +6 -6
- package/dist/wsl-agent-host.bundle.cjs +3381 -1906
- package/dist/wsl-agent-host.js +9 -4
- package/package.json +23 -4
package/dist/ui/index.html
CHANGED
|
@@ -22,39 +22,39 @@ try {
|
|
|
22
22
|
|
|
23
23
|
/* --- Design Tokens --- */
|
|
24
24
|
:root {
|
|
25
|
-
/*
|
|
26
|
-
--bg: #
|
|
25
|
+
/* Neutral charcoal surfaces; chroma belongs to functional accents. */
|
|
26
|
+
--bg: #101010;
|
|
27
27
|
--app-bg: var(--bg);
|
|
28
|
-
--bg2: #
|
|
29
|
-
--bg3: #
|
|
30
|
-
--surface: #
|
|
31
|
-
--surface2: #
|
|
32
|
-
--surface3: #
|
|
28
|
+
--bg2: #181818;
|
|
29
|
+
--bg3: #222222;
|
|
30
|
+
--surface: #292929;
|
|
31
|
+
--surface2: #303030;
|
|
32
|
+
--surface3: #383838;
|
|
33
33
|
|
|
34
34
|
/* Text hierarchy */
|
|
35
|
-
--text: #
|
|
36
|
-
--text-dim: #
|
|
37
|
-
--text-bright: #
|
|
35
|
+
--text: #cecece;
|
|
36
|
+
--text-dim: #949494;
|
|
37
|
+
--text-bright: #f2f2f2;
|
|
38
38
|
--text-accent: #a0b8ff;
|
|
39
39
|
|
|
40
40
|
/* Native select popups need an opaque palette; Windows does not composite glass behind them. */
|
|
41
41
|
--select-color-scheme: dark;
|
|
42
|
-
--select-popup-bg: #
|
|
43
|
-
--select-popup-text: #
|
|
44
|
-
--select-popup-group: #
|
|
42
|
+
--select-popup-bg: #181818;
|
|
43
|
+
--select-popup-text: #f2f2f2;
|
|
44
|
+
--select-popup-group: #cecece;
|
|
45
45
|
--select-popup-selected-bg: #355fce;
|
|
46
46
|
--select-popup-selected-text: #ffffff;
|
|
47
|
-
--select-popup-disabled: #
|
|
47
|
+
--select-popup-disabled: #949494;
|
|
48
48
|
--control-hover-bg: rgba(255,255,255,0.08);
|
|
49
49
|
|
|
50
50
|
/* Native editor palette */
|
|
51
|
-
--editor-bg: #
|
|
51
|
+
--editor-bg: #141414;
|
|
52
52
|
--editor-gutter-bg: rgba(255,255,255,0.025);
|
|
53
|
-
--editor-ghost-text: rgba(
|
|
53
|
+
--editor-ghost-text: rgba(206,206,206,0.48);
|
|
54
54
|
--editor-caret: #ffffff;
|
|
55
55
|
--editor-selection: rgba(91,120,255,0.35);
|
|
56
|
-
--editor-completion-bg: rgba(
|
|
57
|
-
--editor-comment: #
|
|
56
|
+
--editor-completion-bg: rgba(24,24,24,0.97);
|
|
57
|
+
--editor-comment: #949494;
|
|
58
58
|
--editor-string: #9ecbff;
|
|
59
59
|
--editor-number: #79b8ff;
|
|
60
60
|
--editor-keyword: #f97583;
|
|
@@ -63,14 +63,14 @@ try {
|
|
|
63
63
|
--editor-attr: #ffab70;
|
|
64
64
|
--editor-heading: #79b8ff;
|
|
65
65
|
--editor-code: #9ecbff;
|
|
66
|
-
--editor-punct: #
|
|
67
|
-
--review-bg: rgba(
|
|
66
|
+
--editor-punct: #b0b0b0;
|
|
67
|
+
--review-bg: rgba(24,24,24,.94);
|
|
68
68
|
--review-row-hover: rgba(255,255,255,.035);
|
|
69
69
|
--review-add-bg: rgba(56,212,160,.13);
|
|
70
70
|
--review-add-text: #72e6b5;
|
|
71
71
|
--review-del-bg: rgba(255,68,68,.13);
|
|
72
72
|
--review-del-text: #ff9aa2;
|
|
73
|
-
--notice-bg: rgba(
|
|
73
|
+
--notice-bg: rgba(24,24,24,.96);
|
|
74
74
|
--notice-shadow: 0 10px 36px rgba(0,0,0,.32);
|
|
75
75
|
--notice-error-border: rgba(255,105,120,.55);
|
|
76
76
|
--notice-error-text: #ffb0b8;
|
|
@@ -80,13 +80,13 @@ try {
|
|
|
80
80
|
--accent-glow: rgba(91,120,255,0.3);
|
|
81
81
|
--accent2: #38d4a0;
|
|
82
82
|
--accent2-glow: rgba(56,212,160,0.3);
|
|
83
|
-
--chat-canvas:
|
|
83
|
+
--chat-canvas: var(--app-bg);
|
|
84
84
|
|
|
85
85
|
/* Glass layers */
|
|
86
86
|
--glass-alpha: 0.85;
|
|
87
|
-
--glass-rgb-1:
|
|
88
|
-
--glass-rgb-2:
|
|
89
|
-
--glass-rgb-3:
|
|
87
|
+
--glass-rgb-1: 16 16 16;
|
|
88
|
+
--glass-rgb-2: 24 24 24;
|
|
89
|
+
--glass-rgb-3: 34 34 34;
|
|
90
90
|
--modal-surface: rgb(var(--glass-rgb-2) / max(var(--glass-alpha-2), 0.9));
|
|
91
91
|
--glass-alpha-1: 0.6375;
|
|
92
92
|
--glass-alpha-2: 0.68;
|
|
@@ -98,15 +98,14 @@ try {
|
|
|
98
98
|
--glass-border-2: rgba(255,255,255,0.08);
|
|
99
99
|
--glass-border-3: rgba(255,255,255,0.10);
|
|
100
100
|
|
|
101
|
-
/*
|
|
102
|
-
|
|
103
|
-
inner shadow and saturation boost (saturate(140%) == vibrancy). */
|
|
101
|
+
/* Uniform frosted surfaces. Depth belongs to a narrow optical rim and the
|
|
102
|
+
real backdrop, never to a painted light/dark plane across the surface. */
|
|
104
103
|
--liquid-edge-glow: rgba(255,255,255,0.10);
|
|
105
104
|
--liquid-edge-glow-strong: rgba(255,255,255,0.16);
|
|
106
105
|
--liquid-top-highlight: inset 0 1px 0 rgba(255,255,255,0.10);
|
|
107
106
|
--liquid-inner-shadow: inset 0 -1px 0 rgba(0,0,0,0.22);
|
|
108
107
|
--liquid-outer-shadow: 0 8px 32px rgba(0,0,0,0.45);
|
|
109
|
-
--liquid-refraction:
|
|
108
|
+
--liquid-refraction: rgba(255,255,255,0.025);
|
|
110
109
|
|
|
111
110
|
/* Borders & corners */
|
|
112
111
|
--border: rgba(255,255,255,0.06);
|
|
@@ -117,6 +116,8 @@ try {
|
|
|
117
116
|
--radius-lg: 16px;
|
|
118
117
|
--radius-xl: 24px;
|
|
119
118
|
--radius-full: 9999px;
|
|
119
|
+
/* List shells surround a 34px capsule with 7px padding and a 1px rim. */
|
|
120
|
+
--liquid-list-popup-radius: 25px;
|
|
120
121
|
|
|
121
122
|
/* Marquee gradient */
|
|
122
123
|
--g1: #000000;
|
|
@@ -150,6 +151,33 @@ try {
|
|
|
150
151
|
--liquid-float-refraction-amount: 24px;
|
|
151
152
|
--liquid-float-chromatic-aberration: 1;
|
|
152
153
|
--liquid-float-optical-opacity: 1;
|
|
154
|
+
/* Popup tint is spatially uniform; the shared opacity/blur controls still
|
|
155
|
+
govern the material. Menu selection blocks keep their own flat fill. */
|
|
156
|
+
--liquid-popup-rgb: 28 28 28;
|
|
157
|
+
--liquid-popup-alpha: calc(var(--glass-alpha-2) * 0.82);
|
|
158
|
+
--liquid-popup-surface: rgb(var(--liquid-popup-rgb) / var(--liquid-popup-alpha));
|
|
159
|
+
--liquid-popup-blur: calc(var(--glass-blur-3) * 4);
|
|
160
|
+
--liquid-popup-saturation: 1.08;
|
|
161
|
+
--liquid-popup-rim: rgba(255,255,255,0.12);
|
|
162
|
+
--liquid-popup-inner-rim: rgba(255,255,255,0.045);
|
|
163
|
+
--liquid-popup-shadow: 0 12px 32px rgba(0,0,0,0.24), 0 2px 8px rgba(0,0,0,0.16);
|
|
164
|
+
--liquid-popup-refraction-height: 9px;
|
|
165
|
+
--liquid-popup-refraction-amount: 30px;
|
|
166
|
+
--liquid-popup-chromatic-aberration: 1.3;
|
|
167
|
+
--liquid-popup-light-opacity: 0;
|
|
168
|
+
--liquid-light-x: 50%;
|
|
169
|
+
--liquid-light-y: 50%;
|
|
170
|
+
/* Interaction glow is a physical-size light spot, independent of the
|
|
171
|
+
popup's dimensions. Keep the same CSS radius for small menus and large
|
|
172
|
+
settings dialogs; the carrier surface clips it to the popup shape. */
|
|
173
|
+
--liquid-popup-glow-radius: 20px;
|
|
174
|
+
--liquid-popup-stretch: 0;
|
|
175
|
+
--liquid-popup-squash: 0;
|
|
176
|
+
/* Desktop glass keeps the optical pass readable while limiting the
|
|
177
|
+
expensive backdrop region and avoiding oversized bloom on large dialogs. */
|
|
178
|
+
--liquid-popup-edge-width: 1px;
|
|
179
|
+
--liquid-popup-bloom: 0.16;
|
|
180
|
+
--liquid-popup-chromatic-shift: 1px;
|
|
153
181
|
|
|
154
182
|
/* Transitions */
|
|
155
183
|
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
|
|
@@ -232,7 +260,10 @@ try {
|
|
|
232
260
|
--glass-rgb-2: 255 255 255;
|
|
233
261
|
--glass-rgb-3: 255 255 255;
|
|
234
262
|
--modal-surface: rgba(248,250,255,0.82);
|
|
235
|
-
--
|
|
263
|
+
--liquid-popup-rgb: 248 250 253;
|
|
264
|
+
--liquid-popup-rim: rgba(70,82,105,0.13);
|
|
265
|
+
--liquid-popup-inner-rim: rgba(255,255,255,0.65);
|
|
266
|
+
--liquid-popup-shadow: 0 12px 32px rgba(35,45,65,0.10), 0 2px 8px rgba(35,45,65,0.06);
|
|
236
267
|
--modal-shadow: 0 24px 70px rgba(41,55,92,0.20), inset 0 1px 0 rgba(255,255,255,0.62), inset 0 -1px 0 rgba(46,58,92,0.10);
|
|
237
268
|
--glass-border-1: rgba(0,0,0,0.06);
|
|
238
269
|
--glass-border-2: rgba(0,0,0,0.08);
|
|
@@ -298,8 +329,8 @@ html, body {
|
|
|
298
329
|
align-items: center;
|
|
299
330
|
justify-content: center;
|
|
300
331
|
overflow: hidden;
|
|
301
|
-
background: #
|
|
302
|
-
color: #
|
|
332
|
+
background: #101010;
|
|
333
|
+
color: #f2f2f2;
|
|
303
334
|
font-family: 'Segoe UI', Arial, sans-serif;
|
|
304
335
|
}
|
|
305
336
|
|
|
@@ -326,9 +357,9 @@ html, body {
|
|
|
326
357
|
|
|
327
358
|
.startup-cover-copy { min-width: 0; flex: 1; padding-top: 1px; }
|
|
328
359
|
.startup-cover-title { font-size: 18px; font-weight: 700; }
|
|
329
|
-
.startup-cover-status { margin-top: 7px; color: #
|
|
330
|
-
.startup-cover-detail { margin-top: 4px; color: #
|
|
331
|
-
.startup-cover-bar { height: 3px; margin-top: 12px; border-radius: 99px; background: #
|
|
360
|
+
.startup-cover-status { margin-top: 7px; color: #cecece; font-size: 12px; line-height: 1.45; }
|
|
361
|
+
.startup-cover-detail { margin-top: 4px; color: #949494; font-size: 11px; line-height: 1.4; }
|
|
362
|
+
.startup-cover-bar { height: 3px; margin-top: 12px; border-radius: 99px; background: #303030; overflow: hidden; }
|
|
332
363
|
.startup-cover-bar > i { display: block; height: 100%; width: 38%; border-radius: inherit; background: linear-gradient(90deg, #36d399, #60a5fa); }
|
|
333
364
|
|
|
334
365
|
@keyframes startup-cover-pulse {
|
|
@@ -1204,7 +1235,7 @@ button.left-ws-item {
|
|
|
1204
1235
|
overflow: auto;
|
|
1205
1236
|
padding: 6px;
|
|
1206
1237
|
border: 1px solid var(--glass-border-2);
|
|
1207
|
-
border-radius: var(--radius
|
|
1238
|
+
border-radius: var(--liquid-list-popup-radius);
|
|
1208
1239
|
background: var(--modal-surface);
|
|
1209
1240
|
box-shadow: var(--shadow-lg), var(--liquid-top-highlight), var(--liquid-inner-shadow);
|
|
1210
1241
|
}
|
|
@@ -2992,7 +3023,7 @@ button.todo-item { background: transparent; }
|
|
|
2992
3023
|
.stack-card {
|
|
2993
3024
|
width: calc(100% - 40px);
|
|
2994
3025
|
margin: 0 20px;
|
|
2995
|
-
background: rgba(
|
|
3026
|
+
background: rgba(24,24,24,0.92);
|
|
2996
3027
|
backdrop-filter: blur(var(--glass-blur-2)) saturate(125%);
|
|
2997
3028
|
-webkit-backdrop-filter: blur(var(--glass-blur-2)) saturate(125%);
|
|
2998
3029
|
border: 1px solid var(--glass-border-1);
|
|
@@ -3084,7 +3115,7 @@ button.todo-item { background: transparent; }
|
|
|
3084
3115
|
border-bottom: none;
|
|
3085
3116
|
border-radius: 8px 8px 0 0;
|
|
3086
3117
|
border-color: rgba(91,120,255,0.24);
|
|
3087
|
-
background: linear-gradient(90deg, rgba(91,120,255,0.10), rgba(
|
|
3118
|
+
background: linear-gradient(90deg, rgba(91,120,255,0.10), rgba(24,24,24,0.94) 34%);
|
|
3088
3119
|
box-shadow: none;
|
|
3089
3120
|
}
|
|
3090
3121
|
[data-theme="light"] #goal-bar {
|
|
@@ -3114,7 +3145,7 @@ button.todo-item { background: transparent; }
|
|
|
3114
3145
|
border-bottom: none;
|
|
3115
3146
|
border-radius: 8px 8px 0 0;
|
|
3116
3147
|
border-color: rgba(76,196,160,0.26);
|
|
3117
|
-
background: linear-gradient(90deg, rgba(76,196,160,0.12), rgba(
|
|
3148
|
+
background: linear-gradient(90deg, rgba(76,196,160,0.12), rgba(24,24,24,0.94) 34%);
|
|
3118
3149
|
box-shadow: none;
|
|
3119
3150
|
}
|
|
3120
3151
|
[data-theme="light"] #flow-prompt-bar {
|
|
@@ -3132,7 +3163,7 @@ button.todo-item { background: transparent; }
|
|
|
3132
3163
|
}
|
|
3133
3164
|
#flow-prompt-bar.paused {
|
|
3134
3165
|
border-color: rgba(226,183,74,0.34);
|
|
3135
|
-
background: linear-gradient(90deg, rgba(226,183,74,0.14), rgba(
|
|
3166
|
+
background: linear-gradient(90deg, rgba(226,183,74,0.14), rgba(24,24,24,0.94) 34%);
|
|
3136
3167
|
}
|
|
3137
3168
|
#flow-prompt-bar.paused .stack-row::before {
|
|
3138
3169
|
background:#e2b74a;
|
|
@@ -3569,7 +3600,7 @@ button.todo-item { background: transparent; }
|
|
|
3569
3600
|
overflow: auto;
|
|
3570
3601
|
padding: 7px;
|
|
3571
3602
|
border: 1px solid var(--glass-border-2);
|
|
3572
|
-
border-radius: var(--radius
|
|
3603
|
+
border-radius: var(--liquid-list-popup-radius);
|
|
3573
3604
|
background: var(--modal-surface);
|
|
3574
3605
|
box-shadow: var(--shadow-lg), var(--liquid-top-highlight), var(--liquid-inner-shadow);
|
|
3575
3606
|
}
|
|
@@ -3595,6 +3626,10 @@ button.todo-item { background: transparent; }
|
|
|
3595
3626
|
.model-select-menu-option:hover,
|
|
3596
3627
|
.model-select-menu-option:focus-visible { background: var(--control-hover-bg); color: var(--text-bright); outline: none; }
|
|
3597
3628
|
.model-select-menu-option.selected { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--text-bright); }
|
|
3629
|
+
.model-select-menu:has(> .liquid-menu-color-block) .model-select-menu-option.selected,
|
|
3630
|
+
.newmark-select-menu:has(> .liquid-menu-color-block) .newmark-select-option.selected {
|
|
3631
|
+
background: transparent;
|
|
3632
|
+
}
|
|
3598
3633
|
.liquid-selection-source { background: transparent !important; border-color: transparent !important; }
|
|
3599
3634
|
.liquid-selection-source::after { display: none !important; opacity: 0 !important; }
|
|
3600
3635
|
.conv-item.liquid-selection-covered { border-color: transparent !important; }
|
|
@@ -3670,7 +3705,7 @@ button.todo-item { background: transparent; }
|
|
|
3670
3705
|
transition: top 240ms var(--ease-out-expo), left 240ms var(--ease-out-expo), width 240ms var(--ease-out-expo), height 240ms var(--ease-out-expo), opacity 80ms var(--ease-out-expo), transform 24ms linear, translate 110ms var(--ease-out-expo), scale 80ms linear;
|
|
3671
3706
|
will-change: top, left, width, height, transform;
|
|
3672
3707
|
translate: var(--liquid-boundary-x) var(--liquid-boundary-y);
|
|
3673
|
-
scale:
|
|
3708
|
+
scale: var(--liquid-boundary-scale-x, 1) var(--liquid-boundary-scale-y, 1);
|
|
3674
3709
|
}
|
|
3675
3710
|
.liquid-selection-float.visible { opacity: 1; --liquid-lift-scale: 1; }
|
|
3676
3711
|
.liquid-selection-float.liquid-selection-landing {
|
|
@@ -3737,10 +3772,16 @@ button.todo-item { background: transparent; }
|
|
|
3737
3772
|
.liquid-selection-canvas {
|
|
3738
3773
|
position: absolute;
|
|
3739
3774
|
inset: 0;
|
|
3775
|
+
z-index: 0;
|
|
3740
3776
|
width: 100%;
|
|
3741
3777
|
height: 100%;
|
|
3742
3778
|
border-radius: inherit;
|
|
3743
3779
|
pointer-events: none;
|
|
3780
|
+
mix-blend-mode: normal;
|
|
3781
|
+
}
|
|
3782
|
+
.liquid-selection-float > :not(.liquid-selection-canvas) {
|
|
3783
|
+
position: relative;
|
|
3784
|
+
z-index: 1;
|
|
3744
3785
|
}
|
|
3745
3786
|
@keyframes model-menu-in { from { opacity: 0; transform: translateY(5px) scale(.985); } to { opacity: 1; transform: none; } }
|
|
3746
3787
|
|
|
@@ -3778,7 +3819,7 @@ button.todo-item { background: transparent; }
|
|
|
3778
3819
|
overflow: auto;
|
|
3779
3820
|
padding: 7px;
|
|
3780
3821
|
border: 1px solid var(--glass-border-2);
|
|
3781
|
-
border-radius: var(--radius
|
|
3822
|
+
border-radius: var(--liquid-list-popup-radius);
|
|
3782
3823
|
background: var(--modal-surface);
|
|
3783
3824
|
box-shadow: var(--shadow-lg), var(--liquid-top-highlight), var(--liquid-inner-shadow);
|
|
3784
3825
|
}
|
|
@@ -4002,7 +4043,7 @@ button.todo-item { background: transparent; }
|
|
|
4002
4043
|
#submit-btn:active { transform: none; scale: 1; }
|
|
4003
4044
|
|
|
4004
4045
|
#submit-btn.working {
|
|
4005
|
-
background: linear-gradient(135deg, #
|
|
4046
|
+
background: linear-gradient(135deg, #383838 0%, #484848 100%);
|
|
4006
4047
|
border-color: var(--border);
|
|
4007
4048
|
box-shadow: none;
|
|
4008
4049
|
animation: btn-pulse 1.5s ease-in-out infinite;
|
|
@@ -4011,7 +4052,7 @@ button.todo-item { background: transparent; }
|
|
|
4011
4052
|
#submit-btn.running-action {
|
|
4012
4053
|
position: relative;
|
|
4013
4054
|
overflow: visible;
|
|
4014
|
-
background: rgba(
|
|
4055
|
+
background: rgba(20, 20, 20, 0.88);
|
|
4015
4056
|
border-color: rgba(255,255,255,0.08);
|
|
4016
4057
|
box-shadow: 0 0 0 1px rgba(255,255,255,0.04), 0 2px 16px var(--accent-glow);
|
|
4017
4058
|
animation: none;
|
|
@@ -4846,7 +4887,7 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4846
4887
|
box-shadow: var(--modal-shadow);
|
|
4847
4888
|
}
|
|
4848
4889
|
[data-theme="light"] .sub-win.liquid-glass-carrier {
|
|
4849
|
-
background: var(--
|
|
4890
|
+
background: var(--liquid-popup-surface) !important;
|
|
4850
4891
|
}
|
|
4851
4892
|
.memory-lab-search-results[hidden] {
|
|
4852
4893
|
display: none;
|
|
@@ -5125,7 +5166,7 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
5125
5166
|
padding: 7px 9px;
|
|
5126
5167
|
border-radius: var(--radius-md);
|
|
5127
5168
|
border: 1px solid var(--glass-border-1);
|
|
5128
|
-
background: rgba(
|
|
5169
|
+
background: rgba(24, 24, 24, 0.58);
|
|
5129
5170
|
color: var(--text-dim);
|
|
5130
5171
|
font-size: 11px;
|
|
5131
5172
|
overflow: hidden;
|
|
@@ -5164,10 +5205,13 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
5164
5205
|
}
|
|
5165
5206
|
.memory-lab-overview-link {
|
|
5166
5207
|
fill: none;
|
|
5167
|
-
stroke: rgba(
|
|
5208
|
+
stroke: rgba(168, 168, 168, 0.24);
|
|
5168
5209
|
stroke-width: 1.05;
|
|
5169
5210
|
stroke-linecap: round;
|
|
5170
5211
|
}
|
|
5212
|
+
[data-theme="light"] .memory-lab-overview-link {
|
|
5213
|
+
stroke: rgba(150, 168, 210, 0.24);
|
|
5214
|
+
}
|
|
5171
5215
|
.memory-lab-overview-link.hot {
|
|
5172
5216
|
stroke: rgba(126, 220, 255, 0.78);
|
|
5173
5217
|
stroke-width: 1.45;
|
|
@@ -5204,7 +5248,7 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
5204
5248
|
padding: 0 9px 0 7px;
|
|
5205
5249
|
border-radius: 999px;
|
|
5206
5250
|
border: 1px solid var(--glass-border-2);
|
|
5207
|
-
background: rgba(
|
|
5251
|
+
background: rgba(34, 34, 34, 0.72);
|
|
5208
5252
|
color: var(--text);
|
|
5209
5253
|
font: 11px var(--font-ui);
|
|
5210
5254
|
transform: translate(-50%, -50%);
|
|
@@ -5275,7 +5319,7 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
5275
5319
|
padding: 8px 9px;
|
|
5276
5320
|
border-radius: var(--radius-md);
|
|
5277
5321
|
border: 1px solid var(--glass-border-1);
|
|
5278
|
-
background: rgba(
|
|
5322
|
+
background: rgba(20, 20, 20, 0.84);
|
|
5279
5323
|
color: var(--text);
|
|
5280
5324
|
box-shadow: var(--shadow-lg);
|
|
5281
5325
|
pointer-events: none;
|
|
@@ -5753,9 +5797,9 @@ html.newmark-memory-overview-viewer body {
|
|
|
5753
5797
|
|
|
5754
5798
|
|
|
5755
5799
|
/* ============================================================
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5800
|
+
FROSTED GLASS
|
|
5801
|
+
Uniform translucency and real backdrop blur. The neutral rim stays narrow;
|
|
5802
|
+
menu color-block motion never switches to a glass material.
|
|
5759
5803
|
============================================================ */
|
|
5760
5804
|
.liquid-glass {
|
|
5761
5805
|
position: relative;
|
|
@@ -5786,7 +5830,116 @@ html.newmark-memory-overview-viewer body {
|
|
|
5786
5830
|
backdrop-filter: blur(calc(var(--glass-blur-3) * 1.5)) saturate(150%) brightness(1.04);
|
|
5787
5831
|
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 1.5)) saturate(150%) brightness(1.04);
|
|
5788
5832
|
}
|
|
5789
|
-
|
|
5833
|
+
@property --liquid-popup-outset {
|
|
5834
|
+
syntax: '<length>';
|
|
5835
|
+
inherits: true;
|
|
5836
|
+
initial-value: 0px;
|
|
5837
|
+
}
|
|
5838
|
+
.liquid-glass-popup {
|
|
5839
|
+
isolation: isolate;
|
|
5840
|
+
overflow: hidden;
|
|
5841
|
+
clip-path: inset(var(--liquid-popup-edge-y, 0px) var(--liquid-popup-edge-x, 0px) round var(--liquid-popup-radius, var(--radius-xl)));
|
|
5842
|
+
background: var(--liquid-popup-surface);
|
|
5843
|
+
border-color: var(--liquid-popup-rim);
|
|
5844
|
+
backdrop-filter: blur(var(--liquid-popup-blur)) saturate(var(--liquid-popup-saturation));
|
|
5845
|
+
-webkit-backdrop-filter: blur(var(--liquid-popup-blur)) saturate(var(--liquid-popup-saturation));
|
|
5846
|
+
box-shadow: inset 0 0 0 0.5px var(--liquid-popup-inner-rim), var(--liquid-popup-shadow);
|
|
5847
|
+
/* Only the optical boundary responds. Scaling this node also scales every
|
|
5848
|
+
text-bearing descendant and changes pointer-track measurements. */
|
|
5849
|
+
scale: 1;
|
|
5850
|
+
transition: clip-path 120ms var(--ease-out-expo), transform 120ms var(--ease-out-expo), --liquid-popup-outset 120ms var(--ease-out-expo);
|
|
5851
|
+
}
|
|
5852
|
+
.liquid-popup-optical-canvas {
|
|
5853
|
+
position: absolute;
|
|
5854
|
+
inset: 0 !important;
|
|
5855
|
+
z-index: 0;
|
|
5856
|
+
width: 100%;
|
|
5857
|
+
height: 100%;
|
|
5858
|
+
border-radius: inherit;
|
|
5859
|
+
pointer-events: none;
|
|
5860
|
+
/* This is the carrier surface pass, not a top-level screen overlay. */
|
|
5861
|
+
mix-blend-mode: normal;
|
|
5862
|
+
opacity: 1;
|
|
5863
|
+
transform: none !important;
|
|
5864
|
+
translate: none !important;
|
|
5865
|
+
scale: 1 !important;
|
|
5866
|
+
}
|
|
5867
|
+
.liquid-glass-popup > :not(.liquid-popup-optical-canvas) {
|
|
5868
|
+
position: relative;
|
|
5869
|
+
z-index: 1;
|
|
5870
|
+
}
|
|
5871
|
+
.liquid-glass-popup > .liquid-popup-optical-canvas {
|
|
5872
|
+
pointer-events: none;
|
|
5873
|
+
}
|
|
5874
|
+
/* The selected-option block is another carrier-surface pass. Keep it
|
|
5875
|
+
absolute and beneath option content; the generic popup content rule above
|
|
5876
|
+
must not promote it into the text layer or make it participate in layout. */
|
|
5877
|
+
.liquid-glass-popup > .liquid-menu-color-block {
|
|
5878
|
+
position: absolute;
|
|
5879
|
+
z-index: 0;
|
|
5880
|
+
}
|
|
5881
|
+
/* List popups are themselves the scroll surface. The shared popup shell
|
|
5882
|
+
clips optical passes, but its generic overflow:hidden must not shadow the
|
|
5883
|
+
max-height/overflow:auto contract declared by the menu components above. */
|
|
5884
|
+
.conv-action-menu.liquid-glass-popup,
|
|
5885
|
+
.model-select-menu.liquid-glass-popup,
|
|
5886
|
+
.newmark-select-menu.liquid-glass-popup {
|
|
5887
|
+
overflow-y: auto;
|
|
5888
|
+
overflow-x: hidden;
|
|
5889
|
+
}
|
|
5890
|
+
.model-select-menu.liquid-glass-popup,
|
|
5891
|
+
.conv-action-menu.liquid-glass-popup,
|
|
5892
|
+
.newmark-select-menu.liquid-glass-popup {
|
|
5893
|
+
--liquid-popup-radius: var(--liquid-list-popup-radius);
|
|
5894
|
+
}
|
|
5895
|
+
.command-surface.liquid-glass-popup,
|
|
5896
|
+
.sub-win.liquid-glass-popup {
|
|
5897
|
+
--liquid-popup-radius: var(--radius-xl);
|
|
5898
|
+
}
|
|
5899
|
+
#context-inspector.liquid-glass-popup {
|
|
5900
|
+
--liquid-popup-radius: var(--radius-lg);
|
|
5901
|
+
}
|
|
5902
|
+
.liquid-glass-popup.liquid-popup-exit {
|
|
5903
|
+
transform: scale(.78);
|
|
5904
|
+
transform-origin: var(--liquid-popup-origin-x, 50%) var(--liquid-popup-origin-y, 50%);
|
|
5905
|
+
opacity: 0;
|
|
5906
|
+
transition: transform 190ms var(--ease-out-expo), opacity 170ms var(--ease-out-expo), scale 190ms var(--ease-out-expo);
|
|
5907
|
+
}
|
|
5908
|
+
.liquid-glass-popup::before {
|
|
5909
|
+
/* The internal canvas owns touch light. No second CSS lamp or baked
|
|
5910
|
+
illumination plane is composited over the frosted material. */
|
|
5911
|
+
background: none;
|
|
5912
|
+
opacity: 0;
|
|
5913
|
+
mix-blend-mode: normal;
|
|
5914
|
+
}
|
|
5915
|
+
.liquid-glass-popup::after {
|
|
5916
|
+
inset: var(--liquid-popup-edge-y, 0px) var(--liquid-popup-edge-x, 0px);
|
|
5917
|
+
transition: inset 120ms var(--ease-out-expo);
|
|
5918
|
+
border: var(--liquid-popup-edge-width) solid var(--liquid-popup-rim);
|
|
5919
|
+
box-shadow: inset 0 0 0 0.5px var(--liquid-popup-inner-rim);
|
|
5920
|
+
}
|
|
5921
|
+
[data-theme='light'] .liquid-glass-popup {
|
|
5922
|
+
backdrop-filter: blur(var(--liquid-popup-blur)) saturate(var(--liquid-popup-saturation));
|
|
5923
|
+
-webkit-backdrop-filter: blur(var(--liquid-popup-blur)) saturate(var(--liquid-popup-saturation));
|
|
5924
|
+
box-shadow: inset 0 0 0 0.5px var(--liquid-popup-inner-rim), var(--liquid-popup-shadow);
|
|
5925
|
+
}
|
|
5926
|
+
[data-theme='light'] .liquid-glass-popup::before {
|
|
5927
|
+
opacity: 0;
|
|
5928
|
+
mix-blend-mode: normal;
|
|
5929
|
+
}
|
|
5930
|
+
|
|
5931
|
+
/* Keep motion and backdrop work bounded on reduced-motion / low-power PCs. */
|
|
5932
|
+
@media (prefers-reduced-motion: reduce) {
|
|
5933
|
+
.liquid-glass-popup,
|
|
5934
|
+
.liquid-glass-popup.liquid-popup-exit,
|
|
5935
|
+
.liquid-menu-color-block,
|
|
5936
|
+
.liquid-selection-float { transition-duration: 1ms !important; animation-duration: 1ms !important; }
|
|
5937
|
+
.liquid-glass-popup::before { opacity: 0; }
|
|
5938
|
+
}
|
|
5939
|
+
@media (max-width: 900px), (max-height: 680px) {
|
|
5940
|
+
.liquid-glass-popup { --liquid-popup-blur: calc(var(--glass-blur-3) * 3); }
|
|
5941
|
+
}
|
|
5942
|
+
[data-theme='light'] .liquid-glass:not(.liquid-glass-popup) {
|
|
5790
5943
|
backdrop-filter: none;
|
|
5791
5944
|
-webkit-backdrop-filter: none;
|
|
5792
5945
|
}
|
|
@@ -5796,26 +5949,168 @@ html.newmark-memory-overview-viewer body {
|
|
|
5796
5949
|
}
|
|
5797
5950
|
.liquid-glass-carrier {
|
|
5798
5951
|
background: rgb(var(--glass-rgb-2) / var(--carrier-glass-alpha)) !important;
|
|
5799
|
-
backdrop-filter: blur(var(--carrier-glass-blur)) saturate(
|
|
5800
|
-
-webkit-backdrop-filter: blur(var(--carrier-glass-blur)) saturate(
|
|
5952
|
+
backdrop-filter: blur(var(--carrier-glass-blur)) saturate(108%) !important;
|
|
5953
|
+
-webkit-backdrop-filter: blur(var(--carrier-glass-blur)) saturate(108%) !important;
|
|
5954
|
+
}
|
|
5955
|
+
.liquid-glass-popup.liquid-glass-carrier {
|
|
5956
|
+
background: var(--liquid-popup-surface) !important;
|
|
5957
|
+
backdrop-filter: blur(var(--liquid-popup-blur)) saturate(var(--liquid-popup-saturation)) !important;
|
|
5958
|
+
-webkit-backdrop-filter: blur(var(--liquid-popup-blur)) saturate(var(--liquid-popup-saturation)) !important;
|
|
5959
|
+
}
|
|
5960
|
+
/* A list popup IS the scroll container. Absolute pseudo-elements/canvases
|
|
5961
|
+
belong to its scrolling content and carry a second rounded shell away.
|
|
5962
|
+
Paint its rim and touch light on the scrollport's own background instead;
|
|
5963
|
+
native background-attachment:scroll pins this paint without scroll-event
|
|
5964
|
+
compensation, wrappers, or any changes to option geometry/gesture logic. */
|
|
5965
|
+
:is(.conv-action-menu, .model-select-menu, .newmark-select-menu).liquid-glass-popup {
|
|
5966
|
+
background-image: radial-gradient(circle var(--liquid-popup-glow-radius, 20px) at var(--liquid-light-x, 50%) var(--liquid-light-y, 50%), rgba(255,255,255,calc(.24 * var(--liquid-popup-light-opacity, 0))), rgba(255,255,255,0) 100%) !important;
|
|
5967
|
+
background-attachment: scroll;
|
|
5968
|
+
background-origin: padding-box;
|
|
5969
|
+
background-clip: padding-box;
|
|
5970
|
+
/* Clip-path owns inward click feedback within the 8px optical budget.
|
|
5971
|
+
Extend each inset rim to that boundary so its visible edge stays thin. */
|
|
5972
|
+
box-shadow:
|
|
5973
|
+
inset calc(var(--liquid-popup-edge-x, 0px) + 1px) 0 0 var(--liquid-popup-inner-rim),
|
|
5974
|
+
inset calc(-1 * (var(--liquid-popup-edge-x, 0px) + 1px)) 0 0 var(--liquid-popup-inner-rim),
|
|
5975
|
+
inset 0 calc(var(--liquid-popup-edge-y, 0px) + 1px) 0 var(--liquid-popup-inner-rim),
|
|
5976
|
+
inset 0 calc(-1 * (var(--liquid-popup-edge-y, 0px) + 1px)) 0 var(--liquid-popup-inner-rim),
|
|
5977
|
+
var(--liquid-popup-shadow);
|
|
5978
|
+
}
|
|
5979
|
+
:is(.conv-action-menu, .model-select-menu, .newmark-select-menu).liquid-glass-popup::before,
|
|
5980
|
+
:is(.conv-action-menu, .model-select-menu, .newmark-select-menu).liquid-glass-popup::after {
|
|
5981
|
+
content: none;
|
|
5982
|
+
}
|
|
5983
|
+
:is(.conv-action-menu, .model-select-menu, .newmark-select-menu).liquid-glass-popup > .liquid-popup-optical-canvas {
|
|
5984
|
+
display: none;
|
|
5985
|
+
}
|
|
5986
|
+
/* A held outward pull expands the actual frosted material, never the text or
|
|
5987
|
+
scroll container. Native backdrops stay below their own top-layer popover;
|
|
5988
|
+
other popups use a decorative sibling inside their existing overlay. */
|
|
5989
|
+
.liquid-glass-popup[data-liquid-popup-feedback='outward']::backdrop,
|
|
5990
|
+
.liquid-popup-feedback-surface {
|
|
5991
|
+
box-sizing: border-box;
|
|
5992
|
+
inset: auto;
|
|
5993
|
+
margin: 0;
|
|
5994
|
+
padding: 0;
|
|
5995
|
+
left: calc(var(--liquid-feedback-left) - var(--liquid-popup-outset) * var(--liquid-popup-pull-left, 0));
|
|
5996
|
+
top: calc(var(--liquid-feedback-top) - var(--liquid-popup-outset) * var(--liquid-popup-pull-top, 0));
|
|
5997
|
+
width: calc(var(--liquid-feedback-width) + var(--liquid-popup-outset) * (var(--liquid-popup-pull-left, 0) + var(--liquid-popup-pull-right, 0)));
|
|
5998
|
+
height: calc(var(--liquid-feedback-height) + var(--liquid-popup-outset) * (var(--liquid-popup-pull-top, 0) + var(--liquid-popup-pull-bottom, 0)));
|
|
5999
|
+
border: 1px solid var(--liquid-feedback-rim);
|
|
6000
|
+
/* Each corner follows only its adjacent force-facing sides. The opposite
|
|
6001
|
+
contour, including its corners, keeps the popup's original radius. */
|
|
6002
|
+
border-radius:
|
|
6003
|
+
calc(var(--liquid-feedback-radius) + var(--liquid-popup-outset) * var(--liquid-popup-pull-left, 0))
|
|
6004
|
+
calc(var(--liquid-feedback-radius) + var(--liquid-popup-outset) * var(--liquid-popup-pull-right, 0))
|
|
6005
|
+
calc(var(--liquid-feedback-radius) + var(--liquid-popup-outset) * var(--liquid-popup-pull-right, 0))
|
|
6006
|
+
calc(var(--liquid-feedback-radius) + var(--liquid-popup-outset) * var(--liquid-popup-pull-left, 0)) /
|
|
6007
|
+
calc(var(--liquid-feedback-radius) + var(--liquid-popup-outset) * var(--liquid-popup-pull-top, 0))
|
|
6008
|
+
calc(var(--liquid-feedback-radius) + var(--liquid-popup-outset) * var(--liquid-popup-pull-top, 0))
|
|
6009
|
+
calc(var(--liquid-feedback-radius) + var(--liquid-popup-outset) * var(--liquid-popup-pull-bottom, 0))
|
|
6010
|
+
calc(var(--liquid-feedback-radius) + var(--liquid-popup-outset) * var(--liquid-popup-pull-bottom, 0));
|
|
6011
|
+
background: var(--liquid-feedback-color);
|
|
6012
|
+
backdrop-filter: var(--liquid-feedback-filter);
|
|
6013
|
+
-webkit-backdrop-filter: var(--liquid-feedback-filter);
|
|
6014
|
+
box-shadow: inset 0 0 0 .5px var(--liquid-feedback-inner-rim);
|
|
6015
|
+
pointer-events: none;
|
|
6016
|
+
}
|
|
6017
|
+
.liquid-popup-feedback-surface {
|
|
6018
|
+
position: absolute;
|
|
6019
|
+
transition: --liquid-popup-outset 120ms var(--ease-out-expo);
|
|
6020
|
+
}
|
|
6021
|
+
:root .liquid-glass-popup[data-liquid-popup-feedback='outward'] {
|
|
6022
|
+
/* The feedback surface owns this material once. Keeping another background
|
|
6023
|
+
or backdrop here would darken it and leave seams around the old outline. */
|
|
6024
|
+
background-color: transparent !important;
|
|
6025
|
+
border-color: transparent !important;
|
|
6026
|
+
backdrop-filter: none !important;
|
|
6027
|
+
-webkit-backdrop-filter: none !important;
|
|
6028
|
+
box-shadow: none !important;
|
|
6029
|
+
}
|
|
6030
|
+
.liquid-glass-popup[data-liquid-popup-feedback='outward']::after { opacity: 0; }
|
|
6031
|
+
@media (prefers-reduced-motion: reduce) {
|
|
6032
|
+
.liquid-popup-feedback-surface { transition-duration: 1ms !important; }
|
|
6033
|
+
}
|
|
6034
|
+
.model-select-menu-option,
|
|
6035
|
+
.newmark-select-option,
|
|
6036
|
+
.conv-action-menu-item,
|
|
6037
|
+
.command-option,
|
|
6038
|
+
.workspace-type-option {
|
|
6039
|
+
--liquid-option-x: 0px;
|
|
6040
|
+
--liquid-option-y: 0px;
|
|
6041
|
+
--liquid-option-press: 0;
|
|
6042
|
+
--liquid-option-origin-x: 50%;
|
|
6043
|
+
--liquid-option-origin-y: 50%;
|
|
6044
|
+
transform: translate(var(--liquid-option-x), var(--liquid-option-y)) scale(calc(1 + var(--liquid-option-press) * 0.035));
|
|
6045
|
+
transform-origin: var(--liquid-option-origin-x) var(--liquid-option-origin-y);
|
|
6046
|
+
transition: transform 110ms var(--ease-out-expo), background-color 110ms var(--ease-out-expo), filter 110ms var(--ease-out-expo);
|
|
6047
|
+
}
|
|
6048
|
+
.model-select-menu-option,
|
|
6049
|
+
.newmark-select-option {
|
|
6050
|
+
position: relative;
|
|
6051
|
+
z-index: 1;
|
|
6052
|
+
}
|
|
6053
|
+
/* Popup option content stays fixed; the carrier block owns movement and
|
|
6054
|
+
elastic response so text/icons never translate or scale during a drag. */
|
|
6055
|
+
.model-select-menu[data-liquid-direct-options="true"] > .model-select-menu-option,
|
|
6056
|
+
.newmark-select-menu[data-liquid-direct-options="true"] > .newmark-select-option {
|
|
6057
|
+
transform: none !important;
|
|
6058
|
+
scale: 1 !important;
|
|
6059
|
+
filter: none !important;
|
|
6060
|
+
}
|
|
6061
|
+
.liquid-glass-popup .conv-action-menu-item,
|
|
6062
|
+
.liquid-glass-popup .command-option,
|
|
6063
|
+
.liquid-glass-popup .workspace-type-option {
|
|
6064
|
+
transform: none !important;
|
|
6065
|
+
scale: 1 !important;
|
|
6066
|
+
}
|
|
6067
|
+
.liquid-menu-color-block {
|
|
6068
|
+
position: absolute;
|
|
6069
|
+
/* Inline left/top own the flight. An important inset shorthand overrides
|
|
6070
|
+
both coordinates and strands the block after the last option. */
|
|
6071
|
+
inset: auto;
|
|
6072
|
+
z-index: 0;
|
|
6073
|
+
pointer-events: none;
|
|
6074
|
+
background: color-mix(in srgb, var(--accent) 16%, transparent);
|
|
6075
|
+
/* Match the original selected capsule silhouette at every menu width. */
|
|
6076
|
+
border-radius: var(--radius-full);
|
|
6077
|
+
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent);
|
|
6078
|
+
transition: left 240ms var(--ease-out-expo), top 240ms var(--ease-out-expo), width 240ms var(--ease-out-expo), height 240ms var(--ease-out-expo), opacity 80ms var(--ease-out-expo);
|
|
6079
|
+
transform: none !important;
|
|
6080
|
+
translate: none !important;
|
|
6081
|
+
scale: 1 !important;
|
|
6082
|
+
}
|
|
6083
|
+
.liquid-menu-color-block.liquid-block-lifted {
|
|
6084
|
+
/* This class is an interaction-state marker, not a material switch. Keep
|
|
6085
|
+
the base color, outline and content stacking for the entire journey. */
|
|
6086
|
+
backdrop-filter: none;
|
|
6087
|
+
-webkit-backdrop-filter: none;
|
|
6088
|
+
}
|
|
6089
|
+
.liquid-menu-color-block.liquid-block-lifted::after {
|
|
6090
|
+
content: none;
|
|
6091
|
+
}
|
|
6092
|
+
.model-select-menu-option.liquid-option-dragging,
|
|
6093
|
+
.newmark-select-option.liquid-option-dragging {
|
|
6094
|
+
filter: brightness(1.08) saturate(1.08);
|
|
6095
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), 0 5px 16px rgba(0,0,0,0.18);
|
|
5801
6096
|
}
|
|
5802
6097
|
/* List popups use the live carrier refraction only. The old static outer
|
|
5803
6098
|
glow/shadow painted a second fixed background behind the popup. */
|
|
5804
|
-
.conv-action-menu.liquid-glass,
|
|
5805
|
-
.model-select-menu.liquid-glass,
|
|
5806
|
-
.newmark-select-menu.liquid-glass {
|
|
6099
|
+
.conv-action-menu.liquid-glass:not(.liquid-glass-popup),
|
|
6100
|
+
.model-select-menu.liquid-glass:not(.liquid-glass-popup),
|
|
6101
|
+
.newmark-select-menu.liquid-glass:not(.liquid-glass-popup) {
|
|
5807
6102
|
box-shadow: none;
|
|
5808
6103
|
}
|
|
5809
|
-
.conv-action-menu.liquid-glass::before,
|
|
5810
|
-
.model-select-menu.liquid-glass::before,
|
|
5811
|
-
.newmark-select-menu.liquid-glass::before {
|
|
6104
|
+
.conv-action-menu.liquid-glass:not(.liquid-glass-popup)::before,
|
|
6105
|
+
.model-select-menu.liquid-glass:not(.liquid-glass-popup)::before,
|
|
6106
|
+
.newmark-select-menu.liquid-glass:not(.liquid-glass-popup)::before {
|
|
5812
6107
|
background: none;
|
|
5813
6108
|
opacity: 0;
|
|
5814
6109
|
mix-blend-mode: normal;
|
|
5815
6110
|
}
|
|
5816
|
-
.conv-action-menu.liquid-glass::after,
|
|
5817
|
-
.model-select-menu.liquid-glass::after,
|
|
5818
|
-
.newmark-select-menu.liquid-glass::after {
|
|
6111
|
+
.conv-action-menu.liquid-glass:not(.liquid-glass-popup)::after,
|
|
6112
|
+
.model-select-menu.liquid-glass:not(.liquid-glass-popup)::after,
|
|
6113
|
+
.newmark-select-menu.liquid-glass:not(.liquid-glass-popup)::after {
|
|
5819
6114
|
box-shadow: none;
|
|
5820
6115
|
}
|
|
5821
6116
|
.liquid-selection-canvas { opacity: var(--liquid-float-optical-opacity); }
|
|
@@ -5877,6 +6172,11 @@ button:disabled { opacity: 0.5; }
|
|
|
5877
6172
|
.shell-btn::after,
|
|
5878
6173
|
.option-btn::after,
|
|
5879
6174
|
.stack-icon-btn::after { display: none !important; }
|
|
6175
|
+
.model-select-menu-option::after,
|
|
6176
|
+
.newmark-select-option::after,
|
|
6177
|
+
.conv-action-menu-item::after,
|
|
6178
|
+
.command-option::after,
|
|
6179
|
+
.workspace-type-option::after { display: none !important; }
|
|
5880
6180
|
#left-ws-add::after { display: none !important; }
|
|
5881
6181
|
#left-ws-add:active,
|
|
5882
6182
|
.left-collapse-btn:active { transform: none !important; scale: 1 !important; }
|
|
@@ -6328,6 +6628,13 @@ input[type="checkbox"].liquid-switch-dragging::before {
|
|
|
6328
6628
|
<path d="M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7" />
|
|
6329
6629
|
<path d="M7 3v4a1 1 0 0 0 1 1h7" />
|
|
6330
6630
|
</symbol>
|
|
6631
|
+
<symbol id="scale" viewBox="0 0 24 24">
|
|
6632
|
+
<path d="M12 3v18" />
|
|
6633
|
+
<path d="m19 8 3 8a5 5 0 0 1-6 0zV7" />
|
|
6634
|
+
<path d="M3 7h1a17 17 0 0 0 8-2 17 17 0 0 0 8 2h1" />
|
|
6635
|
+
<path d="m5 8 3 8a5 5 0 0 1-6 0zV7" />
|
|
6636
|
+
<path d="M7 21h10" />
|
|
6637
|
+
</symbol>
|
|
6331
6638
|
<symbol id="scroll" viewBox="0 0 24 24">
|
|
6332
6639
|
<path d="M19 17V5a2 2 0 0 0-2-2H4" />
|
|
6333
6640
|
<path d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3" />
|
|
@@ -6593,13 +6900,13 @@ input[type="checkbox"].liquid-switch-dragging::before {
|
|
|
6593
6900
|
<div class="model-select-shell" id="model-select-shell">
|
|
6594
6901
|
<select class="model-select-native" id="model-select" tabindex="-1" aria-hidden="true"></select>
|
|
6595
6902
|
<button type="button" class="tool-select model-select-button" id="model-select-button" aria-haspopup="listbox" aria-expanded="false" onclick="window.toggleModelSelectMenu()" onkeydown="window.handleModelSelectButtonKey(event)"><span class="model-select-button-label" id="model-select-label"></span><svg class="nm-icon tiny" aria-hidden="true" focusable="false"><use href="#chevron-down"></use></svg></button>
|
|
6596
|
-
<div class="model-select-menu liquid-glass liquid-glass-carrier" id="model-select-menu" role="listbox" aria-label="Model" popover="manual"></div>
|
|
6903
|
+
<div class="model-select-menu liquid-glass liquid-glass-carrier liquid-glass-popup" id="model-select-menu" role="listbox" aria-label="Model" popover="manual"></div>
|
|
6597
6904
|
</div>
|
|
6598
|
-
<div class="conv-action-menu liquid-glass liquid-glass-carrier" id="conversation-action-menu" role="menu" aria-label="Conversation actions" popover="manual"></div>
|
|
6905
|
+
<div class="conv-action-menu liquid-glass liquid-glass-carrier liquid-glass-popup" id="conversation-action-menu" role="menu" aria-label="Conversation actions" popover="manual"></div>
|
|
6599
6906
|
<div class="context-token-wrap">
|
|
6600
6907
|
<button type="button" id="context-token-ring" title="Context tokens" aria-haspopup="dialog" aria-expanded="false" onclick="window.toggleContextInspector()" onmouseenter="window.showContextWindowTooltip()" onmouseleave="window.hideContextWindowTooltip()" onfocus="window.showContextWindowTooltip()" onblur="window.hideContextWindowTooltip()"><span class="icon-only-label">Context tokens</span></button>
|
|
6601
6908
|
<div id="context-token-tooltip"></div>
|
|
6602
|
-
<div id="context-inspector" role="dialog" aria-label="Context management" aria-live="polite"></div>
|
|
6909
|
+
<div id="context-inspector" class="liquid-glass liquid-glass-popup" role="dialog" aria-label="Context management" aria-live="polite"></div>
|
|
6603
6910
|
</div>
|
|
6604
6911
|
<select class="tool-select" id="intel-select">
|
|
6605
6912
|
<option value="low">low</option>
|
|
@@ -6713,7 +7020,7 @@ input[type="checkbox"].liquid-switch-dragging::before {
|
|
|
6713
7020
|
|
|
6714
7021
|
<!-- ========== SUB-WINDOW OVERLAY ========== -->
|
|
6715
7022
|
<div class="sub-win-overlay" id="sub-win-overlay" onclick="if(event.target===this)window.closeSubWin()">
|
|
6716
|
-
<div class="sub-win liquid-glass liquid-glass-carrier" id="sub-win" role="dialog" aria-modal="true" aria-labelledby="sub-win-title" tabindex="-1">
|
|
7023
|
+
<div class="sub-win liquid-glass liquid-glass-carrier liquid-glass-popup" id="sub-win" role="dialog" aria-modal="true" aria-labelledby="sub-win-title" tabindex="-1">
|
|
6717
7024
|
<div class="sub-win-header" id="sub-win-header">
|
|
6718
7025
|
<span class="sub-win-title" id="sub-win-title">Window</span>
|
|
6719
7026
|
<button class="sub-win-close" onclick="window.closeSubWin()" title="Close"><svg class="nm-icon small" aria-hidden="true" focusable="false"><use href="#x"></use></svg><span class="icon-only-label">Close</span></button>
|
|
@@ -6722,7 +7029,7 @@ input[type="checkbox"].liquid-switch-dragging::before {
|
|
|
6722
7029
|
</div>
|
|
6723
7030
|
</div>
|
|
6724
7031
|
<div class="command-surface-overlay" id="command-surface-overlay" onclick="if(event.target===this)window.closeCommandSurface()">
|
|
6725
|
-
<section class="command-surface" id="command-surface" role="dialog" aria-modal="true" aria-labelledby="command-surface-title" tabindex="-1">
|
|
7032
|
+
<section class="command-surface liquid-glass liquid-glass-popup" id="command-surface" role="dialog" aria-modal="true" aria-labelledby="command-surface-title" tabindex="-1">
|
|
6726
7033
|
<header class="command-surface-header">
|
|
6727
7034
|
<div class="command-surface-heading">
|
|
6728
7035
|
<div class="command-surface-title" id="command-surface-title"></div>
|
|
@@ -7625,9 +7932,17 @@ var NEWMARK_I18N = {
|
|
|
7625
7932
|
'status.subagentRecords': 'active or retained agent record(s)',
|
|
7626
7933
|
'status.contextCompressed': 'Context compressed',
|
|
7627
7934
|
'status.contextCompression': 'Context compression',
|
|
7628
|
-
'status.contextTokens': 'Context tokens',
|
|
7629
|
-
'status.conversationTokens': 'Conversation
|
|
7630
|
-
'status.cacheHitRate': '
|
|
7935
|
+
'status.contextTokens': 'Context tokens (estimated)',
|
|
7936
|
+
'status.conversationTokens': 'Conversation usage (reported)',
|
|
7937
|
+
'status.cacheHitRate': 'Conversation cache hit rate',
|
|
7938
|
+
'status.usageUnavailable': 'Not reported',
|
|
7939
|
+
'status.cacheReportedPortion': 'Reported portion',
|
|
7940
|
+
'status.lastReportedInput': 'Latest main request input (reported)',
|
|
7941
|
+
'status.requestOverhead': 'System / tools (estimated)',
|
|
7942
|
+
'status.requestOverheadExcluded': 'Not included in history estimate',
|
|
7943
|
+
'status.contextEstimateHistory': 'Current retained history estimate; not provider billing.',
|
|
7944
|
+
'status.contextEstimateRequest': 'Submitted request estimate, including system and tool schemas; not provider billing.',
|
|
7945
|
+
'status.contextEstimateImages': 'Image content is present; image token cost is not included in the text estimate.',
|
|
7631
7946
|
'status.contextNearLimit': 'Context near limit',
|
|
7632
7947
|
'status.contextOverLimit': 'Context over limit',
|
|
7633
7948
|
'status.contextModeFallback': 'fallback',
|
|
@@ -7636,12 +7951,12 @@ var NEWMARK_I18N = {
|
|
|
7636
7951
|
'status.noCompression': 'No compression event in this conversation',
|
|
7637
7952
|
'status.contextInspector': 'Context management',
|
|
7638
7953
|
'status.contextInspectorHint': 'Live context budget; visible chat history stays unchanged.',
|
|
7639
|
-
'status.activeBuild': '
|
|
7640
|
-
'status.longHistory': 'Long history',
|
|
7954
|
+
'status.activeBuild': 'Build history (estimated)',
|
|
7955
|
+
'status.longHistory': 'Long history (estimated)',
|
|
7641
7956
|
'status.trigger': 'Trigger',
|
|
7642
7957
|
'status.retention': 'Retention',
|
|
7643
|
-
'status.hotCache': '
|
|
7644
|
-
'status.coldArchive': '
|
|
7958
|
+
'status.hotCache': 'Local history cache',
|
|
7959
|
+
'status.coldArchive': 'Local history archive',
|
|
7645
7960
|
'status.lastCompression': 'Last compression',
|
|
7646
7961
|
'status.noCompressionShort': 'No compression yet',
|
|
7647
7962
|
'status.compressNow': 'Compress now',
|
|
@@ -8358,9 +8673,17 @@ var NEWMARK_I18N = {
|
|
|
8358
8673
|
'status.subagentRecords': '个活跃或保留的 agent 记录',
|
|
8359
8674
|
'status.contextCompressed': '上下文已压缩',
|
|
8360
8675
|
'status.contextCompression': '上下文压缩',
|
|
8361
|
-
'status.contextTokens': '上下文 token',
|
|
8362
|
-
'status.conversationTokens': '
|
|
8363
|
-
'status.cacheHitRate': '
|
|
8676
|
+
'status.contextTokens': '上下文 token(估算)',
|
|
8677
|
+
'status.conversationTokens': '对话累计用量(实测)',
|
|
8678
|
+
'status.cacheHitRate': '对话总缓存命中率',
|
|
8679
|
+
'status.usageUnavailable': '未上报',
|
|
8680
|
+
'status.cacheReportedPortion': '已上报部分',
|
|
8681
|
+
'status.lastReportedInput': '最近主请求输入(实测)',
|
|
8682
|
+
'status.requestOverhead': '系统 / 工具(估算)',
|
|
8683
|
+
'status.requestOverheadExcluded': '当前历史估算未计入',
|
|
8684
|
+
'status.contextEstimateHistory': '当前保留历史的本地估算,不代表服务端计费用量。',
|
|
8685
|
+
'status.contextEstimateRequest': '已提交请求的本地估算,含系统提示与工具 schema,不代表服务端计费用量。',
|
|
8686
|
+
'status.contextEstimateImages': '请求包含图片;文本估算未计入图片 token 开销。',
|
|
8364
8687
|
'status.contextNearLimit': '上下文接近上限',
|
|
8365
8688
|
'status.contextOverLimit': '上下文已超限',
|
|
8366
8689
|
'status.contextModeFallback': 'fallback',
|
|
@@ -8369,12 +8692,12 @@ var NEWMARK_I18N = {
|
|
|
8369
8692
|
'status.noCompression': '当前对话暂无压缩事件',
|
|
8370
8693
|
'status.contextInspector': '上下文管理',
|
|
8371
8694
|
'status.contextInspectorHint': '实时上下文预算;可见对话历史不会被改写。',
|
|
8372
|
-
'status.activeBuild': '
|
|
8373
|
-
'status.longHistory': '
|
|
8695
|
+
'status.activeBuild': 'Build 内历史(估算)',
|
|
8696
|
+
'status.longHistory': '长期历史(估算)',
|
|
8374
8697
|
'status.trigger': '触发线',
|
|
8375
8698
|
'status.retention': '保留量',
|
|
8376
|
-
'status.hotCache': '
|
|
8377
|
-
'status.coldArchive': '
|
|
8699
|
+
'status.hotCache': '本地历史缓存',
|
|
8700
|
+
'status.coldArchive': '本地历史归档',
|
|
8378
8701
|
'status.lastCompression': '最近一次压缩',
|
|
8379
8702
|
'status.noCompressionShort': '尚未压缩',
|
|
8380
8703
|
'status.compressNow': '立即压缩',
|
|
@@ -9000,22 +9323,18 @@ function syncTerminalShellOptions() {
|
|
|
9000
9323
|
|
|
9001
9324
|
// === API Bridge ===
|
|
9002
9325
|
var api = window.api || {
|
|
9003
|
-
queueAction: async function(action, input) {
|
|
9326
|
+
queueAction: async function(action, input, requestedTarget) {
|
|
9004
9327
|
try {
|
|
9005
|
-
var target = normalizeQueuedConversationTarget(currentConversationTarget());
|
|
9328
|
+
var target = normalizeQueuedConversationTarget(requestedTarget || currentConversationTarget());
|
|
9006
9329
|
if (!target) return { ok: false, error: 'No active conversation target' };
|
|
9007
9330
|
var body = {
|
|
9008
9331
|
workspaceId: target.workspaceId,
|
|
9009
9332
|
conversationId: target.conversationId,
|
|
9010
|
-
action: action
|
|
9011
|
-
id: String(input && input.id || ''),
|
|
9012
|
-
text: String(input && input.text || ''),
|
|
9013
|
-
value: String(input && input.text || ''),
|
|
9014
|
-
requestedMode: String(input && input.requestedMode || 'build'),
|
|
9015
|
-
goalObjective: String(input && input.goalObjective || ''),
|
|
9016
|
-
createdAt: String(input && input.createdAt || ''),
|
|
9333
|
+
action: action
|
|
9017
9334
|
};
|
|
9018
|
-
|
|
9335
|
+
['id', 'text', 'value', 'requestedMode', 'goalObjective', 'createdAt', 'images', 'paused', 'orderedIds'].forEach(function(field) {
|
|
9336
|
+
if (input && Object.prototype.hasOwnProperty.call(input, field)) body[field] = input[field];
|
|
9337
|
+
});
|
|
9019
9338
|
var r = await fetch('/api/queue-action', {
|
|
9020
9339
|
method: 'POST',
|
|
9021
9340
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -9024,11 +9343,20 @@ var api = window.api || {
|
|
|
9024
9343
|
return r.json();
|
|
9025
9344
|
} catch(e) { return { ok: false, error: e.message }; }
|
|
9026
9345
|
},
|
|
9027
|
-
sendMessage: async function(msg,
|
|
9346
|
+
sendMessage: async function(msg, requestedTarget, options) {
|
|
9028
9347
|
try {
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9348
|
+
options = options || {};
|
|
9349
|
+
var target = normalizeQueuedConversationTarget(typeof requestedTarget === 'object' ? requestedTarget : currentConversationTarget(requestedTarget));
|
|
9350
|
+
var body = {message: msg, mode: options.requestedMode || state.mode, model: state.model, intelligence: state.intelligence, inputMode: options.inputMode || state.inputMode};
|
|
9351
|
+
if (target) { body.conversation = target.conversationId; body.conversationId = target.conversationId; body.workspaceId = target.workspaceId; }
|
|
9352
|
+
body.requestedMode = options.requestedMode || state.mode;
|
|
9353
|
+
body.goalObjective = options.goalObjective || '';
|
|
9354
|
+
body.clientMessageId = options.clientMessageId || '';
|
|
9355
|
+
if (body.mode === 'flow') {
|
|
9356
|
+
body.flowName = options.flowName || state.defaultFlow;
|
|
9357
|
+
body.defaultFlow = body.flowName;
|
|
9358
|
+
}
|
|
9359
|
+
if (options.flowStart !== undefined) body.flowStart = options.flowStart;
|
|
9032
9360
|
var r = await fetch('/api/send', {
|
|
9033
9361
|
method: 'POST',
|
|
9034
9362
|
headers: {'Content-Type': 'application/json'},
|
|
@@ -9037,8 +9365,13 @@ var api = window.api || {
|
|
|
9037
9365
|
return r.json();
|
|
9038
9366
|
} catch(e) { return {error: e.message}; }
|
|
9039
9367
|
},
|
|
9040
|
-
getState: async function() {
|
|
9041
|
-
try {
|
|
9368
|
+
getState: async function(requestedTarget) {
|
|
9369
|
+
try {
|
|
9370
|
+
var target = normalizeQueuedConversationTarget(typeof requestedTarget === 'object' ? requestedTarget : currentConversationTarget(requestedTarget));
|
|
9371
|
+
var query = target ? '?' + new URLSearchParams({ workspaceId: target.workspaceId, conversationId: target.conversationId }).toString() : '';
|
|
9372
|
+
var r = await fetch('/api/state' + query);
|
|
9373
|
+
return r.json();
|
|
9374
|
+
}
|
|
9042
9375
|
catch(e) { return {}; }
|
|
9043
9376
|
},
|
|
9044
9377
|
getConversationDraft: async function(conversationId) {
|
|
@@ -9048,8 +9381,19 @@ var api = window.api || {
|
|
|
9048
9381
|
saveConversationDraft: async function(conversationId, draft) {
|
|
9049
9382
|
try { await fetch('/api/conversation-draft', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({conversationId:conversationId || '', draft:draft === null || draft === undefined ? '' : String(draft)})}); } catch(e) {}
|
|
9050
9383
|
},
|
|
9051
|
-
setMode: async function(m) {
|
|
9052
|
-
try {
|
|
9384
|
+
setMode: async function(m, requestedTarget) {
|
|
9385
|
+
try {
|
|
9386
|
+
var target = normalizeQueuedConversationTarget(requestedTarget || currentConversationTarget());
|
|
9387
|
+
var r = await fetch('/api/mode', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(Object.assign({mode:m}, target || {}))});
|
|
9388
|
+
return r.json();
|
|
9389
|
+
} catch(e) { return { ok: false, error: e.message }; }
|
|
9390
|
+
},
|
|
9391
|
+
setInputMode: async function(mode, requestedTarget) {
|
|
9392
|
+
try {
|
|
9393
|
+
var target = normalizeQueuedConversationTarget(requestedTarget || currentConversationTarget());
|
|
9394
|
+
var r = await fetch('/api/input-mode', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(Object.assign({inputMode: mode}, target || {}))});
|
|
9395
|
+
return r.json();
|
|
9396
|
+
} catch(e) { return { ok: false, error: e.message }; }
|
|
9053
9397
|
},
|
|
9054
9398
|
setModel: async function(m) {
|
|
9055
9399
|
try { await fetch('/api/model', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({model:m})}); } catch(e) {}
|
|
@@ -10293,7 +10637,7 @@ function registerRuntimeKey(target, runtimeKey) {
|
|
|
10293
10637
|
if (!state.runtimeKeyAliases) state.runtimeKeyAliases = {};
|
|
10294
10638
|
state.runtimeKeyAliases[base] = runtimeKey;
|
|
10295
10639
|
if (previous !== runtimeKey) {
|
|
10296
|
-
['runningConversations', 'conversationRuntimeStates', 'agentWorkEventsByConversation', 'agentWorkUiByConversation', 'workRunsByTarget', 'workRunsByBranch', 'runtimeBranchPathsByTarget', 'guideMessagesByTarget', 'trackedConversationUntil', 'backendQueuesByTarget', 'activeSendCallsByTarget', 'nextQueueSchedulesByTarget', 'nextQueueDrainsByTarget', 'queuePausedByTarget', 'conversationDrafts'].forEach(function(storeName) {
|
|
10640
|
+
['runningConversations', 'conversationRuntimeStates', 'conversationWorkRevisions', 'agentWorkEventsByConversation', 'agentWorkUiByConversation', 'workRunsByTarget', 'workRunsByBranch', 'runtimeBranchPathsByTarget', 'guideMessagesByTarget', 'trackedConversationUntil', 'backendQueuesByTarget', 'activeSendCallsByTarget', 'nextQueueSchedulesByTarget', 'nextQueueDrainsByTarget', 'queuePausedByTarget', 'conversationDrafts'].forEach(function(storeName) {
|
|
10297
10641
|
var store = state[storeName];
|
|
10298
10642
|
if (!store || store[previous] === undefined) return;
|
|
10299
10643
|
if (store[runtimeKey] === undefined) store[runtimeKey] = store[previous];
|
|
@@ -10535,12 +10879,14 @@ function setConversationRuntimeState(target, status, runId, extra) {
|
|
|
10535
10879
|
target = target || currentConversationTarget();
|
|
10536
10880
|
var key = runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
10537
10881
|
if (!state.conversationRuntimeStates) state.conversationRuntimeStates = {};
|
|
10882
|
+
var previousRuntime = state.conversationRuntimeStates[key];
|
|
10538
10883
|
var next = Object.assign({}, state.conversationRuntimeStates[key] || {}, extra || {}, {
|
|
10539
10884
|
target: { workspaceId: String(target.workspaceId || ''), conversationId: String(target.conversationId || 'default') },
|
|
10540
10885
|
status: String(status || 'idle'),
|
|
10541
10886
|
runId: String(runId || (state.conversationRuntimeStates[key] && state.conversationRuntimeStates[key].runId) || '')
|
|
10542
10887
|
});
|
|
10543
10888
|
state.conversationRuntimeStates[key] = next;
|
|
10889
|
+
if (!previousRuntime || previousRuntime.runId !== next.runId || previousRuntime.status !== next.status) advanceConversationWorkRevision(target);
|
|
10544
10890
|
if (!state.runningConversations) state.runningConversations = {};
|
|
10545
10891
|
if (['running', 'stopping', 'force_restarting'].indexOf(next.status) >= 0) state.runningConversations[key] = next;
|
|
10546
10892
|
else delete state.runningConversations[key];
|
|
@@ -10595,6 +10941,9 @@ function requestBackendStopForProvisionalStart(target, runId) {
|
|
|
10595
10941
|
? api.stopConversation({ target: target, runId: String(runId || ''), force: false })
|
|
10596
10942
|
: (api.abortConversation ? api.abortConversation(target) : Promise.resolve({ action: 'not_running' }));
|
|
10597
10943
|
Promise.resolve(stopPromise).then(function(result) {
|
|
10944
|
+
if (!conversationRunStillOwnsTarget(target, runId)) return;
|
|
10945
|
+
var currentStop = state.conversationRuntimeStates && state.conversationRuntimeStates[runtimeKeyFor(target.workspaceId, target.conversationId)];
|
|
10946
|
+
if (currentStop && currentStop.status !== 'stopping') return;
|
|
10598
10947
|
var action = String(result && result.action || '');
|
|
10599
10948
|
if (action === 'not_running' || action === 'stale') {
|
|
10600
10949
|
return refreshConversationRuntimeAfterStopRace(target, runId, result);
|
|
@@ -10611,6 +10960,9 @@ function requestBackendStopForProvisionalStart(target, runId) {
|
|
|
10611
10960
|
setConversationRuntimeState(target, 'interrupted', runId, { provisional: false });
|
|
10612
10961
|
}
|
|
10613
10962
|
}).catch(function(error) {
|
|
10963
|
+
if (!conversationRunStillOwnsTarget(target, runId)) return;
|
|
10964
|
+
var currentStop = state.conversationRuntimeStates && state.conversationRuntimeStates[runtimeKeyFor(target.workspaceId, target.conversationId)];
|
|
10965
|
+
if (currentStop && currentStop.status !== 'stopping') return;
|
|
10614
10966
|
setConversationRuntimeState(target, 'running', runId, { provisional: false });
|
|
10615
10967
|
showUiNotice(error && error.message ? error.message : String(error), 'error', 'stop-provisional-' + runtimeKeyFor(target.workspaceId, target.conversationId) + '-' + String(runId || ''));
|
|
10616
10968
|
});
|
|
@@ -10728,6 +11080,7 @@ function publicWorkEventForUi(event) {
|
|
|
10728
11080
|
model: String(event.model || ''),
|
|
10729
11081
|
timestamp: String(event.timestamp || ''),
|
|
10730
11082
|
toolName: toolName,
|
|
11083
|
+
toolCallId: event.toolCallId,
|
|
10731
11084
|
toolArgs: type === 'tool_call' ? String(event.toolArgs || '') : '',
|
|
10732
11085
|
workspaceId: String(event.workspaceId || (event.target && event.target.workspaceId) || ''),
|
|
10733
11086
|
workspaceKey: event.workspaceKey,
|
|
@@ -10916,9 +11269,10 @@ function syncWorkRunsSnapshot(runs, target, branchId) {
|
|
|
10916
11269
|
if (liveOnlyEvents.length) {
|
|
10917
11270
|
item.events = dedupeGuideWorkEvents(item.events.concat(liveOnlyEvents)).sort(compareConversationWorkEvents);
|
|
10918
11271
|
item.sequence = Math.max(Number(item.sequence || 0), Number(previous.sequence || 0));
|
|
10919
|
-
if (
|
|
11272
|
+
if (Number(previous.sequence || 0) > Number(run.sequence || 0)) {
|
|
10920
11273
|
item.status = previous.status;
|
|
10921
11274
|
item.endedAt = previous.endedAt;
|
|
11275
|
+
item.expanded = previous.expanded;
|
|
10922
11276
|
}
|
|
10923
11277
|
}
|
|
10924
11278
|
}
|
|
@@ -11473,6 +11827,31 @@ function renderWorkRunEvents(run, includeGuides) {
|
|
|
11473
11827
|
}).join('') + '</div>';
|
|
11474
11828
|
}
|
|
11475
11829
|
|
|
11830
|
+
// A length/ID key alone cannot detect an equal-length correction or the
|
|
11831
|
+
// transition from large live plain text to terminal Markdown. Keep the exact
|
|
11832
|
+
// render input on the DOM node, not in a large data-* attribute or global cache.
|
|
11833
|
+
function workRunEventRenderState(event, run, terminalInterrupted) {
|
|
11834
|
+
var type = String(event && event.type || '').toLowerCase();
|
|
11835
|
+
if (['public_text', 'partial_text', 'response', 'thought'].indexOf(type) < 0) return null;
|
|
11836
|
+
var live = ['running', 'stopping', 'force_restarting'].indexOf(String(run && run.status || '').toLowerCase()) >= 0;
|
|
11837
|
+
return currentLang() + ':' + (live ? 'live' : 'terminal') + ':' + (terminalInterrupted ? 'partial' : 'whole') + ':' + String(event && event.content || '');
|
|
11838
|
+
}
|
|
11839
|
+
|
|
11840
|
+
function rememberWorkRunEventRenderStates(run, body, expanded) {
|
|
11841
|
+
var container = body && body.querySelector('.conversation-work-run-events');
|
|
11842
|
+
if (!container) return;
|
|
11843
|
+
var presented = presentedWorkRunEvents(run, expanded);
|
|
11844
|
+
Array.prototype.forEach.call(container.children, function(node, index) {
|
|
11845
|
+
node._newmarkWorkEventRenderState = workRunEventRenderState(presented.events[index], run, presented.terminalInterrupted);
|
|
11846
|
+
});
|
|
11847
|
+
}
|
|
11848
|
+
|
|
11849
|
+
function setWorkRunStackHtml(stack, html) {
|
|
11850
|
+
if (stack._newmarkWorkStackHtml === html) return;
|
|
11851
|
+
stack.innerHTML = html;
|
|
11852
|
+
stack._newmarkWorkStackHtml = html;
|
|
11853
|
+
}
|
|
11854
|
+
|
|
11476
11855
|
// 增量更新 run body 内的事件列表:保留与新呈现列表 key 一致的前缀节点,
|
|
11477
11856
|
// 从第一个分歧节点起重建尾部。运行中通常只有最后一个文本/thought 节点
|
|
11478
11857
|
// 增长或尾部追加,因此不会整块重绘整个进程渲染。
|
|
@@ -11481,6 +11860,7 @@ function updateWorkRunEventsContainer(run, body, expanded) {
|
|
|
11481
11860
|
var container = body.querySelector ? body.querySelector('.conversation-work-run-events') : null;
|
|
11482
11861
|
if (!container) {
|
|
11483
11862
|
body.innerHTML = renderWorkRunEvents(run, expanded);
|
|
11863
|
+
rememberWorkRunEventRenderStates(run, body, expanded);
|
|
11484
11864
|
return;
|
|
11485
11865
|
}
|
|
11486
11866
|
if (!presented.events.length) {
|
|
@@ -11491,7 +11871,8 @@ function updateWorkRunEventsContainer(run, body, expanded) {
|
|
|
11491
11871
|
var nodes = Array.prototype.slice.call(container.children);
|
|
11492
11872
|
var keep = 0;
|
|
11493
11873
|
while (keep < nodes.length && keep < presented.events.length) {
|
|
11494
|
-
if (String(nodes[keep].getAttribute('data-work-event-key') || '') === workRunPresentedEventKey(presented.events[keep], keep)
|
|
11874
|
+
if (String(nodes[keep].getAttribute('data-work-event-key') || '') === workRunPresentedEventKey(presented.events[keep], keep)
|
|
11875
|
+
&& nodes[keep]._newmarkWorkEventRenderState === workRunEventRenderState(presented.events[keep], run, presented.terminalInterrupted)) keep++;
|
|
11495
11876
|
else break;
|
|
11496
11877
|
}
|
|
11497
11878
|
while (container.children.length > keep) container.removeChild(container.lastChild);
|
|
@@ -11502,6 +11883,7 @@ function updateWorkRunEventsContainer(run, body, expanded) {
|
|
|
11502
11883
|
while (holder.firstChild) {
|
|
11503
11884
|
var node = holder.firstChild;
|
|
11504
11885
|
node.setAttribute('data-work-event-key', key);
|
|
11886
|
+
node._newmarkWorkEventRenderState = workRunEventRenderState(presented.events[i], run, presented.terminalInterrupted);
|
|
11505
11887
|
container.appendChild(node);
|
|
11506
11888
|
}
|
|
11507
11889
|
}
|
|
@@ -11546,6 +11928,7 @@ function updateConversationWorkRunElement(run, element) {
|
|
|
11546
11928
|
'<span class="conversation-work-run-status">' + esc(String(run.status || '')) + '</span>' +
|
|
11547
11929
|
'<span class="conversation-work-run-chevron" aria-hidden="true"></span></button>' +
|
|
11548
11930
|
'<div class="conversation-work-run-body">' + renderWorkRunEvents(run, expanded) + '</div>';
|
|
11931
|
+
rememberWorkRunEventRenderStates(run, element.querySelector('.conversation-work-run-body'), expanded);
|
|
11549
11932
|
} else {
|
|
11550
11933
|
// 就地更新头部,不重建 head 按钮(保住聚焦/动画状态)。
|
|
11551
11934
|
var titleEl = head.querySelector('.conversation-work-run-title');
|
|
@@ -11581,7 +11964,10 @@ function updateConversationWorkRunElement(run, element) {
|
|
|
11581
11964
|
}
|
|
11582
11965
|
var body = element.querySelector('.conversation-work-run-body');
|
|
11583
11966
|
if (body) updateWorkRunEventsContainer(run, body, expanded);
|
|
11584
|
-
else
|
|
11967
|
+
else {
|
|
11968
|
+
element.insertAdjacentHTML('beforeend', '<div class="conversation-work-run-body">' + renderWorkRunEvents(run, expanded) + '</div>');
|
|
11969
|
+
rememberWorkRunEventRenderStates(run, element.querySelector('.conversation-work-run-body'), expanded);
|
|
11970
|
+
}
|
|
11585
11971
|
}
|
|
11586
11972
|
renderPendingGuideMessages(run.target, {});
|
|
11587
11973
|
var wrapper = element.closest ? element.closest('.work-run-message') : null;
|
|
@@ -11594,14 +11980,14 @@ function updateConversationWorkRunElement(run, element) {
|
|
|
11594
11980
|
imageStack.className = 'work-run-collapsed-images';
|
|
11595
11981
|
}
|
|
11596
11982
|
if (imageStack.previousElementSibling !== element) wrapper.insertBefore(imageStack, element.nextSibling);
|
|
11597
|
-
imageStack
|
|
11983
|
+
setWorkRunStackHtml(imageStack, expanded ? '' : displayImages.map(renderWorkDisplayImage).join(''));
|
|
11598
11984
|
var guideStack = wrapper.querySelector(':scope > .work-run-collapsed-guides');
|
|
11599
11985
|
if (!guideStack) {
|
|
11600
11986
|
guideStack = document.createElement('div');
|
|
11601
11987
|
guideStack.className = 'work-run-collapsed-guides';
|
|
11602
11988
|
wrapper.appendChild(guideStack);
|
|
11603
11989
|
}
|
|
11604
|
-
guideStack
|
|
11990
|
+
setWorkRunStackHtml(guideStack, expanded ? '' : workRunGuideEvents(run).map(renderWorkRunGuideMessage).join(''));
|
|
11605
11991
|
}
|
|
11606
11992
|
hydrateWorkDisplayImages(wrapper || element, (run.events || []).map(function(event) { return event && event.displayImage; }).filter(Boolean));
|
|
11607
11993
|
hydrateWorkRunGuideMessages(wrapper || element, run);
|
|
@@ -12085,7 +12471,10 @@ function renderChatMessages(messages, target) {
|
|
|
12085
12471
|
var liveArea = els['chat-area'];
|
|
12086
12472
|
if (!liveArea) return;
|
|
12087
12473
|
var previousRendered = Array.isArray(state.renderedChatMessages) ? state.renderedChatMessages.slice() : [];
|
|
12088
|
-
|
|
12474
|
+
// Staging leaves the live transcript untouched until commit. Keep its nodes
|
|
12475
|
+
// for rollback instead of serializing a potentially long HTML transcript;
|
|
12476
|
+
// this also preserves listeners if rendering fails.
|
|
12477
|
+
var previousNodes = Array.prototype.slice.call(liveArea.childNodes);
|
|
12089
12478
|
var previousScrollTop = liveArea.scrollTop;
|
|
12090
12479
|
var wasAtBottom = shouldAutoScroll(liveArea);
|
|
12091
12480
|
var stagingArea = liveArea.cloneNode(false);
|
|
@@ -12097,7 +12486,7 @@ function renderChatMessages(messages, target) {
|
|
|
12097
12486
|
else liveArea.scrollTop = previousScrollTop;
|
|
12098
12487
|
} catch (error) {
|
|
12099
12488
|
state.renderedChatMessages = previousRendered;
|
|
12100
|
-
liveArea.
|
|
12489
|
+
liveArea.replaceChildren.apply(liveArea, previousNodes);
|
|
12101
12490
|
liveArea.scrollTop = previousScrollTop;
|
|
12102
12491
|
console.error('[ui_chat_render_failed]', error);
|
|
12103
12492
|
showUiNotice(currentLang() === 'zh' ? '对话渲染出现异常,已保留原内容。' : 'Chat rendering failed; the previous content was preserved.', 'error', 'chat-render-failed');
|
|
@@ -12278,20 +12667,25 @@ function renderLoadEarlierButton() {
|
|
|
12278
12667
|
button.className = 'conversation-load-earlier';
|
|
12279
12668
|
button.textContent = currentLang() === 'zh' ? ('加载更早的 ' + Number(before) + ' 条消息') : ('Load ' + Number(before) + ' earlier messages');
|
|
12280
12669
|
button.addEventListener('click', function() {
|
|
12281
|
-
var loadTarget =
|
|
12670
|
+
var loadTarget = target;
|
|
12671
|
+
if (!isActiveConversationTarget(loadTarget) || button.disabled) return;
|
|
12282
12672
|
var loadKey = runtimeKeyFor(loadTarget.workspaceId, loadTarget.conversationId);
|
|
12283
12673
|
var loadBefore = state.conversationLoadedBefore && state.conversationLoadedBefore[loadKey];
|
|
12284
12674
|
if (!Number.isFinite(Number(loadBefore)) || Number(loadBefore) <= 0) return;
|
|
12675
|
+
var loadGeneration = state.conversationLoadGeneration;
|
|
12676
|
+
var loadBranch = JSON.stringify([state.activeConversationBranchId || '', state.viewedConversationBranchNodePath || []]);
|
|
12285
12677
|
button.disabled = true;
|
|
12286
12678
|
api.loadEarlierConversationMessages({ workspaceId: loadTarget.workspaceId, conversationId: loadTarget.conversationId, window: 200, before: loadBefore }).then(function(result) {
|
|
12287
12679
|
button.disabled = false;
|
|
12680
|
+
if (!isActiveConversationTarget(loadTarget) || loadGeneration !== state.conversationLoadGeneration
|
|
12681
|
+
|| loadBranch !== JSON.stringify([state.activeConversationBranchId || '', state.viewedConversationBranchNodePath || []])
|
|
12682
|
+
|| Number(state.conversationLoadedBefore[loadKey]) !== Number(loadBefore)) return;
|
|
12288
12683
|
if (!result || !Array.isArray(result.chatMessages) || !result.chatMessages.length) return;
|
|
12289
12684
|
var newer = state.renderedChatMessages || [];
|
|
12290
|
-
|
|
12685
|
+
var mergedMessages = result.chatMessages.concat(newer);
|
|
12291
12686
|
state.conversationLoadedBefore[loadKey] = Math.max(0, Number(result.windowStart) || 0);
|
|
12292
12687
|
var chatWasAtBottom = shouldAutoScroll(els['chat-area']);
|
|
12293
|
-
|
|
12294
|
-
renderChatMessages(state.renderedChatMessages);
|
|
12688
|
+
renderChatMessages(mergedMessages, loadTarget);
|
|
12295
12689
|
if (!chatWasAtBottom && els['chat-area']) els['chat-area'].scrollTop = 0;
|
|
12296
12690
|
renderLoadEarlierButton();
|
|
12297
12691
|
}).catch(function() { button.disabled = false; });
|
|
@@ -12553,29 +12947,88 @@ function replayActiveAgentWorkEvents() {
|
|
|
12553
12947
|
}
|
|
12554
12948
|
|
|
12555
12949
|
var _conversationsRenderPending = false;
|
|
12950
|
+
function conversationWorkRevision(target) {
|
|
12951
|
+
var key = runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
12952
|
+
return Number(state.conversationWorkRevisions && state.conversationWorkRevisions[key] || 0);
|
|
12953
|
+
}
|
|
12954
|
+
function advanceConversationWorkRevision(target) {
|
|
12955
|
+
if (!state.conversationWorkRevisions) state.conversationWorkRevisions = {};
|
|
12956
|
+
state.conversationWorkRevisions[runtimeKeyFor(target.workspaceId, target.conversationId)] = conversationWorkRevision(target) + 1;
|
|
12957
|
+
}
|
|
12958
|
+
function captureConversationWorkSnapshotRequest(target) {
|
|
12959
|
+
return { target: { workspaceId: target.workspaceId, conversationId: target.conversationId },
|
|
12960
|
+
loadGeneration: state.conversationLoadGeneration, revision: conversationWorkRevision(target) };
|
|
12961
|
+
}
|
|
12962
|
+
function isCurrentConversationWorkSnapshotRequest(request) {
|
|
12963
|
+
return !!request && isActiveConversationTarget(request.target)
|
|
12964
|
+
&& request.loadGeneration === state.conversationLoadGeneration
|
|
12965
|
+
&& request.revision === conversationWorkRevision(request.target);
|
|
12966
|
+
}
|
|
12967
|
+
function conversationRunStillOwnsTarget(target, runId, alternateRunId) {
|
|
12968
|
+
var latest = state.conversationRuntimeStates && state.conversationRuntimeStates[runtimeKeyFor(target.workspaceId, target.conversationId)];
|
|
12969
|
+
return !latest || !latest.runId || String(latest.runId) === String(runId || '')
|
|
12970
|
+
|| (!!alternateRunId && String(latest.runId) === String(alternateRunId));
|
|
12971
|
+
}
|
|
12556
12972
|
var _contextWindowRefreshTimer = null;
|
|
12557
12973
|
var _contextWindowRefreshInFlight = false;
|
|
12558
12974
|
var _contextWindowRefreshTargetKey = '';
|
|
12975
|
+
var _contextWindowRefreshPendingTarget = null;
|
|
12976
|
+
var _contextWindowRefreshRevision = 0;
|
|
12977
|
+
var _contextWindowSnapshotRequestId = 0;
|
|
12978
|
+
|
|
12979
|
+
function captureContextWindowSnapshotRequest(target) {
|
|
12980
|
+
return {
|
|
12981
|
+
target: { workspaceId: target.workspaceId, conversationId: target.conversationId },
|
|
12982
|
+
loadGeneration: state.conversationLoadGeneration,
|
|
12983
|
+
revision: _contextWindowRefreshRevision,
|
|
12984
|
+
requestId: isActiveConversationTarget(target) ? ++_contextWindowSnapshotRequestId : 0
|
|
12985
|
+
};
|
|
12986
|
+
}
|
|
12987
|
+
|
|
12988
|
+
function isCurrentContextWindowSnapshotRequest(request) {
|
|
12989
|
+
return !!request && isActiveConversationTarget(request.target)
|
|
12990
|
+
&& request.loadGeneration === state.conversationLoadGeneration
|
|
12991
|
+
&& request.revision === _contextWindowRefreshRevision
|
|
12992
|
+
&& request.requestId === _contextWindowSnapshotRequestId;
|
|
12993
|
+
}
|
|
12994
|
+
|
|
12995
|
+
function applyContextWindowSnapshot(snapshot, request) {
|
|
12996
|
+
if (!snapshot || !isCurrentContextWindowSnapshotRequest(request)) return false;
|
|
12997
|
+
if (snapshot.contextCompression !== undefined) state.contextCompression = snapshot.contextCompression;
|
|
12998
|
+
if (snapshot.contextWindow !== undefined) state.contextWindow = snapshot.contextWindow;
|
|
12999
|
+
if (snapshot.contextWindow !== undefined || snapshot.contextCompression !== undefined) window.renderContextWindow();
|
|
13000
|
+
return true;
|
|
13001
|
+
}
|
|
12559
13002
|
|
|
12560
13003
|
function scheduleActiveContextWindowRefresh(target) {
|
|
12561
13004
|
if (!api.getState || !target || !isActiveConversationTarget(target)) return;
|
|
12562
13005
|
var targetKey = runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
12563
13006
|
_contextWindowRefreshTargetKey = targetKey;
|
|
12564
|
-
|
|
13007
|
+
_contextWindowRefreshPendingTarget = { workspaceId: target.workspaceId, conversationId: target.conversationId };
|
|
13008
|
+
_contextWindowRefreshRevision += 1;
|
|
13009
|
+
if (_contextWindowRefreshTimer !== null || _contextWindowRefreshInFlight) return;
|
|
12565
13010
|
_contextWindowRefreshTimer = window.setTimeout(function() {
|
|
12566
13011
|
_contextWindowRefreshTimer = null;
|
|
12567
|
-
|
|
13012
|
+
var requestTarget = _contextWindowRefreshPendingTarget;
|
|
13013
|
+
_contextWindowRefreshPendingTarget = null;
|
|
13014
|
+
if (!requestTarget || !isActiveConversationTarget(requestTarget)) return;
|
|
13015
|
+
var requestKey = runtimeKeyFor(requestTarget.workspaceId, requestTarget.conversationId);
|
|
13016
|
+
var requestRevision = _contextWindowRefreshRevision;
|
|
13017
|
+
var loadGeneration = state.conversationLoadGeneration;
|
|
13018
|
+
var contextRequest = captureContextWindowSnapshotRequest(requestTarget);
|
|
12568
13019
|
_contextWindowRefreshInFlight = true;
|
|
12569
|
-
api.getState(
|
|
12570
|
-
if (!snapshot || !isActiveConversationTarget(
|
|
12571
|
-
|
|
12572
|
-
|
|
12573
|
-
window.renderContextWindow();
|
|
12574
|
-
}
|
|
12575
|
-
if (snapshot.contextCompression !== undefined) state.contextCompression = snapshot.contextCompression;
|
|
13020
|
+
api.getState(requestTarget).then(function(snapshot) {
|
|
13021
|
+
if (!snapshot || !isActiveConversationTarget(requestTarget) || _contextWindowRefreshTargetKey !== requestKey
|
|
13022
|
+
|| requestRevision !== _contextWindowRefreshRevision || loadGeneration !== state.conversationLoadGeneration) return;
|
|
13023
|
+
applyContextWindowSnapshot(snapshot, contextRequest);
|
|
12576
13024
|
if (state.rightTab === 'status') window.renderRightStatusPanel();
|
|
12577
13025
|
}).catch(function(){}).then(function() {
|
|
12578
13026
|
_contextWindowRefreshInFlight = false;
|
|
13027
|
+
// A semantic event during IPC still requires one fresh snapshot. Keep
|
|
13028
|
+
// only the latest target, with no polling once the event stream is quiet.
|
|
13029
|
+
var pendingTarget = _contextWindowRefreshPendingTarget;
|
|
13030
|
+
_contextWindowRefreshPendingTarget = null;
|
|
13031
|
+
if (pendingTarget) scheduleActiveContextWindowRefresh(pendingTarget);
|
|
12579
13032
|
});
|
|
12580
13033
|
}, 120);
|
|
12581
13034
|
}
|
|
@@ -12589,8 +13042,49 @@ function scheduleConversationsRender() {
|
|
|
12589
13042
|
});
|
|
12590
13043
|
}
|
|
12591
13044
|
|
|
13045
|
+
function applyWorkspaceConversationList(event) {
|
|
13046
|
+
if (event.stateScope !== 'workspace' || !String(event.workspaceId || '').trim() || !Array.isArray(event.conversations)) return;
|
|
13047
|
+
var workspaceId = String(event.workspaceId).trim();
|
|
13048
|
+
var key = currentWorkspaceKey(workspaceId);
|
|
13049
|
+
var foreground = key === currentWorkspaceKey();
|
|
13050
|
+
var rows = (state.workspaceConversations && state.workspaceConversations[key]) || [];
|
|
13051
|
+
var selectedIndex = state.workspaceActiveConversation && state.workspaceActiveConversation[key];
|
|
13052
|
+
var selected = rows[selectedIndex] || rows.find(function(row) { return row.active; });
|
|
13053
|
+
var selectedId = foreground ? activeConversationId() : String(selected && selected.id || '');
|
|
13054
|
+
// A remote create/rename/delete changes this workspace's list, never the
|
|
13055
|
+
// desktop user's selected conversation or the execution target.
|
|
13056
|
+
applyBackendConversations(event.conversations, selectedId, workspaceId);
|
|
13057
|
+
if (foreground) scheduleConversationsRender();
|
|
13058
|
+
}
|
|
13059
|
+
|
|
13060
|
+
function expandedWorkEventDeltas(event) {
|
|
13061
|
+
if (event && (event.type === 'text' || event.type === 'thought_delta') && Array.isArray(event.coalescedDeltas) && event.coalescedDeltas.length) {
|
|
13062
|
+
return event.coalescedDeltas.map(function(delta) { return Object.assign({}, event, delta, { coalescedDeltas: undefined }); });
|
|
13063
|
+
}
|
|
13064
|
+
return [event];
|
|
13065
|
+
}
|
|
13066
|
+
|
|
13067
|
+
function mergeConversationWorkEventSnapshots(existing, snapshot) {
|
|
13068
|
+
var seen = {};
|
|
13069
|
+
var result = [];
|
|
13070
|
+
existing.concat(snapshot).forEach(function(event) {
|
|
13071
|
+
expandedWorkEventDeltas(event).forEach(function(item) {
|
|
13072
|
+
if (!item) return;
|
|
13073
|
+
var id = String(item.id || [item.timestamp || '', item.type || '', item.content || '', item.toolName || '', item.toolArgs || ''].join('|'));
|
|
13074
|
+
if (seen[id]) return;
|
|
13075
|
+
seen[id] = true;
|
|
13076
|
+
result.push(item);
|
|
13077
|
+
});
|
|
13078
|
+
});
|
|
13079
|
+
return result;
|
|
13080
|
+
}
|
|
13081
|
+
|
|
12592
13082
|
function appendAgentWorkEvent(event) {
|
|
12593
13083
|
if (!event) return;
|
|
13084
|
+
if ((event.type === 'text' || event.type === 'thought_delta') && Array.isArray(event.coalescedDeltas) && event.coalescedDeltas.length) {
|
|
13085
|
+
expandedWorkEventDeltas(event).forEach(appendAgentWorkEvent);
|
|
13086
|
+
return;
|
|
13087
|
+
}
|
|
12594
13088
|
// Subagent (peer) work events are streamed to their own live history overlay
|
|
12595
13089
|
// and never touch the main conversation's chat/runtime state.
|
|
12596
13090
|
if (String(event.actorId || '')) {
|
|
@@ -12613,12 +13107,24 @@ function appendAgentWorkEvent(event) {
|
|
|
12613
13107
|
}
|
|
12614
13108
|
return;
|
|
12615
13109
|
}
|
|
13110
|
+
if (String(event.type || '') === 'conversation_list') {
|
|
13111
|
+
applyWorkspaceConversationList(event);
|
|
13112
|
+
return;
|
|
13113
|
+
}
|
|
12616
13114
|
event = publicWorkEventForUi(event);
|
|
12617
13115
|
var id = String(event.conversationId || (event.target && event.target.conversationId) || 'default');
|
|
12618
13116
|
var workspaceId = String(event.workspaceId || (event.target && event.target.workspaceId) || runtimeWorkspaceId(''));
|
|
12619
13117
|
var target = { workspaceId: workspaceId, conversationId: id };
|
|
12620
13118
|
eventRuntimeKey(event);
|
|
13119
|
+
if (event.runId) advanceConversationWorkRevision(target);
|
|
12621
13120
|
var active = isActiveConversationTarget(target);
|
|
13121
|
+
if (event.type === 'queue_update') {
|
|
13122
|
+
// Queue ownership is independent of a renderer send promise and of the
|
|
13123
|
+
// history branch being viewed. Remote edits must refresh every target.
|
|
13124
|
+
applyConversationCommandSnapshot(event, target);
|
|
13125
|
+
if (active) window.renderInputStack();
|
|
13126
|
+
return;
|
|
13127
|
+
}
|
|
12622
13128
|
var visibleRuntimeBranch = active && isViewingRuntimeConversationBranch(target);
|
|
12623
13129
|
// 模型回退不是隐藏的参数回退:活跃对话发生回退时,把输入框下方的
|
|
12624
13130
|
// 模型选择区同步为实际生效的模型。
|
|
@@ -12685,13 +13191,10 @@ function appendAgentWorkEvent(event) {
|
|
|
12685
13191
|
}
|
|
12686
13192
|
if (active && terminalEvent && api.getState) {
|
|
12687
13193
|
var terminalWorkspaceKey = currentWorkspaceKey();
|
|
13194
|
+
var terminalContextRequest = captureContextWindowSnapshotRequest(target);
|
|
12688
13195
|
api.getState(target).then(function(snapshot) {
|
|
12689
|
-
if (!
|
|
12690
|
-
|
|
12691
|
-
state.contextWindow = snapshot.contextWindow;
|
|
12692
|
-
window.renderContextWindow();
|
|
12693
|
-
}
|
|
12694
|
-
if (snapshot && snapshot.contextCompression !== undefined) state.contextCompression = snapshot.contextCompression;
|
|
13196
|
+
if (!isCurrentContextWindowSnapshotRequest(terminalContextRequest) || terminalWorkspaceKey !== currentWorkspaceKey()) return;
|
|
13197
|
+
applyContextWindowSnapshot(snapshot, terminalContextRequest);
|
|
12695
13198
|
if (state.rightTab === 'status') window.renderRightStatusPanel();
|
|
12696
13199
|
// Backend-owned Goal continuations have no renderer send promise to
|
|
12697
13200
|
// return the final snapshot. Refresh on the terminal work event so a
|
|
@@ -12779,6 +13282,7 @@ function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
|
12779
13282
|
&& !snapshotHasReadableConversationHistory(s)
|
|
12780
13283
|
&& (!options.allowEmptyHistory || conversationHasReadableHistory(snapshotTarget)));
|
|
12781
13284
|
if (s && s.runtimeKey) registerRuntimeKey(snapshotTarget, s.runtimeKey);
|
|
13285
|
+
hydrateConversationDraftSnapshot(s, snapshotTarget);
|
|
12782
13286
|
hydrateConversationBranchState(s);
|
|
12783
13287
|
rebindQueueToRuntimeBranch(snapshotTarget);
|
|
12784
13288
|
if (s && Array.isArray(s.workRuns) && !preserveReadableHistory) {
|
|
@@ -12788,6 +13292,18 @@ function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
|
12788
13292
|
syncWorkRunsSnapshot(s.workRuns, snapshotTarget, viewedBranchIdForSync || undefined);
|
|
12789
13293
|
}
|
|
12790
13294
|
applyAutoRouteRatingState(s);
|
|
13295
|
+
if (s && Number.isFinite(Number(s.totalMessages))) {
|
|
13296
|
+
if (!state.conversationLoadedBefore) state.conversationLoadedBefore = {};
|
|
13297
|
+
var windowKey = runtimeKeyFor(snapshotTarget.workspaceId, s.conversationId || requestedConversationId || 'default');
|
|
13298
|
+
state.conversationLoadedBefore[windowKey] = Math.max(0, Number(s.totalMessages) - (Array.isArray(s.chatMessages) ? s.chatMessages.length : 0));
|
|
13299
|
+
}
|
|
13300
|
+
if (s.contextCompression !== undefined) state.contextCompression = s.contextCompression;
|
|
13301
|
+
if (s.contextWindow !== undefined || s.contextCompression !== undefined) {
|
|
13302
|
+
_contextWindowRefreshRevision += 1;
|
|
13303
|
+
if (s.contextWindow !== undefined) state.contextWindow = s.contextWindow;
|
|
13304
|
+
window.renderContextWindow();
|
|
13305
|
+
}
|
|
13306
|
+
if (state.rightTab === 'status') window.renderRightStatusPanel();
|
|
12791
13307
|
if (s && Array.isArray(s.chatMessages) && !preserveReadableHistory) {
|
|
12792
13308
|
renderChatMessages(s.chatMessages, snapshotTarget);
|
|
12793
13309
|
renderLoadEarlierButton();
|
|
@@ -12797,11 +13313,6 @@ function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
|
12797
13313
|
applyBackendConversations(s.conversations || [], s.conversationId || requestedConversationId);
|
|
12798
13314
|
}
|
|
12799
13315
|
markConversationTracked(s && s.conversationId ? s.conversationId : requestedConversationId, state.conversationTrackMs || 300000, snapshotTarget.workspaceId);
|
|
12800
|
-
if (s && Number.isFinite(Number(s.totalMessages))) {
|
|
12801
|
-
if (!state.conversationLoadedBefore) state.conversationLoadedBefore = {};
|
|
12802
|
-
var windowKey = runtimeKeyFor(snapshotTarget.workspaceId, s.conversationId || requestedConversationId || 'default');
|
|
12803
|
-
state.conversationLoadedBefore[windowKey] = Math.max(0, Number(s.totalMessages) - (Array.isArray(s.chatMessages) ? s.chatMessages.length : 0));
|
|
12804
|
-
}
|
|
12805
13316
|
if (s && s.queued) {
|
|
12806
13317
|
state.backendQueue = setBackendQueueForTarget(s.queued, snapshotTarget);
|
|
12807
13318
|
if (window.syncNextQueueFromBackend) window.syncNextQueueFromBackend(state.backendQueue, snapshotTarget);
|
|
@@ -12811,16 +13322,7 @@ function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
|
12811
13322
|
var eventConversationId = runtimeKeyFor(snapshotTarget.workspaceId, s.conversationId || requestedConversationId || 'default');
|
|
12812
13323
|
if (!state.agentWorkEventsByConversation) state.agentWorkEventsByConversation = {};
|
|
12813
13324
|
var existingEvents = state.agentWorkEventsByConversation[eventConversationId] || [];
|
|
12814
|
-
var
|
|
12815
|
-
var seenEvents = {};
|
|
12816
|
-
var dedupedEvents = [];
|
|
12817
|
-
for (var wi = 0; wi < mergedEvents.length; wi++) {
|
|
12818
|
-
var ev = mergedEvents[wi] || {};
|
|
12819
|
-
var key = ev.id || [ev.timestamp || '', ev.type || '', ev.content || '', ev.toolName || '', ev.toolArgs || ''].join('|');
|
|
12820
|
-
if (seenEvents[key]) continue;
|
|
12821
|
-
seenEvents[key] = true;
|
|
12822
|
-
dedupedEvents.push(ev);
|
|
12823
|
-
}
|
|
13325
|
+
var dedupedEvents = mergeConversationWorkEventSnapshots(existingEvents, s.workEvents || []);
|
|
12824
13326
|
state.agentWorkEventsByConversation[eventConversationId] = dedupedEvents.slice(-Number(state.agentWorkEventLimit || 240));
|
|
12825
13327
|
}
|
|
12826
13328
|
if (s && s.conversationPlan) {
|
|
@@ -12940,7 +13442,7 @@ function setVisibleMode(mode) {
|
|
|
12940
13442
|
}
|
|
12941
13443
|
if (nextMode === 'flow') window.setInputMode('guide');
|
|
12942
13444
|
if (api.setMode) {
|
|
12943
|
-
return Promise.resolve(api.setMode(nextMode)).then(function(result) {
|
|
13445
|
+
return Promise.resolve(api.setMode(nextMode, currentConversationTarget())).then(function(result) {
|
|
12944
13446
|
if (result != null) state._syncedMode = nextMode;
|
|
12945
13447
|
}).catch(function(){});
|
|
12946
13448
|
}
|
|
@@ -13083,8 +13585,24 @@ function conversationDraftParts(key) {
|
|
|
13083
13585
|
};
|
|
13084
13586
|
}
|
|
13085
13587
|
|
|
13086
|
-
function
|
|
13588
|
+
function hydrateConversationDraftSnapshot(snapshot, target) {
|
|
13589
|
+
if (!snapshot || snapshot.draft === undefined) return;
|
|
13590
|
+
var key = conversationDraftKey(target);
|
|
13591
|
+
// A locally edited or explicitly cleared draft is newer than a returning
|
|
13592
|
+
// snapshot. Only the first hydration may populate this conversation's slot.
|
|
13593
|
+
if (Object.prototype.hasOwnProperty.call(state.conversationDrafts, key)) return;
|
|
13594
|
+
state.conversationDrafts[key] = String(snapshot.draft || '');
|
|
13595
|
+
if (isActiveConversationTarget(target) && (!state._promptBoundKey || state._promptBoundKey === key)) {
|
|
13596
|
+
restoreConversationDraft(target);
|
|
13597
|
+
}
|
|
13598
|
+
}
|
|
13599
|
+
|
|
13600
|
+
function saveActiveConversationDraft(fromInput) {
|
|
13087
13601
|
if (!els.prompt) return;
|
|
13602
|
+
var key = state._promptBoundKey || conversationDraftKey();
|
|
13603
|
+
// A blank, not-yet-hydrated composer is not an instruction to erase a stored
|
|
13604
|
+
// draft. A real input event (including deleting all text) is authoritative.
|
|
13605
|
+
if (!fromInput && !els.prompt.value && !Object.prototype.hasOwnProperty.call(state.conversationDrafts, key)) return;
|
|
13088
13606
|
// The prompt content belongs to the conversation it was last restored for.
|
|
13089
13607
|
// While a conversation switch is still resolving (sync not yet complete),
|
|
13090
13608
|
// the bound key stays on the previous conversation so fast switching cannot
|
|
@@ -13097,17 +13615,21 @@ function saveActiveConversationDraft() {
|
|
|
13097
13615
|
// window. The in-memory window is never dropped, so switching back stays instant.
|
|
13098
13616
|
// Writes are debounced per key (100ms) so rapid conversation switching coalesces
|
|
13099
13617
|
// into a single request per conversation.
|
|
13100
|
-
function persistConversationDraftForKey(key) {
|
|
13618
|
+
function persistConversationDraftForKey(key, delayMs) {
|
|
13101
13619
|
if (!api.saveConversationDraft) return;
|
|
13102
13620
|
var parts = conversationDraftParts(key);
|
|
13103
13621
|
if (parts.workspaceId && parts.workspaceId !== String(state.currentWorkspaceId || '')) return;
|
|
13622
|
+
if (!Object.prototype.hasOwnProperty.call(state.conversationDrafts, conversationDraftKey(parts))) return;
|
|
13104
13623
|
if (!state._draftPersistTimers) state._draftPersistTimers = {};
|
|
13105
13624
|
if (state._draftPersistTimers[key]) clearTimeout(state._draftPersistTimers[key]);
|
|
13106
13625
|
state._draftPersistTimers[key] = setTimeout(function() {
|
|
13107
13626
|
delete state._draftPersistTimers[key];
|
|
13108
|
-
|
|
13627
|
+
if (parts.workspaceId && parts.workspaceId !== String(state.currentWorkspaceId || '')) return;
|
|
13628
|
+
var resolvedKey = conversationDraftKey(parts);
|
|
13629
|
+
if (!Object.prototype.hasOwnProperty.call(state.conversationDrafts, resolvedKey)) return;
|
|
13630
|
+
var draft = state.conversationDrafts[resolvedKey];
|
|
13109
13631
|
api.saveConversationDraft(parts.conversationId, draft).catch(function(){});
|
|
13110
|
-
}, 100);
|
|
13632
|
+
}, delayMs === undefined ? 100 : delayMs);
|
|
13111
13633
|
}
|
|
13112
13634
|
|
|
13113
13635
|
function restoreConversationDraft(target) {
|
|
@@ -13128,13 +13650,15 @@ function flushPendingConversationDraftPersists() {
|
|
|
13128
13650
|
delete state._draftPersistTimers[key];
|
|
13129
13651
|
var parts = conversationDraftParts(key);
|
|
13130
13652
|
if (parts.workspaceId && parts.workspaceId !== String(state.currentWorkspaceId || '')) return;
|
|
13131
|
-
var
|
|
13653
|
+
var resolvedKey = conversationDraftKey(parts);
|
|
13654
|
+
if (!Object.prototype.hasOwnProperty.call(state.conversationDrafts, resolvedKey)) return;
|
|
13655
|
+
var draft = state.conversationDrafts[resolvedKey];
|
|
13132
13656
|
if (api.saveConversationDraft) api.saveConversationDraft(parts.conversationId, draft).catch(function(){});
|
|
13133
13657
|
});
|
|
13134
13658
|
}
|
|
13135
13659
|
function clearActiveConversationDraft() {
|
|
13136
13660
|
var key = conversationDraftKey();
|
|
13137
|
-
|
|
13661
|
+
state.conversationDrafts[key] = '';
|
|
13138
13662
|
if (state._draftPersistTimers && state._draftPersistTimers[key]) {
|
|
13139
13663
|
clearTimeout(state._draftPersistTimers[key]);
|
|
13140
13664
|
delete state._draftPersistTimers[key];
|
|
@@ -13242,10 +13766,12 @@ async function refreshConversationRuntimeAfterStopRace(target, expectedRunId, st
|
|
|
13242
13766
|
target = target || currentConversationTarget();
|
|
13243
13767
|
expectedRunId = String(expectedRunId || '');
|
|
13244
13768
|
var action = String(stopResult && stopResult.action || '');
|
|
13769
|
+
var stopRevision = conversationWorkRevision(target);
|
|
13245
13770
|
var snapshot = null;
|
|
13246
13771
|
if (api.getState) {
|
|
13247
13772
|
try { snapshot = await api.getState(target); } catch (_) { snapshot = null; }
|
|
13248
13773
|
}
|
|
13774
|
+
if (!conversationRunStillOwnsTarget(target, expectedRunId) || stopRevision !== conversationWorkRevision(target)) return { stale: true, target: target };
|
|
13249
13775
|
var resolvedTarget = target;
|
|
13250
13776
|
var resolvedRunId = String((stopResult && stopResult.runId) || expectedRunId || '');
|
|
13251
13777
|
var resolvedStatus = action === 'stale' ? 'running' : 'idle';
|
|
@@ -13327,6 +13853,7 @@ window.stopCurrentConversation = async function() {
|
|
|
13327
13853
|
if (api.stopConversation) result = await api.stopConversation({ target: target, runId: runId, force: force });
|
|
13328
13854
|
else if (api.abortConversation) result = await api.abortConversation(currentConversationTarget(conversationId));
|
|
13329
13855
|
var resultAction = String(result && result.action || '');
|
|
13856
|
+
if (!conversationRunStillOwnsTarget(target, runId)) return true;
|
|
13330
13857
|
if (resultAction === 'not_running' || resultAction === 'stale') {
|
|
13331
13858
|
// Natural completion or a newer run won while the first stop IPC was in
|
|
13332
13859
|
// flight. Reconcile from the target snapshot instead of leaving the
|
|
@@ -13338,7 +13865,7 @@ window.stopCurrentConversation = async function() {
|
|
|
13338
13865
|
delete state.pendingProvisionalStops[runtimeKeyFor(target.workspaceId, target.conversationId)];
|
|
13339
13866
|
}
|
|
13340
13867
|
var latestAfterStop = state.conversationRuntimeStates && state.conversationRuntimeStates[runtimeKeyFor(target.workspaceId, target.conversationId)];
|
|
13341
|
-
if (
|
|
13868
|
+
if (latestAfterStop && String(latestAfterStop.runId || '') === runId && String(latestAfterStop.status || '') !== (force ? 'force_restarting' : 'stopping')) {
|
|
13342
13869
|
// A second click or a terminal worker event won the race while the first
|
|
13343
13870
|
// cooperative request was pending. Its older result must not downgrade
|
|
13344
13871
|
// force_restarting/force_interrupted back to interrupted or running.
|
|
@@ -13348,8 +13875,9 @@ window.stopCurrentConversation = async function() {
|
|
|
13348
13875
|
setConversationRuntimeState(target, force ? 'force_interrupted' : 'interrupted', runId);
|
|
13349
13876
|
}
|
|
13350
13877
|
} catch(e) {
|
|
13878
|
+
if (!conversationRunStillOwnsTarget(target, runId)) return false;
|
|
13351
13879
|
var latestAfterError = state.conversationRuntimeStates && state.conversationRuntimeStates[runtimeKeyFor(target.workspaceId, target.conversationId)];
|
|
13352
|
-
if (
|
|
13880
|
+
if (latestAfterError && String(latestAfterError.runId || '') === runId && String(latestAfterError.status || '') !== (force ? 'force_restarting' : 'stopping')) {
|
|
13353
13881
|
return true;
|
|
13354
13882
|
}
|
|
13355
13883
|
setConversationRuntimeState(target, force ? 'stopping' : 'running', runId);
|
|
@@ -13366,6 +13894,7 @@ window.stopCurrentConversation = async function() {
|
|
|
13366
13894
|
};
|
|
13367
13895
|
|
|
13368
13896
|
window.submitCurrentAction = function(source) {
|
|
13897
|
+
if (state.pendingInputEdit) return window.sendMessage();
|
|
13369
13898
|
if (currentFlowRunning() && flowTakeoverMatchesCurrent()) {
|
|
13370
13899
|
if (!promptHasText()) {
|
|
13371
13900
|
window.stopFlowRun();
|
|
@@ -13379,16 +13908,18 @@ window.submitCurrentAction = function(source) {
|
|
|
13379
13908
|
// exits the pause and starts a fresh process. The backend agent:send /
|
|
13380
13909
|
// flow:run handlers clear the suspension (without restoring mode).
|
|
13381
13910
|
window.exitPausedFlowForNewInstruction();
|
|
13382
|
-
if (state.mode === 'flow') return window.submitSelectedFlow();
|
|
13383
|
-
return window.sendMessage(
|
|
13911
|
+
if (state.mode === 'flow') return window.submitSelectedFlow({ text: guideText, target: currentConversationTarget(), inputMode: state.inputMode });
|
|
13912
|
+
return window.sendMessage(state.inputMode, guideText, { requestedMode: state.mode || 'build', target: currentConversationTarget() });
|
|
13384
13913
|
}
|
|
13385
13914
|
if (state.inputMode === 'next') {
|
|
13386
|
-
window.queueFlowNextInput(guideText);
|
|
13387
|
-
return;
|
|
13915
|
+
return window.queueFlowNextInput(guideText);
|
|
13388
13916
|
}
|
|
13389
|
-
if (api.
|
|
13390
|
-
|
|
13917
|
+
if (api.sendMessage) {
|
|
13918
|
+
var flowGuideTarget = currentConversationTarget();
|
|
13919
|
+
return api.sendMessage(guideText, flowGuideTarget, { requestedMode: state.mode || 'flow', inputMode: 'guide', clientMessageId: window.crypto.randomUUID() }).then(function(result) {
|
|
13391
13920
|
if (!result || result.ok === false) throw new Error((result && result.error) || 'Flow Guide was rejected.');
|
|
13921
|
+
applyConversationCommandSnapshot(result, flowGuideTarget);
|
|
13922
|
+
return result;
|
|
13392
13923
|
}).catch(function(error) {
|
|
13393
13924
|
showUiNotice(error && error.message ? error.message : String(error), 'error', 'flow-guide');
|
|
13394
13925
|
});
|
|
@@ -13451,8 +13982,26 @@ function contextInspectorValue(value) {
|
|
|
13451
13982
|
return Number.isFinite(number) ? Math.max(0, Math.round(number)) : 0;
|
|
13452
13983
|
}
|
|
13453
13984
|
|
|
13454
|
-
function
|
|
13455
|
-
return
|
|
13985
|
+
function contextInspectorReportedValue(value) {
|
|
13986
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? Math.round(value) : null;
|
|
13987
|
+
}
|
|
13988
|
+
|
|
13989
|
+
function contextInspectorCell(label, value, title) {
|
|
13990
|
+
return '<div class="context-inspector-cell"><span class="context-inspector-label">' + esc(label) + '</span><span class="context-inspector-value"' + (title ? ' title="' + esc(title) + '"' : '') + '>' + esc(value) + '</span></div>';
|
|
13991
|
+
}
|
|
13992
|
+
|
|
13993
|
+
function positionContextInspector() {
|
|
13994
|
+
if (!state.contextInspectorOpen) return;
|
|
13995
|
+
var panel = document.getElementById('context-inspector');
|
|
13996
|
+
var ring = els['context-token-ring'] || document.getElementById('context-token-ring');
|
|
13997
|
+
if (!panel || !ring || !ring.getBoundingClientRect) return;
|
|
13998
|
+
var topbar = document.getElementById('topbar');
|
|
13999
|
+
var visibleTop = topbar && topbar.getBoundingClientRect ? Math.max(0, topbar.getBoundingClientRect().bottom || 0) : 0;
|
|
14000
|
+
// Preserve the existing anchor and 10px gap while keeping the top visible.
|
|
14001
|
+
// Scroll this inspector's contents when the terminal/window leaves less room.
|
|
14002
|
+
panel.style.maxHeight = Math.max(0, Math.floor(ring.getBoundingClientRect().top - visibleTop - 20)) + 'px';
|
|
14003
|
+
panel.style.overflowY = 'auto';
|
|
14004
|
+
panel.style.overscrollBehavior = 'contain';
|
|
13456
14005
|
}
|
|
13457
14006
|
|
|
13458
14007
|
window.renderContextInspector = function() {
|
|
@@ -13478,8 +14027,39 @@ window.renderContextInspector = function() {
|
|
|
13478
14027
|
var providerInputTokens = contextInspectorValue(c.providerInputTokens);
|
|
13479
14028
|
var providerOutputTokens = contextInspectorValue(c.providerOutputTokens);
|
|
13480
14029
|
var providerCacheReadTokens = contextInspectorValue(c.providerCacheReadTokens);
|
|
13481
|
-
var
|
|
13482
|
-
var
|
|
14030
|
+
var usageRequests = contextInspectorValue(c.providerUsageRequests);
|
|
14031
|
+
var inputReportedRequests = contextInspectorValue(c.providerUsageInputReportedRequests);
|
|
14032
|
+
var outputReportedRequests = contextInspectorValue(c.providerUsageOutputReportedRequests);
|
|
14033
|
+
var cacheReportedRequests = contextInspectorValue(c.providerUsageCacheReportedRequests);
|
|
14034
|
+
var hasLegacyTotals = c.providerUsageHasLegacyTotals === true;
|
|
14035
|
+
var inputAvailable = inputReportedRequests > 0 || hasLegacyTotals;
|
|
14036
|
+
var outputAvailable = outputReportedRequests > 0 || hasLegacyTotals;
|
|
14037
|
+
var usageComplete = usageRequests > 0 && inputReportedRequests === usageRequests && outputReportedRequests === usageRequests && !hasLegacyTotals;
|
|
14038
|
+
var conversationUsageValue = inputAvailable || outputAvailable
|
|
14039
|
+
? (usageComplete ? '' : t('status.cacheReportedPortion') + ' ') + providerTotalTokens + ' · '
|
|
14040
|
+
+ (inputAvailable ? providerInputTokens : t('status.usageUnavailable')) + ' in / '
|
|
14041
|
+
+ (outputAvailable ? providerOutputTokens : t('status.usageUnavailable')) + ' out'
|
|
14042
|
+
: t('status.usageUnavailable');
|
|
14043
|
+
var providerCacheReadRatio = typeof c.providerCacheReadRatio === 'number' && Number.isFinite(c.providerCacheReadRatio) ? c.providerCacheReadRatio : null;
|
|
14044
|
+
var knownCacheReadRatio = typeof c.providerKnownCacheReadRatio === 'number' && Number.isFinite(c.providerKnownCacheReadRatio) ? c.providerKnownCacheReadRatio : null;
|
|
14045
|
+
var fullCacheCoverage = usageRequests > 0 && inputReportedRequests === usageRequests && cacheReportedRequests === usageRequests && !hasLegacyTotals;
|
|
14046
|
+
var cacheHitValue = fullCacheCoverage && providerCacheReadRatio !== null
|
|
14047
|
+
? (Math.round(Math.max(0, Math.min(1, providerCacheReadRatio)) * 1000) / 10) + '% · ' + providerCacheReadTokens + ' cached'
|
|
14048
|
+
: t('status.usageUnavailable');
|
|
14049
|
+
if (!fullCacheCoverage && knownCacheReadRatio !== null) {
|
|
14050
|
+
cacheHitValue += ' · ' + t('status.cacheReportedPortion') + ' ' + (Math.round(Math.max(0, Math.min(1, knownCacheReadRatio)) * 1000) / 10) + '%';
|
|
14051
|
+
}
|
|
14052
|
+
var lastReportedInput = contextInspectorReportedValue(c.requestContext && c.requestContext.inputTokens);
|
|
14053
|
+
var lastRequestDetails = c.requestContext ? [c.requestContext.model, c.requestContext.at].filter(Boolean).join(' · ') : '';
|
|
14054
|
+
var estimateHint = t(c.contextEstimateSource === 'active_request' ? 'status.contextEstimateRequest' : 'status.contextEstimateHistory');
|
|
14055
|
+
if (c.contextEstimateHasImages) estimateHint += ' ' + t('status.contextEstimateImages');
|
|
14056
|
+
var usageCoverageHint = currentLang() === 'zh'
|
|
14057
|
+
? '本次及已保存计量:' + usageRequests + ' 次请求;输入上报 ' + inputReportedRequests + ' 次,输出上报 ' + outputReportedRequests + ' 次,缓存明细上报 ' + cacheReportedRequests + ' 次。命中率按缓存读取 token / 输入 token 加权。'
|
|
14058
|
+
: 'Recorded requests: ' + usageRequests + '; input reported: ' + inputReportedRequests + '; output reported: ' + outputReportedRequests + '; cache details reported: ' + cacheReportedRequests + '. Hit rate is weighted cached input / input tokens.';
|
|
14059
|
+
if (hasLegacyTotals) usageCoverageHint += currentLang() === 'zh' ? ' 含旧累计用量,历史上报覆盖范围未知。' : ' Legacy totals are present; historical reporting coverage is unknown.';
|
|
14060
|
+
if (!fullCacheCoverage && knownCacheReadRatio !== null) usageCoverageHint += currentLang() === 'zh'
|
|
14061
|
+
? ' 已上报部分的输入分母为 ' + contextInspectorValue(c.providerCacheEligibleInputTokens) + ' token。'
|
|
14062
|
+
: ' The reported portion covers ' + contextInspectorValue(c.providerCacheEligibleInputTokens) + ' input tokens.';
|
|
13483
14063
|
var percent = Math.min(100, Math.round((used / max) * 100));
|
|
13484
14064
|
var fillColor = c.warning === 'over_limit' ? 'var(--nm-state-danger)' : (c.warning === 'near_limit' ? 'var(--nm-state-warning)' : 'var(--accent)');
|
|
13485
14065
|
var compression = state.contextCompression || null;
|
|
@@ -13492,25 +14072,36 @@ window.renderContextInspector = function() {
|
|
|
13492
14072
|
'<div class="context-inspector-meter"><div class="context-inspector-meter-fill" style="width:' + percent + '%;background:' + fillColor + '"></div></div>' +
|
|
13493
14073
|
'<div class="context-inspector-grid">' +
|
|
13494
14074
|
contextInspectorCell(t('status.contextTokens'), used + ' / ' + max) +
|
|
13495
|
-
contextInspectorCell(t('status.conversationTokens'),
|
|
14075
|
+
contextInspectorCell(t('status.conversationTokens'), conversationUsageValue) +
|
|
13496
14076
|
contextInspectorCell(t('status.activeBuild'), active + ' / ' + activeTrigger) +
|
|
13497
|
-
contextInspectorCell(t('status.cacheHitRate'),
|
|
14077
|
+
contextInspectorCell(t('status.cacheHitRate'), cacheHitValue) +
|
|
13498
14078
|
contextInspectorCell(t('status.longHistory'), history + ' / ' + historyTrigger) +
|
|
13499
14079
|
contextInspectorCell(t('status.retention'), activeRetention + ' + ' + historyRetention) +
|
|
13500
14080
|
contextInspectorCell(t('status.hotCache'), contextInspectorValue(c.cacheEntries) + ' entries') +
|
|
13501
14081
|
contextInspectorCell(t('status.coldArchive'), contextInspectorValue(c.archiveEntries) + ' entries') +
|
|
14082
|
+
contextInspectorCell(t('status.requestOverhead'), c.contextEstimateSource === 'active_request' ? contextInspectorValue(c.systemPromptTokens) + ' / ' + contextInspectorValue(c.toolSchemaTokens) : t('status.requestOverheadExcluded')) +
|
|
14083
|
+
contextInspectorCell(t('status.lastReportedInput'), lastReportedInput === null ? t('status.usageUnavailable') : String(lastReportedInput), lastRequestDetails) +
|
|
13502
14084
|
'</div>' +
|
|
14085
|
+
'<div class="context-inspector-meta">' + esc(estimateHint) + '</div>' +
|
|
14086
|
+
'<div class="context-inspector-meta">' + esc(usageCoverageHint) + '</div>' +
|
|
13503
14087
|
'<div class="context-inspector-section"><div class="context-inspector-section-title">' + esc(t('status.lastCompression')) + '</div><div class="context-inspector-meta">' + esc(last) + '</div>' +
|
|
13504
14088
|
(compression && compression.model ? '<div class="context-inspector-meta">' + esc(compression.model) + '</div>' : '') + '</div>' +
|
|
13505
14089
|
'<div class="context-inspector-actions"><button type="button" class="sec-btn primary" onclick="window.compressContextNow()"' + (busy || running ? ' disabled' : '') + '>' + esc(busy ? t('status.compressing') : t('status.compressNow')) + '</button></div>';
|
|
14090
|
+
positionContextInspector();
|
|
13506
14091
|
};
|
|
13507
14092
|
|
|
13508
14093
|
window.closeContextInspector = function() {
|
|
13509
|
-
state.contextInspectorOpen
|
|
13510
|
-
|
|
14094
|
+
if (!state.contextInspectorOpen) return;
|
|
14095
|
+
var panel = document.getElementById('context-inspector');
|
|
14096
|
+
var ring = document.getElementById('context-token-ring');
|
|
14097
|
+
window.animateLiquidPopupExit(panel, ring, function() {
|
|
14098
|
+
state.contextInspectorOpen = false;
|
|
14099
|
+
window.renderContextInspector();
|
|
14100
|
+
});
|
|
13511
14101
|
};
|
|
13512
14102
|
|
|
13513
14103
|
(function() {
|
|
14104
|
+
window.addEventListener('resize', positionContextInspector);
|
|
13514
14105
|
document.addEventListener('click', function(event) {
|
|
13515
14106
|
if (!state.contextInspectorOpen) return;
|
|
13516
14107
|
var ring = document.getElementById('context-token-ring');
|
|
@@ -13525,6 +14116,7 @@ window.toggleContextInspector = function() {
|
|
|
13525
14116
|
state.contextInspectorOpen = !state.contextInspectorOpen;
|
|
13526
14117
|
window.hideContextWindowTooltip();
|
|
13527
14118
|
window.renderContextInspector();
|
|
14119
|
+
if (state.contextInspectorOpen) scheduleActiveContextWindowRefresh(currentConversationTarget());
|
|
13528
14120
|
};
|
|
13529
14121
|
|
|
13530
14122
|
window.compressContextNow = function() {
|
|
@@ -13539,15 +14131,22 @@ window.compressContextNow = function() {
|
|
|
13539
14131
|
}
|
|
13540
14132
|
state.contextMutationPending = true;
|
|
13541
14133
|
window.renderContextInspector();
|
|
13542
|
-
|
|
14134
|
+
var compressionTarget = currentConversationTarget();
|
|
14135
|
+
var compressionContextRequest = captureContextWindowSnapshotRequest(compressionTarget);
|
|
14136
|
+
return Promise.resolve(api.compressContext({ target: compressionTarget, force: true })).then(function(result) {
|
|
13543
14137
|
if (!result || result.ok === false) throw new Error((result && result.error) || t('status.compressionFailed'));
|
|
13544
|
-
if (result
|
|
13545
|
-
|
|
13546
|
-
|
|
14138
|
+
if (!applyContextWindowSnapshot(result, compressionContextRequest)) {
|
|
14139
|
+
// A concurrent read may have sampled the budget before compression
|
|
14140
|
+
// completed. Query again after the mutation rather than publishing an
|
|
14141
|
+
// old mutation response over newer usage/model state.
|
|
14142
|
+
if (isActiveConversationTarget(compressionTarget)) scheduleActiveContextWindowRefresh(compressionTarget);
|
|
14143
|
+
return result;
|
|
14144
|
+
}
|
|
13547
14145
|
window.renderRightStatusPanel();
|
|
13548
14146
|
showUiNotice(t('status.compressedNow'), 'success', 'context-compress-success');
|
|
13549
14147
|
return result;
|
|
13550
14148
|
}).catch(function(error) {
|
|
14149
|
+
if (!isCurrentContextWindowSnapshotRequest(compressionContextRequest)) return;
|
|
13551
14150
|
showUiNotice(error && error.message ? error.message : t('status.compressionFailed'), 'error', 'context-compress-error');
|
|
13552
14151
|
}).finally(function() {
|
|
13553
14152
|
state.contextMutationPending = false;
|
|
@@ -13583,11 +14182,22 @@ window.closeModelSelectMenu = function(options) {
|
|
|
13583
14182
|
var shell = document.getElementById('model-select-shell');
|
|
13584
14183
|
var button = document.getElementById('model-select-button');
|
|
13585
14184
|
var menu = document.getElementById('model-select-menu');
|
|
13586
|
-
|
|
13587
|
-
|
|
13588
|
-
if (
|
|
13589
|
-
|
|
13590
|
-
|
|
14185
|
+
var wasOpen = shell && shell.classList.contains('open') ||
|
|
14186
|
+
!!(menu && typeof menu.matches === 'function' && menu.matches(':popover-open'));
|
|
14187
|
+
if (!wasOpen) {
|
|
14188
|
+
if (shell) shell.classList.remove('open');
|
|
14189
|
+
if (button) button.setAttribute('aria-expanded', 'false');
|
|
14190
|
+
window.setLiquidSidebarGestureLock('popup:model-select', false);
|
|
14191
|
+
if (options && options.focus && button) button.focus();
|
|
14192
|
+
return;
|
|
14193
|
+
}
|
|
14194
|
+
window.animateLiquidPopupExit(menu, button, function() {
|
|
14195
|
+
if (shell) shell.classList.remove('open');
|
|
14196
|
+
if (menu && typeof menu.hidePopover === 'function' && menu.matches(':popover-open')) menu.hidePopover();
|
|
14197
|
+
if (button) button.setAttribute('aria-expanded', 'false');
|
|
14198
|
+
window.setLiquidSidebarGestureLock('popup:model-select', false);
|
|
14199
|
+
if (options && options.focus && button) button.focus();
|
|
14200
|
+
});
|
|
13591
14201
|
};
|
|
13592
14202
|
|
|
13593
14203
|
window.toggleModelSelectMenu = function(forceOpen) {
|
|
@@ -13628,11 +14238,21 @@ window.selectModelFromMenu = function(value) {
|
|
|
13628
14238
|
window.closeConversationActionMenu = function(options) {
|
|
13629
14239
|
var menu = document.getElementById('conversation-action-menu');
|
|
13630
14240
|
var anchor = state.conversationActionAnchor;
|
|
13631
|
-
|
|
13632
|
-
|
|
13633
|
-
|
|
13634
|
-
|
|
13635
|
-
|
|
14241
|
+
var wasOpen = !!(menu && typeof menu.matches === 'function' && menu.matches(':popover-open'));
|
|
14242
|
+
if (!wasOpen) {
|
|
14243
|
+
document.querySelectorAll('.conv-more-btn[aria-expanded="true"]').forEach(function(item) { item.setAttribute('aria-expanded', 'false'); });
|
|
14244
|
+
state.conversationActionAnchor = null;
|
|
14245
|
+
window.setLiquidSidebarGestureLock('popup:conversation-action', false);
|
|
14246
|
+
if (options && options.focus && anchor && anchor.focus) anchor.focus();
|
|
14247
|
+
return;
|
|
14248
|
+
}
|
|
14249
|
+
window.animateLiquidPopupExit(menu, anchor, function() {
|
|
14250
|
+
if (menu && typeof menu.hidePopover === 'function' && menu.matches(':popover-open')) menu.hidePopover();
|
|
14251
|
+
document.querySelectorAll('.conv-more-btn[aria-expanded="true"]').forEach(function(item) { item.setAttribute('aria-expanded', 'false'); });
|
|
14252
|
+
state.conversationActionAnchor = null;
|
|
14253
|
+
window.setLiquidSidebarGestureLock('popup:conversation-action', false);
|
|
14254
|
+
if (options && options.focus && anchor && anchor.focus) anchor.focus();
|
|
14255
|
+
});
|
|
13636
14256
|
};
|
|
13637
14257
|
|
|
13638
14258
|
window.positionConversationActionMenu = function(anchor, menu, pos) {
|
|
@@ -13959,6 +14579,7 @@ function createKyantLiquidRenderer() {
|
|
|
13959
14579
|
renderFrame: 0,
|
|
13960
14580
|
frameId: '',
|
|
13961
14581
|
lastGeometry: null,
|
|
14582
|
+
lastSurfaceState: null,
|
|
13962
14583
|
pendingGeometry: null,
|
|
13963
14584
|
uniformState: Object.create(null),
|
|
13964
14585
|
invalid: false
|
|
@@ -14002,6 +14623,7 @@ function acquireKyantLiquidRenderer(float) {
|
|
|
14002
14623
|
renderer.displayCanvas = displayCanvas;
|
|
14003
14624
|
renderer.displayContext = displayContext;
|
|
14004
14625
|
renderer.lastGeometry = null;
|
|
14626
|
+
renderer.lastSurfaceState = null;
|
|
14005
14627
|
renderer.pendingGeometry = null;
|
|
14006
14628
|
return renderer;
|
|
14007
14629
|
}
|
|
@@ -14018,6 +14640,7 @@ function uploadKyantLiquidFrame(renderer, frame) {
|
|
|
14018
14640
|
// float geometry has not changed. Otherwise the geometry cache skips the
|
|
14019
14641
|
// draw and leaves the previously captured backdrop on screen.
|
|
14020
14642
|
renderer.lastGeometry = null;
|
|
14643
|
+
renderer.lastSurfaceState = null;
|
|
14021
14644
|
return true;
|
|
14022
14645
|
}
|
|
14023
14646
|
|
|
@@ -14077,8 +14700,13 @@ function attachKyantLiquidRenderer(float, frame) {
|
|
|
14077
14700
|
var pixelWidth = Math.max(1, Math.round(width*renderScale));
|
|
14078
14701
|
var pixelHeight = Math.max(1, Math.round(height*renderScale));
|
|
14079
14702
|
var geometryKey = [left.toFixed(2),top.toFixed(2),width.toFixed(2),height.toFixed(2),pixelWidth,pixelHeight].join(':');
|
|
14080
|
-
|
|
14703
|
+
var glow = float._liquidGlow;
|
|
14704
|
+
var surfaceStateKey = glow && glow.opacity > 0
|
|
14705
|
+
? [glow.x.toFixed(4),glow.y.toFixed(4),glow.opacity.toFixed(3)].join(':')
|
|
14706
|
+
: 'no-glow';
|
|
14707
|
+
if (renderer.lastGeometry === geometryKey && renderer.lastSurfaceState === surfaceStateKey) return;
|
|
14081
14708
|
renderer.lastGeometry = geometryKey;
|
|
14709
|
+
renderer.lastSurfaceState = surfaceStateKey;
|
|
14082
14710
|
if (canvas.width !== pixelWidth) canvas.width = pixelWidth;
|
|
14083
14711
|
if (canvas.height !== pixelHeight) canvas.height = pixelHeight;
|
|
14084
14712
|
if (displayCanvas.width !== pixelWidth) displayCanvas.width = pixelWidth;
|
|
@@ -14108,10 +14736,48 @@ function attachKyantLiquidRenderer(float, frame) {
|
|
|
14108
14736
|
uniformChanged('chromaticAberration',[configuredChromatic],function() { gl.uniform1f(uniforms.chromaticAberration,configuredChromatic); });
|
|
14109
14737
|
gl.drawArrays(gl.TRIANGLES,0,6);
|
|
14110
14738
|
displayContext.clearRect(0,0,pixelWidth,pixelHeight);
|
|
14739
|
+
// The WebGL shader discards pixels outside the capsule, but the 2D
|
|
14740
|
+
// interaction glow is painted after that pass. Clip both passes to the
|
|
14741
|
+
// same rounded silhouette so a radial light can never bleed into the
|
|
14742
|
+
// surrounding page or produce a square compositor halo.
|
|
14743
|
+
var clipped = typeof displayContext.save === 'function' &&
|
|
14744
|
+
typeof displayContext.beginPath === 'function' &&
|
|
14745
|
+
typeof displayContext.clip === 'function' &&
|
|
14746
|
+
(typeof displayContext.roundRect === 'function' || typeof displayContext.arcTo === 'function');
|
|
14747
|
+
if (clipped) {
|
|
14748
|
+
displayContext.save();
|
|
14749
|
+
displayContext.beginPath();
|
|
14750
|
+
var clipRadius = Math.min(pixelWidth, pixelHeight) * 0.5;
|
|
14751
|
+
if (typeof displayContext.roundRect === 'function') {
|
|
14752
|
+
displayContext.roundRect(0, 0, pixelWidth, pixelHeight, clipRadius);
|
|
14753
|
+
} else {
|
|
14754
|
+
displayContext.moveTo(clipRadius, 0);
|
|
14755
|
+
displayContext.arcTo(pixelWidth, 0, pixelWidth, pixelHeight, clipRadius);
|
|
14756
|
+
displayContext.arcTo(pixelWidth, pixelHeight, 0, pixelHeight, clipRadius);
|
|
14757
|
+
displayContext.arcTo(0, pixelHeight, 0, 0, clipRadius);
|
|
14758
|
+
displayContext.arcTo(0, 0, pixelWidth, 0, clipRadius);
|
|
14759
|
+
displayContext.closePath();
|
|
14760
|
+
}
|
|
14761
|
+
displayContext.clip();
|
|
14762
|
+
}
|
|
14111
14763
|
displayContext.drawImage(canvas,0,0);
|
|
14764
|
+
if (glow && glow.opacity > 0) {
|
|
14765
|
+
var gx = Math.max(0, Math.min(pixelWidth, glow.x * pixelWidth));
|
|
14766
|
+
var gy = Math.max(0, Math.min(pixelHeight, glow.y * pixelHeight));
|
|
14767
|
+
var radius = Math.max(18, Math.min(pixelWidth, pixelHeight) * 0.72);
|
|
14768
|
+
var gradient = displayContext.createRadialGradient(gx, gy, 0, gx, gy, radius);
|
|
14769
|
+
gradient.addColorStop(0, 'rgba(255,255,255,' + (0.08 + glow.opacity * 0.18).toFixed(3) + ')');
|
|
14770
|
+
gradient.addColorStop(1, 'rgba(255,255,255,0)');
|
|
14771
|
+
displayContext.fillStyle = gradient;
|
|
14772
|
+
displayContext.globalCompositeOperation = 'screen';
|
|
14773
|
+
displayContext.fillRect(0, 0, pixelWidth, pixelHeight);
|
|
14774
|
+
}
|
|
14775
|
+
if (clipped) displayContext.restore();
|
|
14112
14776
|
float._kyantDrawReady = true;
|
|
14113
14777
|
float.dataset.kyantDrawReady = 'true';
|
|
14114
|
-
if (displayCanvas.parentNode !== float
|
|
14778
|
+
if (displayCanvas.parentNode !== float || float.firstChild !== displayCanvas) {
|
|
14779
|
+
float.insertBefore(displayCanvas, float.firstChild);
|
|
14780
|
+
}
|
|
14115
14781
|
};
|
|
14116
14782
|
if (immediate) draw();
|
|
14117
14783
|
else renderer.renderFrame = requestAnimationFrame(draw);
|
|
@@ -14123,6 +14789,7 @@ function attachKyantLiquidRenderer(float, frame) {
|
|
|
14123
14789
|
if (renderer.renderFrame) cancelAnimationFrame(renderer.renderFrame);
|
|
14124
14790
|
renderer.renderFrame = 0;
|
|
14125
14791
|
renderer.lastGeometry = null;
|
|
14792
|
+
renderer.lastSurfaceState = null;
|
|
14126
14793
|
renderer.pendingGeometry = null;
|
|
14127
14794
|
displayCanvas.remove();
|
|
14128
14795
|
renderer.displayCanvas = null;
|
|
@@ -14165,6 +14832,175 @@ function liquidInteractionEdgePx() {
|
|
|
14165
14832
|
return liquidGlassCssNumber('--liquid-interaction-edge', 6);
|
|
14166
14833
|
}
|
|
14167
14834
|
|
|
14835
|
+
/*
|
|
14836
|
+
* Popup deformation is deliberately expressed in physical CSS pixels. The
|
|
14837
|
+
* old implementation wrote a percentage scale directly (3.5% at full pull),
|
|
14838
|
+
* which made a large dialog visibly wobble much farther than a compact menu.
|
|
14839
|
+
* Convert a bounded edge travel back into a scale factor per axis so the
|
|
14840
|
+
* visual envelope stays stable regardless of the popup's dimensions. The
|
|
14841
|
+
* full-pull response reaches the hard 4px edge budget without exceeding it.
|
|
14842
|
+
*/
|
|
14843
|
+
var LIQUID_POPUP_MAX_EDGE_JITTER_PX = 4;
|
|
14844
|
+
var LIQUID_HOLD_DRAG_ACTIVATION_MS = 80;
|
|
14845
|
+
var LIQUID_POPUP_DRAG_RESPONSE = 1;
|
|
14846
|
+
var LIQUID_POPUP_DRAG_DISTANCE_PX = 90;
|
|
14847
|
+
function clearLiquidPopupOutset(popup, immediate) {
|
|
14848
|
+
var feedback = popup && popup._liquidPopupFeedback;
|
|
14849
|
+
if (!feedback) return;
|
|
14850
|
+
feedback.target = 0;
|
|
14851
|
+
feedback.surface.style.setProperty('--liquid-popup-outset', '0px');
|
|
14852
|
+
feedback.outset = '0.000px';
|
|
14853
|
+
clearTimeout(feedback.clearTimer);
|
|
14854
|
+
function dispose() {
|
|
14855
|
+
if (popup._liquidPopupFeedback !== feedback) return;
|
|
14856
|
+
cancelAnimationFrame(feedback.frame);
|
|
14857
|
+
if (feedback.layer) feedback.layer.remove();
|
|
14858
|
+
popup.removeAttribute('data-liquid-popup-feedback');
|
|
14859
|
+
popup.style.removeProperty('--liquid-popup-outset');
|
|
14860
|
+
['left', 'right', 'top', 'bottom'].forEach(function(side) {
|
|
14861
|
+
popup.style.removeProperty('--liquid-popup-pull-' + side);
|
|
14862
|
+
});
|
|
14863
|
+
if (immediate && !feedback.layer && popup.getAnimations) {
|
|
14864
|
+
popup.getAnimations().forEach(function(animation) {
|
|
14865
|
+
if (animation.transitionProperty === '--liquid-popup-outset') animation.cancel();
|
|
14866
|
+
});
|
|
14867
|
+
}
|
|
14868
|
+
popup._liquidPopupFeedback = null;
|
|
14869
|
+
}
|
|
14870
|
+
if (immediate) dispose();
|
|
14871
|
+
else feedback.clearTimer = setTimeout(dispose, 150);
|
|
14872
|
+
}
|
|
14873
|
+
|
|
14874
|
+
function setLiquidPopupOutset(popup, amount, dx, dy) {
|
|
14875
|
+
amount = Math.max(0, Math.min(LIQUID_POPUP_MAX_EDGE_JITTER_PX, amount));
|
|
14876
|
+
var distance = Math.hypot(dx || 0, dy || 0);
|
|
14877
|
+
if (!popup || !amount || !distance) { clearLiquidPopupOutset(popup, false); return; }
|
|
14878
|
+
var feedback = popup._liquidPopupFeedback;
|
|
14879
|
+
if (!feedback) {
|
|
14880
|
+
var style = getComputedStyle(popup);
|
|
14881
|
+
if (!popup.isConnected || style.display === 'none' || style.visibility === 'hidden') return;
|
|
14882
|
+
var nativePopover = popup.matches(':popover-open');
|
|
14883
|
+
var layer = null;
|
|
14884
|
+
if (!nativePopover) {
|
|
14885
|
+
layer = document.createElement('div');
|
|
14886
|
+
layer.className = 'liquid-popup-feedback-surface';
|
|
14887
|
+
layer.setAttribute('aria-hidden', 'true');
|
|
14888
|
+
layer.style.zIndex = style.zIndex === 'auto' ? '0' : style.zIndex;
|
|
14889
|
+
popup.parentNode.insertBefore(layer, popup);
|
|
14890
|
+
}
|
|
14891
|
+
var surface = layer || popup;
|
|
14892
|
+
surface.style.setProperty('--liquid-popup-outset', '0px');
|
|
14893
|
+
surface.style.setProperty('--liquid-feedback-color', style.getPropertyValue('--liquid-popup-surface'));
|
|
14894
|
+
surface.style.setProperty('--liquid-feedback-filter', style.backdropFilter);
|
|
14895
|
+
surface.style.setProperty('--liquid-feedback-rim', style.getPropertyValue('--liquid-popup-rim'));
|
|
14896
|
+
surface.style.setProperty('--liquid-feedback-inner-rim', style.getPropertyValue('--liquid-popup-inner-rim'));
|
|
14897
|
+
surface.style.setProperty('--liquid-feedback-radius', style.borderTopLeftRadius);
|
|
14898
|
+
feedback = { surface: surface, layer: layer, nativePopover: nativePopover, target: 0, frame: 0, clearTimer: 0, geometry: '', outset: '' };
|
|
14899
|
+
popup._liquidPopupFeedback = feedback;
|
|
14900
|
+
function frame() {
|
|
14901
|
+
if (popup._liquidPopupFeedback !== feedback) return;
|
|
14902
|
+
var currentStyle = getComputedStyle(popup);
|
|
14903
|
+
var rect = popup.getBoundingClientRect();
|
|
14904
|
+
if (!popup.isConnected || currentStyle.display === 'none' || currentStyle.visibility === 'hidden' ||
|
|
14905
|
+
popup.classList.contains('liquid-popup-exit') || !rect.width || !rect.height ||
|
|
14906
|
+
(feedback.nativePopover && !popup.matches(':popover-open'))) {
|
|
14907
|
+
clearLiquidPopupOutset(popup, true);
|
|
14908
|
+
return;
|
|
14909
|
+
}
|
|
14910
|
+
var left = rect.left, top = rect.top, width = rect.width, height = rect.height, pixelScale = 1;
|
|
14911
|
+
if (layer && layer.offsetParent) {
|
|
14912
|
+
var parent = layer.offsetParent;
|
|
14913
|
+
var parentRect = parent.getBoundingClientRect();
|
|
14914
|
+
var scaleX = parent.offsetWidth ? parentRect.width / parent.offsetWidth : 1;
|
|
14915
|
+
var scaleY = parent.offsetHeight ? parentRect.height / parent.offsetHeight : 1;
|
|
14916
|
+
scaleX = scaleX || 1; scaleY = scaleY || 1;
|
|
14917
|
+
left = (left - parentRect.left) / scaleX + parent.scrollLeft - parent.clientLeft;
|
|
14918
|
+
top = (top - parentRect.top) / scaleY + parent.scrollTop - parent.clientTop;
|
|
14919
|
+
width /= scaleX; height /= scaleY;
|
|
14920
|
+
pixelScale = Math.max(scaleX, scaleY);
|
|
14921
|
+
}
|
|
14922
|
+
var geometry = [left, top, width, height, currentStyle.opacity].join(',');
|
|
14923
|
+
if (feedback.geometry !== geometry) {
|
|
14924
|
+
feedback.geometry = geometry;
|
|
14925
|
+
surface.style.setProperty('--liquid-feedback-left', left + 'px');
|
|
14926
|
+
surface.style.setProperty('--liquid-feedback-top', top + 'px');
|
|
14927
|
+
surface.style.setProperty('--liquid-feedback-width', width + 'px');
|
|
14928
|
+
surface.style.setProperty('--liquid-feedback-height', height + 'px');
|
|
14929
|
+
if (layer) layer.style.opacity = currentStyle.opacity;
|
|
14930
|
+
}
|
|
14931
|
+
var outset = (feedback.target / pixelScale).toFixed(3) + 'px';
|
|
14932
|
+
if (feedback.outset !== outset) {
|
|
14933
|
+
feedback.outset = outset;
|
|
14934
|
+
surface.style.setProperty('--liquid-popup-outset', outset);
|
|
14935
|
+
}
|
|
14936
|
+
feedback.frame = requestAnimationFrame(frame);
|
|
14937
|
+
}
|
|
14938
|
+
/* Resolve the zero-sized feedback boundary before its first transition. */
|
|
14939
|
+
frame();
|
|
14940
|
+
if (popup._liquidPopupFeedback !== feedback) return;
|
|
14941
|
+
popup.setAttribute('data-liquid-popup-feedback', 'outward');
|
|
14942
|
+
getComputedStyle(surface).getPropertyValue('--liquid-popup-outset');
|
|
14943
|
+
}
|
|
14944
|
+
clearTimeout(feedback.clearTimer);
|
|
14945
|
+
/* Keep the response magnitude's existing transition. Direction changes are
|
|
14946
|
+
immediate, so reversing a pull never stretches the old opposite side. */
|
|
14947
|
+
var direction = [(dx || 0) / distance, (dy || 0) / distance];
|
|
14948
|
+
var sides = { left: Math.max(0, -direction[0]), right: Math.max(0, direction[0]), top: Math.max(0, -direction[1]), bottom: Math.max(0, direction[1]) };
|
|
14949
|
+
Object.keys(sides).forEach(function(side) {
|
|
14950
|
+
feedback.surface.style.setProperty('--liquid-popup-pull-' + side, sides[side].toFixed(6));
|
|
14951
|
+
});
|
|
14952
|
+
feedback.target = amount;
|
|
14953
|
+
}
|
|
14954
|
+
|
|
14955
|
+
function setLiquidPopupDeformation(popup, dx, dy, clear) {
|
|
14956
|
+
if (!popup) return;
|
|
14957
|
+
if (clear) {
|
|
14958
|
+
clearLiquidPopupOutset(popup, false);
|
|
14959
|
+
popup.style.setProperty('--liquid-popup-stretch', '0');
|
|
14960
|
+
popup.style.setProperty('--liquid-popup-squash', '0');
|
|
14961
|
+
popup.style.setProperty('scale', '1 1');
|
|
14962
|
+
popup.style.setProperty('--liquid-popup-edge-x', '0px');
|
|
14963
|
+
popup.style.setProperty('--liquid-popup-edge-y', '0px');
|
|
14964
|
+
return;
|
|
14965
|
+
}
|
|
14966
|
+
var distance = Math.hypot(dx || 0, dy || 0);
|
|
14967
|
+
if (!distance) return;
|
|
14968
|
+
var amount = Math.min(1, distance / LIQUID_POPUP_DRAG_DISTANCE_PX) * LIQUID_POPUP_DRAG_RESPONSE;
|
|
14969
|
+
var horizontal = Math.abs(dx || 0) >= Math.abs(dy || 0);
|
|
14970
|
+
var axisSize = Math.max(1, horizontal ? (popup.clientWidth || 0) : (popup.clientHeight || 0));
|
|
14971
|
+
var crossSize = Math.max(1, horizontal ? (popup.clientHeight || 0) : (popup.clientWidth || 0));
|
|
14972
|
+
/* Outward held feedback grows the optical surface, not an inward clip. */
|
|
14973
|
+
var edgeJitter = Math.min(LIQUID_POPUP_MAX_EDGE_JITTER_PX, amount * LIQUID_POPUP_MAX_EDGE_JITTER_PX);
|
|
14974
|
+
var crossJitter = edgeJitter * 0.24;
|
|
14975
|
+
var stretch = (edgeJitter * 2) / axisSize;
|
|
14976
|
+
var squash = -((crossJitter * 2) / crossSize);
|
|
14977
|
+
var stretchX = horizontal ? stretch : squash;
|
|
14978
|
+
var squashY = horizontal ? squash : stretch;
|
|
14979
|
+
popup.style.setProperty('--liquid-popup-stretch', stretchX.toFixed(6));
|
|
14980
|
+
popup.style.setProperty('--liquid-popup-squash', squashY.toFixed(6));
|
|
14981
|
+
popup.style.setProperty('scale', '1');
|
|
14982
|
+
popup.style.setProperty('--liquid-popup-edge-x', '0px');
|
|
14983
|
+
popup.style.setProperty('--liquid-popup-edge-y', '0px');
|
|
14984
|
+
setLiquidPopupOutset(popup, edgeJitter, dx, dy);
|
|
14985
|
+
}
|
|
14986
|
+
|
|
14987
|
+
function setLiquidPopupPressDeformation(popup) {
|
|
14988
|
+
if (!popup) return;
|
|
14989
|
+
clearLiquidPopupOutset(popup, true);
|
|
14990
|
+
var width = Math.max(1, popup.clientWidth || 1);
|
|
14991
|
+
var height = Math.max(1, popup.clientHeight || 1);
|
|
14992
|
+
/* Keep click feedback fixed-size as well; it is intentionally smaller than
|
|
14993
|
+
the drag budget so a tap never produces a large-dialog jump. */
|
|
14994
|
+
var edgeJitter = Math.min(4, LIQUID_POPUP_MAX_EDGE_JITTER_PX * 0.4);
|
|
14995
|
+
var stretch = (edgeJitter * 2) / width;
|
|
14996
|
+
var squash = -((edgeJitter * 0.24 * 2) / height);
|
|
14997
|
+
popup.style.setProperty('--liquid-popup-stretch', stretch.toFixed(6));
|
|
14998
|
+
popup.style.setProperty('--liquid-popup-squash', squash.toFixed(6));
|
|
14999
|
+
popup.style.setProperty('scale', '1');
|
|
15000
|
+
popup.style.setProperty('--liquid-popup-edge-x', (edgeJitter * 0.24).toFixed(3) + 'px');
|
|
15001
|
+
popup.style.setProperty('--liquid-popup-edge-y', edgeJitter.toFixed(3) + 'px');
|
|
15002
|
+
}
|
|
15003
|
+
|
|
14168
15004
|
function startLiquidMotionTracking(float) {
|
|
14169
15005
|
if (!float || float._liquidMotionFrame) return;
|
|
14170
15006
|
var lastTime = 0;
|
|
@@ -14199,9 +15035,23 @@ function startLiquidMotionTracking(float) {
|
|
|
14199
15035
|
var speed = reducedMotion ? 0 : Math.hypot(velocityX, velocityY);
|
|
14200
15036
|
var amount = Math.max(0, Math.min(1, (speed - 8) / 700));
|
|
14201
15037
|
var angle = speed > 1 ? Math.atan2(velocityY, velocityX) : 0;
|
|
15038
|
+
// Percentage-only velocity stretch exceeds 4px on wide capsules. Bound
|
|
15039
|
+
// the combined motion + boundary scales by the untransformed half-diagonal,
|
|
15040
|
+
// so the displacement of every corner fits the remaining pixel budget.
|
|
15041
|
+
var halfDiagonal = Math.max(1, Math.hypot(float.offsetWidth, float.offsetHeight) * .5);
|
|
15042
|
+
var boundaryX = parseFloat(float.style.getPropertyValue('--liquid-boundary-x')) || 0;
|
|
15043
|
+
var boundaryY = parseFloat(float.style.getPropertyValue('--liquid-boundary-y')) || 0;
|
|
15044
|
+
var boundaryPull = parseFloat(float.style.getPropertyValue('--liquid-boundary-pull')) || 0;
|
|
15045
|
+
var available = Math.max(0, LIQUID_POPUP_MAX_EDGE_JITTER_PX - Math.hypot(boundaryX, boundaryY));
|
|
15046
|
+
var motionStretch = amount * .075;
|
|
15047
|
+
var boundaryStretch = boundaryPull * .06;
|
|
15048
|
+
var requestedStretch = motionStretch + boundaryStretch + motionStretch * boundaryStretch;
|
|
15049
|
+
var budgetRatio = requestedStretch > 0 ? Math.min(1, available / halfDiagonal / requestedStretch) : 1;
|
|
14202
15050
|
float.style.setProperty('--liquid-motion-angle', angle + 'rad');
|
|
14203
|
-
float.style.setProperty('--liquid-motion-stretch', String(1 +
|
|
14204
|
-
float.style.setProperty('--liquid-motion-squash', String(1 - amount * .035));
|
|
15051
|
+
float.style.setProperty('--liquid-motion-stretch', String(1 + motionStretch * budgetRatio));
|
|
15052
|
+
float.style.setProperty('--liquid-motion-squash', String(1 - amount * .035 * budgetRatio));
|
|
15053
|
+
float.style.setProperty('--liquid-boundary-scale-x', String(1 + boundaryStretch * budgetRatio));
|
|
15054
|
+
float.style.setProperty('--liquid-boundary-scale-y', String(1 - boundaryPull * .025 * budgetRatio));
|
|
14205
15055
|
if (window.__liquidPerfCounts) float.dataset.liquidMotionAmount = amount.toFixed(4);
|
|
14206
15056
|
if (moved && float._renderKyantGlass) float._renderKyantGlass({ left:left, top:top, width:width, height:height }, true);
|
|
14207
15057
|
float._liquidMotionFrame = requestAnimationFrame(frame);
|
|
@@ -14218,6 +15068,8 @@ function stopLiquidMotionTracking(float) {
|
|
|
14218
15068
|
float.style.removeProperty('--liquid-motion-angle');
|
|
14219
15069
|
float.style.removeProperty('--liquid-motion-stretch');
|
|
14220
15070
|
float.style.removeProperty('--liquid-motion-squash');
|
|
15071
|
+
float.style.removeProperty('--liquid-boundary-scale-x');
|
|
15072
|
+
float.style.removeProperty('--liquid-boundary-scale-y');
|
|
14221
15073
|
}
|
|
14222
15074
|
|
|
14223
15075
|
function waitForLiquidSelectionArrival(float, onArrive) {
|
|
@@ -14277,6 +15129,11 @@ function beginLiquidSelectionLift(float) {
|
|
|
14277
15129
|
|
|
14278
15130
|
function wireLiquidMenuInteractions(menu) {
|
|
14279
15131
|
if (!menu || menu.dataset.liquidInteractions === 'true') return;
|
|
15132
|
+
if (menu.dataset.liquidDirectOptions === 'true') return;
|
|
15133
|
+
menu.dataset.liquidDirectOptions = 'true';
|
|
15134
|
+
// Popup options now interact as their own color block. No top-layer glass
|
|
15135
|
+
// float is created; the shell itself owns the liquid refraction and light.
|
|
15136
|
+
return;
|
|
14280
15137
|
menu.dataset.liquidInteractions = 'true';
|
|
14281
15138
|
var pointer = null;
|
|
14282
15139
|
var source = null;
|
|
@@ -14435,41 +15292,657 @@ function wireLiquidMenuInteractions(menu) {
|
|
|
14435
15292
|
}, true);
|
|
14436
15293
|
}
|
|
14437
15294
|
|
|
14438
|
-
|
|
14439
|
-
|
|
14440
|
-
|
|
14441
|
-
if (!container || container.dataset.liquidRailInteractions === 'true') return;
|
|
14442
|
-
container.dataset.liquidRailInteractions = 'true';
|
|
15295
|
+
function wireDirectLiquidMenuInteractions(menu) {
|
|
15296
|
+
if (!menu || menu.dataset.liquidDirectOptions === 'true') return;
|
|
15297
|
+
menu.dataset.liquidDirectOptions = 'true';
|
|
14443
15298
|
var pointer = null;
|
|
14444
15299
|
var source = null;
|
|
14445
15300
|
var target = null;
|
|
14446
|
-
var
|
|
14447
|
-
var
|
|
14448
|
-
var
|
|
14449
|
-
var
|
|
14450
|
-
var
|
|
14451
|
-
function
|
|
15301
|
+
var holdTimer = 0;
|
|
15302
|
+
var dragging = false;
|
|
15303
|
+
var moved = false;
|
|
15304
|
+
var startX = 0;
|
|
15305
|
+
var startY = 0;
|
|
15306
|
+
function optionAt(event) {
|
|
14452
15307
|
var hit = event && document.elementFromPoint ? document.elementFromPoint(event.clientX, event.clientY) : event.target;
|
|
14453
|
-
var node = hit && hit.closest ? hit.closest(
|
|
14454
|
-
return node &&
|
|
15308
|
+
var node = hit && hit.closest ? hit.closest('button.model-select-menu-option, button.newmark-select-option') : null;
|
|
15309
|
+
return node && menu.contains(node) && !node.disabled ? node : null;
|
|
14455
15310
|
}
|
|
14456
|
-
function
|
|
14457
|
-
if (!
|
|
14458
|
-
|
|
14459
|
-
|
|
14460
|
-
|
|
14461
|
-
|
|
14462
|
-
|
|
14463
|
-
|
|
14464
|
-
|
|
14465
|
-
|
|
14466
|
-
|
|
15311
|
+
function applyPress(option, event) {
|
|
15312
|
+
if (!option) return;
|
|
15313
|
+
var rect = option.getBoundingClientRect();
|
|
15314
|
+
option.style.setProperty('--liquid-option-origin-x', ((event.clientX - rect.left) / Math.max(1, rect.width) * 100) + '%');
|
|
15315
|
+
option.style.setProperty('--liquid-option-origin-y', ((event.clientY - rect.top) / Math.max(1, rect.height) * 100) + '%');
|
|
15316
|
+
option.style.setProperty('--liquid-option-press', '1');
|
|
15317
|
+
option.style.setProperty('--liquid-option-x', '0px');
|
|
15318
|
+
option.style.setProperty('--liquid-option-y', '0px');
|
|
15319
|
+
}
|
|
15320
|
+
function clearOption(option) {
|
|
15321
|
+
if (!option) return;
|
|
15322
|
+
option.style.removeProperty('--liquid-option-press');
|
|
15323
|
+
option.style.removeProperty('--liquid-option-x');
|
|
15324
|
+
option.style.removeProperty('--liquid-option-y');
|
|
15325
|
+
option.classList.remove('liquid-option-dragging');
|
|
15326
|
+
}
|
|
15327
|
+
function nearestOption(event) {
|
|
15328
|
+
var options = Array.from(menu.querySelectorAll('button.model-select-menu-option, button.newmark-select-option'))
|
|
15329
|
+
.filter(function(option) { return !option.disabled && option.getClientRects().length; });
|
|
15330
|
+
if (!options.length) return source;
|
|
15331
|
+
var nearest = options[0];
|
|
15332
|
+
var nearestDistance = Infinity;
|
|
15333
|
+
options.forEach(function(option) {
|
|
15334
|
+
var rect = option.getBoundingClientRect();
|
|
15335
|
+
var distance = Math.hypot(event.clientX - (rect.left + rect.width / 2), event.clientY - (rect.top + rect.height / 2));
|
|
15336
|
+
if (distance < nearestDistance) { nearest = option; nearestDistance = distance; }
|
|
15337
|
+
});
|
|
15338
|
+
return nearest;
|
|
15339
|
+
}
|
|
15340
|
+
function onPointerMove(event) {
|
|
15341
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
15342
|
+
event.preventDefault();
|
|
15343
|
+
var dx = event.clientX - startX;
|
|
15344
|
+
var dy = event.clientY - startY;
|
|
15345
|
+
var distance = Math.hypot(dx, dy);
|
|
15346
|
+
if (!dragging) {
|
|
15347
|
+
if (distance < 4) return;
|
|
15348
|
+
moved = true;
|
|
15349
|
+
var resisted = Math.min(LIQUID_POPUP_MAX_EDGE_JITTER_PX, Math.sqrt(distance) * 1.25);
|
|
15350
|
+
var ratio = resisted / Math.max(1, distance);
|
|
15351
|
+
source.style.setProperty('--liquid-option-x', (dx * ratio) + 'px');
|
|
15352
|
+
source.style.setProperty('--liquid-option-y', (dy * ratio) + 'px');
|
|
15353
|
+
source.style.setProperty('--liquid-option-press', '0.2');
|
|
15354
|
+
return;
|
|
14467
15355
|
}
|
|
14468
|
-
|
|
14469
|
-
|
|
14470
|
-
|
|
14471
|
-
|
|
14472
|
-
|
|
15356
|
+
target = nearestOption(event);
|
|
15357
|
+
source.style.setProperty('--liquid-option-x', dx + 'px');
|
|
15358
|
+
source.style.setProperty('--liquid-option-y', dy + 'px');
|
|
15359
|
+
}
|
|
15360
|
+
function onPointerUp(event) {
|
|
15361
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
15362
|
+
clearTimeout(holdTimer);
|
|
15363
|
+
holdTimer = 0;
|
|
15364
|
+
window.removeEventListener('pointermove', onPointerMove, true);
|
|
15365
|
+
window.removeEventListener('pointerup', onPointerUp, true);
|
|
15366
|
+
window.removeEventListener('pointercancel', onPointerCancel, true);
|
|
15367
|
+
try { menu.releasePointerCapture(pointer); } catch (_) {}
|
|
15368
|
+
var commit = dragging ? (target || source) : source;
|
|
15369
|
+
clearOption(source);
|
|
15370
|
+
pointer = null;
|
|
15371
|
+
source = null;
|
|
15372
|
+
target = null;
|
|
15373
|
+
dragging = false;
|
|
15374
|
+
moved = false;
|
|
15375
|
+
if (commit && commit.isConnected) commit.click();
|
|
15376
|
+
}
|
|
15377
|
+
function onPointerCancel(event) {
|
|
15378
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
15379
|
+
clearTimeout(holdTimer);
|
|
15380
|
+
holdTimer = 0;
|
|
15381
|
+
window.removeEventListener('pointermove', onPointerMove, true);
|
|
15382
|
+
window.removeEventListener('pointerup', onPointerUp, true);
|
|
15383
|
+
window.removeEventListener('pointercancel', onPointerCancel, true);
|
|
15384
|
+
try { menu.releasePointerCapture(pointer); } catch (_) {}
|
|
15385
|
+
clearOption(source);
|
|
15386
|
+
pointer = null;
|
|
15387
|
+
source = null;
|
|
15388
|
+
target = null;
|
|
15389
|
+
dragging = false;
|
|
15390
|
+
moved = false;
|
|
15391
|
+
}
|
|
15392
|
+
menu.addEventListener('pointerdown', function(event) {
|
|
15393
|
+
var pressed = optionAt(event);
|
|
15394
|
+
if (!pressed || event.button !== 0) return;
|
|
15395
|
+
event.preventDefault();
|
|
15396
|
+
// The block's source is always the current selected row; the row pressed
|
|
15397
|
+
// by the user is the destination for the pickup flight.
|
|
15398
|
+
source = menu.querySelector('button.model-select-menu-option.selected, button.newmark-select-option.selected') || pressed;
|
|
15399
|
+
target = pressed;
|
|
15400
|
+
pointer = event.pointerId;
|
|
15401
|
+
startX = event.clientX;
|
|
15402
|
+
startY = event.clientY;
|
|
15403
|
+
moved = false;
|
|
15404
|
+
dragging = false;
|
|
15405
|
+
applyPress(source, event);
|
|
15406
|
+
menu.setPointerCapture(pointer);
|
|
15407
|
+
window.addEventListener('pointermove', onPointerMove, true);
|
|
15408
|
+
window.addEventListener('pointerup', onPointerUp, true);
|
|
15409
|
+
window.addEventListener('pointercancel', onPointerCancel, true);
|
|
15410
|
+
holdTimer = setTimeout(function() {
|
|
15411
|
+
holdTimer = 0;
|
|
15412
|
+
if (source && !moved && !dragging) {
|
|
15413
|
+
dragging = true;
|
|
15414
|
+
source.classList.add('liquid-option-dragging');
|
|
15415
|
+
source.style.setProperty('--liquid-option-press', '0.45');
|
|
15416
|
+
}
|
|
15417
|
+
}, LIQUID_HOLD_DRAG_ACTIVATION_MS);
|
|
15418
|
+
}, true);
|
|
15419
|
+
}
|
|
15420
|
+
|
|
15421
|
+
function wireDirectLiquidMenuInteractionsV2(menu) {
|
|
15422
|
+
if (!menu) return;
|
|
15423
|
+
var COLOR_BLOCK_FLIGHT_MS = 240;
|
|
15424
|
+
function ensureBlock() {
|
|
15425
|
+
var block = menu._liquidColorBlock;
|
|
15426
|
+
if (block && block.isConnected && block.parentNode === menu) {
|
|
15427
|
+
// During a source→target flight the selected state is intentionally
|
|
15428
|
+
// still the old option. Re-anchoring here would snap the block back to
|
|
15429
|
+
// that source while a select rerender is being processed.
|
|
15430
|
+
if (!menu.dataset.liquidPendingCommit) positionSelectedBlock();
|
|
15431
|
+
return;
|
|
15432
|
+
}
|
|
15433
|
+
// `innerHTML` replacement detaches children, including the color block.
|
|
15434
|
+
// Reuse the expando-backed node when available instead of creating a new
|
|
15435
|
+
// block (which also loses its in-flight geometry and transition state).
|
|
15436
|
+
if (!block) {
|
|
15437
|
+
block = document.createElement('div');
|
|
15438
|
+
block.className = 'liquid-menu-color-block';
|
|
15439
|
+
}
|
|
15440
|
+
menu.appendChild(block);
|
|
15441
|
+
menu._liquidColorBlock = block;
|
|
15442
|
+
if (!menu.dataset.liquidPendingCommit) positionSelectedBlock();
|
|
15443
|
+
}
|
|
15444
|
+
function positionBlock(option) {
|
|
15445
|
+
var block = menu._liquidColorBlock;
|
|
15446
|
+
if (!block || !option || !option.getClientRects().length) return;
|
|
15447
|
+
block.style.left = option.offsetLeft + 'px';
|
|
15448
|
+
block.style.top = option.offsetTop + 'px';
|
|
15449
|
+
block.style.width = option.offsetWidth + 'px';
|
|
15450
|
+
block.style.height = option.offsetHeight + 'px';
|
|
15451
|
+
block.style.opacity = '1';
|
|
15452
|
+
}
|
|
15453
|
+
function setBlockLight(event) {
|
|
15454
|
+
var block = menu._liquidColorBlock;
|
|
15455
|
+
if (!block || !event) return;
|
|
15456
|
+
// PC mouse interaction deliberately has no moving light source. Keep this
|
|
15457
|
+
// hook available for non-mouse pointers so the shared component remains
|
|
15458
|
+
// touch-capable without changing any glass geometry or material.
|
|
15459
|
+
block.style.setProperty('--liquid-block-light-opacity', event.pointerType === 'mouse' ? '0' : '1');
|
|
15460
|
+
if (event.pointerType === 'mouse') return;
|
|
15461
|
+
var rect = block.getBoundingClientRect();
|
|
15462
|
+
if (!rect.width || !rect.height) return;
|
|
15463
|
+
block.style.setProperty('--liquid-block-light-x', Math.max(0, Math.min(100, ((event.clientX - rect.left) / rect.width) * 100)) + '%');
|
|
15464
|
+
block.style.setProperty('--liquid-block-light-y', Math.max(0, Math.min(100, ((event.clientY - rect.top) / rect.height) * 100)) + '%');
|
|
15465
|
+
}
|
|
15466
|
+
function setBlockLifted(lifted) {
|
|
15467
|
+
var block = menu._liquidColorBlock;
|
|
15468
|
+
if (!block) return;
|
|
15469
|
+
block.classList.toggle('liquid-block-lifted', !!lifted);
|
|
15470
|
+
}
|
|
15471
|
+
function positionSelectedBlock() {
|
|
15472
|
+
positionBlock(menu.querySelector('button.model-select-menu-option.selected, button.newmark-select-option.selected'));
|
|
15473
|
+
}
|
|
15474
|
+
function optionAt(event) {
|
|
15475
|
+
var hit = event && document.elementFromPoint ? document.elementFromPoint(event.clientX, event.clientY) : event.target;
|
|
15476
|
+
var node = hit && hit.closest ? hit.closest('button.model-select-menu-option, button.newmark-select-option') : null;
|
|
15477
|
+
return node && menu.contains(node) && !node.disabled ? node : null;
|
|
15478
|
+
}
|
|
15479
|
+
function nearestOption(event) {
|
|
15480
|
+
var options = Array.from(menu.querySelectorAll('button.model-select-menu-option, button.newmark-select-option'))
|
|
15481
|
+
.filter(function(option) { return !option.disabled && option.getClientRects().length; });
|
|
15482
|
+
if (!options.length) return source;
|
|
15483
|
+
var nearest = options[0];
|
|
15484
|
+
var nearestDistance = Infinity;
|
|
15485
|
+
options.forEach(function(option) {
|
|
15486
|
+
var rect = option.getBoundingClientRect();
|
|
15487
|
+
var distance = Math.hypot(event.clientX - (rect.left + rect.width / 2), event.clientY - (rect.top + rect.height / 2));
|
|
15488
|
+
if (distance < nearestDistance) { nearest = option; nearestDistance = distance; }
|
|
15489
|
+
});
|
|
15490
|
+
return nearest;
|
|
15491
|
+
}
|
|
15492
|
+
function trackBlock(dx, dy) {
|
|
15493
|
+
var options = Array.from(menu.querySelectorAll('button.model-select-menu-option, button.newmark-select-option'))
|
|
15494
|
+
.filter(function(option) { return !option.disabled && option.getClientRects().length; });
|
|
15495
|
+
if (!options.length || !menu._liquidColorBlock) return;
|
|
15496
|
+
var first = options[0];
|
|
15497
|
+
var last = options[options.length - 1];
|
|
15498
|
+
var vertical = options.length < 2 || Math.abs(options[1].offsetTop - first.offsetTop) >= Math.abs(options[1].offsetLeft - first.offsetLeft);
|
|
15499
|
+
var minPos = vertical ? Math.min(first.offsetTop, last.offsetTop) : Math.min(first.offsetLeft, last.offsetLeft);
|
|
15500
|
+
var maxPos = vertical ? Math.max(first.offsetTop, last.offsetTop) : Math.max(first.offsetLeft, last.offsetLeft);
|
|
15501
|
+
var rawPos = vertical ? blockStartTop + dy : blockStartLeft + dx;
|
|
15502
|
+
var clampedPos = Math.max(minPos, Math.min(maxPos, rawPos));
|
|
15503
|
+
var boundaryDelta = rawPos - clampedPos;
|
|
15504
|
+
var block = menu._liquidColorBlock;
|
|
15505
|
+
block.style.transition = 'none';
|
|
15506
|
+
block.style.left = (vertical ? blockStartLeft : clampedPos) + 'px';
|
|
15507
|
+
block.style.top = (vertical ? clampedPos : blockStartTop) + 'px';
|
|
15508
|
+
return { vertical: vertical, boundaryDelta: boundaryDelta };
|
|
15509
|
+
}
|
|
15510
|
+
function animateBlockTo(option, done) {
|
|
15511
|
+
var block = menu._liquidColorBlock;
|
|
15512
|
+
if (!block || !option || !block.isConnected) {
|
|
15513
|
+
if (done) done();
|
|
15514
|
+
return;
|
|
15515
|
+
}
|
|
15516
|
+
var targetLeft = option.offsetLeft + 'px';
|
|
15517
|
+
var targetTop = option.offsetTop + 'px';
|
|
15518
|
+
var targetWidth = option.offsetWidth + 'px';
|
|
15519
|
+
var targetHeight = option.offsetHeight + 'px';
|
|
15520
|
+
var flightId = String(Date.now()) + ':' + Math.random();
|
|
15521
|
+
menu.dataset.liquidPendingCommit = flightId;
|
|
15522
|
+
if (menu._liquidColorFlightTimer) clearTimeout(menu._liquidColorFlightTimer);
|
|
15523
|
+
// Freeze the current (source or pointer-tracked) geometry, force a style
|
|
15524
|
+
// flush, then use two animation frames: the first commits the no-transition
|
|
15525
|
+
// source frame, the second starts the stylesheet transition. One frame is
|
|
15526
|
+
// not sufficient on Chromium when pointerup and the style write share the
|
|
15527
|
+
// same rendering update.
|
|
15528
|
+
freezeBlockGeometry(block);
|
|
15529
|
+
block.getBoundingClientRect();
|
|
15530
|
+
void block.offsetWidth;
|
|
15531
|
+
requestAnimationFrame(function() {
|
|
15532
|
+
if (menu.dataset.liquidPendingCommit !== flightId || !block.isConnected) return;
|
|
15533
|
+
requestAnimationFrame(function() {
|
|
15534
|
+
if (menu.dataset.liquidPendingCommit !== flightId || !block.isConnected) return;
|
|
15535
|
+
block.style.transition = '';
|
|
15536
|
+
block.style.left = targetLeft;
|
|
15537
|
+
block.style.top = targetTop;
|
|
15538
|
+
block.style.width = targetWidth;
|
|
15539
|
+
block.style.height = targetHeight;
|
|
15540
|
+
menu._liquidColorFlightTimer = setTimeout(function() {
|
|
15541
|
+
menu._liquidColorFlightTimer = 0;
|
|
15542
|
+
if (menu.dataset.liquidPendingCommit !== flightId) return;
|
|
15543
|
+
if (done) done();
|
|
15544
|
+
}, COLOR_BLOCK_FLIGHT_MS + 16);
|
|
15545
|
+
});
|
|
15546
|
+
});
|
|
15547
|
+
}
|
|
15548
|
+
function animateBlockToLongPressRow(option, done) {
|
|
15549
|
+
var block = menu._liquidColorBlock;
|
|
15550
|
+
if (!block || !option || !block.isConnected) {
|
|
15551
|
+
if (done) done();
|
|
15552
|
+
return;
|
|
15553
|
+
}
|
|
15554
|
+
var targetLeft = option.offsetLeft + 'px';
|
|
15555
|
+
var targetTop = option.offsetTop + 'px';
|
|
15556
|
+
var targetWidth = option.offsetWidth + 'px';
|
|
15557
|
+
var targetHeight = option.offsetHeight + 'px';
|
|
15558
|
+
var pickupId = ++pickupGeneration;
|
|
15559
|
+
setBlockLifted(true);
|
|
15560
|
+
freezeBlockGeometry(block);
|
|
15561
|
+
block.getBoundingClientRect();
|
|
15562
|
+
void block.offsetWidth;
|
|
15563
|
+
requestAnimationFrame(function() {
|
|
15564
|
+
if (pickupId !== pickupGeneration || !block.isConnected) return;
|
|
15565
|
+
requestAnimationFrame(function() {
|
|
15566
|
+
if (pickupId !== pickupGeneration || !block.isConnected) return;
|
|
15567
|
+
block.style.transition = '';
|
|
15568
|
+
block.style.left = targetLeft;
|
|
15569
|
+
block.style.top = targetTop;
|
|
15570
|
+
block.style.width = targetWidth;
|
|
15571
|
+
block.style.height = targetHeight;
|
|
15572
|
+
pickupTimer = setTimeout(function() {
|
|
15573
|
+
pickupTimer = 0;
|
|
15574
|
+
if (pickupId === pickupGeneration && done) done();
|
|
15575
|
+
}, COLOR_BLOCK_FLIGHT_MS + 16);
|
|
15576
|
+
});
|
|
15577
|
+
});
|
|
15578
|
+
}
|
|
15579
|
+
function freezeBlockGeometry(block) {
|
|
15580
|
+
var style = getComputedStyle(block);
|
|
15581
|
+
var left = style.left;
|
|
15582
|
+
var top = style.top;
|
|
15583
|
+
var width = style.width;
|
|
15584
|
+
var height = style.height;
|
|
15585
|
+
block.style.transition = 'none';
|
|
15586
|
+
block.style.left = left;
|
|
15587
|
+
block.style.top = top;
|
|
15588
|
+
block.style.width = width;
|
|
15589
|
+
block.style.height = height;
|
|
15590
|
+
}
|
|
15591
|
+
function cancelPickup() {
|
|
15592
|
+
pickupGeneration += 1;
|
|
15593
|
+
clearTimeout(pickupTimer);
|
|
15594
|
+
pickupTimer = 0;
|
|
15595
|
+
blockLiftAnimating = false;
|
|
15596
|
+
}
|
|
15597
|
+
function applyPress(option, event) {
|
|
15598
|
+
// Popup option content is inert. All visual feedback is carried by the
|
|
15599
|
+
// moving capsule block and the popup shell; never write transform/press
|
|
15600
|
+
// variables onto the text-bearing option itself.
|
|
15601
|
+
void option;
|
|
15602
|
+
void event;
|
|
15603
|
+
setLiquidPopupPressDeformation(menu.closest('.liquid-glass-popup'));
|
|
15604
|
+
}
|
|
15605
|
+
function clearOption(option) {
|
|
15606
|
+
if (!option) return;
|
|
15607
|
+
option.style.removeProperty('--liquid-option-press');
|
|
15608
|
+
option.style.removeProperty('--liquid-option-x');
|
|
15609
|
+
option.style.removeProperty('--liquid-option-y');
|
|
15610
|
+
option.classList.remove('liquid-option-dragging');
|
|
15611
|
+
}
|
|
15612
|
+
// `syncModelSelectSurface`/`syncNewmarkSelect` rebuild option markup with
|
|
15613
|
+
// `innerHTML`, which detaches the old block while leaving the expando
|
|
15614
|
+
// reference truthy. Always reconcile the carrier before the dataset guard
|
|
15615
|
+
// so a rerender gets a live block again (and an existing one is re-anchored
|
|
15616
|
+
// to the current selection).
|
|
15617
|
+
ensureBlock();
|
|
15618
|
+
if (menu.dataset.liquidDirectOptions === 'true') return;
|
|
15619
|
+
menu.dataset.liquidDirectOptions = 'true';
|
|
15620
|
+
var pointer = null;
|
|
15621
|
+
var source = null;
|
|
15622
|
+
var target = null;
|
|
15623
|
+
var holdTimer = 0;
|
|
15624
|
+
var dragging = false;
|
|
15625
|
+
var blockLiftAnimating = false;
|
|
15626
|
+
var pickupGeneration = 0;
|
|
15627
|
+
var pickupTimer = 0;
|
|
15628
|
+
var latestPointerEvent = null;
|
|
15629
|
+
var moved = false;
|
|
15630
|
+
var startX = 0;
|
|
15631
|
+
var startY = 0;
|
|
15632
|
+
var blockStartLeft = 0;
|
|
15633
|
+
var blockStartTop = 0;
|
|
15634
|
+
// Hidden menus have no option geometry. Once the popover has its final
|
|
15635
|
+
// visible width/position, establish the idle source without a fake flight
|
|
15636
|
+
// from zero (or from the previous opening). Active gestures own their frame.
|
|
15637
|
+
menu._liquidSyncSelectedBlock = function() {
|
|
15638
|
+
if (pointer !== null || menu.dataset.liquidPendingCommit) return;
|
|
15639
|
+
var block = menu._liquidColorBlock;
|
|
15640
|
+
if (!block || !menu.getClientRects().length) return;
|
|
15641
|
+
var transition = block.style.transition;
|
|
15642
|
+
block.style.transition = 'none';
|
|
15643
|
+
positionSelectedBlock();
|
|
15644
|
+
void block.offsetWidth;
|
|
15645
|
+
block.style.transition = transition;
|
|
15646
|
+
};
|
|
15647
|
+
function updatePopupShellDrag(dx, dy, clear) {
|
|
15648
|
+
var popup = menu.closest('.liquid-glass-popup');
|
|
15649
|
+
if (!popup) return;
|
|
15650
|
+
setLiquidPopupDeformation(popup, dx, dy, clear);
|
|
15651
|
+
}
|
|
15652
|
+
function onPointerMove(event) {
|
|
15653
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
15654
|
+
event.preventDefault();
|
|
15655
|
+
latestPointerEvent = event;
|
|
15656
|
+
var dx = event.clientX - startX;
|
|
15657
|
+
var dy = event.clientY - startY;
|
|
15658
|
+
var distance = Math.hypot(dx, dy);
|
|
15659
|
+
if (!dragging) {
|
|
15660
|
+
moved = distance >= 4;
|
|
15661
|
+
return;
|
|
15662
|
+
}
|
|
15663
|
+
target = nearestOption(event);
|
|
15664
|
+
if (blockLiftAnimating) {
|
|
15665
|
+
setBlockLight(event);
|
|
15666
|
+
return;
|
|
15667
|
+
}
|
|
15668
|
+
var track = trackBlock(dx, dy);
|
|
15669
|
+
setBlockLight(event);
|
|
15670
|
+
// A normal rail drag stays rigid and follows only its track. Deform the
|
|
15671
|
+
// popup shell only when a held drag pushes beyond the rail endpoint.
|
|
15672
|
+
if (track && Math.abs(track.boundaryDelta) > 0.01) {
|
|
15673
|
+
updatePopupShellDrag(track.vertical ? 0 : track.boundaryDelta, track.vertical ? track.boundaryDelta : 0, false);
|
|
15674
|
+
} else {
|
|
15675
|
+
updatePopupShellDrag(0, 0, true);
|
|
15676
|
+
}
|
|
15677
|
+
}
|
|
15678
|
+
function finishAndCommit(commit) {
|
|
15679
|
+
clearTimeout(holdTimer);
|
|
15680
|
+
holdTimer = 0;
|
|
15681
|
+
// Release/cancel owns the flight state. Without clearing this flag here,
|
|
15682
|
+
// a pointerup during the pickup animation leaves the next interaction in
|
|
15683
|
+
// a permanently non-tracking state.
|
|
15684
|
+
cancelPickup();
|
|
15685
|
+
window.removeEventListener('pointermove', onPointerMove, true);
|
|
15686
|
+
window.removeEventListener('pointerup', onPointerUp, true);
|
|
15687
|
+
window.removeEventListener('pointercancel', onPointerCancel, true);
|
|
15688
|
+
try { menu.releasePointerCapture(pointer); } catch (_) {}
|
|
15689
|
+
var option = commit || source;
|
|
15690
|
+
var popup = menu.closest('.liquid-glass-popup');
|
|
15691
|
+
if (popup) {
|
|
15692
|
+
if (dragging) {
|
|
15693
|
+
setLiquidPopupDeformation(popup, 0, 0, true);
|
|
15694
|
+
} else {
|
|
15695
|
+
setLiquidPopupPressDeformation(popup);
|
|
15696
|
+
setTimeout(function() {
|
|
15697
|
+
if (popup.isConnected && pointer === null) {
|
|
15698
|
+
setLiquidPopupDeformation(popup, 0, 0, true);
|
|
15699
|
+
}
|
|
15700
|
+
}, 120);
|
|
15701
|
+
}
|
|
15702
|
+
}
|
|
15703
|
+
clearOption(source);
|
|
15704
|
+
pointer = null;
|
|
15705
|
+
source = null;
|
|
15706
|
+
target = null;
|
|
15707
|
+
dragging = false;
|
|
15708
|
+
moved = false;
|
|
15709
|
+
if (!option || !option.isConnected) return;
|
|
15710
|
+
// Let the color block finish its full source→target flight before the
|
|
15711
|
+
// change handler rerenders the menu. Otherwise the selected-state rebuild
|
|
15712
|
+
// snaps the block back to the old geometry halfway through the animation.
|
|
15713
|
+
var selectedBefore = menu.querySelector('button.model-select-menu-option.selected, button.newmark-select-option.selected');
|
|
15714
|
+
if (option && option.isConnected && selectedBefore && (selectedBefore !== option || menu._liquidColorBlock.classList.contains('liquid-block-lifted'))) {
|
|
15715
|
+
animateBlockTo(option, function() {
|
|
15716
|
+
if (!option.isConnected) {
|
|
15717
|
+
setBlockLifted(false);
|
|
15718
|
+
delete menu.dataset.liquidPendingCommit;
|
|
15719
|
+
return;
|
|
15720
|
+
}
|
|
15721
|
+
setBlockLifted(false);
|
|
15722
|
+
option.click();
|
|
15723
|
+
delete menu.dataset.liquidPendingCommit;
|
|
15724
|
+
});
|
|
15725
|
+
} else if (option && option.isConnected) {
|
|
15726
|
+
// Pointerup is followed by a trusted native click for the same gesture.
|
|
15727
|
+
// Suppress only that one follow-up click; keyboard activation remains a
|
|
15728
|
+
// normal trusted click because it does not set this pointer-owned flag.
|
|
15729
|
+
menu.dataset.liquidSuppressTrustedClick = 'true';
|
|
15730
|
+
setBlockLifted(false);
|
|
15731
|
+
option.click();
|
|
15732
|
+
setTimeout(function() { delete menu.dataset.liquidSuppressTrustedClick; }, 0);
|
|
15733
|
+
}
|
|
15734
|
+
}
|
|
15735
|
+
function onPointerUp(event) {
|
|
15736
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
15737
|
+
finishAndCommit(dragging ? target : target || source);
|
|
15738
|
+
}
|
|
15739
|
+
function onPointerCancel(event) {
|
|
15740
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
15741
|
+
clearTimeout(holdTimer);
|
|
15742
|
+
holdTimer = 0;
|
|
15743
|
+
window.removeEventListener('pointermove', onPointerMove, true);
|
|
15744
|
+
window.removeEventListener('pointerup', onPointerUp, true);
|
|
15745
|
+
window.removeEventListener('pointercancel', onPointerCancel, true);
|
|
15746
|
+
try { menu.releasePointerCapture(pointer); } catch (_) {}
|
|
15747
|
+
cancelPickup();
|
|
15748
|
+
if (menu._liquidColorBlock) freezeBlockGeometry(menu._liquidColorBlock);
|
|
15749
|
+
positionSelectedBlock();
|
|
15750
|
+
setBlockLifted(false);
|
|
15751
|
+
updatePopupShellDrag(0, 0, true);
|
|
15752
|
+
clearOption(source);
|
|
15753
|
+
pointer = null;
|
|
15754
|
+
source = null;
|
|
15755
|
+
target = null;
|
|
15756
|
+
dragging = false;
|
|
15757
|
+
moved = false;
|
|
15758
|
+
}
|
|
15759
|
+
menu.addEventListener('pointerdown', function(event) {
|
|
15760
|
+
var pressed = optionAt(event);
|
|
15761
|
+
if (!pressed || event.button !== 0) return;
|
|
15762
|
+
if (menu.dataset.liquidPendingCommit) return;
|
|
15763
|
+
event.preventDefault();
|
|
15764
|
+
// The capsule starts from the currently selected row and flies to the
|
|
15765
|
+
// row where the pointer was pressed; option content remains stationary.
|
|
15766
|
+
source = menu.querySelector('button.model-select-menu-option.selected, button.newmark-select-option.selected') || pressed;
|
|
15767
|
+
target = pressed;
|
|
15768
|
+
pointer = event.pointerId;
|
|
15769
|
+
startX = event.clientX;
|
|
15770
|
+
startY = event.clientY;
|
|
15771
|
+
latestPointerEvent = event;
|
|
15772
|
+
moved = false;
|
|
15773
|
+
dragging = false;
|
|
15774
|
+
applyPress(source, event);
|
|
15775
|
+
var block = menu._liquidColorBlock;
|
|
15776
|
+
if (block) {
|
|
15777
|
+
blockStartLeft = parseFloat(block.style.left || '0') || source.offsetLeft || 0;
|
|
15778
|
+
blockStartTop = parseFloat(block.style.top || '0') || source.offsetTop || 0;
|
|
15779
|
+
}
|
|
15780
|
+
menu.setPointerCapture(pointer);
|
|
15781
|
+
window.addEventListener('pointermove', onPointerMove, true);
|
|
15782
|
+
window.addEventListener('pointerup', onPointerUp, true);
|
|
15783
|
+
window.addEventListener('pointercancel', onPointerCancel, true);
|
|
15784
|
+
holdTimer = setTimeout(function() {
|
|
15785
|
+
holdTimer = 0;
|
|
15786
|
+
if (source && !moved && !dragging) {
|
|
15787
|
+
dragging = true;
|
|
15788
|
+
blockLiftAnimating = true;
|
|
15789
|
+
// The held press ends exactly when the selected block begins pickup.
|
|
15790
|
+
updatePopupShellDrag(0, 0, true);
|
|
15791
|
+
setBlockLifted(true);
|
|
15792
|
+
setBlockLight({ clientX: startX, clientY: startY, pointerType: event.pointerType });
|
|
15793
|
+
// The carrier starts at the currently selected row and flies to the
|
|
15794
|
+
// row where the long press began. Its drag baseline must then remain
|
|
15795
|
+
// at that destination, otherwise the first pointermove snaps it back
|
|
15796
|
+
// to the original selected row.
|
|
15797
|
+
var pickupTarget = target || source;
|
|
15798
|
+
animateBlockToLongPressRow(pickupTarget, function() {
|
|
15799
|
+
blockLiftAnimating = false;
|
|
15800
|
+
var landed = pickupTarget;
|
|
15801
|
+
blockStartLeft = landed ? landed.offsetLeft : blockStartLeft;
|
|
15802
|
+
blockStartTop = landed ? landed.offsetTop : blockStartTop;
|
|
15803
|
+
if (latestPointerEvent && pointer !== null) onPointerMove(latestPointerEvent);
|
|
15804
|
+
});
|
|
15805
|
+
}
|
|
15806
|
+
}, LIQUID_HOLD_DRAG_ACTIVATION_MS);
|
|
15807
|
+
}, true);
|
|
15808
|
+
// A physical mouse/touch activation produces a trusted native click after
|
|
15809
|
+
// pointerup. The pointer path above owns selection timing, so suppress that
|
|
15810
|
+
// native click while the color block is in flight. The delayed
|
|
15811
|
+
// `option.click()` is synthetic (`isTrusted === false`) and is intentionally
|
|
15812
|
+
// allowed through to the existing model/generic select commit handlers.
|
|
15813
|
+
menu.addEventListener('click', function(event) {
|
|
15814
|
+
var option = event.target && event.target.closest
|
|
15815
|
+
? event.target.closest('button.model-select-menu-option, button.newmark-select-option')
|
|
15816
|
+
: null;
|
|
15817
|
+
if (!option || !menu.contains(option) || !event.isTrusted) return;
|
|
15818
|
+
if (!menu.dataset.liquidPendingCommit && !menu.dataset.liquidSuppressTrustedClick) return;
|
|
15819
|
+
event.preventDefault();
|
|
15820
|
+
event.stopImmediatePropagation();
|
|
15821
|
+
}, true);
|
|
15822
|
+
ensureBlock();
|
|
15823
|
+
}
|
|
15824
|
+
|
|
15825
|
+
window.animateLiquidPopupExit = function(popup, anchor, done) {
|
|
15826
|
+
if (!popup) { if (done) done(); return; }
|
|
15827
|
+
clearLiquidPopupOutset(popup, true);
|
|
15828
|
+
if (popup._liquidExitTimer) clearTimeout(popup._liquidExitTimer);
|
|
15829
|
+
var rect = popup.getBoundingClientRect();
|
|
15830
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
15831
|
+
if (anchor && anchor.getBoundingClientRect) {
|
|
15832
|
+
var anchorRect = anchor.getBoundingClientRect();
|
|
15833
|
+
popup.style.setProperty(
|
|
15834
|
+
'--liquid-popup-origin-x',
|
|
15835
|
+
((anchorRect.left + anchorRect.width / 2 - rect.left) / rect.width * 100) + '%',
|
|
15836
|
+
);
|
|
15837
|
+
popup.style.setProperty(
|
|
15838
|
+
'--liquid-popup-origin-y',
|
|
15839
|
+
((anchorRect.top + anchorRect.height / 2 - rect.top) / rect.height * 100) + '%',
|
|
15840
|
+
);
|
|
15841
|
+
} else {
|
|
15842
|
+
popup.style.setProperty('--liquid-popup-origin-x', '50%');
|
|
15843
|
+
popup.style.setProperty('--liquid-popup-origin-y', '50%');
|
|
15844
|
+
}
|
|
15845
|
+
}
|
|
15846
|
+
popup.classList.add('liquid-popup-exit');
|
|
15847
|
+
popup._liquidExitTimer = setTimeout(function() {
|
|
15848
|
+
popup._liquidExitTimer = 0;
|
|
15849
|
+
popup.classList.remove('liquid-popup-exit');
|
|
15850
|
+
popup.style.removeProperty('--liquid-popup-origin-x');
|
|
15851
|
+
popup.style.removeProperty('--liquid-popup-origin-y');
|
|
15852
|
+
if (done) done();
|
|
15853
|
+
}, 210);
|
|
15854
|
+
};
|
|
15855
|
+
|
|
15856
|
+
function wireDirectPopupOptionPress() {
|
|
15857
|
+
if (document.documentElement.dataset.directPopupOptionPress === 'true') return;
|
|
15858
|
+
document.documentElement.dataset.directPopupOptionPress = 'true';
|
|
15859
|
+
var optionSelector = '.conv-action-menu-item, .command-option, .workspace-type-option';
|
|
15860
|
+
function optionAt(event) {
|
|
15861
|
+
var hit = event && document.elementFromPoint ? document.elementFromPoint(event.clientX, event.clientY) : event.target;
|
|
15862
|
+
return hit && hit.closest ? hit.closest(optionSelector) : null;
|
|
15863
|
+
}
|
|
15864
|
+
function applyPress(option, event) {
|
|
15865
|
+
var rect = option.getBoundingClientRect();
|
|
15866
|
+
option.style.setProperty('--liquid-option-origin-x', ((event.clientX - rect.left) / Math.max(1, rect.width) * 100) + '%');
|
|
15867
|
+
option.style.setProperty('--liquid-option-origin-y', ((event.clientY - rect.top) / Math.max(1, rect.height) * 100) + '%');
|
|
15868
|
+
option.style.setProperty('--liquid-option-press', '1');
|
|
15869
|
+
}
|
|
15870
|
+
function clearOption(option) {
|
|
15871
|
+
if (!option) return;
|
|
15872
|
+
option.style.removeProperty('--liquid-option-press');
|
|
15873
|
+
option.style.removeProperty('--liquid-option-x');
|
|
15874
|
+
option.style.removeProperty('--liquid-option-y');
|
|
15875
|
+
}
|
|
15876
|
+
document.addEventListener('pointerdown', function(event) {
|
|
15877
|
+
var option = optionAt(event);
|
|
15878
|
+
if (!option || option.disabled || event.button !== 0) return;
|
|
15879
|
+
var menu = option.closest('.model-select-menu, .newmark-select-menu');
|
|
15880
|
+
if (menu && menu.dataset.liquidDirectOptions === 'true') return;
|
|
15881
|
+
applyPress(option, event);
|
|
15882
|
+
// Content never follows the pointer inside a popup. The shell owns any
|
|
15883
|
+
// boundary/light response; this handler only supplies click-center scale.
|
|
15884
|
+
return;
|
|
15885
|
+
var startX = event.clientX;
|
|
15886
|
+
var startY = event.clientY;
|
|
15887
|
+
function onMove(move) {
|
|
15888
|
+
var dx = move.clientX - startX;
|
|
15889
|
+
var dy = move.clientY - startY;
|
|
15890
|
+
var distance = Math.hypot(dx, dy);
|
|
15891
|
+
if (distance < 4) return;
|
|
15892
|
+
move.preventDefault();
|
|
15893
|
+
var resisted = Math.min(LIQUID_POPUP_MAX_EDGE_JITTER_PX, Math.sqrt(distance) * 1.25);
|
|
15894
|
+
var ratio = resisted / Math.max(1, distance);
|
|
15895
|
+
option.style.setProperty('--liquid-option-x', (dx * ratio) + 'px');
|
|
15896
|
+
option.style.setProperty('--liquid-option-y', (dy * ratio) + 'px');
|
|
15897
|
+
option.style.setProperty('--liquid-option-press', '0.2');
|
|
15898
|
+
}
|
|
15899
|
+
function clear() {
|
|
15900
|
+
document.removeEventListener('pointermove', onMove, true);
|
|
15901
|
+
document.removeEventListener('pointerup', clear, true);
|
|
15902
|
+
document.removeEventListener('pointercancel', clear, true);
|
|
15903
|
+
clearOption(option);
|
|
15904
|
+
}
|
|
15905
|
+
document.addEventListener('pointermove', onMove, true);
|
|
15906
|
+
document.addEventListener('pointerup', clear, true);
|
|
15907
|
+
document.addEventListener('pointercancel', clear, true);
|
|
15908
|
+
}, true);
|
|
15909
|
+
}
|
|
15910
|
+
|
|
15911
|
+
// The same single floating capsule is used by the left workspace rail and
|
|
15912
|
+
// right sidebar tabs. Delegation keeps it valid while either rail rerenders.
|
|
15913
|
+
function wireLiquidRailInteractions(container, selector) {
|
|
15914
|
+
if (!container || container.dataset.liquidRailInteractions === 'true') return;
|
|
15915
|
+
container.dataset.liquidRailInteractions = 'true';
|
|
15916
|
+
var pointer = null;
|
|
15917
|
+
var source = null;
|
|
15918
|
+
var target = null;
|
|
15919
|
+
var float = null;
|
|
15920
|
+
var generation = 0;
|
|
15921
|
+
var pressX = 0;
|
|
15922
|
+
var pressY = 0;
|
|
15923
|
+
var gestureMoved = false;
|
|
15924
|
+
function itemAt(event) {
|
|
15925
|
+
var hit = event && document.elementFromPoint ? document.elementFromPoint(event.clientX, event.clientY) : event.target;
|
|
15926
|
+
var node = hit && hit.closest ? hit.closest(selector) : null;
|
|
15927
|
+
return node && container.contains(node) && !node.disabled ? node : null;
|
|
15928
|
+
}
|
|
15929
|
+
function position(item) {
|
|
15930
|
+
if (!item || !float) return;
|
|
15931
|
+
float.style.setProperty('--liquid-boundary-x', '0px');
|
|
15932
|
+
float.style.setProperty('--liquid-boundary-y', '0px');
|
|
15933
|
+
float.style.setProperty('--liquid-boundary-pull', '0');
|
|
15934
|
+
if (float._liquidCovered && float._liquidCovered !== item) {
|
|
15935
|
+
float._liquidCovered.classList.remove('liquid-selection-covered');
|
|
15936
|
+
}
|
|
15937
|
+
if (item !== source) {
|
|
15938
|
+
item.classList.add('liquid-selection-covered');
|
|
15939
|
+
float._liquidCovered = item;
|
|
15940
|
+
}
|
|
15941
|
+
if (float.classList.contains('liquid-selection-viewport')) {
|
|
15942
|
+
var rect = item.getBoundingClientRect();
|
|
15943
|
+
var edge = liquidInteractionEdgePx();
|
|
15944
|
+
keepLiquidFloatExpansionInsideViewport(float, rect);
|
|
15945
|
+
float._liquidTargetRect = { left: rect.left - edge, top: rect.top - edge, width: rect.width + edge * 2, height: rect.height + edge * 2 };
|
|
14473
15946
|
float.style.left = (rect.left - edge) + 'px';
|
|
14474
15947
|
float.style.top = (rect.top - edge) + 'px';
|
|
14475
15948
|
float.style.width = (rect.width + edge * 2) + 'px';
|
|
@@ -14510,7 +15983,7 @@ function wireLiquidRailInteractions(container, selector) {
|
|
|
14510
15983
|
var maxCenter = Math.max.apply(null, centers);
|
|
14511
15984
|
var projectedCenter = Math.max(minCenter, Math.min(maxCenter, pointerAxis));
|
|
14512
15985
|
var blockedDelta = pointerAxis < minCenter ? pointerAxis - minCenter : pointerAxis > maxCenter ? pointerAxis - maxCenter : 0;
|
|
14513
|
-
var resisted = Math.sign(blockedDelta) * Math.sqrt(Math.abs(blockedDelta)) * 0.6;
|
|
15986
|
+
var resisted = Math.sign(blockedDelta) * Math.min(LIQUID_POPUP_MAX_EDGE_JITTER_PX, Math.sqrt(Math.abs(blockedDelta)) * 0.6);
|
|
14514
15987
|
var edge = liquidInteractionEdgePx();
|
|
14515
15988
|
var left = horizontal ? projectedCenter - rect.width / 2 - edge : rect.left - edge;
|
|
14516
15989
|
var top = horizontal ? rect.top - edge : projectedCenter - rect.height / 2 - edge;
|
|
@@ -14697,7 +16170,7 @@ function wireLegacyLiquidButtons() {
|
|
|
14697
16170
|
var distance = Math.hypot(dx, dy);
|
|
14698
16171
|
if (distance < 2) return;
|
|
14699
16172
|
move.preventDefault();
|
|
14700
|
-
var resistedDistance = Math.min(
|
|
16173
|
+
var resistedDistance = Math.min(LIQUID_POPUP_MAX_EDGE_JITTER_PX, Math.sqrt(distance) * 1.25);
|
|
14701
16174
|
var ratio = resistedDistance / Math.max(1, distance);
|
|
14702
16175
|
float.style.setProperty('--liquid-boundary-x', (dx * ratio).toFixed(2) + 'px');
|
|
14703
16176
|
float.style.setProperty('--liquid-boundary-y', (dy * ratio).toFixed(2) + 'px');
|
|
@@ -14749,7 +16222,9 @@ function wireFixedPrimaryLiquidButtons() {
|
|
|
14749
16222
|
'.msg-action-btn', '.conv-more-btn', '.provider-icon-btn',
|
|
14750
16223
|
'.work-review-toggle', '.conversation-work-run-head',
|
|
14751
16224
|
'.shell-block-header', '.diff-block-header', '.work-review-head',
|
|
14752
|
-
'.message-edit-submit', '.message-edit-cancel'
|
|
16225
|
+
'.message-edit-submit', '.message-edit-cancel',
|
|
16226
|
+
'.model-select-menu-option', '.newmark-select-option',
|
|
16227
|
+
'.conv-action-menu-item', '.command-option', '.workspace-type-option'
|
|
14753
16228
|
].join(',');
|
|
14754
16229
|
document.addEventListener('pointerdown', function(event) {
|
|
14755
16230
|
var source = event.target && event.target.closest ? event.target.closest(selector) : null;
|
|
@@ -14788,7 +16263,7 @@ function wireFixedPrimaryLiquidButtons() {
|
|
|
14788
16263
|
var distance = Math.hypot(dx, dy);
|
|
14789
16264
|
if (distance < 2) return;
|
|
14790
16265
|
value.preventDefault();
|
|
14791
|
-
var resistedDistance = Math.min(
|
|
16266
|
+
var resistedDistance = Math.min(LIQUID_POPUP_MAX_EDGE_JITTER_PX, Math.sqrt(distance) * 1.25);
|
|
14792
16267
|
var ratio = resistedDistance / Math.max(1, distance);
|
|
14793
16268
|
float.style.setProperty('--liquid-boundary-x', (dx * ratio).toFixed(2) + 'px');
|
|
14794
16269
|
float.style.setProperty('--liquid-boundary-y', (dy * ratio).toFixed(2) + 'px');
|
|
@@ -14844,6 +16319,267 @@ function wireFixedPrimaryLiquidButtons() {
|
|
|
14844
16319
|
}, true);
|
|
14845
16320
|
}
|
|
14846
16321
|
|
|
16322
|
+
function wireLiquidPopupShellInteractions() {
|
|
16323
|
+
var active = null;
|
|
16324
|
+
var clearTimer = 0;
|
|
16325
|
+
function popupAt(target) {
|
|
16326
|
+
return target && target.closest ? target.closest('.liquid-glass-popup') : null;
|
|
16327
|
+
}
|
|
16328
|
+
function ensureOpticalCanvas(popup) {
|
|
16329
|
+
if (!popup) return null;
|
|
16330
|
+
var canvas = popup._liquidPopupOpticalCanvas;
|
|
16331
|
+
if (canvas && canvas.isConnected && canvas.parentNode === popup) return canvas;
|
|
16332
|
+
canvas = document.createElement('canvas');
|
|
16333
|
+
canvas.className = 'liquid-popup-optical-canvas';
|
|
16334
|
+
popup.insertBefore(canvas, popup.firstChild);
|
|
16335
|
+
popup._liquidPopupOpticalCanvas = canvas;
|
|
16336
|
+
popup._liquidPopupLight = { x: 0.5, y: 0.5, opacity: 0 };
|
|
16337
|
+
popup._liquidPopupLightPending = null;
|
|
16338
|
+
popup._liquidPopupLightFrame = 0;
|
|
16339
|
+
popup._liquidPopupDrawQueued = false;
|
|
16340
|
+
function resize() {
|
|
16341
|
+
if (!canvas.isConnected) return;
|
|
16342
|
+
var width = Math.max(1, popup.clientWidth);
|
|
16343
|
+
var height = Math.max(1, popup.clientHeight);
|
|
16344
|
+
var dpr = Math.min(window.devicePixelRatio || 1, 1.5);
|
|
16345
|
+
canvas.width = Math.max(1, Math.round(width * dpr));
|
|
16346
|
+
canvas.height = Math.max(1, Math.round(height * dpr));
|
|
16347
|
+
canvas.style.width = width + 'px';
|
|
16348
|
+
canvas.style.height = height + 'px';
|
|
16349
|
+
queuePopupOptics(popup);
|
|
16350
|
+
}
|
|
16351
|
+
if (window.ResizeObserver) {
|
|
16352
|
+
popup._liquidPopupOpticalObserver = new ResizeObserver(resize);
|
|
16353
|
+
popup._liquidPopupOpticalObserver.observe(popup);
|
|
16354
|
+
}
|
|
16355
|
+
resize();
|
|
16356
|
+
return canvas;
|
|
16357
|
+
}
|
|
16358
|
+
function ensureAllPopupCanvases(root) {
|
|
16359
|
+
(root || document).querySelectorAll('.liquid-glass-popup').forEach(function(popup) {
|
|
16360
|
+
ensureOpticalCanvas(popup);
|
|
16361
|
+
});
|
|
16362
|
+
}
|
|
16363
|
+
function drawPopupOptics(popup) {
|
|
16364
|
+
var canvas = popup && popup._liquidPopupOpticalCanvas;
|
|
16365
|
+
if (!canvas) return;
|
|
16366
|
+
var context = canvas.getContext('2d');
|
|
16367
|
+
if (!context) return;
|
|
16368
|
+
var light = popup._liquidPopupLight || { x: 0.5, y: 0.5, opacity: 0 };
|
|
16369
|
+
var width = canvas.width;
|
|
16370
|
+
var height = canvas.height;
|
|
16371
|
+
context.clearRect(0, 0, width, height);
|
|
16372
|
+
if (!(light.opacity > 0)) return;
|
|
16373
|
+
var popupCssWidth = Math.max(1, popup.clientWidth || canvas.clientWidth || 1);
|
|
16374
|
+
var canvasScale = width / popupCssWidth;
|
|
16375
|
+
var clipped = typeof context.save === 'function' &&
|
|
16376
|
+
typeof context.beginPath === 'function' &&
|
|
16377
|
+
typeof context.clip === 'function' &&
|
|
16378
|
+
(typeof context.roundRect === 'function' || typeof context.arcTo === 'function');
|
|
16379
|
+
if (clipped) {
|
|
16380
|
+
context.save();
|
|
16381
|
+
context.beginPath();
|
|
16382
|
+
var popupStyle = getComputedStyle(popup);
|
|
16383
|
+
var popupRadiusCss = parseFloat(popupStyle.borderTopLeftRadius) || liquidGlassCssNumber('--liquid-popup-radius', 24);
|
|
16384
|
+
var radius = Math.min(Math.max(0, popupRadiusCss * canvasScale), Math.min(width, height) * 0.5);
|
|
16385
|
+
if (typeof context.roundRect === 'function') {
|
|
16386
|
+
context.roundRect(0, 0, width, height, radius);
|
|
16387
|
+
} else {
|
|
16388
|
+
context.moveTo(radius, 0);
|
|
16389
|
+
context.arcTo(width, 0, width, height, radius);
|
|
16390
|
+
context.arcTo(width, height, 0, height, radius);
|
|
16391
|
+
context.arcTo(0, height, 0, 0, radius);
|
|
16392
|
+
context.arcTo(0, 0, width, 0, radius);
|
|
16393
|
+
context.closePath();
|
|
16394
|
+
}
|
|
16395
|
+
context.clip();
|
|
16396
|
+
}
|
|
16397
|
+
// Keep the interaction spot in physical CSS pixels. The previous
|
|
16398
|
+
// min(width,height) multiplier made a large dialog produce a huge light
|
|
16399
|
+
// field while a compact menu barely showed one. The backing canvas is
|
|
16400
|
+
// DPR-scaled, so convert the fixed 20px CSS radius to canvas pixels.
|
|
16401
|
+
var configuredRadius = liquidGlassCssNumber('--liquid-popup-glow-radius', 20);
|
|
16402
|
+
var radius = Math.max(1, configuredRadius * canvasScale);
|
|
16403
|
+
var gradient = context.createRadialGradient(light.x * width, light.y * height, 0, light.x * width, light.y * height, radius);
|
|
16404
|
+
gradient.addColorStop(0, 'rgba(255,255,255,' + (0.08 + light.opacity * 0.16).toFixed(3) + ')');
|
|
16405
|
+
gradient.addColorStop(1, 'rgba(255,255,255,0)');
|
|
16406
|
+
context.fillStyle = gradient;
|
|
16407
|
+
context.fillRect(0, 0, width, height);
|
|
16408
|
+
if (clipped) context.restore();
|
|
16409
|
+
}
|
|
16410
|
+
function queuePopupOptics(popup) {
|
|
16411
|
+
if (!popup || popup._liquidPopupDrawQueued) return;
|
|
16412
|
+
popup._liquidPopupDrawQueued = true;
|
|
16413
|
+
requestAnimationFrame(function() {
|
|
16414
|
+
popup._liquidPopupDrawQueued = false;
|
|
16415
|
+
if (popup.isConnected) drawPopupOptics(popup);
|
|
16416
|
+
});
|
|
16417
|
+
}
|
|
16418
|
+
function updateLight(popup, event, opacity) {
|
|
16419
|
+
if (!popup) return;
|
|
16420
|
+
// PC mouse pointers do not paint an interaction bloom. Keep the popup
|
|
16421
|
+
// shell, refraction and deformation active; only clear the pointer light.
|
|
16422
|
+
if (event && event.pointerType === 'mouse') {
|
|
16423
|
+
popup._liquidPopupLightPending = null;
|
|
16424
|
+
if (popup._liquidPopupLight) popup._liquidPopupLight.opacity = 0;
|
|
16425
|
+
popup.style.setProperty('--liquid-popup-light-opacity', '0');
|
|
16426
|
+
queuePopupOptics(popup);
|
|
16427
|
+
return;
|
|
16428
|
+
}
|
|
16429
|
+
ensureOpticalCanvas(popup);
|
|
16430
|
+
var rect = popup.getBoundingClientRect();
|
|
16431
|
+
if (!rect.width || !rect.height) return;
|
|
16432
|
+
var x = Math.max(0, Math.min(100, (event.clientX - rect.left) / rect.width * 100));
|
|
16433
|
+
var y = Math.max(0, Math.min(100, (event.clientY - rect.top) / rect.height * 100));
|
|
16434
|
+
popup._liquidPopupLightPending = { x: x / 100, y: y / 100, opacity: opacity };
|
|
16435
|
+
if (popup._liquidPopupLightFrame) return;
|
|
16436
|
+
popup._liquidPopupLightFrame = requestAnimationFrame(function() {
|
|
16437
|
+
popup._liquidPopupLightFrame = 0;
|
|
16438
|
+
if (!popup.isConnected) return;
|
|
16439
|
+
var pending = popup._liquidPopupLightPending;
|
|
16440
|
+
if (!pending) return;
|
|
16441
|
+
popup._liquidPopupLightPending = null;
|
|
16442
|
+
popup.style.setProperty('--liquid-light-x', (pending.x * 100).toFixed(2) + '%');
|
|
16443
|
+
popup.style.setProperty('--liquid-light-y', (pending.y * 100).toFixed(2) + '%');
|
|
16444
|
+
popup.style.setProperty('--liquid-popup-light-opacity', String(pending.opacity));
|
|
16445
|
+
popup._liquidPopupLight = pending;
|
|
16446
|
+
queuePopupOptics(popup);
|
|
16447
|
+
});
|
|
16448
|
+
}
|
|
16449
|
+
function clearPopup(popup) {
|
|
16450
|
+
if (!popup) return;
|
|
16451
|
+
popup.style.setProperty('--liquid-popup-light-opacity', '0');
|
|
16452
|
+
setLiquidPopupDeformation(popup, 0, 0, true);
|
|
16453
|
+
if (popup._liquidPopupLight) popup._liquidPopupLight.opacity = 0;
|
|
16454
|
+
popup._liquidPopupLightPending = null;
|
|
16455
|
+
if (popup._liquidPopupLightFrame) {
|
|
16456
|
+
cancelAnimationFrame(popup._liquidPopupLightFrame);
|
|
16457
|
+
popup._liquidPopupLightFrame = 0;
|
|
16458
|
+
}
|
|
16459
|
+
queuePopupOptics(popup);
|
|
16460
|
+
}
|
|
16461
|
+
function scheduleClear(popup, delay) {
|
|
16462
|
+
if (clearTimer) clearTimeout(clearTimer);
|
|
16463
|
+
clearTimer = setTimeout(function() {
|
|
16464
|
+
clearTimer = 0;
|
|
16465
|
+
clearPopup(popup);
|
|
16466
|
+
}, delay);
|
|
16467
|
+
}
|
|
16468
|
+
document.addEventListener('pointerover', function(event) {
|
|
16469
|
+
var popup = popupAt(event.target);
|
|
16470
|
+
if (popup) updateLight(popup, event, 0.35);
|
|
16471
|
+
}, true);
|
|
16472
|
+
document.addEventListener('toggle', function(event) {
|
|
16473
|
+
var popup = popupAt(event.target);
|
|
16474
|
+
if (popup && event.newState === 'closed') clearLiquidPopupOutset(popup, true);
|
|
16475
|
+
if (popup && event.newState === 'open') ensureOpticalCanvas(popup);
|
|
16476
|
+
}, true);
|
|
16477
|
+
ensureAllPopupCanvases(document);
|
|
16478
|
+
if (window.MutationObserver && document.body) {
|
|
16479
|
+
var popupObserver = new MutationObserver(function(records) {
|
|
16480
|
+
records.forEach(function(record) {
|
|
16481
|
+
Array.from(record.addedNodes || []).forEach(function(node) {
|
|
16482
|
+
if (node.nodeType !== 1) return;
|
|
16483
|
+
if (node.matches && node.matches('.liquid-glass-popup')) ensureOpticalCanvas(node);
|
|
16484
|
+
ensureAllPopupCanvases(node);
|
|
16485
|
+
});
|
|
16486
|
+
});
|
|
16487
|
+
});
|
|
16488
|
+
popupObserver.observe(document.body, { childList: true, subtree: true });
|
|
16489
|
+
}
|
|
16490
|
+
document.addEventListener('pointermove', function(event) {
|
|
16491
|
+
var popup = popupAt(event.target);
|
|
16492
|
+
if (popup && (!active || active.popup !== popup)) updateLight(popup, event, 0.35);
|
|
16493
|
+
}, true);
|
|
16494
|
+
document.addEventListener('pointerdown', function(event) {
|
|
16495
|
+
var popup = popupAt(event.target);
|
|
16496
|
+
if (!popup) return;
|
|
16497
|
+
if (clearTimer) { clearTimeout(clearTimer); clearTimer = 0; }
|
|
16498
|
+
active = { popup: popup, x: event.clientX, y: event.clientY, pointer: event.pointerId, startedAt: performance.now(), trackOwned: popup.dataset.liquidDirectOptions === 'true' };
|
|
16499
|
+
updateLight(popup, event, 1);
|
|
16500
|
+
// A click still gives the carrier shell a short, visible deformation even
|
|
16501
|
+
// when the pointer does not travel far enough to enter hold-drag mode.
|
|
16502
|
+
if (!active.trackOwned) setLiquidPopupPressDeformation(popup);
|
|
16503
|
+
}, true);
|
|
16504
|
+
window.addEventListener('pointermove', function(event) {
|
|
16505
|
+
if (!active || event.pointerId !== active.pointer) return;
|
|
16506
|
+
if (!active.popup || !active.popup.isConnected) {
|
|
16507
|
+
clearPopup(active.popup);
|
|
16508
|
+
active = null;
|
|
16509
|
+
return;
|
|
16510
|
+
}
|
|
16511
|
+
var dx = event.clientX - active.x;
|
|
16512
|
+
var dy = event.clientY - active.y;
|
|
16513
|
+
// A menu owns its rail and endpoint resistance. The generic shell may
|
|
16514
|
+
// respond only to a held gesture without a movement track.
|
|
16515
|
+
if (!active.trackOwned && performance.now() - active.startedAt >= LIQUID_HOLD_DRAG_ACTIVATION_MS) {
|
|
16516
|
+
setLiquidPopupDeformation(active.popup, dx, dy, false);
|
|
16517
|
+
}
|
|
16518
|
+
updateLight(active.popup, event, 1);
|
|
16519
|
+
}, true);
|
|
16520
|
+
window.addEventListener('pointerup', function() {
|
|
16521
|
+
if (!active) return;
|
|
16522
|
+
scheduleClear(active.popup, 120);
|
|
16523
|
+
active = null;
|
|
16524
|
+
}, true);
|
|
16525
|
+
window.addEventListener('pointercancel', function() {
|
|
16526
|
+
if (!active) return;
|
|
16527
|
+
clearPopup(active.popup);
|
|
16528
|
+
if (clearTimer) { clearTimeout(clearTimer); clearTimer = 0; }
|
|
16529
|
+
active = null;
|
|
16530
|
+
}, true);
|
|
16531
|
+
function cancelPopupFeedback() {
|
|
16532
|
+
if (active) {
|
|
16533
|
+
clearLiquidPopupOutset(active.popup, true);
|
|
16534
|
+
clearPopup(active.popup);
|
|
16535
|
+
active = null;
|
|
16536
|
+
}
|
|
16537
|
+
if (clearTimer) { clearTimeout(clearTimer); clearTimer = 0; }
|
|
16538
|
+
}
|
|
16539
|
+
window.addEventListener('blur', cancelPopupFeedback, true);
|
|
16540
|
+
window.addEventListener('pagehide', cancelPopupFeedback, true);
|
|
16541
|
+
}
|
|
16542
|
+
|
|
16543
|
+
function wireLiquidFloatGlowInteractions() {
|
|
16544
|
+
if (document.documentElement.dataset.liquidFloatGlowWired === 'true') return;
|
|
16545
|
+
document.documentElement.dataset.liquidFloatGlowWired = 'true';
|
|
16546
|
+
function activeFloats() {
|
|
16547
|
+
return Array.from(document.querySelectorAll('.liquid-selection-float.visible, .liquid-selection-float.liquid-option-dragging'))
|
|
16548
|
+
.filter(function(float) { return float.isConnected; });
|
|
16549
|
+
}
|
|
16550
|
+
function update(float, event, opacity) {
|
|
16551
|
+
if (!float || !float.isConnected) return;
|
|
16552
|
+
if (event && event.pointerType === 'mouse') {
|
|
16553
|
+
if (float._liquidGlow && float._liquidGlow.opacity > 0) {
|
|
16554
|
+
float._liquidGlow = { x: 0.5, y: 0.5, opacity: 0 };
|
|
16555
|
+
if (float._renderKyantGlass) float._renderKyantGlass(null, true);
|
|
16556
|
+
}
|
|
16557
|
+
return;
|
|
16558
|
+
}
|
|
16559
|
+
var rect = float.getBoundingClientRect();
|
|
16560
|
+
if (!rect.width || !rect.height) return;
|
|
16561
|
+
float._liquidGlow = {
|
|
16562
|
+
x: Math.max(0, Math.min(1, (event.clientX - rect.left) / rect.width)),
|
|
16563
|
+
y: Math.max(0, Math.min(1, (event.clientY - rect.top) / rect.height)),
|
|
16564
|
+
opacity: opacity,
|
|
16565
|
+
};
|
|
16566
|
+
if (float._renderKyantGlass) float._renderKyantGlass(null, true);
|
|
16567
|
+
}
|
|
16568
|
+
function clear(float) {
|
|
16569
|
+
if (!float) return;
|
|
16570
|
+
float._liquidGlow = { x: 0.5, y: 0.5, opacity: 0 };
|
|
16571
|
+
if (float._renderKyantGlass) float._renderKyantGlass(null, true);
|
|
16572
|
+
}
|
|
16573
|
+
document.addEventListener('pointermove', function(event) {
|
|
16574
|
+
activeFloats().forEach(function(float) { update(float, event, 0.5); });
|
|
16575
|
+
}, true);
|
|
16576
|
+
document.addEventListener('pointerdown', function(event) {
|
|
16577
|
+
activeFloats().forEach(function(float) { update(float, event, 1); });
|
|
16578
|
+
}, true);
|
|
16579
|
+
document.addEventListener('pointerup', function() { activeFloats().forEach(clear); }, true);
|
|
16580
|
+
document.addEventListener('pointercancel', function() { activeFloats().forEach(clear); }, true);
|
|
16581
|
+
}
|
|
16582
|
+
|
|
14847
16583
|
requestAnimationFrame(function() {
|
|
14848
16584
|
document.querySelectorAll('.liquid-click-source').forEach(function(source) {
|
|
14849
16585
|
source.classList.remove('liquid-click-source');
|
|
@@ -14860,6 +16596,9 @@ requestAnimationFrame(function() {
|
|
|
14860
16596
|
wireLiquidRailInteractions(document.getElementById('right-tabs'), '.tab-btn[data-tab]');
|
|
14861
16597
|
wireLegacyLiquidButtons();
|
|
14862
16598
|
wireFixedPrimaryLiquidButtons();
|
|
16599
|
+
wireLiquidPopupShellInteractions();
|
|
16600
|
+
wireLiquidFloatGlowInteractions();
|
|
16601
|
+
wireDirectPopupOptionPress();
|
|
14863
16602
|
});
|
|
14864
16603
|
|
|
14865
16604
|
// Checkbox switches use one pointer-captured glass thumb that follows the
|
|
@@ -15015,8 +16754,14 @@ window.syncModelSelectSurface = function() {
|
|
|
15015
16754
|
html += '<button type="button" class="model-select-menu-option' + (child.value === sel.value ? ' selected' : '') + '" role="option" aria-selected="' + (child.value === sel.value ? 'true' : 'false') + '" data-value="' + escAttr(child.value) + '" onclick="window.selectModelFromMenu(this.getAttribute(\'data-value\'))">' + esc(child.textContent || child.value) + '</button>';
|
|
15016
16755
|
}
|
|
15017
16756
|
});
|
|
16757
|
+
// Rebuilding the option list must not destroy the carrier block. A pending
|
|
16758
|
+
// source→target flight intentionally keeps the block's interpolated inline
|
|
16759
|
+
// geometry while the selected state is updated only after the flight ends.
|
|
16760
|
+
var colorBlock = menu._liquidColorBlock;
|
|
16761
|
+
if (colorBlock && colorBlock.parentNode === menu) colorBlock.remove();
|
|
15018
16762
|
menu.innerHTML = html;
|
|
15019
|
-
|
|
16763
|
+
if (colorBlock) menu.appendChild(colorBlock);
|
|
16764
|
+
wireDirectLiquidMenuInteractionsV2(menu);
|
|
15020
16765
|
};
|
|
15021
16766
|
|
|
15022
16767
|
document.addEventListener('pointerdown', function(event) {
|
|
@@ -15027,13 +16772,23 @@ document.addEventListener('pointerdown', function(event) {
|
|
|
15027
16772
|
window.closeNewmarkSelect = function(shell, options) {
|
|
15028
16773
|
shell = shell || document.querySelector('.newmark-select-shell.open');
|
|
15029
16774
|
if (!shell) return;
|
|
15030
|
-
shell.classList.remove('open');
|
|
15031
|
-
window.setLiquidSidebarGestureLock('popup:select:' + (shell.dataset.selectId || 'anonymous'), false);
|
|
15032
16775
|
var menu = shell.querySelector('.newmark-select-menu');
|
|
15033
|
-
if (menu && typeof menu.hidePopover === 'function' && menu.matches(':popover-open')) menu.hidePopover();
|
|
15034
16776
|
var button = shell.querySelector('.newmark-select-button');
|
|
15035
|
-
|
|
15036
|
-
if (
|
|
16777
|
+
var wasOpen = !!(menu && typeof menu.matches === 'function' && menu.matches(':popover-open'));
|
|
16778
|
+
if (!wasOpen) {
|
|
16779
|
+
shell.classList.remove('open');
|
|
16780
|
+
window.setLiquidSidebarGestureLock('popup:select:' + (shell.dataset.selectId || 'anonymous'), false);
|
|
16781
|
+
if (button) button.setAttribute('aria-expanded', 'false');
|
|
16782
|
+
if (options && options.focus && button) button.focus();
|
|
16783
|
+
return;
|
|
16784
|
+
}
|
|
16785
|
+
window.animateLiquidPopupExit(menu, button, function() {
|
|
16786
|
+
shell.classList.remove('open');
|
|
16787
|
+
window.setLiquidSidebarGestureLock('popup:select:' + (shell.dataset.selectId || 'anonymous'), false);
|
|
16788
|
+
if (menu && typeof menu.hidePopover === 'function' && menu.matches(':popover-open')) menu.hidePopover();
|
|
16789
|
+
if (button) button.setAttribute('aria-expanded', 'false');
|
|
16790
|
+
if (options && options.focus && button) button.focus();
|
|
16791
|
+
});
|
|
15037
16792
|
};
|
|
15038
16793
|
|
|
15039
16794
|
window.positionSelectPopup = function(button, menu) {
|
|
@@ -15054,6 +16809,7 @@ window.positionSelectPopup = function(button, menu) {
|
|
|
15054
16809
|
menu.style.left = left + 'px';
|
|
15055
16810
|
menu.style.top = (openAbove ? rect.top - visibleHeight - margin : rect.bottom + margin) + 'px';
|
|
15056
16811
|
menu.dataset.popupDirection = openAbove ? 'up' : 'down';
|
|
16812
|
+
if (menu._liquidSyncSelectedBlock) menu._liquidSyncSelectedBlock();
|
|
15057
16813
|
};
|
|
15058
16814
|
|
|
15059
16815
|
window.positionNewmarkSelectMenu = function(shell) {
|
|
@@ -15080,10 +16836,12 @@ window.syncNewmarkSelectWidth = function(select, shell) {
|
|
|
15080
16836
|
if (!select || !shell || shell.dataset.autoReadableWidth !== 'true') return;
|
|
15081
16837
|
var nativeWidth = Number(shell.dataset.nativeSelectWidth || 0);
|
|
15082
16838
|
var width = Math.max(nativeWidth, window.selectReadableControlWidth(select));
|
|
15083
|
-
|
|
15084
|
-
shell.style.
|
|
15085
|
-
shell.style.
|
|
15086
|
-
if (shell.
|
|
16839
|
+
var widthCss = width + 'px';
|
|
16840
|
+
if (shell.style.width !== widthCss) shell.style.width = widthCss;
|
|
16841
|
+
if (shell.style.minWidth !== widthCss) shell.style.minWidth = widthCss;
|
|
16842
|
+
if (shell.style.maxWidth !== 'none') shell.style.maxWidth = 'none';
|
|
16843
|
+
var flexCss = '0 0 ' + widthCss;
|
|
16844
|
+
if (shell.dataset.toolbarSelect === 'true' && shell.style.flex !== flexCss) shell.style.flex = flexCss;
|
|
15087
16845
|
};
|
|
15088
16846
|
|
|
15089
16847
|
window.syncNewmarkSelect = function(select) {
|
|
@@ -15093,9 +16851,11 @@ window.syncNewmarkSelect = function(select) {
|
|
|
15093
16851
|
var menu = shell.querySelector('.newmark-select-menu');
|
|
15094
16852
|
var button = shell.querySelector('.newmark-select-button');
|
|
15095
16853
|
var selected = select.options[select.selectedIndex] || select.options[0];
|
|
15096
|
-
|
|
15097
|
-
if (
|
|
15098
|
-
|
|
16854
|
+
var display = select.dataset.newmarkVisible === 'false' || select.style.display === 'none' ? 'none' : '';
|
|
16855
|
+
if (shell.style.display !== display) shell.style.display = display;
|
|
16856
|
+
var selectedText = selected ? selected.textContent : '';
|
|
16857
|
+
if (label && label.textContent !== selectedText) label.textContent = selectedText;
|
|
16858
|
+
if (button && button.disabled !== !!select.disabled) button.disabled = !!select.disabled;
|
|
15099
16859
|
window.syncNewmarkSelectWidth(select, shell);
|
|
15100
16860
|
if (!menu) return;
|
|
15101
16861
|
var html = '';
|
|
@@ -15111,8 +16871,18 @@ window.syncNewmarkSelect = function(select) {
|
|
|
15111
16871
|
html += '<button type="button" class="model-select-menu-option newmark-select-option' + (child.value === select.value ? ' selected' : '') + '" role="option" aria-selected="' + (child.value === select.value ? 'true' : 'false') + '" data-value="' + escAttr(child.value) + '"' + (child.disabled ? ' disabled' : '') + '>' + esc(child.textContent || child.value) + '</button>';
|
|
15112
16872
|
}
|
|
15113
16873
|
});
|
|
16874
|
+
// Snapshot refreshes commonly carry unchanged options. Rebuilding here
|
|
16875
|
+
// invalidates layout and detaches the moving carrier without a visual change.
|
|
16876
|
+
if (menu._newmarkSelectOptionsHtml === html) return;
|
|
16877
|
+
// Preserve the moving carrier across option markup rebuilds. Without this,
|
|
16878
|
+
// dispatching the native change event detaches the block before the delayed
|
|
16879
|
+
// color-block commit can finish, so users only see an instant snap.
|
|
16880
|
+
var colorBlock = menu._liquidColorBlock;
|
|
16881
|
+
if (colorBlock && colorBlock.parentNode === menu) colorBlock.remove();
|
|
15114
16882
|
menu.innerHTML = html;
|
|
15115
|
-
|
|
16883
|
+
menu._newmarkSelectOptionsHtml = html;
|
|
16884
|
+
if (colorBlock) menu.appendChild(colorBlock);
|
|
16885
|
+
wireDirectLiquidMenuInteractionsV2(menu);
|
|
15116
16886
|
};
|
|
15117
16887
|
|
|
15118
16888
|
window.enhanceNewmarkSelect = function(select) {
|
|
@@ -15153,7 +16923,7 @@ window.enhanceNewmarkSelect = function(select) {
|
|
|
15153
16923
|
button.setAttribute('aria-label', String(accessibleLabel).trim());
|
|
15154
16924
|
button.innerHTML = '<span class="newmark-select-button-label"></span>' + iconSvg('chevron-down', '', 'tiny');
|
|
15155
16925
|
var menu = document.createElement('div');
|
|
15156
|
-
menu.className = 'model-select-menu newmark-select-menu liquid-glass liquid-glass-carrier';
|
|
16926
|
+
menu.className = 'model-select-menu newmark-select-menu liquid-glass liquid-glass-carrier liquid-glass-popup';
|
|
15157
16927
|
menu.setAttribute('role', 'listbox');
|
|
15158
16928
|
menu.setAttribute('popover', 'manual');
|
|
15159
16929
|
select.parentNode.insertBefore(shell, select);
|
|
@@ -15357,6 +17127,15 @@ function bindBackendQueuedRequestToTarget(text, target, occurrence) {
|
|
|
15357
17127
|
request.backendManaged = true;
|
|
15358
17128
|
request.provenance = 'backend-follow-up';
|
|
15359
17129
|
request.backendOccurrence = Math.max(0, Number(occurrence || 0));
|
|
17130
|
+
request.queueItemId = queueItemIdForText(text, target, request);
|
|
17131
|
+
var queueItem = queueItemsForTarget(target).find(function(item) { return item.id === request.queueItemId; });
|
|
17132
|
+
if (queueItem) {
|
|
17133
|
+
request.requestedMode = queueItem.requestedMode;
|
|
17134
|
+
request.goalObjective = queueItem.goalObjective;
|
|
17135
|
+
request.createdAt = queueItem.createdAt;
|
|
17136
|
+
request.runId = queueItem.runId;
|
|
17137
|
+
request.images = (queueItem.images || []).map(function(image) { return Object.assign({}, image); });
|
|
17138
|
+
}
|
|
15360
17139
|
return request;
|
|
15361
17140
|
}
|
|
15362
17141
|
|
|
@@ -15422,27 +17201,14 @@ function rebindQueueToRuntimeBranch(target) {
|
|
|
15422
17201
|
}
|
|
15423
17202
|
|
|
15424
17203
|
function pauseQueueForTarget(target) {
|
|
15425
|
-
|
|
15426
|
-
if (!key) return false;
|
|
15427
|
-
if (!state.queuePausedByTarget) state.queuePausedByTarget = {};
|
|
15428
|
-
state.queuePausedByTarget[key] = true;
|
|
15429
|
-
if (state.nextQueueSchedulesByTarget) delete state.nextQueueSchedulesByTarget[key];
|
|
15430
|
-
if (state.nextQueueDrainsByTarget) delete state.nextQueueDrainsByTarget[key];
|
|
15431
|
-
return true;
|
|
17204
|
+
return window.queueAction('queue_set_pause', { paused: true }, target);
|
|
15432
17205
|
}
|
|
15433
17206
|
|
|
15434
17207
|
window.toggleQueuePause = function() {
|
|
15435
17208
|
var target = currentConversationTarget();
|
|
15436
17209
|
var key = queueRuntimeKey(target);
|
|
15437
17210
|
if (!key) return;
|
|
15438
|
-
|
|
15439
|
-
if (currentFlowRunning() && state.queuePausedByTarget[key]) {
|
|
15440
|
-
window.stopFlowRun({ resumeQueue: true });
|
|
15441
|
-
return;
|
|
15442
|
-
}
|
|
15443
|
-
state.queuePausedByTarget[key] = !state.queuePausedByTarget[key];
|
|
15444
|
-
window.renderInputStack();
|
|
15445
|
-
if (!state.queuePausedByTarget[key]) window.scheduleNextQueueDrain(target);
|
|
17211
|
+
return window.queueAction('queue_set_pause', { paused: !(state.queuePausedByTarget && state.queuePausedByTarget[key]) }, target);
|
|
15446
17212
|
};
|
|
15447
17213
|
|
|
15448
17214
|
function refreshNextPromptForTarget(target) {
|
|
@@ -15493,13 +17259,22 @@ function setQueueItemsForTarget(items, target) {
|
|
|
15493
17259
|
goalObjective: String(item && item.goalObjective || ''),
|
|
15494
17260
|
runId: String(item && item.runId || ''),
|
|
15495
17261
|
createdAt: String(item && item.createdAt || ''),
|
|
17262
|
+
images: (Array.isArray(item && item.images) ? item.images : []).map(function(image) { return Object.assign({}, image); }),
|
|
15496
17263
|
};
|
|
15497
17264
|
});
|
|
15498
17265
|
}
|
|
15499
17266
|
|
|
15500
|
-
function queueItemIdForText(text, target) {
|
|
15501
|
-
var items = queueItemsForTarget(target);
|
|
17267
|
+
function queueItemIdForText(text, target, request) {
|
|
17268
|
+
var items = queueItemsForTarget(target).filter(function(item) { return !item.queueMode || item.queueMode === 'followUp'; });
|
|
15502
17269
|
var normalized = String(text || '');
|
|
17270
|
+
// Never redirect a removed/edited row to another item with equal text.
|
|
17271
|
+
if (request && request.queueItemId) {
|
|
17272
|
+
return items.some(function(item) { return item.id === request.queueItemId; }) ? request.queueItemId : '';
|
|
17273
|
+
}
|
|
17274
|
+
if (request && Number.isFinite(request.backendOccurrence)) {
|
|
17275
|
+
var occurrenceItem = items[request.backendOccurrence];
|
|
17276
|
+
return occurrenceItem && String(occurrenceItem.text || '') === normalized ? occurrenceItem.id : '';
|
|
17277
|
+
}
|
|
15503
17278
|
for (var i = 0; i < items.length; i++) {
|
|
15504
17279
|
if (String(items[i].text || '') === normalized) return items[i].id;
|
|
15505
17280
|
}
|
|
@@ -15508,11 +17283,68 @@ function queueItemIdForText(text, target) {
|
|
|
15508
17283
|
|
|
15509
17284
|
function syncQueueItemsFromSnapshot(snapshot, target) {
|
|
15510
17285
|
if (snapshot && Array.isArray(snapshot.queueItems)) setQueueItemsForTarget(snapshot.queueItems, target);
|
|
17286
|
+
applyConversationCommandSnapshot(snapshot, target);
|
|
15511
17287
|
}
|
|
15512
17288
|
|
|
15513
|
-
|
|
17289
|
+
function applyConversationCommandSnapshot(snapshot, target) {
|
|
17290
|
+
if (!snapshot || typeof snapshot !== 'object') return;
|
|
17291
|
+
target = normalizeQueuedConversationTarget(target || snapshot.target || currentConversationTarget());
|
|
17292
|
+
if (!target) return;
|
|
17293
|
+
var key = runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
17294
|
+
if (typeof snapshot.queuePaused === 'boolean') {
|
|
17295
|
+
if (!state.queuePausedByTarget) state.queuePausedByTarget = {};
|
|
17296
|
+
state.queuePausedByTarget[key] = snapshot.queuePaused;
|
|
17297
|
+
}
|
|
17298
|
+
if (Array.isArray(snapshot.queueItems)) setQueueItemsForTarget(snapshot.queueItems, target);
|
|
17299
|
+
var queue = snapshot.queued && typeof snapshot.queued === 'object' ? snapshot.queued : snapshot.queue;
|
|
17300
|
+
if (queue && typeof queue === 'object') {
|
|
17301
|
+
setBackendQueueForTarget(queue, target);
|
|
17302
|
+
window.syncNextQueueFromBackend(queue, target);
|
|
17303
|
+
}
|
|
17304
|
+
if (Object.prototype.hasOwnProperty.call(snapshot, 'flowSuspension') || Object.prototype.hasOwnProperty.call(snapshot, 'flowRunning')) {
|
|
17305
|
+
var suspension = snapshot.flowSuspension;
|
|
17306
|
+
var flow = snapshot.flowRunning;
|
|
17307
|
+
var record = flowTakeoverRecordFor(target);
|
|
17308
|
+
var pendingStart = record.pendingStart && !record.pendingStart.cancelled ? record.pendingStart : null;
|
|
17309
|
+
record.running = !!(suspension || flow || pendingStart);
|
|
17310
|
+
record.paused = !!(suspension && suspension.reason === 'interrupted');
|
|
17311
|
+
record.name = String(suspension && suspension.workflowName || flow && flow.name || pendingStart && pendingStart.name || '');
|
|
17312
|
+
if (suspension && suspension.input !== undefined) record.promptText = String(suspension.input || '');
|
|
17313
|
+
if (isActiveConversationTarget(target) && window.renderFlowTakeover) window.renderFlowTakeover(record.running, record.name, { target: target, interrupted: record.paused, message: suspension && suspension.message });
|
|
17314
|
+
}
|
|
17315
|
+
if (!isActiveConversationTarget(target)) return;
|
|
17316
|
+
if (['build', 'chat', 'plan', 'goal', 'flow'].indexOf(snapshot.mode) >= 0) {
|
|
17317
|
+
state.mode = snapshot.mode;
|
|
17318
|
+
state._syncedMode = snapshot.mode;
|
|
17319
|
+
if (els['mode-select']) {
|
|
17320
|
+
els['mode-select'].value = snapshot.mode;
|
|
17321
|
+
window.syncNewmarkSelect(els['mode-select']);
|
|
17322
|
+
}
|
|
17323
|
+
var flowSelect = document.getElementById('flow-select');
|
|
17324
|
+
if (flowSelect) {
|
|
17325
|
+
flowSelect.dataset.newmarkVisible = snapshot.mode === 'flow' ? 'true' : 'false';
|
|
17326
|
+
window.syncNewmarkSelect(flowSelect);
|
|
17327
|
+
}
|
|
17328
|
+
}
|
|
17329
|
+
if (snapshot.inputMode === 'next' || snapshot.inputMode === 'guide') {
|
|
17330
|
+
window.setInputMode(snapshot.inputMode, false);
|
|
17331
|
+
state._syncedInputMode = snapshot.inputMode;
|
|
17332
|
+
}
|
|
17333
|
+
}
|
|
17334
|
+
|
|
17335
|
+
window.queueAction = function(action, input, target) {
|
|
15514
17336
|
if (!api.queueAction) return Promise.resolve({ ok: false, error: 'queueAction unavailable' });
|
|
15515
|
-
|
|
17337
|
+
var owner = normalizeQueuedConversationTarget(target || currentConversationTarget());
|
|
17338
|
+
return Promise.resolve(api.queueAction(action, input, owner)).then(function(result) {
|
|
17339
|
+
applyConversationCommandSnapshot(result, owner);
|
|
17340
|
+
if (isActiveConversationTarget(owner)) window.renderInputStack();
|
|
17341
|
+
if (result && result.ok === false) showUiNotice(result.error || 'Queue action failed.', 'error', 'queue-' + action);
|
|
17342
|
+
return result;
|
|
17343
|
+
}).catch(function(error) {
|
|
17344
|
+
var message = error && error.message ? error.message : String(error);
|
|
17345
|
+
showUiNotice(message, 'error', 'queue-' + action);
|
|
17346
|
+
return { ok: false, error: message };
|
|
17347
|
+
});
|
|
15516
17348
|
};
|
|
15517
17349
|
function setBackendQueueForTarget(queue, target) {
|
|
15518
17350
|
var owner = normalizeQueuedConversationTarget(target || currentConversationTarget());
|
|
@@ -15524,6 +17356,33 @@ function setBackendQueueForTarget(queue, target) {
|
|
|
15524
17356
|
return normalized;
|
|
15525
17357
|
}
|
|
15526
17358
|
|
|
17359
|
+
function queueRequestIndex(request) {
|
|
17360
|
+
if (!request) return -1;
|
|
17361
|
+
var requests = state.nextQueueRequests || [];
|
|
17362
|
+
if (!request.queueItemId) return requests.indexOf(request);
|
|
17363
|
+
return requests.findIndex(function(candidate) {
|
|
17364
|
+
return candidate && candidate.queueItemId === request.queueItemId && queuedRequestMatchesTarget(candidate, request.target);
|
|
17365
|
+
});
|
|
17366
|
+
}
|
|
17367
|
+
|
|
17368
|
+
window.queueIndexFromElement = function(element) {
|
|
17369
|
+
var row = element && element.closest('.queue-item');
|
|
17370
|
+
if (!row) return -1;
|
|
17371
|
+
var owner = { workspaceId: row.getAttribute('data-queue-workspace'), conversationId: row.getAttribute('data-queue-conversation') };
|
|
17372
|
+
if (!queuedRequestMatchesTarget({ target: owner }, currentConversationTarget())) return -1;
|
|
17373
|
+
var id = row.getAttribute('data-queue-id');
|
|
17374
|
+
return id ? queueRequestIndex({ target: owner, queueItemId: id }) : -1;
|
|
17375
|
+
};
|
|
17376
|
+
|
|
17377
|
+
window.refreshQueueRowIndexes = function() {
|
|
17378
|
+
// Background targets can change global array positions without changing
|
|
17379
|
+
// the visible rows. Retain their DOM/drag state and update only the index.
|
|
17380
|
+
document.querySelectorAll('#queue-list .queue-item[data-queue-id]').forEach(function(row) {
|
|
17381
|
+
var index = window.queueIndexFromElement(row);
|
|
17382
|
+
if (row.getAttribute('data-queue-index') !== String(index)) row.setAttribute('data-queue-index', String(index));
|
|
17383
|
+
});
|
|
17384
|
+
};
|
|
17385
|
+
|
|
15527
17386
|
window.syncNextQueueFromBackend = function(queue, target) {
|
|
15528
17387
|
target = normalizeQueuedConversationTarget(target || currentConversationTarget());
|
|
15529
17388
|
if (!target) return;
|
|
@@ -15534,7 +17393,7 @@ window.syncNextQueueFromBackend = function(queue, target) {
|
|
|
15534
17393
|
var retainedRequests = [];
|
|
15535
17394
|
for (var index = 0; index < state.nextQueue.length; index++) {
|
|
15536
17395
|
var existingRequest = state.nextQueueRequests[index];
|
|
15537
|
-
if (
|
|
17396
|
+
if (queuedRequestMatchesTarget(existingRequest, target)) continue;
|
|
15538
17397
|
retainedQueue.push(state.nextQueue[index]);
|
|
15539
17398
|
retainedRequests.push(existingRequest || null);
|
|
15540
17399
|
}
|
|
@@ -15548,6 +17407,11 @@ window.syncNextQueueFromBackend = function(queue, target) {
|
|
|
15548
17407
|
state.nextQueueRequests.push(bindBackendQueuedRequestToTarget(text, target, occurrence));
|
|
15549
17408
|
}
|
|
15550
17409
|
refreshNextPromptForTarget(target);
|
|
17410
|
+
if (state.pendingInputEdit && state.pendingInputEdit.kind === 'queue') {
|
|
17411
|
+
state._editingQueueIndex = queueRequestIndex(state.pendingInputEdit.request);
|
|
17412
|
+
}
|
|
17413
|
+
if (state.queueDragRequest) state.queueDragIndex = queueRequestIndex(state.queueDragRequest);
|
|
17414
|
+
window.refreshQueueRowIndexes();
|
|
15551
17415
|
};
|
|
15552
17416
|
|
|
15553
17417
|
window.renderQueuePanel = function() {
|
|
@@ -15596,20 +17460,22 @@ window.renderQueuePanel = function() {
|
|
|
15596
17460
|
// dev-0.5.5: a backend-managed item is editable/deletable when the kernel
|
|
15597
17461
|
// exposed its stable queueItem id (mobile-originated queue entries now
|
|
15598
17462
|
// carry ids through queueItems). Items without an id stay locked.
|
|
15599
|
-
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[queueIndex] || '', currentConversationTarget()) : '';
|
|
17463
|
+
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[queueIndex] || '', currentConversationTarget(), request) : '';
|
|
17464
|
+
var eventIndex = queueItemId ? 'window.queueIndexFromElement(this)' : String(queueIndex);
|
|
17465
|
+
var identityAttributes = queueItemId ? ' data-queue-id="' + escAttr(queueItemId) + '" data-queue-workspace="' + escAttr(target.workspaceId) + '" data-queue-conversation="' + escAttr(target.conversationId) + '"' : '';
|
|
15600
17466
|
var operable = !backendManaged || !!queueItemId;
|
|
15601
17467
|
var dragAttributes = operable
|
|
15602
|
-
? ' draggable="true" ondragstart="window.startQueueDrag(event,' +
|
|
17468
|
+
? ' draggable="true" ondragstart="window.startQueueDrag(event,' + eventIndex + ')" ondragover="window.overQueueDrag(event,' + eventIndex + ')" ondrop="window.dropQueueDrag(event,' + eventIndex + ')" ondragend="window.endQueueDrag(event)"'
|
|
15603
17469
|
: ' draggable="false"';
|
|
15604
17470
|
var dragHandle = operable
|
|
15605
17471
|
? '<button class="queue-drag" title="' + escAttr(t('queue.title')) + '">' + iconSvg('grip-vertical', t('queue.title'), 'tiny') + '</button>'
|
|
15606
17472
|
: '<span class="queue-drag" aria-hidden="true">' + iconSvg('grip-vertical', t('queue.title'), 'tiny') + '</span>';
|
|
15607
17473
|
var inputAttributes = ' readonly aria-readonly="true"';
|
|
15608
17474
|
var actions = operable ?
|
|
15609
|
-
'<button type="button" class="stack-icon-btn queue-guide-btn" onclick="window.guideQueueItem(' +
|
|
15610
|
-
'<button class="stack-icon-btn" onclick="window.focusQueueItem(' +
|
|
15611
|
-
'<button class="stack-icon-btn danger" onclick="window.deleteQueueItem(' +
|
|
15612
|
-
html += '<div class="queue-item" data-queue-index="' + queueIndex + '"' + dragAttributes + '>' +
|
|
17475
|
+
'<button type="button" class="stack-icon-btn queue-guide-btn" onclick="window.guideQueueItem(' + eventIndex + ')" title="' + escAttr(t('queue.guideNow')) + '">' + iconSvg('corner-down-right', t('queue.guideNow'), 'tiny') + '<span>' + esc(t('queue.guideAction')) + '</span></button>' +
|
|
17476
|
+
'<button class="stack-icon-btn" onclick="window.focusQueueItem(' + eventIndex + ')" title="' + escAttr(t('common.edit')) + '">' + iconSvg('square-pen', t('common.edit'), 'tiny') + '</button>' +
|
|
17477
|
+
'<button class="stack-icon-btn danger" onclick="window.deleteQueueItem(' + eventIndex + ')" title="' + escAttr(t('common.delete')) + '">' + iconSvg('trash', t('common.delete'), 'tiny') + '</button>' : '';
|
|
17478
|
+
html += '<div class="queue-item" data-queue-index="' + queueIndex + '"' + identityAttributes + dragAttributes + '>' +
|
|
15613
17479
|
dragHandle +
|
|
15614
17480
|
'<input class="queue-edit" value="' + escAttr(state.nextQueue[queueIndex] || '') + '"' + inputAttributes + '>' +
|
|
15615
17481
|
'<span class="stack-actions">' + actions + '</span>' +
|
|
@@ -15643,7 +17509,7 @@ window.focusQueueItem = function(idx) {
|
|
|
15643
17509
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
15644
17510
|
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) return;
|
|
15645
17511
|
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
15646
|
-
if (backendManaged && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget())) return;
|
|
17512
|
+
if (backendManaged && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget(), request)) return;
|
|
15647
17513
|
state._editingQueueIndex = idx;
|
|
15648
17514
|
state.pendingInputEdit = { kind: 'queue', index: idx, request: request, target: currentConversationTarget(), originalText: String(state.nextQueue[idx] || '') };
|
|
15649
17515
|
els.prompt.value = String(state.nextQueue[idx] || '');
|
|
@@ -15661,7 +17527,7 @@ window.deleteQueueItem = function(idx) {
|
|
|
15661
17527
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
15662
17528
|
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) return;
|
|
15663
17529
|
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
15664
|
-
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget()) : '';
|
|
17530
|
+
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget(), request) : '';
|
|
15665
17531
|
if (backendManaged && !queueItemId) return;
|
|
15666
17532
|
if (!state.queueHiddenItems) state.queueHiddenItems = {};
|
|
15667
17533
|
if (state.nextQueue[idx]) state.queueHiddenItems[queueHiddenItemKey(state.nextQueue[idx], currentConversationTarget())] = true;
|
|
@@ -15694,10 +17560,9 @@ window.guideQueueItem = function(idx) {
|
|
|
15694
17560
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
15695
17561
|
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) return;
|
|
15696
17562
|
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
15697
|
-
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget()) : '';
|
|
15698
|
-
if (backendManaged
|
|
17563
|
+
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget(), request) : '';
|
|
17564
|
+
if (!backendManaged || !queueItemId) return;
|
|
15699
17565
|
var target = normalizeQueuedConversationTarget(request.target) || currentConversationTarget();
|
|
15700
|
-
var deliverDuringRun = isCurrentConversationRunning();
|
|
15701
17566
|
var next = state.nextQueue.splice(idx, 1)[0];
|
|
15702
17567
|
var nextRequest = state.nextQueueRequests ? state.nextQueueRequests.splice(idx, 1)[0] : null;
|
|
15703
17568
|
if (!state.queueHiddenItems) state.queueHiddenItems = {};
|
|
@@ -15707,7 +17572,7 @@ window.guideQueueItem = function(idx) {
|
|
|
15707
17572
|
if (backendManaged) {
|
|
15708
17573
|
// The kernel owns this entry; guide it there so the mobile side stays
|
|
15709
17574
|
// in sync. On failure restore the local row.
|
|
15710
|
-
return window.queueAction('queue_guide', { id: queueItemId }).then(function(outcome) {
|
|
17575
|
+
return window.queueAction('queue_guide', { id: queueItemId }, target).then(function(outcome) {
|
|
15711
17576
|
if (!outcome || outcome.ok === false) {
|
|
15712
17577
|
restoreQueueItemAfterGuideFailure(idx, next, nextRequest, target, true);
|
|
15713
17578
|
showUiNotice(outcome && outcome.error ? outcome.error : (currentLang() === 'zh' ? 'Guide 投递失败' : 'Guide delivery failed'), 'error', 'queue-guide-' + queueItemId);
|
|
@@ -15718,39 +17583,21 @@ window.guideQueueItem = function(idx) {
|
|
|
15718
17583
|
return { ok: false, error: error && error.message ? error.message : String(error) };
|
|
15719
17584
|
});
|
|
15720
17585
|
}
|
|
15721
|
-
if (next) {
|
|
15722
|
-
var delivery = window.sendMessage('guide', next, {
|
|
15723
|
-
fromQueue: true,
|
|
15724
|
-
forceBuild: true,
|
|
15725
|
-
deliverAsGuide: deliverDuringRun,
|
|
15726
|
-
queuedRequest: nextRequest
|
|
15727
|
-
});
|
|
15728
|
-
if (deliverDuringRun && delivery && typeof delivery.then === 'function') {
|
|
15729
|
-
return Promise.resolve(delivery).then(function(outcome) {
|
|
15730
|
-
if (outcome && outcome.ok === false) {
|
|
15731
|
-
restoreQueueItemAfterGuideFailure(idx, next, nextRequest, target, !outcome.guideReceipt);
|
|
15732
|
-
}
|
|
15733
|
-
return outcome;
|
|
15734
|
-
}, function(error) {
|
|
15735
|
-
restoreQueueItemAfterGuideFailure(idx, next, nextRequest, target, true);
|
|
15736
|
-
return { ok: false, error: error && error.message ? error.message : String(error) };
|
|
15737
|
-
});
|
|
15738
|
-
}
|
|
15739
|
-
return delivery;
|
|
15740
|
-
}
|
|
15741
17586
|
};
|
|
15742
17587
|
|
|
15743
17588
|
window.startQueueDrag = function(event, idx) {
|
|
17589
|
+
state.queueDragRequest = null;
|
|
15744
17590
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
15745
17591
|
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) {
|
|
15746
17592
|
state.queueDragIndex = -1;
|
|
15747
17593
|
return;
|
|
15748
17594
|
}
|
|
15749
|
-
if (queuedRequestIsBackendManaged(request) && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget())) {
|
|
17595
|
+
if (queuedRequestIsBackendManaged(request) && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget(), request)) {
|
|
15750
17596
|
state.queueDragIndex = -1;
|
|
15751
17597
|
return;
|
|
15752
17598
|
}
|
|
15753
17599
|
state.queueDragIndex = idx;
|
|
17600
|
+
state.queueDragRequest = request;
|
|
15754
17601
|
state.queueDragTargetIndex = idx;
|
|
15755
17602
|
if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';
|
|
15756
17603
|
var row = event.currentTarget;
|
|
@@ -15784,12 +17631,14 @@ function previewQueueDrag(targetIndex) {
|
|
|
15784
17631
|
|
|
15785
17632
|
window.overQueueDrag = function(event, idx) {
|
|
15786
17633
|
event.preventDefault();
|
|
17634
|
+
if (idx < 0) return;
|
|
15787
17635
|
if (event.dataTransfer) event.dataTransfer.dropEffect = 'move';
|
|
15788
17636
|
if (state.queueDragIndex >= 0) previewQueueDrag(queueDropTargetIndex(event, idx));
|
|
15789
17637
|
};
|
|
15790
17638
|
|
|
15791
17639
|
window.dropQueueDrag = function(event, idx) {
|
|
15792
17640
|
event.preventDefault();
|
|
17641
|
+
if (idx < 0) return;
|
|
15793
17642
|
var from = state.queueDragIndex;
|
|
15794
17643
|
idx = typeof queueDropTargetIndex === 'function' ? queueDropTargetIndex(event, idx) : idx;
|
|
15795
17644
|
if (!state.nextQueue || from < 0 || from === idx || from >= state.nextQueue.length) return;
|
|
@@ -15799,30 +17648,35 @@ window.dropQueueDrag = function(event, idx) {
|
|
|
15799
17648
|
|| !queuedRequestMatchesTarget(destinationRequest, currentConversationTarget())) return;
|
|
15800
17649
|
var sourceBackend = queuedRequestIsBackendManaged(sourceRequest);
|
|
15801
17650
|
var destinationBackend = queuedRequestIsBackendManaged(destinationRequest);
|
|
15802
|
-
if ((sourceBackend && !queueItemIdForText(state.nextQueue[from] || '', currentConversationTarget()))
|
|
15803
|
-
|| (destinationBackend && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget()))) return;
|
|
17651
|
+
if ((sourceBackend && !queueItemIdForText(state.nextQueue[from] || '', currentConversationTarget(), sourceRequest))
|
|
17652
|
+
|| (destinationBackend && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget(), destinationRequest))) return;
|
|
15804
17653
|
var item = state.nextQueue.splice(from, 1)[0];
|
|
15805
17654
|
var requestItem = state.nextQueueRequests ? state.nextQueueRequests.splice(from, 1)[0] : null;
|
|
15806
17655
|
state.nextQueue.splice(idx, 0, item);
|
|
15807
17656
|
if (state.nextQueueRequests) state.nextQueueRequests.splice(idx, 0, requestItem || null);
|
|
15808
17657
|
state.queueDragIndex = -1;
|
|
17658
|
+
state.queueDragRequest = null;
|
|
15809
17659
|
refreshNextPromptForTarget(currentConversationTarget());
|
|
15810
|
-
window.renderInputStack();
|
|
15811
17660
|
if (sourceBackend || destinationBackend) {
|
|
15812
17661
|
// The kernel owns at least one affected entry: forward the complete
|
|
15813
17662
|
// ordered id list so mobile and desktop stay in sync.
|
|
15814
17663
|
var visibleIndexes = queueIndexesForTarget(currentConversationTarget());
|
|
15815
17664
|
var orderedIds = visibleIndexes.map(function(vi) {
|
|
15816
|
-
|
|
17665
|
+
var queuedRequest = state.nextQueueRequests && state.nextQueueRequests[vi];
|
|
17666
|
+
return queuedRequestIsBackendManaged(queuedRequest) ? queueItemIdForText(state.nextQueue[vi] || '', currentConversationTarget(), queuedRequest) : '';
|
|
15817
17667
|
}).filter(function(id) { return !!id; });
|
|
15818
17668
|
if (orderedIds.length) {
|
|
15819
17669
|
window.queueAction('queue_reorder', { orderedIds: orderedIds });
|
|
15820
17670
|
}
|
|
15821
17671
|
}
|
|
17672
|
+
// Rendering reloads the last backend snapshot. Submit dragged identities
|
|
17673
|
+
// first so that reconciliation cannot undo the requested order.
|
|
17674
|
+
window.renderInputStack();
|
|
15822
17675
|
};
|
|
15823
17676
|
|
|
15824
17677
|
window.endQueueDrag = function(event) {
|
|
15825
17678
|
state.queueDragIndex = -1;
|
|
17679
|
+
state.queueDragRequest = null;
|
|
15826
17680
|
state.queueDragTargetIndex = -1;
|
|
15827
17681
|
document.querySelectorAll('#queue-list .queue-item').forEach(function(row) {
|
|
15828
17682
|
row.classList.remove('queue-drag-displaced', 'dragging');
|
|
@@ -15843,8 +17697,9 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
15843
17697
|
opts = opts || {};
|
|
15844
17698
|
var requestedMode = opts.requestedMode || state.mode || 'build';
|
|
15845
17699
|
var effectiveInputMode = modeOverride || state.inputMode || 'guide';
|
|
15846
|
-
|
|
15847
|
-
|
|
17700
|
+
// Preserve the selected delivery mode. The shared owner decides whether
|
|
17701
|
+
// Guide steers a live run or starts a new instruction when the target is idle.
|
|
17702
|
+
if (requestedMode === 'flow' && !state.pendingInputEdit) {
|
|
15848
17703
|
effectiveInputMode = 'guide';
|
|
15849
17704
|
if (!opts.fromQueue) {
|
|
15850
17705
|
if (currentFlowRunning()) {
|
|
@@ -15855,9 +17710,13 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
15855
17710
|
els.prompt.dispatchEvent(new Event('input', { bubbles: true }));
|
|
15856
17711
|
}
|
|
15857
17712
|
if (state.inputMode === 'next') return window.queueFlowNextInput(activeFlowGuide);
|
|
15858
|
-
|
|
17713
|
+
var activeFlowTarget = opts.target || currentConversationTarget();
|
|
17714
|
+
return api.sendMessage(activeFlowGuide, activeFlowTarget, { requestedMode: requestedMode, inputMode: 'guide', clientMessageId: window.crypto.randomUUID() }).then(function(result) {
|
|
17715
|
+
applyConversationCommandSnapshot(result, activeFlowTarget);
|
|
17716
|
+
return result;
|
|
17717
|
+
});
|
|
15859
17718
|
}
|
|
15860
|
-
return window.submitSelectedFlow();
|
|
17719
|
+
return window.submitSelectedFlow({ text: typeof queuedText === 'string' ? queuedText : String(els.prompt.value || ''), target: opts.target || currentConversationTarget(), inputMode: modeOverride || state.inputMode });
|
|
15861
17720
|
}
|
|
15862
17721
|
}
|
|
15863
17722
|
if (state.pendingInputEdit && typeof queuedText !== 'string' && !opts.fromQueue) {
|
|
@@ -15875,36 +17734,18 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
15875
17734
|
var editIndex = Number(pendingEdit.index);
|
|
15876
17735
|
var editRequest = pendingEdit.request || (state.nextQueueRequests && state.nextQueueRequests[editIndex]);
|
|
15877
17736
|
var editBackendManaged = queuedRequestIsBackendManaged(editRequest);
|
|
15878
|
-
var editQueueItemId = editBackendManaged ? queueItemIdForText(String(
|
|
17737
|
+
var editQueueItemId = editBackendManaged ? queueItemIdForText(String(pendingEdit.originalText || ''), pendingEdit.target, editRequest) : '';
|
|
15879
17738
|
if (editBackendManaged && !editQueueItemId) {
|
|
15880
17739
|
showUiNotice(currentLang() === 'zh' ? '无法编辑该队列项:缺少后端 id' : 'Cannot edit queue item: backend id missing', 'error', 'queue-edit-' + editIndex);
|
|
15881
17740
|
return { ok: false, error: 'missing backend queue id' };
|
|
15882
17741
|
}
|
|
15883
|
-
if (
|
|
15884
|
-
|
|
15885
|
-
|
|
15886
|
-
|
|
15887
|
-
|
|
15888
|
-
|
|
15889
|
-
|
|
15890
|
-
refreshNextPromptForTarget(pendingEdit.target);
|
|
15891
|
-
window.renderInputStack();
|
|
15892
|
-
return { ok: true, queued: true };
|
|
15893
|
-
}
|
|
15894
|
-
state.nextQueue[editIndex] = editedValue;
|
|
15895
|
-
if (state.nextQueueRequests[editIndex]) {
|
|
15896
|
-
state.nextQueueRequests[editIndex].text = editedValue;
|
|
15897
|
-
state.nextQueueRequests[editIndex].images = editedImages;
|
|
15898
|
-
}
|
|
15899
|
-
refreshNextPromptForTarget(pendingEdit.target);
|
|
15900
|
-
window.renderInputStack();
|
|
15901
|
-
return { ok: true, queued: true };
|
|
15902
|
-
}
|
|
15903
|
-
state.nextQueue.splice(editIndex, 1);
|
|
15904
|
-
var editedRequest = state.nextQueueRequests.splice(editIndex, 1)[0] || pendingEdit.request;
|
|
15905
|
-
if (editedRequest) { editedRequest.text = editedValue; editedRequest.images = editedImages; }
|
|
15906
|
-
window.renderInputStack();
|
|
15907
|
-
return window.sendMessage('guide', editedValue, { fromQueue: true, forceBuild: true, deliverAsGuide: isCurrentConversationRunning(), queuedRequest: editedRequest });
|
|
17742
|
+
if (!editBackendManaged) return { ok: false, error: 'Queue entry has no shared owner' };
|
|
17743
|
+
return window.queueAction(effectiveInputMode === 'next' ? 'queue_update' : 'queue_guide', {
|
|
17744
|
+
id: editQueueItemId, text: editedValue,
|
|
17745
|
+
requestedMode: String(editRequest.requestedMode || 'build'),
|
|
17746
|
+
goalObjective: String(editRequest.goalObjective || ''),
|
|
17747
|
+
createdAt: String(editRequest.createdAt || ''), images: editedImages
|
|
17748
|
+
}, pendingEdit.target);
|
|
15908
17749
|
}
|
|
15909
17750
|
if (pendingEdit.kind === 'goal') {
|
|
15910
17751
|
var previousGoalEditMode = pendingEdit.previousMode || 'goal';
|
|
@@ -15926,19 +17767,11 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
15926
17767
|
restoreModeAfterGoalEdit(previousGoalEditMode);
|
|
15927
17768
|
});
|
|
15928
17769
|
}
|
|
15929
|
-
if (!state.nextQueue) state.nextQueue = [];
|
|
15930
|
-
if (!state.nextQueueRequests) state.nextQueueRequests = [];
|
|
15931
|
-
state.nextQueue.push(editedValue);
|
|
15932
|
-
var goalRequest = bindQueuedRequestToTarget({ text: editedValue, images: editedImages }, editedValue, currentConversationTarget(), queueBranchPathForTarget(currentConversationTarget(), 'runtime'));
|
|
15933
|
-
goalRequest.goalDeclaration = true;
|
|
15934
|
-
goalRequest.goalObjective = editedValue;
|
|
15935
|
-
goalRequest.requestedMode = 'goal';
|
|
15936
|
-
state.nextQueueRequests.push(goalRequest);
|
|
15937
17770
|
state.queueCollapsed = false;
|
|
15938
|
-
|
|
15939
|
-
|
|
15940
|
-
|
|
15941
|
-
|
|
17771
|
+
return window.queueAction('queue_enqueue', {
|
|
17772
|
+
id: window.crypto.randomUUID(), text: editedValue, images: editedImages,
|
|
17773
|
+
requestedMode: 'goal', goalObjective: editedValue, createdAt: new Date().toISOString()
|
|
17774
|
+
}, currentConversationTarget()).finally(function() { restoreModeAfterGoalEdit(previousGoalEditMode); });
|
|
15942
17775
|
}
|
|
15943
17776
|
}
|
|
15944
17777
|
var queuedRequestRecord = opts.queuedRequest && typeof opts.queuedRequest === 'object' ? opts.queuedRequest : null;
|
|
@@ -15978,8 +17811,8 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
15978
17811
|
displayText = continuePhrase;
|
|
15979
17812
|
}
|
|
15980
17813
|
|
|
15981
|
-
var
|
|
15982
|
-
var
|
|
17814
|
+
var lockedTarget = normalizeQueuedConversationTarget(opts.target || currentConversationTarget());
|
|
17815
|
+
var lockedConversationId = lockedTarget.conversationId;
|
|
15983
17816
|
var lockedRuntimeKey = runtimeKeyFor(lockedTarget.workspaceId, lockedTarget.conversationId);
|
|
15984
17817
|
var activeRuntime = runningConversationRecord(lockedConversationId, lockedTarget.workspaceId);
|
|
15985
17818
|
var conversationRunning = !!activeRuntime;
|
|
@@ -15991,7 +17824,6 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
15991
17824
|
if (reconciledLocalRuntime) {
|
|
15992
17825
|
activeRuntime = runningConversationRecord(lockedConversationId, lockedTarget.workspaceId);
|
|
15993
17826
|
conversationRunning = !!activeRuntime;
|
|
15994
|
-
if (!conversationRunning && effectiveInputMode === 'guide') effectiveInputMode = 'next';
|
|
15995
17827
|
}
|
|
15996
17828
|
}
|
|
15997
17829
|
|
|
@@ -16015,13 +17847,11 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16015
17847
|
queuedModeRequest.goalDeclaration = true;
|
|
16016
17848
|
queuedModeRequest.goalObjective = rawText;
|
|
16017
17849
|
}
|
|
16018
|
-
var queueResult = await
|
|
16019
|
-
|
|
16020
|
-
|
|
17850
|
+
var queueResult = await api.sendMessage({ text: rawText, images: queuedModeRequest.images }, lockedTarget, {
|
|
17851
|
+
clientMessageId: queuedModeRequest.clientMessageId,
|
|
17852
|
+
inputMode: 'next',
|
|
16021
17853
|
requestedMode: requestedMode,
|
|
16022
|
-
goalObjective: requestedMode === 'goal' ? rawText : ''
|
|
16023
|
-
images: queuedModeRequest.images,
|
|
16024
|
-
createdAt: queuedModeRequest.createdAt || new Date().toISOString()
|
|
17854
|
+
goalObjective: requestedMode === 'goal' ? rawText : ''
|
|
16025
17855
|
});
|
|
16026
17856
|
if (!queueResult || queueResult.ok === false) {
|
|
16027
17857
|
showUiNotice(queueResult && queueResult.error ? queueResult.error : (currentLang() === 'zh' ? '消息加入队列失败' : 'Failed to queue message'), 'error', 'queue-enqueue-' + queuedModeRequest.clientMessageId);
|
|
@@ -16029,6 +17859,7 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16029
17859
|
}
|
|
16030
17860
|
if (queueResult.queued) setBackendQueueForTarget(queueResult.queued, lockedTarget);
|
|
16031
17861
|
if (Array.isArray(queueResult.queueItems)) setQueueItemsForTarget(queueResult.queueItems, lockedTarget);
|
|
17862
|
+
applyConversationCommandSnapshot(queueResult, lockedTarget);
|
|
16032
17863
|
window.syncNextQueueFromBackend(queueResult.queued || backendQueueForTarget(lockedTarget), lockedTarget);
|
|
16033
17864
|
els.prompt.value = '';
|
|
16034
17865
|
clearActiveConversationDraft();
|
|
@@ -16086,8 +17917,19 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16086
17917
|
allowStatusReset: retryingUnacknowledgedGuide
|
|
16087
17918
|
}, lockedTarget);
|
|
16088
17919
|
renderPendingGuideMessages(lockedTarget, {});
|
|
16089
|
-
var enqueue = api.
|
|
16090
|
-
|
|
17920
|
+
var enqueue = api.sendMessage(Object.assign({}, envelope, { visibleMode: requestedMode }), lockedTarget, {
|
|
17921
|
+
requestedMode: requestedMode, inputMode: 'guide', goalObjective: envelope.goalObjective,
|
|
17922
|
+
clientMessageId: clientMessageId
|
|
17923
|
+
});
|
|
17924
|
+
return Promise.resolve(enqueue).then(function(result) {
|
|
17925
|
+
applyConversationCommandSnapshot(result, lockedTarget);
|
|
17926
|
+
var receipt = result && (result.receipt || result.guideReceipt) || result;
|
|
17927
|
+
if (result && !result.receipt && !result.guideReceipt && ['completed', 'interrupted', 'idle', 'error'].indexOf(result.status) >= 0) {
|
|
17928
|
+
// A stale renderer may have displayed a run which the shared owner
|
|
17929
|
+
// already finalized. That same command now owns its fresh response.
|
|
17930
|
+
if (isActiveConversationTarget(lockedTarget) && Array.isArray(result.chatMessages)) renderChatMessages(result.chatMessages, lockedTarget);
|
|
17931
|
+
return result;
|
|
17932
|
+
}
|
|
16091
17933
|
if (!receipt || typeof receipt !== 'object' || !String(receipt.status || '').trim()) {
|
|
16092
17934
|
throw new Error(currentLang() === 'zh' ? '未收到有效的 Guide 回执' : 'A valid Guide acknowledgement was not received');
|
|
16093
17935
|
}
|
|
@@ -16202,7 +18044,12 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16202
18044
|
updateSubmitButtonState();
|
|
16203
18045
|
var executionMode = opts.goalDeclaration || requestedMode === 'goal' ? 'build'
|
|
16204
18046
|
: ((opts.forceBuild || idleBuildNextImmediate) && requestedMode === 'build' ? 'build' : requestedMode);
|
|
16205
|
-
|
|
18047
|
+
// The command freezes its mode and delivery policy; selector persistence
|
|
18048
|
+
// must not rewrite the mode of an already running target before acceptance.
|
|
18049
|
+
if (api.setModel && state.model !== state._syncedModel) {
|
|
18050
|
+
var selectedModelResult = await api.setModel(state.model);
|
|
18051
|
+
if (selectedModelResult != null) state._syncedModel = state.model;
|
|
18052
|
+
}
|
|
16206
18053
|
if (consumeProvisionalStop(lockedTarget, localRunId)) {
|
|
16207
18054
|
// Stop/Escape won before the backend accepted the prompt. Do not send the
|
|
16208
18055
|
// provider request after the user has already cancelled it.
|
|
@@ -16242,14 +18089,21 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16242
18089
|
}
|
|
16243
18090
|
var responseMsg = null;
|
|
16244
18091
|
var sendFailure = '';
|
|
18092
|
+
var sendLoadGeneration = state.conversationLoadGeneration;
|
|
16245
18093
|
try {
|
|
16246
18094
|
var activationBeforeSend = pendingConversationActivation(lockedTarget);
|
|
16247
18095
|
if (activationBeforeSend) await activationBeforeSend;
|
|
16248
|
-
var sendPromise = api.sendMessage(requestMessage, lockedTarget
|
|
18096
|
+
var sendPromise = api.sendMessage(requestMessage, lockedTarget, {
|
|
18097
|
+
requestedMode: requestedMode, inputMode: effectiveInputMode,
|
|
18098
|
+
goalObjective: opts.goalObjective || '',
|
|
18099
|
+
clientMessageId: String(requestMessage && requestMessage.clientMessageId || localRunId)
|
|
18100
|
+
});
|
|
16249
18101
|
if (requestedMode === 'goal') activateSubmittedGoal(opts.goalObjective || rawText);
|
|
16250
18102
|
var r = await sendPromise;
|
|
18103
|
+
var sendResultOwnsRuntime = conversationRunStillOwnsTarget(lockedTarget, r && r.runId, localRunId);
|
|
18104
|
+
if (sendResultOwnsRuntime) applyConversationCommandSnapshot(r, lockedTarget);
|
|
16251
18105
|
if (r && r.error) sendFailure = String(r.error);
|
|
16252
|
-
if (isActiveConversationTarget(lockedTarget)) applyReturnedGoalState(r);
|
|
18106
|
+
if (sendResultOwnsRuntime && isActiveConversationTarget(lockedTarget)) applyReturnedGoalState(r);
|
|
16253
18107
|
if (r && r.runId) {
|
|
16254
18108
|
var anchorStore = workRunAnchorIndexStore(lockedTarget);
|
|
16255
18109
|
if (anchorStore[localRunId] !== undefined) {
|
|
@@ -16258,8 +18112,13 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16258
18112
|
}
|
|
16259
18113
|
}
|
|
16260
18114
|
if (r && r.runtimeKey) registerRuntimeKey(lockedTarget, r.runtimeKey);
|
|
16261
|
-
if (
|
|
16262
|
-
|
|
18115
|
+
if (sendResultOwnsRuntime && r && r.runId) {
|
|
18116
|
+
var observedSendRuntime = state.conversationRuntimeStates && state.conversationRuntimeStates[runtimeKeyFor(lockedTarget.workspaceId, lockedTarget.conversationId)];
|
|
18117
|
+
var receivedSendStatus = observedSendRuntime && observedSendRuntime.runId === r.runId && ['interrupted', 'force_interrupted', 'error'].indexOf(observedSendRuntime.status) >= 0
|
|
18118
|
+
? observedSendRuntime.status : (r.status || 'running');
|
|
18119
|
+
setConversationRuntimeState(lockedTarget, receivedSendStatus, r.runId, { provisional: false, generation: r.generation || 0, runtimeKey: r.runtimeKey || '' });
|
|
18120
|
+
}
|
|
18121
|
+
var stillActive = sendResultOwnsRuntime && sendLoadGeneration === state.conversationLoadGeneration && isActiveConversationTarget(lockedTarget) && isViewingRuntimeConversationBranch(lockedTarget);
|
|
16263
18122
|
if (stillActive) applyAutoRouteRatingState(r);
|
|
16264
18123
|
if (stillActive && r && Array.isArray(r.workRuns)) syncWorkRunsSnapshot(r.workRuns, lockedTarget);
|
|
16265
18124
|
var responseOwnedByRun = !!(r && Array.isArray(r.workRuns) && r.workRuns.some(function(run) {
|
|
@@ -16273,7 +18132,7 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16273
18132
|
};
|
|
16274
18133
|
upsertWorkReviewRecord(lockedUi, reviewRecord.diffs, reviewRecord.runId, [localRunId]);
|
|
16275
18134
|
}
|
|
16276
|
-
if (stillActive && !responseOwnedByRun) {
|
|
18135
|
+
if (stillActive && !responseOwnedByRun && !(r && (r.receipt || r.guideReceipt || (r.runtime && r.runtime.running)))) {
|
|
16277
18136
|
responseMsg = lockedUi.activeWorkflowMsg;
|
|
16278
18137
|
if (r && r.error) {
|
|
16279
18138
|
if (!responseMsg) responseMsg = addMsg('assistant', '', state.mode, state.model);
|
|
@@ -16308,7 +18167,7 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16308
18167
|
addWorkReview(item.diffs || item, item.runId || '', [localRunId]);
|
|
16309
18168
|
});
|
|
16310
18169
|
}
|
|
16311
|
-
if (r && r.conversations) {
|
|
18170
|
+
if (sendResultOwnsRuntime && r && r.conversations) {
|
|
16312
18171
|
applyBackendConversations(r.conversations || [], stillActive ? lockedConversationId : activeConversationId(), lockedTarget.workspaceId);
|
|
16313
18172
|
if (stillActive) renderConversations();
|
|
16314
18173
|
}
|
|
@@ -16324,7 +18183,7 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16324
18183
|
state.subagents = r.subagents;
|
|
16325
18184
|
if (state.rightTab === 'subagent') window.renderSubagentList();
|
|
16326
18185
|
}
|
|
16327
|
-
if (r && r.queued) {
|
|
18186
|
+
if (sendResultOwnsRuntime && r && r.queued) {
|
|
16328
18187
|
var responseQueue = setBackendQueueForTarget(r.queued, lockedTarget);
|
|
16329
18188
|
window.syncNextQueueFromBackend(responseQueue, lockedTarget);
|
|
16330
18189
|
syncQueueItemsFromSnapshot(r, lockedTarget);
|
|
@@ -16339,23 +18198,20 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16339
18198
|
}
|
|
16340
18199
|
} catch(e) {
|
|
16341
18200
|
sendFailure = e && e.message ? String(e.message) : String(e || 'Agent run failed.');
|
|
16342
|
-
if (isActiveConversationTarget(lockedTarget)) {
|
|
18201
|
+
if (conversationRunStillOwnsTarget(lockedTarget, localRunId) && sendLoadGeneration === state.conversationLoadGeneration && isActiveConversationTarget(lockedTarget)) {
|
|
16343
18202
|
responseMsg = conversationWorkUiState(lockedConversationId, lockedTarget.workspaceId).activeWorkflowMsg || addMsg('assistant', '', state.mode, state.model);
|
|
16344
18203
|
updateMsg(responseMsg, formatChatError(sendFailure, 'Agent run failed.'), 'error', state.mode, state.model);
|
|
16345
18204
|
}
|
|
16346
18205
|
}
|
|
16347
18206
|
if (api.getState) {
|
|
18207
|
+
var completedContextRequest = captureContextWindowSnapshotRequest(lockedTarget);
|
|
18208
|
+
var completedWorkRequest = captureConversationWorkSnapshotRequest(lockedTarget);
|
|
16348
18209
|
api.getState(lockedTarget).then(function(s) {
|
|
18210
|
+
if (!isCurrentConversationWorkSnapshotRequest(completedWorkRequest)) return;
|
|
16349
18211
|
if (s && s.runtimeKey) registerRuntimeKey(lockedTarget, s.runtimeKey);
|
|
16350
18212
|
var stillActiveAfterRefresh = isActiveConversationTarget(lockedTarget) && isViewingRuntimeConversationBranch(lockedTarget);
|
|
16351
18213
|
if (stillActiveAfterRefresh) applyReturnedGoalState(s);
|
|
16352
|
-
if (stillActiveAfterRefresh
|
|
16353
|
-
state.contextCompression = s.contextCompression;
|
|
16354
|
-
}
|
|
16355
|
-
if (stillActiveAfterRefresh && s && s.contextWindow !== undefined) {
|
|
16356
|
-
state.contextWindow = s.contextWindow;
|
|
16357
|
-
window.renderContextWindow();
|
|
16358
|
-
}
|
|
18214
|
+
if (stillActiveAfterRefresh) applyContextWindowSnapshot(s, completedContextRequest);
|
|
16359
18215
|
if (stillActiveAfterRefresh && s && s.subagents) state.subagents = s.subagents;
|
|
16360
18216
|
if (stillActiveAfterRefresh && s && s.linkedPlan) state.linkedPlan = s.linkedPlan;
|
|
16361
18217
|
if (stillActiveAfterRefresh && s && s.fileDiffs) state.fileDiffs = s.fileDiffs;
|
|
@@ -16388,10 +18244,6 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16388
18244
|
}
|
|
16389
18245
|
}).catch(function(){});
|
|
16390
18246
|
}
|
|
16391
|
-
if (api.setMode && executionMode !== state.mode) {
|
|
16392
|
-
var resumedModeResult = await api.setMode(state.mode).catch(function() { return null; });
|
|
16393
|
-
if (resumedModeResult != null) state._syncedMode = state.mode;
|
|
16394
|
-
}
|
|
16395
18247
|
var finalRuntime = state.conversationRuntimeStates && state.conversationRuntimeStates[lockedRuntimeKey];
|
|
16396
18248
|
var finalRunMatches = !finalRuntime || !finalRuntime.runId || finalRuntime.runId === localRunId || (r && finalRuntime.runId === r.runId);
|
|
16397
18249
|
if (sendFailure) {
|
|
@@ -16401,9 +18253,16 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16401
18253
|
if (finalRunMatches) {
|
|
16402
18254
|
setConversationRuntimeState(lockedTarget, 'error', (r && r.runId) || (finalRuntime && finalRuntime.runId) || localRunId, { provisional: false });
|
|
16403
18255
|
}
|
|
18256
|
+
} else if (finalRunMatches && r && r.runtime && (r.runtime.running || r.runtime.stopRequested)) {
|
|
18257
|
+
setConversationRuntimeState(lockedTarget, r.runtime.stopRequested ? 'stopping' : 'running', r.runtime.runId || r.runId || localRunId, { provisional: false });
|
|
16404
18258
|
} else if (finalRunMatches) {
|
|
16405
|
-
|
|
18259
|
+
var settledStatus = finalRuntime && ['interrupted', 'force_interrupted', 'error'].indexOf(finalRuntime.status) >= 0
|
|
18260
|
+
? finalRuntime.status : ((r && ['interrupted', 'force_interrupted'].indexOf(r.status) >= 0) ? r.status : 'completed');
|
|
18261
|
+
setConversationRuntimeState(lockedTarget, settledStatus, (r && r.runId) || localRunId);
|
|
16406
18262
|
}
|
|
18263
|
+
// The synchronous final status above belongs to this same send completion.
|
|
18264
|
+
// A later IPC event still advances the revision and invalidates the read.
|
|
18265
|
+
if (completedWorkRequest) completedWorkRequest.revision = conversationWorkRevision(lockedTarget);
|
|
16407
18266
|
setWorking(!!runningConversationRecord(activeConversationId()));
|
|
16408
18267
|
window.renderInputStack();
|
|
16409
18268
|
var canonicalLockedRuntimeKey = runtimeKeyFor(lockedTarget.workspaceId, lockedTarget.conversationId);
|
|
@@ -16416,107 +18275,14 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
16416
18275
|
window.drainNextQueue();
|
|
16417
18276
|
};
|
|
16418
18277
|
|
|
16419
|
-
window.scheduleNextQueueDrain = function(
|
|
16420
|
-
|
|
16421
|
-
|
|
16422
|
-
if (state.queuePausedByTarget && state.queuePausedByTarget[runtimeKeyFor(expectedTarget.workspaceId, expectedTarget.conversationId)]) return;
|
|
16423
|
-
var expectedKey = runtimeKeyFor(expectedTarget.workspaceId, expectedTarget.conversationId);
|
|
16424
|
-
if (!state.nextQueueSchedulesByTarget) state.nextQueueSchedulesByTarget = {};
|
|
16425
|
-
var existing = state.nextQueueSchedulesByTarget[expectedKey];
|
|
16426
|
-
if (existing) return;
|
|
16427
|
-
var schedule = { target: expectedTarget };
|
|
16428
|
-
state.nextQueueSchedulesByTarget[expectedKey] = schedule;
|
|
16429
|
-
queueMicrotask(function() {
|
|
16430
|
-
if (state.nextQueueSchedulesByTarget[expectedKey] !== schedule) return;
|
|
16431
|
-
var activeTarget = currentConversationTarget();
|
|
16432
|
-
if (runtimeKeyFor(activeTarget.workspaceId, activeTarget.conversationId) !== expectedKey) {
|
|
16433
|
-
delete state.nextQueueSchedulesByTarget[expectedKey];
|
|
16434
|
-
return;
|
|
16435
|
-
}
|
|
16436
|
-
if (state.activeSendCallsByTarget && state.activeSendCallsByTarget[expectedKey]) {
|
|
16437
|
-
delete state.nextQueueSchedulesByTarget[expectedKey];
|
|
16438
|
-
return;
|
|
16439
|
-
}
|
|
16440
|
-
if (state.queuePausedByTarget && state.queuePausedByTarget[runtimeKeyFor(expectedTarget.workspaceId, expectedTarget.conversationId)]) {
|
|
16441
|
-
delete state.nextQueueSchedulesByTarget[expectedKey];
|
|
16442
|
-
return;
|
|
16443
|
-
}
|
|
16444
|
-
if (isCurrentConversationRunning()) {
|
|
16445
|
-
delete state.nextQueueSchedulesByTarget[expectedKey];
|
|
16446
|
-
return;
|
|
16447
|
-
}
|
|
16448
|
-
window.drainNextQueue();
|
|
16449
|
-
if (state.nextQueueSchedulesByTarget[expectedKey] === schedule) {
|
|
16450
|
-
delete state.nextQueueSchedulesByTarget[expectedKey];
|
|
16451
|
-
}
|
|
16452
|
-
});
|
|
18278
|
+
window.scheduleNextQueueDrain = function() {
|
|
18279
|
+
// Queue consumption belongs to the shared conversation command owner.
|
|
18280
|
+
// This compatibility hook is presentation-only; it cannot schedule a send.
|
|
16453
18281
|
};
|
|
16454
18282
|
|
|
16455
18283
|
window.drainNextQueue = function() {
|
|
16456
|
-
|
|
16457
|
-
|
|
16458
|
-
var target = currentConversationTarget();
|
|
16459
|
-
if (state.queuePausedByTarget && state.queuePausedByTarget[runtimeKeyFor(target.workspaceId, target.conversationId)]) return;
|
|
16460
|
-
var targetKey = runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
16461
|
-
if (state.activeSendCallsByTarget && state.activeSendCallsByTarget[targetKey]) return;
|
|
16462
|
-
if (!state.nextQueueDrainsByTarget) state.nextQueueDrainsByTarget = {};
|
|
16463
|
-
if (state.nextQueueDrainsByTarget[targetKey]) return;
|
|
16464
|
-
var indexes = queueIndexesForRuntimeTarget(target).filter(function(index) {
|
|
16465
|
-
return !queuedRequestIsBackendManaged(state.nextQueueRequests && state.nextQueueRequests[index]);
|
|
16466
|
-
});
|
|
16467
|
-
if (!indexes.length) return;
|
|
16468
|
-
var index = indexes[0];
|
|
16469
|
-
var nextRequest = state.nextQueueRequests ? state.nextQueueRequests[index] : null;
|
|
16470
|
-
if (!queuedRequestMatchesTarget(nextRequest, target)) return;
|
|
16471
|
-
var drainToken = { request: nextRequest, target: target };
|
|
16472
|
-
state.nextQueueDrainsByTarget[targetKey] = drainToken;
|
|
16473
|
-
queueMicrotask(function() {
|
|
16474
|
-
if (state.nextQueueDrainsByTarget[targetKey] !== drainToken) return;
|
|
16475
|
-
var activeTarget = currentConversationTarget();
|
|
16476
|
-
if (runtimeKeyFor(activeTarget.workspaceId, activeTarget.conversationId) !== targetKey
|
|
16477
|
-
|| (state.activeSendCallsByTarget && state.activeSendCallsByTarget[targetKey])
|
|
16478
|
-
|| isCurrentConversationRunning()) {
|
|
16479
|
-
delete state.nextQueueDrainsByTarget[targetKey];
|
|
16480
|
-
return;
|
|
16481
|
-
}
|
|
16482
|
-
var currentIndex = state.nextQueueRequests ? state.nextQueueRequests.indexOf(nextRequest) : -1;
|
|
16483
|
-
if (currentIndex < 0 || queuedRequestIsBackendManaged(nextRequest) || !queuedRequestMatchesTarget(nextRequest, activeTarget)) {
|
|
16484
|
-
delete state.nextQueueDrainsByTarget[targetKey];
|
|
16485
|
-
return;
|
|
16486
|
-
}
|
|
16487
|
-
var next = state.nextQueue[currentIndex];
|
|
16488
|
-
state.nextQueue.splice(currentIndex, 1);
|
|
16489
|
-
state.nextQueueRequests.splice(currentIndex, 1);
|
|
16490
|
-
refreshNextPromptForTarget(activeTarget);
|
|
16491
|
-
window.renderInputStack();
|
|
16492
|
-
var started = false;
|
|
16493
|
-
var sendAttempt = window.sendMessage('guide', next, {
|
|
16494
|
-
fromQueue: true,
|
|
16495
|
-
forceBuild: !nextRequest.goalDeclaration,
|
|
16496
|
-
goalDeclaration: !!nextRequest.goalDeclaration,
|
|
16497
|
-
goalObjective: next,
|
|
16498
|
-
requestedMode: nextRequest.requestedMode || (nextRequest.goalDeclaration ? 'goal' : state.mode),
|
|
16499
|
-
queuedRequest: nextRequest,
|
|
16500
|
-
onStarted: function() { started = true; }
|
|
16501
|
-
});
|
|
16502
|
-
Promise.resolve(sendAttempt).then(function(result) {
|
|
16503
|
-
if (started && !(result && result.ok === false)) return;
|
|
16504
|
-
if ((state.nextQueueRequests || []).indexOf(nextRequest) >= 0) return;
|
|
16505
|
-
var rollbackIndex = Math.max(0, Math.min(currentIndex, state.nextQueue.length));
|
|
16506
|
-
state.nextQueue.splice(rollbackIndex, 0, next);
|
|
16507
|
-
state.nextQueueRequests.splice(rollbackIndex, 0, nextRequest);
|
|
16508
|
-
}).catch(function() {
|
|
16509
|
-
if ((state.nextQueueRequests || []).indexOf(nextRequest) >= 0) return;
|
|
16510
|
-
var rollbackIndex = Math.max(0, Math.min(currentIndex, state.nextQueue.length));
|
|
16511
|
-
state.nextQueue.splice(rollbackIndex, 0, next);
|
|
16512
|
-
state.nextQueueRequests.splice(rollbackIndex, 0, nextRequest);
|
|
16513
|
-
}).finally(function() {
|
|
16514
|
-
if (state.nextQueueDrainsByTarget[targetKey] === drainToken) delete state.nextQueueDrainsByTarget[targetKey];
|
|
16515
|
-
refreshNextPromptForTarget(activeTarget);
|
|
16516
|
-
window.renderInputStack();
|
|
16517
|
-
window.scheduleNextQueueDrain(activeTarget);
|
|
16518
|
-
});
|
|
16519
|
-
});
|
|
18284
|
+
// Backend snapshots contain the only executable queue. Rendering must never
|
|
18285
|
+
// remove an item or re-submit its text as a second independent command.
|
|
16520
18286
|
};
|
|
16521
18287
|
|
|
16522
18288
|
// === Sidebar Controls ===
|
|
@@ -17044,10 +18810,13 @@ window.refreshRightStatus = function(options) {
|
|
|
17044
18810
|
return Promise.resolve(null);
|
|
17045
18811
|
}
|
|
17046
18812
|
var statusTarget = currentConversationTarget();
|
|
18813
|
+
var statusLoadGeneration = state.conversationLoadGeneration;
|
|
18814
|
+
var statusContextRequest = captureContextWindowSnapshotRequest(statusTarget);
|
|
17047
18815
|
return api.getState(statusTarget).then(function(s) {
|
|
18816
|
+
if (!isActiveConversationTarget(statusTarget) || statusLoadGeneration !== state.conversationLoadGeneration) return null;
|
|
17048
18817
|
applyAutoRouteRatingState(s);
|
|
17049
18818
|
renderAutoRouteRatingControls();
|
|
17050
|
-
|
|
18819
|
+
applyConversationCommandSnapshot(s, statusTarget);
|
|
17051
18820
|
if (s && s.model !== undefined) state.model = s.model;
|
|
17052
18821
|
if (s && s.modelLabel) state.modelLabel = s.modelLabel;
|
|
17053
18822
|
if (s && s.status) state.agentStatus = s.status;
|
|
@@ -17094,28 +18863,21 @@ window.refreshRightStatus = function(options) {
|
|
|
17094
18863
|
statusFlowRecord.lastSubmission = { name: String(s.flowRunning.name || ''), input: '', components: [] };
|
|
17095
18864
|
}
|
|
17096
18865
|
renderFlowTakeover(true, String(s.flowRunning.name || statusFlowRecord.name || ''), { target: statusTarget });
|
|
17097
|
-
} else if (flowTargetMatchesCurrent(statusTarget) && s && s.flowSuspension === null && !s.flowRunning) {
|
|
18866
|
+
} else if (flowTargetMatchesCurrent(statusTarget) && s && s.flowSuspension === null && !s.flowRunning && !(statusFlowRecord.pendingStart && !statusFlowRecord.pendingStart.cancelled)) {
|
|
17098
18867
|
// The backend authoritatively reports no Flow for this conversation.
|
|
17099
18868
|
renderFlowTakeover(false, '', { target: statusTarget });
|
|
17100
18869
|
}
|
|
17101
18870
|
if (s && s.conversationPlan) state.conversationPlan = normalizeConversationPlan(s.conversationPlan);
|
|
17102
|
-
|
|
17103
|
-
|
|
17104
|
-
if (!Object.prototype.hasOwnProperty.call(state.conversationDrafts, restoredDraftKey)) {
|
|
17105
|
-
state.conversationDrafts[restoredDraftKey] = String(s.draft || '');
|
|
17106
|
-
}
|
|
17107
|
-
}
|
|
17108
|
-
if (s && s.contextCompression !== undefined) {
|
|
17109
|
-
state.contextCompression = s.contextCompression;
|
|
17110
|
-
}
|
|
17111
|
-
if (s && s.contextWindow !== undefined) {
|
|
17112
|
-
state.contextWindow = s.contextWindow;
|
|
17113
|
-
window.renderContextWindow();
|
|
17114
|
-
}
|
|
18871
|
+
hydrateConversationDraftSnapshot(s, statusTarget);
|
|
18872
|
+
applyContextWindowSnapshot(s, statusContextRequest);
|
|
17115
18873
|
window.renderSubagentList();
|
|
17116
18874
|
if (state.rightTab === 'plan') window.renderConversationPlan();
|
|
17117
18875
|
window.renderRightStatusPanel();
|
|
17118
18876
|
}).catch(function(err) {
|
|
18877
|
+
if (!isActiveConversationTarget(statusTarget) || statusLoadGeneration !== state.conversationLoadGeneration) {
|
|
18878
|
+
if (options.required === true) throw err;
|
|
18879
|
+
return null;
|
|
18880
|
+
}
|
|
17119
18881
|
var target = document.getElementById('right-status-content');
|
|
17120
18882
|
if (target) target.innerHTML = '<div class="right-empty">' + esc(t('status.unavailable')) + ': ' + esc(err.message || String(err)) + '</div>';
|
|
17121
18883
|
if (options.required === true) throw err;
|
|
@@ -18104,15 +19866,17 @@ window.closeSubWin = function() {
|
|
|
18104
19866
|
});
|
|
18105
19867
|
return;
|
|
18106
19868
|
}
|
|
18107
|
-
els['sub-win-overlay'].classList.remove('open');
|
|
18108
|
-
state.activeSubWindowView = null;
|
|
18109
19869
|
var win = els['sub-win'];
|
|
18110
|
-
|
|
18111
|
-
|
|
18112
|
-
|
|
18113
|
-
|
|
18114
|
-
|
|
18115
|
-
|
|
19870
|
+
window.animateLiquidPopupExit(win, null, function() {
|
|
19871
|
+
els['sub-win-overlay'].classList.remove('open');
|
|
19872
|
+
state.activeSubWindowView = null;
|
|
19873
|
+
if (win) { win.classList.remove('memory-lab-window', 'settings-window'); win.style.left = ''; win.style.top = ''; win.style.margin = ''; }
|
|
19874
|
+
var restoreFocus = subWindowOriginFocus;
|
|
19875
|
+
subWindowOriginFocus = null;
|
|
19876
|
+
updateApplicationInertState();
|
|
19877
|
+
requestAnimationFrame(function() {
|
|
19878
|
+
if (restoreFocus && restoreFocus.isConnected && typeof restoreFocus.focus === 'function') restoreFocus.focus({ preventScroll: true });
|
|
19879
|
+
});
|
|
18116
19880
|
});
|
|
18117
19881
|
};
|
|
18118
19882
|
|
|
@@ -18201,7 +19965,7 @@ function renderGeneralSettings() {
|
|
|
18201
19965
|
}
|
|
18202
19966
|
var backgroundColor = /^#[0-9a-f]{6}$/i.test(String(state.backgroundColor || ''))
|
|
18203
19967
|
? state.backgroundColor
|
|
18204
|
-
: (state.theme === 'light' ? '#F0F2F8' : '#
|
|
19968
|
+
: (state.theme === 'light' ? '#F0F2F8' : '#101010');
|
|
18205
19969
|
var fontFamily = String(state.fontFamily || '');
|
|
18206
19970
|
var wslOptions = (state.wslDistros || []).map(function(name) { return '<option value="' + escAttr(name) + '"' + ((state.agentBackend && state.agentBackend.distro) === name ? ' selected' : '') + '>' + esc(name) + '</option>'; }).join('');
|
|
18207
19971
|
var wslHtml = state.platform === 'win32' ? '<div class="setting-row">' +
|
|
@@ -20219,13 +21983,15 @@ window.resumeInterruptedFlow = async function() {
|
|
|
20219
21983
|
};
|
|
20220
21984
|
|
|
20221
21985
|
window.resumeFlowWith = async function(feedback, isInterruptedResume) {
|
|
21986
|
+
var resumeTarget = currentConversationTarget();
|
|
20222
21987
|
var resumeRecord = currentFlowTakeoverRecord();
|
|
20223
21988
|
resumeRecord.lastRelayedError = null;
|
|
20224
21989
|
try {
|
|
20225
|
-
var result = await api.resumeFlow(feedback,
|
|
20226
|
-
|
|
20227
|
-
|
|
20228
|
-
|
|
21990
|
+
var result = await api.resumeFlow(feedback, resumeTarget);
|
|
21991
|
+
applyConversationCommandSnapshot(result, resumeTarget);
|
|
21992
|
+
if (result && Array.isArray(result.chatMessages) && isActiveConversationTarget(resumeTarget)) {
|
|
21993
|
+
if (Array.isArray(result.workRuns)) renderConversationWorkRuns(result.workRuns, resumeTarget);
|
|
21994
|
+
renderChatMessages(result.chatMessages, resumeTarget);
|
|
20229
21995
|
}
|
|
20230
21996
|
if (result && result.alreadyRunning) {
|
|
20231
21997
|
// The backend reports the Flow is already running (a stale resume click
|
|
@@ -20234,83 +22000,60 @@ window.resumeFlowWith = async function(feedback, isInterruptedResume) {
|
|
|
20234
22000
|
// sync.
|
|
20235
22001
|
resumeRecord.running = true;
|
|
20236
22002
|
resumeRecord.paused = false;
|
|
20237
|
-
window.renderFlowTakeover(true, result.flow || resumeRecord.name || (resumeRecord.lastSubmission && resumeRecord.lastSubmission.name) || '', { target:
|
|
22003
|
+
window.renderFlowTakeover(true, result.flow || resumeRecord.name || (resumeRecord.lastSubmission && resumeRecord.lastSubmission.name) || '', { target: resumeTarget });
|
|
20238
22004
|
return result;
|
|
20239
22005
|
}
|
|
20240
22006
|
if (result && result.pending) {
|
|
20241
22007
|
state.pendingOptions = result.options || [];
|
|
20242
22008
|
renderPendingOptionsInChat(state.pendingOptions);
|
|
20243
22009
|
if (result.interrupted) {
|
|
20244
|
-
renderFlowTakeover(true, result.name || (resumeRecord.lastSubmission && resumeRecord.lastSubmission.name), { interrupted: true, message: result.error, target:
|
|
22010
|
+
renderFlowTakeover(true, result.name || (resumeRecord.lastSubmission && resumeRecord.lastSubmission.name), { interrupted: true, message: result.error, target: resumeTarget });
|
|
20245
22011
|
}
|
|
20246
22012
|
return result;
|
|
20247
22013
|
}
|
|
20248
22014
|
var completed = !!(result && result.ok);
|
|
20249
|
-
var resumeTarget = currentConversationTarget();
|
|
20250
22015
|
if (!completed && !flowRelayedErrorWasShown(resumeTarget)) showUiNotice(formatChatError((result && result.error) || 'Flow resume failed'), 'error', 'flow-resume-error');
|
|
20251
22016
|
else if (completed) addMsg('assistant', '[System] Flow complete.', 'flow', state.model);
|
|
20252
|
-
|
|
20253
|
-
await
|
|
20254
|
-
if (completed && settlement && settlement.shouldDrain) window.scheduleNextQueueDrain(settlement.target);
|
|
22017
|
+
stopFlowRunInternal({}, resumeTarget);
|
|
22018
|
+
if (api.getState) applyConversationCommandSnapshot(await api.getState(resumeTarget), resumeTarget);
|
|
20255
22019
|
return result;
|
|
20256
22020
|
} catch (error) {
|
|
20257
|
-
var catchTarget =
|
|
22021
|
+
var catchTarget = resumeTarget;
|
|
20258
22022
|
if (!flowRelayedErrorWasShown(catchTarget)) showUiNotice(formatChatError(error && error.message ? error.message : String(error), 'Flow resume failed.'), 'error', 'flow-resume-error');
|
|
20259
22023
|
stopFlowRunInternal({ keepPaused: true }, catchTarget);
|
|
20260
|
-
await
|
|
22024
|
+
if (api.getState) applyConversationCommandSnapshot(await api.getState(catchTarget), catchTarget);
|
|
20261
22025
|
return { ok: false, error: error && error.message ? error.message : String(error) };
|
|
20262
22026
|
}
|
|
20263
22027
|
};
|
|
20264
22028
|
|
|
20265
|
-
function removeFlowOwnedQueueRequest(
|
|
20266
|
-
|
|
20267
|
-
var index = (state.nextQueueRequests || []).indexOf(lease.request);
|
|
20268
|
-
if (index < 0) return;
|
|
20269
|
-
state.nextQueue.splice(index, 1);
|
|
20270
|
-
state.nextQueueRequests.splice(index, 1);
|
|
20271
|
-
refreshNextPromptForTarget(lease.target);
|
|
22029
|
+
function removeFlowOwnedQueueRequest() {
|
|
22030
|
+
// Flow queue leases are owned and settled by the shared backend.
|
|
20272
22031
|
}
|
|
20273
22032
|
|
|
20274
22033
|
function settleFlowQueueTakeover(options, target) {
|
|
20275
|
-
|
|
20276
|
-
target = target || currentConversationTarget();
|
|
20277
|
-
var record = flowTakeoverRecordFor(target);
|
|
20278
|
-
var lease = record.queueLease || null;
|
|
22034
|
+
var record = flowTakeoverRecordFor(target || currentConversationTarget());
|
|
20279
22035
|
record.queueLease = null;
|
|
20280
|
-
|
|
20281
|
-
removeFlowOwnedQueueRequest(lease);
|
|
20282
|
-
var key = queueRuntimeKey(lease.target);
|
|
20283
|
-
if (!state.queuePausedByTarget) state.queuePausedByTarget = {};
|
|
20284
|
-
state.queuePausedByTarget[key] = options.resumeQueue === true
|
|
20285
|
-
? false
|
|
20286
|
-
: options.keepPaused === true
|
|
20287
|
-
? true
|
|
20288
|
-
: !!lease.wasPaused;
|
|
20289
|
-
return {
|
|
20290
|
-
target: lease.target,
|
|
20291
|
-
shouldDrain: !state.queuePausedByTarget[key]
|
|
20292
|
-
};
|
|
22036
|
+
return null;
|
|
20293
22037
|
}
|
|
20294
22038
|
|
|
20295
|
-
window.queueFlowNextInput = function(text) {
|
|
22039
|
+
window.queueFlowNextInput = async function(text) {
|
|
20296
22040
|
text = String(text || '').trim();
|
|
20297
22041
|
if (!text) return false;
|
|
20298
22042
|
var target = currentConversationTarget();
|
|
20299
|
-
var
|
|
20300
|
-
|
|
20301
|
-
|
|
20302
|
-
|
|
20303
|
-
state.nextQueueRequests.push(request);
|
|
20304
|
-
pauseQueueForTarget(target);
|
|
22043
|
+
var result = await api.sendMessage(text, target, {
|
|
22044
|
+
requestedMode: 'build', inputMode: 'next', clientMessageId: window.crypto.randomUUID()
|
|
22045
|
+
});
|
|
22046
|
+
applyConversationCommandSnapshot(result, target);
|
|
20305
22047
|
state.queueCollapsed = false;
|
|
20306
|
-
refreshNextPromptForTarget(target);
|
|
20307
22048
|
window.renderInputStack();
|
|
20308
|
-
|
|
22049
|
+
if (result && result.ok === false) showUiNotice(result.error || 'Failed to queue Flow input.', 'error', 'flow-next');
|
|
22050
|
+
return result;
|
|
20309
22051
|
};
|
|
20310
22052
|
|
|
20311
22053
|
function stopFlowRunInternal(options, target) {
|
|
20312
22054
|
target = target || currentConversationTarget();
|
|
20313
22055
|
var record = flowTakeoverRecordFor(target);
|
|
22056
|
+
if (record.pendingStart) record.pendingStart.cancelled = true;
|
|
20314
22057
|
record.running = false;
|
|
20315
22058
|
record.paused = false;
|
|
20316
22059
|
record.promptText = '';
|
|
@@ -20341,9 +22084,14 @@ function stopFlowRunInternal(options, target) {
|
|
|
20341
22084
|
window.stopFlowRun = async function(options) {
|
|
20342
22085
|
options = options || {};
|
|
20343
22086
|
if (!flowTakeoverMatchesCurrent()) return { ok: false, action: 'not_running', error: 'The active Flow belongs to another conversation.' };
|
|
22087
|
+
var stopTarget = currentConversationTarget();
|
|
22088
|
+
var stopRecord = flowTakeoverRecordFor(stopTarget);
|
|
22089
|
+
if (stopRecord.pendingStart) stopRecord.pendingStart.cancelled = true;
|
|
22090
|
+
if (options.resumeQueue === true) return window.queueAction('queue_set_pause', { paused: false }, stopTarget);
|
|
20344
22091
|
if (options.resumeQueue !== true) options.keepPaused = true;
|
|
20345
22092
|
if (api.stopFlow) {
|
|
20346
|
-
var stopResult = await api.stopFlow(
|
|
22093
|
+
var stopResult = await api.stopFlow(stopTarget).catch(function() { return null; });
|
|
22094
|
+
applyConversationCommandSnapshot(stopResult, stopTarget);
|
|
20347
22095
|
var action = String(stopResult && stopResult.action || '');
|
|
20348
22096
|
// First Stop/Esc while a Flow is RUNNING: cooperative abort. The flow:run
|
|
20349
22097
|
// handler turns this into an interrupted suspension (renders the paused
|
|
@@ -20351,9 +22099,8 @@ window.stopFlowRun = async function(options) {
|
|
|
20351
22099
|
if (action === 'stopping') return stopResult;
|
|
20352
22100
|
}
|
|
20353
22101
|
// Paused flow (or already force-stopped): tear down immediately.
|
|
20354
|
-
|
|
20355
|
-
await
|
|
20356
|
-
if (settlement && settlement.shouldDrain) window.scheduleNextQueueDrain(settlement.target);
|
|
22102
|
+
stopFlowRunInternal({}, stopTarget);
|
|
22103
|
+
if (api.getState) applyConversationCommandSnapshot(await api.getState(stopTarget), stopTarget);
|
|
20357
22104
|
addMsg('assistant', '[System] Flow stopped manually.', 'flow', '');
|
|
20358
22105
|
};
|
|
20359
22106
|
|
|
@@ -20412,7 +22159,8 @@ window.saveFlowToDisk = async function(workIdx) {
|
|
|
20412
22159
|
window.closeSubWin();
|
|
20413
22160
|
};
|
|
20414
22161
|
|
|
20415
|
-
window.runFlowWork = async function(workIdx) {
|
|
22162
|
+
window.runFlowWork = async function(workIdx, command) {
|
|
22163
|
+
command = command || {};
|
|
20416
22164
|
var work = state.flowWorks[workIdx];
|
|
20417
22165
|
if (!work || !work.components || work.components.length === 0) {
|
|
20418
22166
|
addMsg('assistant', '[System] ' + t('flow.emptyRun'), 'error', '');
|
|
@@ -20420,19 +22168,17 @@ window.runFlowWork = async function(workIdx) {
|
|
|
20420
22168
|
return;
|
|
20421
22169
|
}
|
|
20422
22170
|
var normalized = window.normalizeFlowWork({ name: work.name, components: work.components || [] });
|
|
20423
|
-
var flowInput = (els.prompt && els.prompt.value.trim()) || '';
|
|
20424
|
-
var flowTarget = currentConversationTarget();
|
|
20425
|
-
var
|
|
20426
|
-
var flowQueueCount = queueIndexesForTarget(flowTarget).length;
|
|
20427
|
-
var flowDeliveryMode = state.inputMode === 'next' ? 'next' : 'guide';
|
|
20428
|
-
if (flowDeliveryMode === 'guide' && isCurrentConversationRunning()) {
|
|
20429
|
-
showUiNotice(currentLang() === 'zh' ? '正在运行 Build 时,Flow 必须以 Next 提交。' : 'While a Build is running, Flow must be submitted as Next.', 'error', 'flow-guide-running');
|
|
20430
|
-
return;
|
|
20431
|
-
}
|
|
20432
|
-
pauseQueueForTarget(flowTarget);
|
|
22171
|
+
var flowInput = typeof command.text === 'string' ? command.text : ((els.prompt && els.prompt.value.trim()) || '');
|
|
22172
|
+
var flowTarget = normalizeQueuedConversationTarget(command.target || currentConversationTarget());
|
|
22173
|
+
var flowDeliveryMode = (command.inputMode || state.inputMode) === 'next' ? 'next' : 'guide';
|
|
20433
22174
|
state.queueCollapsed = false;
|
|
20434
22175
|
refreshNextPromptForTarget(flowTarget);
|
|
20435
22176
|
var flowRecord = flowTakeoverRecordFor(flowTarget);
|
|
22177
|
+
if (flowRecord.pendingStart) flowRecord.pendingStart.cancelled = true;
|
|
22178
|
+
// Saving and the shared command are asynchronous. Backend idle snapshots
|
|
22179
|
+
// cannot cancel an unsent command; only explicit stop/supersession can.
|
|
22180
|
+
var pendingStart = { name: normalized.name, cancelled: false };
|
|
22181
|
+
flowRecord.pendingStart = pendingStart;
|
|
20436
22182
|
flowRecord.promptText = flowInput;
|
|
20437
22183
|
flowRecord.lastSubmission = {
|
|
20438
22184
|
name: normalized.name,
|
|
@@ -20443,11 +22189,6 @@ window.runFlowWork = async function(workIdx) {
|
|
|
20443
22189
|
});
|
|
20444
22190
|
})
|
|
20445
22191
|
};
|
|
20446
|
-
flowRecord.queueLease = {
|
|
20447
|
-
target: flowTarget,
|
|
20448
|
-
wasPaused: flowWasPaused,
|
|
20449
|
-
request: null
|
|
20450
|
-
};
|
|
20451
22192
|
if (els.prompt) {
|
|
20452
22193
|
els.prompt.value = '';
|
|
20453
22194
|
els.prompt.placeholder = t('flow.runningPlaceholder');
|
|
@@ -20475,24 +22216,14 @@ window.runFlowWork = async function(workIdx) {
|
|
|
20475
22216
|
if (api.saveFlow) {
|
|
20476
22217
|
await api.saveFlow(normalized);
|
|
20477
22218
|
}
|
|
20478
|
-
|
|
20479
|
-
|
|
20480
|
-
|
|
20481
|
-
|
|
20482
|
-
|
|
20483
|
-
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
updateSubmitButtonState();
|
|
20487
|
-
renderConversations();
|
|
20488
|
-
}
|
|
20489
|
-
if (api.setMode) {
|
|
20490
|
-
await api.setMode('flow');
|
|
20491
|
-
state._syncedMode = 'flow';
|
|
20492
|
-
}
|
|
20493
|
-
state.mode = 'flow';
|
|
20494
|
-
var r = await api.runFlow(normalized.name, flowInput, 0);
|
|
20495
|
-
if (r && r.chatMessages) {
|
|
22219
|
+
if (flowRecord.pendingStart !== pendingStart || pendingStart.cancelled) return;
|
|
22220
|
+
var r = await api.sendMessage(flowInput, flowTarget, {
|
|
22221
|
+
requestedMode: 'flow', inputMode: flowDeliveryMode, flowName: normalized.name,
|
|
22222
|
+
flowStart: 0, clientMessageId: window.crypto.randomUUID()
|
|
22223
|
+
});
|
|
22224
|
+
if (flowRecord.pendingStart !== pendingStart) return r;
|
|
22225
|
+
applyConversationCommandSnapshot(r, flowTarget);
|
|
22226
|
+
if (r && r.chatMessages && isActiveConversationTarget(flowTarget)) {
|
|
20496
22227
|
if (Array.isArray(r.workRuns)) renderConversationWorkRuns(r.workRuns, flowTarget);
|
|
20497
22228
|
renderChatMessages(r.chatMessages);
|
|
20498
22229
|
if (r.conversations) {
|
|
@@ -20524,10 +22255,13 @@ window.runFlowWork = async function(workIdx) {
|
|
|
20524
22255
|
showUiNotice(formatChatError(err && err.message ? err.message : String(err), 'Flow failed.'), 'error', 'flow-run-error');
|
|
20525
22256
|
}
|
|
20526
22257
|
} finally {
|
|
20527
|
-
if (
|
|
20528
|
-
|
|
20529
|
-
|
|
20530
|
-
|
|
22258
|
+
if (flowRecord.pendingStart === pendingStart) {
|
|
22259
|
+
flowRecord.pendingStart = null;
|
|
22260
|
+
if (!flowSuspended) {
|
|
22261
|
+
stopFlowRunInternal({}, flowTarget);
|
|
22262
|
+
if (api.getState) applyConversationCommandSnapshot(await api.getState(flowTarget), flowTarget);
|
|
22263
|
+
}
|
|
22264
|
+
}
|
|
20531
22265
|
}
|
|
20532
22266
|
};
|
|
20533
22267
|
|
|
@@ -20612,9 +22346,10 @@ window.loadFileTree = async function(options) {
|
|
|
20612
22346
|
}
|
|
20613
22347
|
};
|
|
20614
22348
|
|
|
20615
|
-
window.submitSelectedFlow = function() {
|
|
22349
|
+
window.submitSelectedFlow = function(command) {
|
|
22350
|
+
command = command || { text: String(els.prompt && els.prompt.value || ''), target: currentConversationTarget(), inputMode: state.inputMode };
|
|
20616
22351
|
if (!state._flowsLoaded && window.ensureFlowsLoaded) {
|
|
20617
|
-
return window.ensureFlowsLoaded().then(function() { return window.submitSelectedFlow(); });
|
|
22352
|
+
return window.ensureFlowsLoaded().then(function() { return window.submitSelectedFlow(command); });
|
|
20618
22353
|
}
|
|
20619
22354
|
var selected = String(state.defaultFlow || (document.getElementById('flow-select') && document.getElementById('flow-select').value) || '');
|
|
20620
22355
|
var workIdx = state.flowWorks.findIndex(function(work) { return String(work && work.name || '') === selected; });
|
|
@@ -20625,7 +22360,7 @@ window.submitSelectedFlow = function() {
|
|
|
20625
22360
|
showUiNotice(message, 'error', 'flow-select-required');
|
|
20626
22361
|
return { ok: false, error: message };
|
|
20627
22362
|
}
|
|
20628
|
-
return window.runFlowWork(workIdx);
|
|
22363
|
+
return window.runFlowWork(workIdx, command);
|
|
20629
22364
|
};
|
|
20630
22365
|
|
|
20631
22366
|
function renderTreeNodes(nodes, parent, depth) {
|
|
@@ -21437,9 +23172,18 @@ function updateSubagentHistoryMeta(record) {
|
|
|
21437
23172
|
|
|
21438
23173
|
window.closeSubagentHistory = function() {
|
|
21439
23174
|
var overlay = document.getElementById('subagent-history-overlay');
|
|
21440
|
-
if (overlay)
|
|
21441
|
-
|
|
21442
|
-
|
|
23175
|
+
if (!overlay) {
|
|
23176
|
+
state.subagentHistoryActorId = '';
|
|
23177
|
+
_subagentLive = null;
|
|
23178
|
+
return;
|
|
23179
|
+
}
|
|
23180
|
+
var popup = overlay.querySelector('.liquid-glass-popup');
|
|
23181
|
+
function finish() {
|
|
23182
|
+
overlay.remove();
|
|
23183
|
+
state.subagentHistoryActorId = '';
|
|
23184
|
+
_subagentLive = null;
|
|
23185
|
+
}
|
|
23186
|
+
if (popup) window.animateLiquidPopupExit(popup, null, finish); else finish();
|
|
21443
23187
|
};
|
|
21444
23188
|
|
|
21445
23189
|
window.openSubagentHistory = function(id) {
|
|
@@ -21451,7 +23195,7 @@ window.openSubagentHistory = function(id) {
|
|
|
21451
23195
|
}
|
|
21452
23196
|
state.subagentHistoryActorId = agent.id;
|
|
21453
23197
|
var existing = document.getElementById('subagent-history-overlay');
|
|
21454
|
-
if (existing)
|
|
23198
|
+
if (existing) window.closeSubagentHistory();
|
|
21455
23199
|
var lines = [];
|
|
21456
23200
|
lines.push('<div style="font-size:13px;color:var(--text);font-weight:600;margin-bottom:4px;">' + esc(agent.name || agent.displayName || t('subagent.historyTitle')) + '</div>');
|
|
21457
23201
|
lines.push('<div style="font-size:10px;color:var(--text-dim);word-break:break-all;margin-bottom:8px;">' + esc(agent.qualifiedName || agent.id || '') + '</div>');
|
|
@@ -21468,7 +23212,7 @@ window.openSubagentHistory = function(id) {
|
|
|
21468
23212
|
var overlay = document.createElement('div');
|
|
21469
23213
|
overlay.id = 'subagent-history-overlay';
|
|
21470
23214
|
overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:9999;display:flex;align-items:center;justify-content:center;padding:24px;';
|
|
21471
|
-
overlay.innerHTML = '<div style="width:min(680px, calc(100vw - 48px));max-height:80vh;display:flex;flex-direction:column;background:var(--bg);border:1px solid var(--glass-border-2);border-radius:12px;padding:16px;box-shadow:0 20px 60px rgba(0,0,0,.35);">' +
|
|
23215
|
+
overlay.innerHTML = '<div class="liquid-glass liquid-glass-carrier liquid-glass-popup" style="width:min(680px, calc(100vw - 48px));max-height:80vh;display:flex;flex-direction:column;background:var(--bg);border:1px solid var(--glass-border-2);border-radius:12px;padding:16px;box-shadow:0 20px 60px rgba(0,0,0,.35);">' +
|
|
21472
23216
|
'<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;flex:0 0 auto;"><div style="flex:1;font-size:12px;color:var(--text-dim);">' + esc(t('subagent.liveHistory')) + '</div><button type="button" style="border:1px solid var(--glass-border-1);border-radius:8px;background:var(--glass-bg-1);color:var(--text);padding:6px 10px;cursor:pointer;">' + esc(t('common.close')) + '</button></div>' +
|
|
21473
23217
|
'<div style="flex:0 0 auto;">' + lines.join('') + '</div>' +
|
|
21474
23218
|
'<div id="subagent-history-body" style="flex:1 1 auto;overflow:auto;margin-top:8px;min-height:80px;"></div>' +
|
|
@@ -22048,14 +23792,16 @@ function syncBackendConversation() {
|
|
|
22048
23792
|
var conv = convs[state.activeConversation] || convs[0];
|
|
22049
23793
|
var requestedConversationId = String((conv && conv.id) || '');
|
|
22050
23794
|
var requestedWorkspaceKey = currentWorkspaceKey();
|
|
23795
|
+
var requestedLoadGeneration = state.conversationLoadGeneration;
|
|
22051
23796
|
function requestIsCurrent() {
|
|
22052
|
-
return requestedWorkspaceKey === currentWorkspaceKey() &&
|
|
23797
|
+
return requestedLoadGeneration === state.conversationLoadGeneration && requestedWorkspaceKey === currentWorkspaceKey() &&
|
|
22053
23798
|
requestedConversationId === String(activeConversationId() || 'default');
|
|
22054
23799
|
}
|
|
22055
23800
|
if (conv && api.ensureConversation) {
|
|
22056
23801
|
var requestedTarget = currentConversationTarget(conv.id);
|
|
22057
23802
|
state.activeBackendConversationId = conv.id;
|
|
22058
23803
|
var historyPromise = loadActiveConversationMessages(conv.id);
|
|
23804
|
+
requestedLoadGeneration = state.conversationLoadGeneration;
|
|
22059
23805
|
var activate = api.activateConversation ? api.activateConversation(requestedTarget) : api.ensureConversation(requestedTarget);
|
|
22060
23806
|
var activationPromise = activate.then(function(s) {
|
|
22061
23807
|
var id = (s && s.conversationId) || conv.id;
|
|
@@ -22178,7 +23924,7 @@ function followConversationDragPointer() {
|
|
|
22178
23924
|
// Keep the reorder anchor fixed at the edge, but give a held glass float a
|
|
22179
23925
|
// damped visual pull so blocked dragging still feels elastic and alive.
|
|
22180
23926
|
var blockedDelta = projectedTop < minTop ? projectedTop - minTop : projectedTop > maxTop ? projectedTop - maxTop : 0;
|
|
22181
|
-
var resisted = Math.sign(blockedDelta) * Math.sqrt(Math.abs(blockedDelta)) * 0.6;
|
|
23927
|
+
var resisted = Math.sign(blockedDelta) * Math.min(LIQUID_POPUP_MAX_EDGE_JITTER_PX, Math.sqrt(Math.abs(blockedDelta)) * 0.6);
|
|
22182
23928
|
var edge = liquidInteractionEdgePx();
|
|
22183
23929
|
drag.float.style.left = (rowRect.left - edge) + 'px';
|
|
22184
23930
|
drag.float.style.top = (trackTop - edge) + 'px';
|
|
@@ -22399,13 +24145,7 @@ function renderConversations() {
|
|
|
22399
24145
|
displaySummary += ' · ' + String(conv.id || '').slice(-8);
|
|
22400
24146
|
}
|
|
22401
24147
|
var running = !!(runtimeState && ['running', 'stopping', 'force_restarting'].indexOf(String(runtimeState.status || '')) >= 0);
|
|
22402
|
-
var
|
|
22403
|
-
? '<span class="conv-runtime-badge ' + escAttr(String(runtimeState.status)) + '">' + esc(String(runtimeState.status)) + '</span>' : '';
|
|
22404
|
-
var branchCommIcon = conv.branchCommunication
|
|
22405
|
-
? '<span class="conv-branch-comm-icon" title="' + escAttr(t('conversation.branchCommunicationBadge')) + '" aria-label="' + escAttr(t('conversation.branchCommunicationBadge')) + '">' + iconSvg('git-branch', t('conversation.branchCommunicationBadge')) + '</span>' : '';
|
|
22406
|
-
var innerKey = [String(displaySummary), String(conv.messageCount || ''), String(runtimeState && runtimeState.status || ''), conv.branchCommunication ? '1' : '0'].join('|');
|
|
22407
|
-
var nextInner = '<span class="conv-summary" title="' + escAttr(String(conv.id || '')) + '">' + esc(displaySummary) + (conv.messageCount ? ' (' + esc(String(conv.messageCount)) + ')' : '') + '</span>' + branchCommIcon + runtimeBadge +
|
|
22408
|
-
'<button class="conv-more-btn" aria-haspopup="menu" aria-expanded="false" onclick="event.stopPropagation();window.openConversationActionMenu(this, this.closest(".conv-item").getAttribute("data-conversation-id"))" title="' + escAttr(t('conversation.more')) + '">' + iconOnly('ellipsis', t('conversation.more')) + '</button>';
|
|
24148
|
+
var innerKey = [currentLang(), String(displaySummary), String(conv.messageCount || ''), String(runtimeState && runtimeState.status || ''), conv.branchCommunication ? '1' : '0'].join('|');
|
|
22409
24149
|
var div = existingRows[String(conv.id || '')];
|
|
22410
24150
|
if (div) {
|
|
22411
24151
|
keepRows[String(conv.id || '')] = true;
|
|
@@ -22485,11 +24225,19 @@ function renderConversations() {
|
|
|
22485
24225
|
});
|
|
22486
24226
|
existingRows[String(conv.id || '')] = div;
|
|
22487
24227
|
}
|
|
22488
|
-
|
|
22489
|
-
if (
|
|
22490
|
-
|
|
22491
|
-
div.
|
|
24228
|
+
var nextClassName = 'conv-item' + (conv.active ? ' active' : '') + (running ? ' marquee-border' : '');
|
|
24229
|
+
if (div.className !== nextClassName) div.className = nextClassName;
|
|
24230
|
+
var nextTabIndex = conv.active ? '0' : '-1';
|
|
24231
|
+
if (div.getAttribute('tabindex') !== nextTabIndex) div.setAttribute('tabindex', nextTabIndex);
|
|
24232
|
+
var nextCurrent = conv.active ? 'true' : 'false';
|
|
24233
|
+
if (div.getAttribute('aria-current') !== nextCurrent) div.setAttribute('aria-current', nextCurrent);
|
|
22492
24234
|
if (div.getAttribute('data-inner-key') !== innerKey) {
|
|
24235
|
+
var runtimeBadge = runtimeState && runtimeState.status && runtimeState.status !== 'idle'
|
|
24236
|
+
? '<span class="conv-runtime-badge ' + escAttr(String(runtimeState.status)) + '">' + esc(String(runtimeState.status)) + '</span>' : '';
|
|
24237
|
+
var branchCommIcon = conv.branchCommunication
|
|
24238
|
+
? '<span class="conv-branch-comm-icon" title="' + escAttr(t('conversation.branchCommunicationBadge')) + '" aria-label="' + escAttr(t('conversation.branchCommunicationBadge')) + '">' + iconSvg('git-branch', t('conversation.branchCommunicationBadge')) + '</span>' : '';
|
|
24239
|
+
var nextInner = '<span class="conv-summary" title="' + escAttr(String(conv.id || '')) + '">' + esc(displaySummary) + (conv.messageCount ? ' (' + esc(String(conv.messageCount)) + ')' : '') + '</span>' + branchCommIcon + runtimeBadge +
|
|
24240
|
+
'<button class="conv-more-btn" aria-haspopup="menu" aria-expanded="false" onclick="event.stopPropagation();window.openConversationActionMenu(this, this.closest(".conv-item").getAttribute("data-conversation-id"))" title="' + escAttr(t('conversation.more')) + '">' + iconOnly('ellipsis', t('conversation.more')) + '</button>';
|
|
22493
24241
|
div.setAttribute('data-inner-key', innerKey);
|
|
22494
24242
|
div.innerHTML = nextInner;
|
|
22495
24243
|
}
|
|
@@ -25702,11 +27450,14 @@ window.closeCommandSurface = function(options) {
|
|
|
25702
27450
|
options = options || {};
|
|
25703
27451
|
var overlay = document.getElementById('command-surface-overlay');
|
|
25704
27452
|
if (!overlay || !overlay.classList.contains('open')) return;
|
|
25705
|
-
|
|
25706
|
-
updateApplicationInertState();
|
|
27453
|
+
var panel = document.getElementById('command-surface');
|
|
25707
27454
|
var restore = guiCommandSurfaceOriginFocus;
|
|
25708
|
-
|
|
25709
|
-
|
|
27455
|
+
window.animateLiquidPopupExit(panel, null, function() {
|
|
27456
|
+
overlay.classList.remove('open');
|
|
27457
|
+
updateApplicationInertState();
|
|
27458
|
+
guiCommandSurfaceOriginFocus = null;
|
|
27459
|
+
if (options.restoreFocus !== false) requestAnimationFrame(function(){ if (restore && restore.isConnected && typeof restore.focus === 'function') restore.focus({ preventScroll:true }); });
|
|
27460
|
+
});
|
|
25710
27461
|
return restore;
|
|
25711
27462
|
};
|
|
25712
27463
|
|
|
@@ -26197,6 +27948,7 @@ function schedulePostStartupUiRendering() {
|
|
|
26197
27948
|
conversationId: String(s.conversationId || state.activeBackendConversationId || 'default')
|
|
26198
27949
|
};
|
|
26199
27950
|
if (s.runtimeKey) registerRuntimeKey(initialSnapshotTarget, s.runtimeKey);
|
|
27951
|
+
hydrateConversationDraftSnapshot(s, initialSnapshotTarget);
|
|
26200
27952
|
if (Array.isArray(s.workRuns)) syncWorkRunsSnapshot(s.workRuns, initialSnapshotTarget);
|
|
26201
27953
|
if (s.chatMessages) {
|
|
26202
27954
|
hydrateConversationBranchState(s);
|
|
@@ -26318,7 +28070,8 @@ function schedulePostStartupUiRendering() {
|
|
|
26318
28070
|
}
|
|
26319
28071
|
});
|
|
26320
28072
|
els.prompt.addEventListener('input', function() {
|
|
26321
|
-
saveActiveConversationDraft();
|
|
28073
|
+
saveActiveConversationDraft(true);
|
|
28074
|
+
persistConversationDraftForKey(state._promptBoundKey || conversationDraftKey(), 700);
|
|
26322
28075
|
schedulePromptInputRefresh();
|
|
26323
28076
|
});
|
|
26324
28077
|
els.prompt.addEventListener('paste', function(e) {
|
|
@@ -26357,6 +28110,12 @@ function schedulePostStartupUiRendering() {
|
|
|
26357
28110
|
if (els['mode-select']) {
|
|
26358
28111
|
els['mode-select'].addEventListener('change', async function() {
|
|
26359
28112
|
state.mode = this.value;
|
|
28113
|
+
var selectedMode = state.mode;
|
|
28114
|
+
var selectedTarget = currentConversationTarget();
|
|
28115
|
+
// Submit before asynchronous Flow-list loading so an older selector
|
|
28116
|
+
// event cannot overwrite a newer user selection after loading finishes.
|
|
28117
|
+
var modeSelection = selectedMode !== state._syncedMode
|
|
28118
|
+
? Promise.resolve(api.setMode(selectedMode, selectedTarget)) : Promise.resolve(selectedMode);
|
|
26360
28119
|
if (window.renderInputStack) window.renderInputStack();
|
|
26361
28120
|
// Flow mode: show flow selector
|
|
26362
28121
|
var flowSel = document.getElementById('flow-select');
|
|
@@ -26369,33 +28128,30 @@ function schedulePostStartupUiRendering() {
|
|
|
26369
28128
|
window.renderFlowSelector();
|
|
26370
28129
|
}
|
|
26371
28130
|
}
|
|
26372
|
-
//
|
|
26373
|
-
|
|
26374
|
-
|
|
26375
|
-
|
|
26376
|
-
}
|
|
28131
|
+
// Shared selection persists the visible mode without creating a Goal
|
|
28132
|
+
// or replacing the mode of an already running Flow component.
|
|
28133
|
+
var modeResult = await modeSelection;
|
|
28134
|
+
if (modeResult != null && isActiveConversationTarget(selectedTarget) && state.mode === selectedMode) state._syncedMode = selectedMode;
|
|
26377
28135
|
});
|
|
26378
28136
|
}
|
|
26379
28137
|
|
|
26380
28138
|
if (els['model-select']) {
|
|
26381
28139
|
els['model-select'].addEventListener('change', async function() {
|
|
26382
|
-
|
|
28140
|
+
var selectedContextTarget = currentConversationTarget();
|
|
28141
|
+
var selectedContextGeneration = state.conversationLoadGeneration;
|
|
28142
|
+
var selectedContextModel = this.value;
|
|
28143
|
+
state.model = selectedContextModel;
|
|
26383
28144
|
window.syncModelSelectSurface();
|
|
26384
28145
|
if (state.model !== state._syncedModel) {
|
|
26385
|
-
var modelResult = await api.setModel(
|
|
26386
|
-
if (modelResult != null
|
|
28146
|
+
var modelResult = await api.setModel(selectedContextModel);
|
|
28147
|
+
if (modelResult != null && isActiveConversationTarget(selectedContextTarget)
|
|
28148
|
+
&& selectedContextGeneration === state.conversationLoadGeneration && state.model === selectedContextModel) state._syncedModel = selectedContextModel;
|
|
26387
28149
|
}
|
|
26388
28150
|
// The context ring/inspector must immediately reflect the newly selected
|
|
26389
28151
|
// model's configured context window rather than waiting for the next send.
|
|
26390
|
-
if (api.getState)
|
|
26391
|
-
|
|
26392
|
-
|
|
26393
|
-
if (s && s.contextWindow !== undefined && isActiveConversationTarget(ctxTarget) && isViewingRuntimeConversationBranch(ctxTarget)) {
|
|
26394
|
-
state.contextWindow = s.contextWindow;
|
|
26395
|
-
window.renderContextWindow();
|
|
26396
|
-
}
|
|
26397
|
-
}).catch(function(){});
|
|
26398
|
-
}
|
|
28152
|
+
if (api.getState && isActiveConversationTarget(selectedContextTarget)
|
|
28153
|
+
&& selectedContextGeneration === state.conversationLoadGeneration && state.model === selectedContextModel
|
|
28154
|
+
&& isViewingRuntimeConversationBranch(selectedContextTarget)) scheduleActiveContextWindowRefresh(selectedContextTarget);
|
|
26399
28155
|
});
|
|
26400
28156
|
}
|
|
26401
28157
|
|
|
@@ -26492,3 +28248,5 @@ function schedulePostStartupUiRendering() {
|
|
|
26492
28248
|
</script>
|
|
26493
28249
|
</body>
|
|
26494
28250
|
</html>
|
|
28251
|
+
|
|
28252
|
+
|