newmark-agent 0.5.4 → 0.5.8
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 +230 -66
- package/dist/core/agent.d.ts +1 -1
- package/dist/core/agent.js +55 -25
- package/dist/core/agentKernelRunner.js +55 -9
- package/dist/core/autoRouter.d.ts +1 -0
- package/dist/core/autoRouter.js +9 -0
- package/dist/core/conversationKernel.d.ts +12 -0
- package/dist/core/conversationKernel.js +32 -1
- package/dist/core/electronUtilityRuntimePool.js +7 -2
- package/dist/core/emptyResponseRetry.d.ts +16 -0
- package/dist/core/emptyResponseRetry.js +25 -0
- package/dist/core/terminalOutputBuffer.d.ts +24 -0
- package/dist/core/terminalOutputBuffer.js +92 -0
- package/dist/core/types.d.ts +21 -0
- package/dist/core/wslAgentRuntimePool.js +6 -2
- package/dist/llm/provider.d.ts +4 -4
- package/dist/llm/provider.js +55 -21
- package/dist/main.js +46 -3
- package/dist/preload.js +1 -0
- package/dist/providers/chat-completions.adapter.js +17 -4
- package/dist/providers/provider-events.d.ts +13 -0
- package/dist/providers/provider-events.js +55 -4
- package/dist/providers/responses.adapter.js +23 -9
- package/dist/server.js +21 -0
- package/dist/ui/index.html +2415 -216
- package/dist/ui/lucide-sprite.svg +4 -0
- package/dist/wsl-agent-host.bundle.cjs +230 -66
- package/package.json +10 -3
package/dist/ui/index.html
CHANGED
|
@@ -80,6 +80,7 @@ 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: color-mix(in srgb, var(--app-bg) 92%, var(--accent) 8%);
|
|
83
84
|
|
|
84
85
|
/* Glass layers */
|
|
85
86
|
--glass-alpha: 0.85;
|
|
@@ -97,6 +98,16 @@ try {
|
|
|
97
98
|
--glass-border-2: rgba(255,255,255,0.08);
|
|
98
99
|
--glass-border-3: rgba(255,255,255,0.10);
|
|
99
100
|
|
|
101
|
+
/* Kyant AndroidLiquidGlass (backdrop) liquid-glass parity tokens.
|
|
102
|
+
CSS equivalent of the library lens edge refraction, top highlight,
|
|
103
|
+
inner shadow and saturation boost (saturate(140%) == vibrancy). */
|
|
104
|
+
--liquid-edge-glow: rgba(255,255,255,0.10);
|
|
105
|
+
--liquid-edge-glow-strong: rgba(255,255,255,0.16);
|
|
106
|
+
--liquid-top-highlight: inset 0 1px 0 rgba(255,255,255,0.10);
|
|
107
|
+
--liquid-inner-shadow: inset 0 -1px 0 rgba(0,0,0,0.22);
|
|
108
|
+
--liquid-outer-shadow: 0 8px 32px rgba(0,0,0,0.45);
|
|
109
|
+
--liquid-refraction: linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0) 18%, rgba(0,0,0,0) 82%, rgba(0,0,0,0.18));
|
|
110
|
+
|
|
100
111
|
/* Borders & corners */
|
|
101
112
|
--border: rgba(255,255,255,0.06);
|
|
102
113
|
--border-hover: rgba(255,255,255,0.12);
|
|
@@ -130,6 +141,15 @@ try {
|
|
|
130
141
|
--glass-blur-1: 1.2px;
|
|
131
142
|
--glass-blur-2: 2.4px;
|
|
132
143
|
--glass-blur-3: 3px;
|
|
144
|
+
/* One physical glass setting, two material roles. Carrier glass frosts the
|
|
145
|
+
backdrop; the moving float stays clear and only refracts/ disperses it. */
|
|
146
|
+
--carrier-glass-blur: 6.6px;
|
|
147
|
+
--carrier-glass-alpha: 0.374;
|
|
148
|
+
--liquid-interaction-edge: 6px;
|
|
149
|
+
--liquid-float-refraction-height: var(--liquid-interaction-edge);
|
|
150
|
+
--liquid-float-refraction-amount: 24px;
|
|
151
|
+
--liquid-float-chromatic-aberration: 1;
|
|
152
|
+
--liquid-float-optical-opacity: 1;
|
|
133
153
|
|
|
134
154
|
/* Transitions */
|
|
135
155
|
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
|
|
@@ -211,8 +231,9 @@ try {
|
|
|
211
231
|
--glass-rgb-1: 255 255 255;
|
|
212
232
|
--glass-rgb-2: 255 255 255;
|
|
213
233
|
--glass-rgb-3: 255 255 255;
|
|
214
|
-
--modal-surface: rgba(248,250,255,0.
|
|
215
|
-
--modal-
|
|
234
|
+
--modal-surface: rgba(248,250,255,0.82);
|
|
235
|
+
--settings-modal-surface: rgba(255,255,255,0.94);
|
|
236
|
+
--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);
|
|
216
237
|
--glass-border-1: rgba(0,0,0,0.06);
|
|
217
238
|
--glass-border-2: rgba(0,0,0,0.08);
|
|
218
239
|
--glass-border-3: rgba(0,0,0,0.10);
|
|
@@ -223,6 +244,7 @@ try {
|
|
|
223
244
|
--shadow-md: 0 2px 8px rgba(40,52,86,0.07);
|
|
224
245
|
--shadow-lg: 0 6px 18px rgba(40,52,86,0.09);
|
|
225
246
|
--accent-glow: rgba(74,104,223,0.25);
|
|
247
|
+
--chat-canvas: color-mix(in srgb, var(--app-bg) 96%, var(--accent) 4%);
|
|
226
248
|
}
|
|
227
249
|
|
|
228
250
|
/* --- Reset & Base --- */
|
|
@@ -357,6 +379,9 @@ html, body {
|
|
|
357
379
|
border: 0;
|
|
358
380
|
}
|
|
359
381
|
button:focus, select:focus, input:focus, textarea:focus, [contenteditable]:focus { outline: none; }
|
|
382
|
+
/* Liquid-glass controls use one continuous capsule edge. Equal-width icon
|
|
383
|
+
buttons naturally become circles; wider controls become end-rounded pills. */
|
|
384
|
+
button { border-radius: var(--radius-full) !important; }
|
|
360
385
|
:where(button, select, input, textarea, [contenteditable], [tabindex]):focus-visible {
|
|
361
386
|
outline: 2px solid var(--accent);
|
|
362
387
|
outline-offset: 2px;
|
|
@@ -505,6 +530,12 @@ button:focus, select:focus, input:focus, textarea:focus, [contenteditable]:focus
|
|
|
505
530
|
transition: opacity var(--duration-normal) var(--ease-out-expo), transform var(--duration-normal) var(--ease-out-expo), visibility 0ms linear var(--duration-normal);
|
|
506
531
|
}
|
|
507
532
|
|
|
533
|
+
#left-tool-surface {
|
|
534
|
+
/* Gesture delegation only: do not introduce another flex item. Its children
|
|
535
|
+
retain the original direct-child layout inside #left-content. */
|
|
536
|
+
display: contents;
|
|
537
|
+
}
|
|
538
|
+
|
|
508
539
|
#left-thumb {
|
|
509
540
|
display: flex;
|
|
510
541
|
position: absolute;
|
|
@@ -630,7 +661,7 @@ button.left-nav-icon { width: calc(100% - 12px); }
|
|
|
630
661
|
transition: max-width var(--duration-slow) var(--ease-out-expo) 80ms, opacity var(--duration-normal) var(--ease-out-expo) 40ms, transform var(--duration-slow) var(--ease-out-expo) 80ms, visibility 0ms linear 200ms;
|
|
631
662
|
}
|
|
632
663
|
|
|
633
|
-
#left.collapsing #left-
|
|
664
|
+
#left.collapsing #left-tool-surface > div[style*="uppercase"],
|
|
634
665
|
#left.collapsing #left-ws-header,
|
|
635
666
|
#left.collapsing #left-ws-add {
|
|
636
667
|
opacity: 0;
|
|
@@ -676,7 +707,7 @@ button.left-nav-icon { width: calc(100% - 12px); }
|
|
|
676
707
|
vertical-align: top;
|
|
677
708
|
}
|
|
678
709
|
|
|
679
|
-
#left.collapsed #left-
|
|
710
|
+
#left.collapsed #left-tool-surface > div[style*="uppercase"] {
|
|
680
711
|
height: 0;
|
|
681
712
|
padding: 0;
|
|
682
713
|
overflow: hidden;
|
|
@@ -774,6 +805,7 @@ button.left-nav-icon { width: calc(100% - 12px); }
|
|
|
774
805
|
transition: background var(--duration-fast) var(--ease-out-expo), width var(--duration-normal) var(--ease-out-expo), height var(--duration-normal) var(--ease-out-expo), padding var(--duration-normal) var(--ease-out-expo), margin var(--duration-normal) var(--ease-out-expo), gap var(--duration-normal) var(--ease-out-expo);
|
|
775
806
|
white-space: nowrap;
|
|
776
807
|
overflow: hidden;
|
|
808
|
+
border-radius: var(--radius-full);
|
|
777
809
|
}
|
|
778
810
|
|
|
779
811
|
button.left-ws-item {
|
|
@@ -789,7 +821,7 @@ button.left-ws-item {
|
|
|
789
821
|
|
|
790
822
|
.left-ws-item .ws-icon {
|
|
791
823
|
width: 20px; height: 20px;
|
|
792
|
-
border-radius:
|
|
824
|
+
border-radius: 50%;
|
|
793
825
|
background: linear-gradient(135deg, var(--accent), #7b93ff);
|
|
794
826
|
color: #fff;
|
|
795
827
|
font-size: 10px;
|
|
@@ -932,8 +964,8 @@ button.left-ws-item {
|
|
|
932
964
|
width: 0;
|
|
933
965
|
flex-shrink: 0;
|
|
934
966
|
background: var(--glass-bg-3);
|
|
935
|
-
backdrop-filter: blur(var(--glass-blur-
|
|
936
|
-
-webkit-backdrop-filter: blur(var(--glass-blur-
|
|
967
|
+
backdrop-filter: blur(var(--glass-blur-1));
|
|
968
|
+
-webkit-backdrop-filter: blur(var(--glass-blur-1));
|
|
937
969
|
border-right: 1px solid var(--glass-border-1);
|
|
938
970
|
display: flex;
|
|
939
971
|
flex-direction: column;
|
|
@@ -991,6 +1023,7 @@ button.left-ws-item {
|
|
|
991
1023
|
flex: 1;
|
|
992
1024
|
overflow-y: auto;
|
|
993
1025
|
padding: 4px 8px;
|
|
1026
|
+
position: relative;
|
|
994
1027
|
}
|
|
995
1028
|
|
|
996
1029
|
.conv-item {
|
|
@@ -999,7 +1032,7 @@ button.left-ws-item {
|
|
|
999
1032
|
gap: 6px;
|
|
1000
1033
|
padding: 8px 10px;
|
|
1001
1034
|
margin: 2px 0;
|
|
1002
|
-
border-radius: var(--radius-
|
|
1035
|
+
border-radius: var(--radius-full);
|
|
1003
1036
|
cursor: pointer;
|
|
1004
1037
|
background: var(--glass-bg-1);
|
|
1005
1038
|
border: 1px solid var(--glass-border-1);
|
|
@@ -1009,7 +1042,7 @@ button.left-ws-item {
|
|
|
1009
1042
|
.conv-item:hover { background: var(--glass-bg-2); border-color: var(--border-hover); }
|
|
1010
1043
|
.conv-item.active { border-color: var(--accent); background: rgba(91,120,255,0.1); }
|
|
1011
1044
|
.conv-item.marquee-border::before {
|
|
1012
|
-
border-radius: var(--radius-
|
|
1045
|
+
border-radius: var(--radius-full);
|
|
1013
1046
|
}
|
|
1014
1047
|
|
|
1015
1048
|
.conv-summary {
|
|
@@ -1053,8 +1086,52 @@ button.left-ws-item {
|
|
|
1053
1086
|
to { transform: rotate(360deg); }
|
|
1054
1087
|
}
|
|
1055
1088
|
|
|
1056
|
-
.conv-item.dragging { opacity: 0
|
|
1057
|
-
.conv-item.
|
|
1089
|
+
.conv-item.dragging { opacity: 0; }
|
|
1090
|
+
#conversation-list.conversation-reordering .conv-item.active:not(.conversation-glass-dragging) {
|
|
1091
|
+
border-color: var(--glass-border-1);
|
|
1092
|
+
background: var(--glass-bg-1);
|
|
1093
|
+
}
|
|
1094
|
+
.conv-item.conversation-glass-dragging {
|
|
1095
|
+
background: transparent;
|
|
1096
|
+
background-color: transparent !important;
|
|
1097
|
+
background-image: none !important;
|
|
1098
|
+
backdrop-filter: none;
|
|
1099
|
+
-webkit-backdrop-filter: none;
|
|
1100
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), 0 8px 22px rgba(0,0,0,.3);
|
|
1101
|
+
transform: scale(1.06);
|
|
1102
|
+
}
|
|
1103
|
+
.conversation-selection-float.conversation-drag-following {
|
|
1104
|
+
transition: opacity 120ms var(--ease-out-expo) !important;
|
|
1105
|
+
}
|
|
1106
|
+
.conversation-drag-content {
|
|
1107
|
+
position: absolute;
|
|
1108
|
+
inset: 9px;
|
|
1109
|
+
z-index: 2;
|
|
1110
|
+
display: flex;
|
|
1111
|
+
align-items: center;
|
|
1112
|
+
gap: 6px;
|
|
1113
|
+
min-width: 0;
|
|
1114
|
+
padding: 8px 10px;
|
|
1115
|
+
box-sizing: border-box;
|
|
1116
|
+
border: 0;
|
|
1117
|
+
border-radius: var(--radius-full);
|
|
1118
|
+
background: transparent;
|
|
1119
|
+
color: var(--text);
|
|
1120
|
+
pointer-events: none;
|
|
1121
|
+
transform: scale(1);
|
|
1122
|
+
transform-origin: center;
|
|
1123
|
+
}
|
|
1124
|
+
.conversation-drag-content .conv-summary { min-width: 0; flex: 1; }
|
|
1125
|
+
.conversation-drag-content button { pointer-events: none; }
|
|
1126
|
+
.conversation-selection-float.liquid-selection-landing > .conversation-drag-content {
|
|
1127
|
+
/* The optical shell contracts into the destination color block. Keep the
|
|
1128
|
+
carried label/icons at their original size until ownership returns. */
|
|
1129
|
+
transform: scale(calc(1 / .86));
|
|
1130
|
+
transition: transform 120ms var(--ease-out-expo);
|
|
1131
|
+
}
|
|
1132
|
+
.conversation-selection-float.liquid-selection-landing > .conversation-drag-content {
|
|
1133
|
+
transition-duration: 180ms;
|
|
1134
|
+
}
|
|
1058
1135
|
.conv-rename-input {
|
|
1059
1136
|
flex: 1;
|
|
1060
1137
|
min-width: 0;
|
|
@@ -1124,10 +1201,9 @@ button.left-ws-item {
|
|
|
1124
1201
|
overflow: auto;
|
|
1125
1202
|
padding: 6px;
|
|
1126
1203
|
border: 1px solid var(--glass-border-2);
|
|
1127
|
-
border-radius: var(--radius-
|
|
1204
|
+
border-radius: var(--radius-xl);
|
|
1128
1205
|
background: var(--modal-surface);
|
|
1129
|
-
box-shadow: var(--shadow-lg);
|
|
1130
|
-
backdrop-filter: blur(var(--glass-blur-3)) saturate(1.08);
|
|
1206
|
+
box-shadow: var(--shadow-lg), var(--liquid-top-highlight), var(--liquid-inner-shadow);
|
|
1131
1207
|
}
|
|
1132
1208
|
.conv-action-menu:popover-open { display: block; animation: model-menu-in 150ms var(--ease-out-expo); }
|
|
1133
1209
|
|
|
@@ -1227,6 +1303,7 @@ button.left-ws-item {
|
|
|
1227
1303
|
backdrop-filter: blur(var(--glass-blur-2)) saturate(130%);
|
|
1228
1304
|
-webkit-backdrop-filter: blur(var(--glass-blur-2)) saturate(130%);
|
|
1229
1305
|
border-left: 1px solid var(--glass-border-1);
|
|
1306
|
+
border-radius: var(--radius-lg) 0 0 var(--radius-lg);
|
|
1230
1307
|
display: flex;
|
|
1231
1308
|
flex-direction: column;
|
|
1232
1309
|
overflow: hidden;
|
|
@@ -1236,7 +1313,7 @@ button.left-ws-item {
|
|
|
1236
1313
|
#right.open {
|
|
1237
1314
|
width: var(--right-width);
|
|
1238
1315
|
opacity: 1;
|
|
1239
|
-
box-shadow:
|
|
1316
|
+
box-shadow: none;
|
|
1240
1317
|
}
|
|
1241
1318
|
|
|
1242
1319
|
[data-theme="light"] #right.open {
|
|
@@ -1792,8 +1869,6 @@ button.left-ws-item {
|
|
|
1792
1869
|
height: 34px;
|
|
1793
1870
|
flex-shrink: 0;
|
|
1794
1871
|
background: var(--glass-bg-3);
|
|
1795
|
-
backdrop-filter: blur(var(--glass-blur-3)) saturate(130%);
|
|
1796
|
-
-webkit-backdrop-filter: blur(var(--glass-blur-3)) saturate(130%);
|
|
1797
1872
|
border-top: 1px solid var(--glass-border-1);
|
|
1798
1873
|
display: flex;
|
|
1799
1874
|
flex-direction: column;
|
|
@@ -2057,6 +2132,8 @@ button.left-ws-item {
|
|
|
2057
2132
|
overflow: hidden;
|
|
2058
2133
|
position: relative;
|
|
2059
2134
|
min-height: 0;
|
|
2135
|
+
/* Opaque theme-tinted foundation below the transcript, rails and glass. */
|
|
2136
|
+
background-color: var(--chat-canvas);
|
|
2060
2137
|
}
|
|
2061
2138
|
|
|
2062
2139
|
#chat-area {
|
|
@@ -2460,9 +2537,12 @@ button.left-ws-item {
|
|
|
2460
2537
|
text-align: left;
|
|
2461
2538
|
cursor: pointer;
|
|
2462
2539
|
font: 600 12px var(--font);
|
|
2540
|
+
transition: none !important;
|
|
2541
|
+
animation: none !important;
|
|
2542
|
+
transform: none !important;
|
|
2463
2543
|
}
|
|
2464
2544
|
|
|
2465
|
-
.conversation-work-run-head:hover { color: var(--text-bright); }
|
|
2545
|
+
.conversation-work-run-head:hover { color: var(--text-bright); filter: none; }
|
|
2466
2546
|
|
|
2467
2547
|
.conversation-work-run-chevron {
|
|
2468
2548
|
width: 8px;
|
|
@@ -2471,7 +2551,7 @@ button.left-ws-item {
|
|
|
2471
2551
|
border-right: 1px solid var(--text-dim);
|
|
2472
2552
|
border-bottom: 1px solid var(--text-dim);
|
|
2473
2553
|
transform: rotate(45deg);
|
|
2474
|
-
transition:
|
|
2554
|
+
transition: none;
|
|
2475
2555
|
}
|
|
2476
2556
|
|
|
2477
2557
|
.conversation-work-run.collapsed .conversation-work-run-chevron { transform: rotate(-45deg); }
|
|
@@ -3296,8 +3376,7 @@ button.todo-item { background: transparent; }
|
|
|
3296
3376
|
padding: 12px 16px;
|
|
3297
3377
|
border-top: 1px solid var(--glass-border-2);
|
|
3298
3378
|
background: var(--glass-bg-2);
|
|
3299
|
-
|
|
3300
|
-
-webkit-backdrop-filter: blur(var(--glass-blur-3)) saturate(140%);
|
|
3379
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow);
|
|
3301
3380
|
}
|
|
3302
3381
|
|
|
3303
3382
|
#input-area textarea {
|
|
@@ -3443,10 +3522,9 @@ button.todo-item { background: transparent; }
|
|
|
3443
3522
|
overflow: auto;
|
|
3444
3523
|
padding: 7px;
|
|
3445
3524
|
border: 1px solid var(--glass-border-2);
|
|
3446
|
-
border-radius: var(--radius-
|
|
3525
|
+
border-radius: var(--radius-xl);
|
|
3447
3526
|
background: var(--modal-surface);
|
|
3448
|
-
box-shadow: var(--shadow-lg);
|
|
3449
|
-
backdrop-filter: blur(var(--glass-blur-3)) saturate(1.08);
|
|
3527
|
+
box-shadow: var(--shadow-lg), var(--liquid-top-highlight), var(--liquid-inner-shadow);
|
|
3450
3528
|
}
|
|
3451
3529
|
.model-select-menu:popover-open { display: block; animation: model-menu-in 150ms var(--ease-out-expo); }
|
|
3452
3530
|
|
|
@@ -3470,6 +3548,85 @@ button.todo-item { background: transparent; }
|
|
|
3470
3548
|
.model-select-menu-option:hover,
|
|
3471
3549
|
.model-select-menu-option:focus-visible { background: var(--control-hover-bg); color: var(--text-bright); outline: none; }
|
|
3472
3550
|
.model-select-menu-option.selected { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--text-bright); }
|
|
3551
|
+
.liquid-selection-source { background: transparent !important; border-color: transparent !important; }
|
|
3552
|
+
.liquid-selection-source::after { display: none !important; opacity: 0 !important; }
|
|
3553
|
+
.conv-item.liquid-selection-covered { border-color: transparent !important; }
|
|
3554
|
+
.conv-item.liquid-selection-source.marquee-border::before,
|
|
3555
|
+
.conv-item.liquid-selection-covered.marquee-border::before { opacity: 0 !important; }
|
|
3556
|
+
/* Rails and selectable menus are governed by one shared moving glass block.
|
|
3557
|
+
Suppress the generic pressed pseudo-glass so the destination never creates
|
|
3558
|
+
a second block while the source block is flying toward it. */
|
|
3559
|
+
.model-select-menu-option::after,
|
|
3560
|
+
.newmark-select-option::after,
|
|
3561
|
+
.left-nav-icon::after,
|
|
3562
|
+
.lt-item::after,
|
|
3563
|
+
.left-ws-item::after,
|
|
3564
|
+
.tab-btn::after,
|
|
3565
|
+
.stab-btn[data-stab]::after,
|
|
3566
|
+
.stab-btn[data-plugin-tab]::after,
|
|
3567
|
+
.mode-toggle-btn[data-mode]::after,
|
|
3568
|
+
.memory-lab-view-menu button[data-memory-view]::after {
|
|
3569
|
+
display: none !important;
|
|
3570
|
+
opacity: 0 !important;
|
|
3571
|
+
}
|
|
3572
|
+
.left-ws-item.liquid-selection-source,
|
|
3573
|
+
.tab-btn.liquid-selection-source { background: transparent !important; }
|
|
3574
|
+
#left-ws-list,
|
|
3575
|
+
#right-tabs { position: relative; }
|
|
3576
|
+
.left-nav-icon.liquid-tool-active,
|
|
3577
|
+
.lt-item.liquid-tool-active { background: var(--control-hover-bg); color: var(--text-bright); }
|
|
3578
|
+
.liquid-selection-float {
|
|
3579
|
+
--liquid-lift-scale: .94;
|
|
3580
|
+
--liquid-motion-angle: 0rad;
|
|
3581
|
+
--liquid-motion-stretch: 1;
|
|
3582
|
+
--liquid-motion-squash: 1;
|
|
3583
|
+
position: absolute;
|
|
3584
|
+
z-index: 4;
|
|
3585
|
+
display: flex;
|
|
3586
|
+
align-items: center;
|
|
3587
|
+
padding: 6px 10px;
|
|
3588
|
+
box-sizing: border-box;
|
|
3589
|
+
border-radius: var(--radius-full);
|
|
3590
|
+
color: var(--text-bright);
|
|
3591
|
+
pointer-events: none;
|
|
3592
|
+
overflow: visible;
|
|
3593
|
+
opacity: 0;
|
|
3594
|
+
transform: scale(var(--liquid-lift-scale)) rotate(var(--liquid-motion-angle)) scaleX(var(--liquid-motion-stretch)) scaleY(var(--liquid-motion-squash)) rotate(calc(-1 * var(--liquid-motion-angle)));
|
|
3595
|
+
transform-origin: center;
|
|
3596
|
+
background: transparent;
|
|
3597
|
+
background-color: transparent !important;
|
|
3598
|
+
background-image: none !important;
|
|
3599
|
+
backdrop-filter: none;
|
|
3600
|
+
-webkit-backdrop-filter: none;
|
|
3601
|
+
box-shadow: inset 0 1px 0 rgba(255,255,255,0.22), inset 0 -1px 0 rgba(0,0,0,0.18), 0 9px 24px rgba(0,0,0,0.28);
|
|
3602
|
+
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;
|
|
3603
|
+
will-change: top, left, width, height, transform;
|
|
3604
|
+
}
|
|
3605
|
+
.liquid-selection-float.visible { opacity: 1; --liquid-lift-scale: 1; }
|
|
3606
|
+
.liquid-selection-float.liquid-selection-landing {
|
|
3607
|
+
opacity: 0;
|
|
3608
|
+
--liquid-lift-scale: .86;
|
|
3609
|
+
transform: scale(var(--liquid-lift-scale)) rotate(var(--liquid-motion-angle)) scaleX(var(--liquid-motion-stretch)) scaleY(var(--liquid-motion-squash)) rotate(calc(-1 * var(--liquid-motion-angle)));
|
|
3610
|
+
transition: opacity 120ms var(--ease-out-expo), transform 120ms var(--ease-out-expo) !important;
|
|
3611
|
+
}
|
|
3612
|
+
.liquid-selection-float.liquid-selection-viewport {
|
|
3613
|
+
position: fixed;
|
|
3614
|
+
inset: auto;
|
|
3615
|
+
z-index: 1000;
|
|
3616
|
+
margin: 0;
|
|
3617
|
+
border: 0;
|
|
3618
|
+
}
|
|
3619
|
+
.liquid-selection-float.liquid-selection-primed { transition: none !important; }
|
|
3620
|
+
.liquid-selection-float.liquid-selection-viewport:popover-open { display: flex; }
|
|
3621
|
+
.liquid-selection-float::-webkit-scrollbar { display: none; }
|
|
3622
|
+
.liquid-selection-canvas {
|
|
3623
|
+
position: absolute;
|
|
3624
|
+
inset: 0;
|
|
3625
|
+
width: 100%;
|
|
3626
|
+
height: 100%;
|
|
3627
|
+
border-radius: inherit;
|
|
3628
|
+
pointer-events: none;
|
|
3629
|
+
}
|
|
3473
3630
|
@keyframes model-menu-in { from { opacity: 0; transform: translateY(5px) scale(.985); } to { opacity: 1; transform: none; } }
|
|
3474
3631
|
|
|
3475
3632
|
.newmark-select-shell {
|
|
@@ -3508,8 +3665,7 @@ button.todo-item { background: transparent; }
|
|
|
3508
3665
|
border: 1px solid var(--glass-border-2);
|
|
3509
3666
|
border-radius: var(--radius-lg);
|
|
3510
3667
|
background: var(--modal-surface);
|
|
3511
|
-
box-shadow: var(--shadow-lg);
|
|
3512
|
-
backdrop-filter: blur(var(--glass-blur-3)) saturate(1.08);
|
|
3668
|
+
box-shadow: var(--shadow-lg), var(--liquid-top-highlight), var(--liquid-inner-shadow);
|
|
3513
3669
|
}
|
|
3514
3670
|
.newmark-select-shell.open .newmark-select-menu { display: block; animation: model-menu-in 150ms var(--ease-out-expo); }
|
|
3515
3671
|
.newmark-select-menu-group { padding: 7px 9px 4px; color: var(--text-accent); font: 700 11px var(--font-ui); }
|
|
@@ -3530,6 +3686,7 @@ button.todo-item { background: transparent; }
|
|
|
3530
3686
|
.newmark-select-option:hover,
|
|
3531
3687
|
.newmark-select-option:focus-visible { background: var(--control-hover-bg); color: var(--text-bright); outline: none; }
|
|
3532
3688
|
.newmark-select-option.selected { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--text-bright); }
|
|
3689
|
+
.newmark-select-option.liquid-selection-source { background: transparent !important; }
|
|
3533
3690
|
.newmark-select-option:disabled { opacity: .42; cursor: not-allowed; }
|
|
3534
3691
|
.setting-control > .newmark-select-shell,
|
|
3535
3692
|
.auto-input-group > .newmark-select-shell,
|
|
@@ -3668,9 +3825,11 @@ button.todo-item { background: transparent; }
|
|
|
3668
3825
|
.mode-toggle {
|
|
3669
3826
|
display: flex;
|
|
3670
3827
|
border: 1px solid var(--border);
|
|
3671
|
-
border-radius: var(--radius-
|
|
3672
|
-
overflow:
|
|
3828
|
+
border-radius: var(--radius-full);
|
|
3829
|
+
overflow: visible;
|
|
3673
3830
|
flex-shrink: 0;
|
|
3831
|
+
position: relative;
|
|
3832
|
+
padding: 2px;
|
|
3674
3833
|
}
|
|
3675
3834
|
|
|
3676
3835
|
.mode-toggle-btn {
|
|
@@ -3686,9 +3845,25 @@ button.todo-item { background: transparent; }
|
|
|
3686
3845
|
}
|
|
3687
3846
|
|
|
3688
3847
|
.mode-toggle-btn:hover { background: rgba(255,255,255,0.05); color: var(--text); }
|
|
3689
|
-
.mode-toggle-btn.active { background: rgba(91,120,255,0.15); color: var(--accent); }
|
|
3848
|
+
.mode-toggle-btn.active { background: rgba(91,120,255,0.15); color: var(--accent); border-radius: var(--radius-full); }
|
|
3690
3849
|
.mode-toggle-btn:active { transform: scale(0.95) translateZ(0); }
|
|
3691
3850
|
|
|
3851
|
+
.workspace-type-pager {
|
|
3852
|
+
display: grid;
|
|
3853
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
3854
|
+
position: relative;
|
|
3855
|
+
gap: 2px;
|
|
3856
|
+
padding: 3px;
|
|
3857
|
+
margin-bottom: 12px;
|
|
3858
|
+
border: 1px solid var(--border);
|
|
3859
|
+
border-radius: var(--radius-full);
|
|
3860
|
+
overflow: visible;
|
|
3861
|
+
background: var(--control-bg);
|
|
3862
|
+
}
|
|
3863
|
+
.workspace-type-option { display:flex; align-items:center; justify-content:center; min-width:0; padding:7px 10px; border-radius:var(--radius-full); color:var(--text-dim); font-size:12px; cursor:pointer; user-select:none; }
|
|
3864
|
+
.workspace-type-option.active { background:var(--control-hover-bg); color:var(--text-bright); }
|
|
3865
|
+
.workspace-type-option input { position:absolute; opacity:0; pointer-events:none; }
|
|
3866
|
+
|
|
3692
3867
|
#submit-btn {
|
|
3693
3868
|
width: 34px; height: 34px;
|
|
3694
3869
|
border-radius: 50%;
|
|
@@ -3809,12 +3984,12 @@ button.todo-item { background: transparent; }
|
|
|
3809
3984
|
linear-gradient(#fff 0 0) content-box,
|
|
3810
3985
|
linear-gradient(#fff 0 0);
|
|
3811
3986
|
mask-composite: exclude;
|
|
3812
|
-
/*
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
animation:
|
|
3987
|
+
/* Live marquee rotation. The @property-registered --marquee-angle animates
|
|
3988
|
+
the conic-gradient sweep; Chromium promotes the masked layer so the border
|
|
3989
|
+
stays composited. Release smoke tests verify the animation is active while
|
|
3990
|
+
ordinary text input remains responsive (see release-ui-render-performance
|
|
3991
|
+
and release-ui-icon smoke). */
|
|
3992
|
+
animation: marquee-rotate var(--marquee-speed) linear infinite;
|
|
3818
3993
|
z-index: -1;
|
|
3819
3994
|
pointer-events: none;
|
|
3820
3995
|
transform: translateZ(0);
|
|
@@ -3825,7 +4000,7 @@ button.todo-item { background: transparent; }
|
|
|
3825
4000
|
background: linear-gradient(var(--marquee-angle, 0deg), var(--g1), var(--g2), var(--g3), var(--g4), var(--g1));
|
|
3826
4001
|
}
|
|
3827
4002
|
@keyframes marquee-rotate-fallback { 0% { filter: hue-rotate(0deg); } 100% { filter: hue-rotate(360deg); } }
|
|
3828
|
-
.marquee-border::before { animation:
|
|
4003
|
+
.marquee-border::before { animation: marquee-rotate-fallback var(--marquee-speed) linear infinite; }
|
|
3829
4004
|
}
|
|
3830
4005
|
|
|
3831
4006
|
.working-glow {
|
|
@@ -4071,16 +4246,24 @@ button.todo-item { background: transparent; }
|
|
|
4071
4246
|
============================================================ */
|
|
4072
4247
|
.settings-tabs {
|
|
4073
4248
|
display: flex;
|
|
4249
|
+
position: relative;
|
|
4074
4250
|
gap: 4px;
|
|
4075
|
-
|
|
4076
|
-
|
|
4251
|
+
width: fit-content;
|
|
4252
|
+
max-width: 100%;
|
|
4253
|
+
padding: 4px;
|
|
4254
|
+
border: 1px solid var(--glass-border-1);
|
|
4255
|
+
border-radius: var(--radius-full);
|
|
4256
|
+
background: var(--glass-bg-1);
|
|
4077
4257
|
margin-bottom: 14px;
|
|
4258
|
+
overflow-x: auto;
|
|
4259
|
+
scrollbar-width: none;
|
|
4078
4260
|
}
|
|
4261
|
+
.settings-tabs::-webkit-scrollbar { display: none; }
|
|
4079
4262
|
|
|
4080
4263
|
.stab-btn {
|
|
4081
4264
|
padding: 6px 14px;
|
|
4082
4265
|
border: 1px solid var(--border);
|
|
4083
|
-
border-radius: var(--radius-
|
|
4266
|
+
border-radius: var(--radius-full);
|
|
4084
4267
|
background: transparent;
|
|
4085
4268
|
color: var(--text-dim);
|
|
4086
4269
|
font-size: 12px;
|
|
@@ -4391,6 +4574,42 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4391
4574
|
box-shadow: 0 0 4px rgba(0,0,0,0.3);
|
|
4392
4575
|
}
|
|
4393
4576
|
|
|
4577
|
+
#settings-glass-opacity {
|
|
4578
|
+
--liquid-range-progress: 85%;
|
|
4579
|
+
height: 6px;
|
|
4580
|
+
border-radius: var(--radius-full);
|
|
4581
|
+
background: linear-gradient(90deg, var(--accent) 0 var(--liquid-range-progress), var(--glass-border-2) var(--liquid-range-progress) 100%);
|
|
4582
|
+
cursor: pointer;
|
|
4583
|
+
}
|
|
4584
|
+
#settings-glass-opacity::-webkit-slider-thumb {
|
|
4585
|
+
width: 16px;
|
|
4586
|
+
height: 16px;
|
|
4587
|
+
border: 0;
|
|
4588
|
+
background: var(--accent);
|
|
4589
|
+
box-shadow: 0 2px 6px rgba(0,0,0,.3);
|
|
4590
|
+
transition: width 120ms var(--ease-out-expo), height 120ms var(--ease-out-expo), transform 120ms var(--ease-out-expo), box-shadow 120ms var(--ease-out-expo);
|
|
4591
|
+
}
|
|
4592
|
+
#settings-glass-opacity.liquid-range-dragging::-webkit-slider-thumb {
|
|
4593
|
+
width: 26px;
|
|
4594
|
+
height: 20px;
|
|
4595
|
+
border-radius: var(--radius-full);
|
|
4596
|
+
background: var(--liquid-refraction);
|
|
4597
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%);
|
|
4598
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%);
|
|
4599
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), 0 8px 20px rgba(0,0,0,.34);
|
|
4600
|
+
transform: translateY(-2px) scale(1.22);
|
|
4601
|
+
transition: none;
|
|
4602
|
+
}
|
|
4603
|
+
.settings-glass-range-line { display:flex; align-items:center; gap:10px; }
|
|
4604
|
+
.settings-glass-range-line #settings-glass-opacity { flex:1; min-width:0; }
|
|
4605
|
+
#settings-glass-value {
|
|
4606
|
+
min-width: 42px;
|
|
4607
|
+
color: var(--text-bright);
|
|
4608
|
+
font: 650 11px var(--font-mono);
|
|
4609
|
+
text-align: right;
|
|
4610
|
+
font-variant-numeric: tabular-nums;
|
|
4611
|
+
}
|
|
4612
|
+
|
|
4394
4613
|
.setting-desc {
|
|
4395
4614
|
font-size: 10px;
|
|
4396
4615
|
color: var(--text-dim);
|
|
@@ -4504,9 +4723,13 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4504
4723
|
}
|
|
4505
4724
|
|
|
4506
4725
|
[data-theme="light"] .sub-win {
|
|
4507
|
-
backdrop-filter: blur(calc(var(--glass-blur-3) *
|
|
4508
|
-
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) *
|
|
4509
|
-
border-color: rgba(
|
|
4726
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 2.2)) saturate(165%) brightness(1.01);
|
|
4727
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 2.2)) saturate(165%) brightness(1.01);
|
|
4728
|
+
border-color: rgba(54,68,106,0.14);
|
|
4729
|
+
box-shadow: var(--modal-shadow);
|
|
4730
|
+
}
|
|
4731
|
+
[data-theme="light"] .sub-win.liquid-glass-carrier {
|
|
4732
|
+
background: var(--settings-modal-surface) !important;
|
|
4510
4733
|
}
|
|
4511
4734
|
.memory-lab-search-results[hidden] {
|
|
4512
4735
|
display: none;
|
|
@@ -4521,7 +4744,7 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4521
4744
|
align-items: center;
|
|
4522
4745
|
gap: 4px;
|
|
4523
4746
|
padding: 3px;
|
|
4524
|
-
border-radius: var(--radius-
|
|
4747
|
+
border-radius: var(--radius-full);
|
|
4525
4748
|
border: 1px solid var(--glass-border-1);
|
|
4526
4749
|
background: var(--glass-bg-1);
|
|
4527
4750
|
}
|
|
@@ -4572,7 +4795,7 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4572
4795
|
.memory-lab-overview-actions select {
|
|
4573
4796
|
height: 26px;
|
|
4574
4797
|
padding: 0 9px;
|
|
4575
|
-
border-radius: var(--radius-
|
|
4798
|
+
border-radius: var(--radius-full);
|
|
4576
4799
|
border: 1px solid transparent;
|
|
4577
4800
|
background: transparent;
|
|
4578
4801
|
color: var(--text-dim);
|
|
@@ -5400,6 +5623,202 @@ html.newmark-memory-overview-viewer body {
|
|
|
5400
5623
|
opacity: 0.8;
|
|
5401
5624
|
}
|
|
5402
5625
|
|
|
5626
|
+
|
|
5627
|
+
/* ============================================================
|
|
5628
|
+
LIQUID GLASS (Kyant AndroidLiquidGlass backdrop parity)
|
|
5629
|
+
CSS equivalent of the library lens/edge refraction, top
|
|
5630
|
+
highlight, inner shadow, outer shadow and vibrancy boost.
|
|
5631
|
+
============================================================ */
|
|
5632
|
+
.liquid-glass {
|
|
5633
|
+
position: relative;
|
|
5634
|
+
backdrop-filter: blur(var(--glass-blur-3)) saturate(140%) brightness(1.02);
|
|
5635
|
+
-webkit-backdrop-filter: blur(var(--glass-blur-3)) saturate(140%) brightness(1.02);
|
|
5636
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), var(--liquid-outer-shadow);
|
|
5637
|
+
}
|
|
5638
|
+
.liquid-glass::before {
|
|
5639
|
+
content: '';
|
|
5640
|
+
position: absolute;
|
|
5641
|
+
inset: 0;
|
|
5642
|
+
pointer-events: none;
|
|
5643
|
+
border-radius: inherit;
|
|
5644
|
+
background: var(--liquid-refraction);
|
|
5645
|
+
opacity: 0.9;
|
|
5646
|
+
mix-blend-mode: screen;
|
|
5647
|
+
}
|
|
5648
|
+
.liquid-glass::after {
|
|
5649
|
+
content: '';
|
|
5650
|
+
position: absolute;
|
|
5651
|
+
inset: 0;
|
|
5652
|
+
pointer-events: none;
|
|
5653
|
+
border-radius: inherit;
|
|
5654
|
+
border: 1px solid var(--liquid-edge-glow);
|
|
5655
|
+
box-shadow: inset 0 0 0 0.5px var(--liquid-edge-glow-strong);
|
|
5656
|
+
}
|
|
5657
|
+
.liquid-glass-strong {
|
|
5658
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 1.5)) saturate(150%) brightness(1.04);
|
|
5659
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 1.5)) saturate(150%) brightness(1.04);
|
|
5660
|
+
}
|
|
5661
|
+
[data-theme='light'] .liquid-glass {
|
|
5662
|
+
backdrop-filter: none;
|
|
5663
|
+
-webkit-backdrop-filter: none;
|
|
5664
|
+
}
|
|
5665
|
+
[data-theme='light'] .liquid-glass::before {
|
|
5666
|
+
opacity: 0.45;
|
|
5667
|
+
mix-blend-mode: normal;
|
|
5668
|
+
}
|
|
5669
|
+
.liquid-glass-carrier {
|
|
5670
|
+
background: rgb(var(--glass-rgb-2) / var(--carrier-glass-alpha)) !important;
|
|
5671
|
+
backdrop-filter: blur(var(--carrier-glass-blur)) saturate(150%) brightness(1.02) !important;
|
|
5672
|
+
-webkit-backdrop-filter: blur(var(--carrier-glass-blur)) saturate(150%) brightness(1.02) !important;
|
|
5673
|
+
}
|
|
5674
|
+
/* List popups use the live carrier refraction only. The old static outer
|
|
5675
|
+
glow/shadow painted a second fixed background behind the popup. */
|
|
5676
|
+
.conv-action-menu.liquid-glass,
|
|
5677
|
+
.model-select-menu.liquid-glass,
|
|
5678
|
+
.newmark-select-menu.liquid-glass {
|
|
5679
|
+
box-shadow: none;
|
|
5680
|
+
}
|
|
5681
|
+
.conv-action-menu.liquid-glass::before,
|
|
5682
|
+
.model-select-menu.liquid-glass::before,
|
|
5683
|
+
.newmark-select-menu.liquid-glass::before {
|
|
5684
|
+
background: none;
|
|
5685
|
+
opacity: 0;
|
|
5686
|
+
mix-blend-mode: normal;
|
|
5687
|
+
}
|
|
5688
|
+
.conv-action-menu.liquid-glass::after,
|
|
5689
|
+
.model-select-menu.liquid-glass::after,
|
|
5690
|
+
.newmark-select-menu.liquid-glass::after {
|
|
5691
|
+
box-shadow: none;
|
|
5692
|
+
}
|
|
5693
|
+
.liquid-selection-canvas { opacity: var(--liquid-float-optical-opacity); }
|
|
5694
|
+
|
|
5695
|
+
/* Buttons retain their component-owned static surface. The Kyant-like glass
|
|
5696
|
+
layer exists only during press and falls away with the color block. */
|
|
5697
|
+
button {
|
|
5698
|
+
position: relative;
|
|
5699
|
+
isolation: isolate;
|
|
5700
|
+
transition-property: scale, filter, border-color, background-color, box-shadow, color;
|
|
5701
|
+
transition-duration: 120ms;
|
|
5702
|
+
transition-timing-function: var(--ease-out-expo);
|
|
5703
|
+
}
|
|
5704
|
+
[data-theme="light"] .conv-action-menu,
|
|
5705
|
+
[data-theme="light"] .model-select-menu,
|
|
5706
|
+
[data-theme="light"] .newmark-select-menu,
|
|
5707
|
+
[data-theme="light"] .sub-win {
|
|
5708
|
+
background-color: transparent;
|
|
5709
|
+
}
|
|
5710
|
+
button::after {
|
|
5711
|
+
content: '';
|
|
5712
|
+
position: absolute;
|
|
5713
|
+
inset: -2px;
|
|
5714
|
+
z-index: -1;
|
|
5715
|
+
pointer-events: none;
|
|
5716
|
+
border-radius: inherit;
|
|
5717
|
+
opacity: 0;
|
|
5718
|
+
background: var(--liquid-refraction);
|
|
5719
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%) brightness(1.04);
|
|
5720
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%) brightness(1.04);
|
|
5721
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), 0 4px 14px rgba(0,0,0,0.18);
|
|
5722
|
+
transition: opacity 90ms var(--ease-out-expo);
|
|
5723
|
+
}
|
|
5724
|
+
button:hover:not(:disabled) {
|
|
5725
|
+
filter: brightness(1.06);
|
|
5726
|
+
}
|
|
5727
|
+
button:active:not(:disabled) {
|
|
5728
|
+
scale: 1.03;
|
|
5729
|
+
filter: brightness(1.04);
|
|
5730
|
+
background-color: transparent !important;
|
|
5731
|
+
box-shadow: none;
|
|
5732
|
+
}
|
|
5733
|
+
button:active:not(:disabled)::after { opacity: 1; }
|
|
5734
|
+
button:disabled { opacity: 0.5; }
|
|
5735
|
+
|
|
5736
|
+
/* Borderless utility actions stay visually quiet; they never gain a glass plate. */
|
|
5737
|
+
.msg-action-btn::after,
|
|
5738
|
+
.top-btn::after,
|
|
5739
|
+
.conv-more-btn::after,
|
|
5740
|
+
.provider-icon-btn::after,
|
|
5741
|
+
.work-review-toggle::after,
|
|
5742
|
+
button.conversation-work-run-head::after,
|
|
5743
|
+
button.shell-block-header::after,
|
|
5744
|
+
button.diff-block-header::after,
|
|
5745
|
+
button.work-review-head::after { display: none !important; }
|
|
5746
|
+
button.conversation-work-run-head:active:not(:disabled),
|
|
5747
|
+
button.shell-block-header:active:not(:disabled),
|
|
5748
|
+
button.diff-block-header:active:not(:disabled),
|
|
5749
|
+
button.work-review-head:active:not(:disabled) {
|
|
5750
|
+
scale: 1;
|
|
5751
|
+
filter: none;
|
|
5752
|
+
background-color: revert !important;
|
|
5753
|
+
}
|
|
5754
|
+
|
|
5755
|
+
/* Two-position liquid track. The thumb becomes glass only while held. */
|
|
5756
|
+
input[type="checkbox"] {
|
|
5757
|
+
--liquid-switch-x: 0px;
|
|
5758
|
+
--liquid-switch-progress: 0%;
|
|
5759
|
+
appearance: none;
|
|
5760
|
+
-webkit-appearance: none;
|
|
5761
|
+
width: 42px !important;
|
|
5762
|
+
height: 24px !important;
|
|
5763
|
+
min-width: 42px;
|
|
5764
|
+
margin: 0;
|
|
5765
|
+
padding: 0;
|
|
5766
|
+
border: 1px solid var(--glass-border-2);
|
|
5767
|
+
border-radius: var(--radius-full);
|
|
5768
|
+
background: linear-gradient(90deg, var(--accent) 0 var(--liquid-switch-progress), var(--glass-bg-3) var(--liquid-switch-progress) 100%);
|
|
5769
|
+
cursor: pointer;
|
|
5770
|
+
position: relative;
|
|
5771
|
+
vertical-align: middle;
|
|
5772
|
+
transition: background 160ms var(--ease-out-expo), border-color 160ms var(--ease-out-expo);
|
|
5773
|
+
}
|
|
5774
|
+
input[type="checkbox"]:checked {
|
|
5775
|
+
--liquid-switch-x: 18px;
|
|
5776
|
+
--liquid-switch-progress: 100%;
|
|
5777
|
+
border-color: color-mix(in srgb, var(--accent) 72%, white);
|
|
5778
|
+
}
|
|
5779
|
+
input[type="checkbox"]::before {
|
|
5780
|
+
content: '';
|
|
5781
|
+
position: absolute;
|
|
5782
|
+
left: 2px;
|
|
5783
|
+
top: 2px;
|
|
5784
|
+
width: 18px;
|
|
5785
|
+
height: 18px;
|
|
5786
|
+
border-radius: 50%;
|
|
5787
|
+
background: var(--text-bright);
|
|
5788
|
+
box-shadow: 0 2px 5px rgba(0,0,0,.28);
|
|
5789
|
+
transform: translateX(var(--liquid-switch-x)) scale(1);
|
|
5790
|
+
transition: transform 160ms var(--ease-out-expo), box-shadow 100ms var(--ease-out-expo), background 100ms var(--ease-out-expo);
|
|
5791
|
+
}
|
|
5792
|
+
input[type="checkbox"].liquid-switch-dragging::before {
|
|
5793
|
+
left: 1px;
|
|
5794
|
+
width: 22px;
|
|
5795
|
+
border-radius: var(--radius-full);
|
|
5796
|
+
background: var(--liquid-refraction);
|
|
5797
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%);
|
|
5798
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%);
|
|
5799
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), 0 7px 18px rgba(0,0,0,.32);
|
|
5800
|
+
transform: translateX(var(--liquid-switch-x)) translateY(-2px) scale(1.22);
|
|
5801
|
+
transition: none;
|
|
5802
|
+
}
|
|
5803
|
+
|
|
5804
|
+
/* Center composer and terminal stay solid. Only their left/right edge catches
|
|
5805
|
+
light so the glass sidebars remain the visual frame. */
|
|
5806
|
+
#input-area,
|
|
5807
|
+
#bottom {
|
|
5808
|
+
backdrop-filter: none;
|
|
5809
|
+
-webkit-backdrop-filter: none;
|
|
5810
|
+
box-shadow: inset 1px 0 0 var(--liquid-edge-glow), inset -1px 0 0 var(--liquid-edge-glow);
|
|
5811
|
+
}
|
|
5812
|
+
|
|
5813
|
+
/* The live indicator is strictly a continuous black/white/black/white border.
|
|
5814
|
+
Keep it above generic glass pseudo-elements when classes are combined. */
|
|
5815
|
+
.marquee-border::before,
|
|
5816
|
+
.liquid-glass.marquee-border::before {
|
|
5817
|
+
background: conic-gradient(from var(--marquee-angle, 0deg), #000 0deg, #fff 90deg, #000 180deg, #fff 270deg, #000 360deg);
|
|
5818
|
+
opacity: 1;
|
|
5819
|
+
mix-blend-mode: normal;
|
|
5820
|
+
animation: marquee-rotate var(--marquee-speed) linear infinite;
|
|
5821
|
+
}
|
|
5403
5822
|
</style>
|
|
5404
5823
|
</head>
|
|
5405
5824
|
<body>
|
|
@@ -5625,6 +6044,10 @@ html.newmark-memory-overview-viewer body {
|
|
|
5625
6044
|
<symbol id="minus" viewBox="0 0 24 24">
|
|
5626
6045
|
<path d="M5 12h14" />
|
|
5627
6046
|
</symbol>
|
|
6047
|
+
<symbol id="mouse" viewBox="0 0 24 24">
|
|
6048
|
+
<rect x="5" y="2" width="14" height="20" rx="7" />
|
|
6049
|
+
<path d="M12 6v4" />
|
|
6050
|
+
</symbol>
|
|
5628
6051
|
<symbol id="move" viewBox="0 0 24 24">
|
|
5629
6052
|
<path d="M12 2v20" />
|
|
5630
6053
|
<path d="m15 19-3 3-3-3" />
|
|
@@ -5875,6 +6298,7 @@ html.newmark-memory-overview-viewer body {
|
|
|
5875
6298
|
<button type="button" class="lt-item" id="lt-ws-icon" onclick="window.toggleSecondarySidebar()" title="Workspaces"><svg class="nm-icon small" aria-hidden="true" focusable="false"><use href="#folder-open"></use></svg><span class="icon-only-label">Workspaces</span></button>
|
|
5876
6299
|
</div>
|
|
5877
6300
|
<div id="left-content">
|
|
6301
|
+
<div id="left-tool-surface">
|
|
5878
6302
|
<div style="padding:10px 12px 4px;font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:0.05em;flex-shrink:0;">Tools</div>
|
|
5879
6303
|
<button type="button" class="left-nav-icon" onclick="window.showNewConversationPage()" title="New conversation">
|
|
5880
6304
|
<span class="icon"><svg class="nm-icon small" aria-hidden="true" focusable="false"><use href="#message-square"></use></svg></span><span>New chat</span>
|
|
@@ -5894,6 +6318,7 @@ html.newmark-memory-overview-viewer body {
|
|
|
5894
6318
|
<button type="button" class="left-nav-icon" onclick="window.openSettings()">
|
|
5895
6319
|
<span class="icon"><svg class="nm-icon small" aria-hidden="true" focusable="false"><use href="#settings"></use></svg></span><span>Settings</span>
|
|
5896
6320
|
</button>
|
|
6321
|
+
</div>
|
|
5897
6322
|
<div id="left-ws-section">
|
|
5898
6323
|
<div id="left-ws-header">Workspaces</div>
|
|
5899
6324
|
<div id="left-ws-list" role="list" aria-label="Workspaces"></div>
|
|
@@ -5993,9 +6418,9 @@ html.newmark-memory-overview-viewer body {
|
|
|
5993
6418
|
<div class="model-select-shell" id="model-select-shell">
|
|
5994
6419
|
<select class="model-select-native" id="model-select" tabindex="-1" aria-hidden="true"></select>
|
|
5995
6420
|
<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>
|
|
5996
|
-
<div class="model-select-menu" id="model-select-menu" role="listbox" aria-label="Model" popover="manual"></div>
|
|
6421
|
+
<div class="model-select-menu liquid-glass liquid-glass-carrier" id="model-select-menu" role="listbox" aria-label="Model" popover="manual"></div>
|
|
5997
6422
|
</div>
|
|
5998
|
-
<div class="conv-action-menu" id="conversation-action-menu" role="menu" aria-label="Conversation actions" popover="manual"></div>
|
|
6423
|
+
<div class="conv-action-menu liquid-glass liquid-glass-carrier" id="conversation-action-menu" role="menu" aria-label="Conversation actions" popover="manual"></div>
|
|
5999
6424
|
<div class="context-token-wrap">
|
|
6000
6425
|
<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>
|
|
6001
6426
|
<div id="context-token-tooltip"></div>
|
|
@@ -6113,7 +6538,7 @@ html.newmark-memory-overview-viewer body {
|
|
|
6113
6538
|
|
|
6114
6539
|
<!-- ========== SUB-WINDOW OVERLAY ========== -->
|
|
6115
6540
|
<div class="sub-win-overlay" id="sub-win-overlay" onclick="if(event.target===this)window.closeSubWin()">
|
|
6116
|
-
<div class="sub-win" id="sub-win" role="dialog" aria-modal="true" aria-labelledby="sub-win-title" tabindex="-1">
|
|
6541
|
+
<div class="sub-win liquid-glass liquid-glass-carrier" id="sub-win" role="dialog" aria-modal="true" aria-labelledby="sub-win-title" tabindex="-1">
|
|
6117
6542
|
<div class="sub-win-header" id="sub-win-header">
|
|
6118
6543
|
<span class="sub-win-title" id="sub-win-title">Window</span>
|
|
6119
6544
|
<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>
|
|
@@ -6292,6 +6717,7 @@ var state = {
|
|
|
6292
6717
|
activeSendCallsByTarget: {},
|
|
6293
6718
|
backendQueue: { steering: [], followUp: [] },
|
|
6294
6719
|
backendQueuesByTarget: {},
|
|
6720
|
+
queueItemsByTarget: {},
|
|
6295
6721
|
defaultFlow: '',
|
|
6296
6722
|
settingsActiveTab: 'general',
|
|
6297
6723
|
globalAgentPrompt: '',
|
|
@@ -7077,6 +7503,7 @@ var NEWMARK_I18N = {
|
|
|
7077
7503
|
'conversation.branchCommunicationBadge': 'Branch communication',
|
|
7078
7504
|
'conversation.loadingIsolated': 'Loading isolated conversation...',
|
|
7079
7505
|
'conversation.locked': 'Current conversation is locked while the agent is working.',
|
|
7506
|
+
'conversation.emptyWorkspace': 'No conversations in this workspace yet.',
|
|
7080
7507
|
'queue.next': 'Next',
|
|
7081
7508
|
'terminal.notConnected': 'Terminal not connected',
|
|
7082
7509
|
'common.error': 'Error',
|
|
@@ -7808,6 +8235,7 @@ var NEWMARK_I18N = {
|
|
|
7808
8235
|
'conversation.branchCommunicationBadge': '分支交流',
|
|
7809
8236
|
'conversation.loadingIsolated': '正在加载隔离对话...',
|
|
7810
8237
|
'conversation.locked': 'Agent 工作中,当前对话已锁定。',
|
|
8238
|
+
'conversation.emptyWorkspace': '当前工作区还没有对话。',
|
|
7811
8239
|
'queue.next': '下一轮',
|
|
7812
8240
|
'terminal.notConnected': '终端未连接',
|
|
7813
8241
|
'common.error': '错误',
|
|
@@ -7920,9 +8348,9 @@ function applyLanguageToUi() {
|
|
|
7920
8348
|
var nextBtn = document.querySelector('.mode-toggle-btn[data-mode="next"]');
|
|
7921
8349
|
if (guideBtn) guideBtn.textContent = t('input.guide');
|
|
7922
8350
|
if (nextBtn) nextBtn.textContent = t('input.next');
|
|
7923
|
-
var toolsHead = document.querySelector('#left-
|
|
8351
|
+
var toolsHead = document.querySelector('#left-tool-surface > div:first-child');
|
|
7924
8352
|
if (toolsHead) toolsHead.textContent = t('left.tools');
|
|
7925
|
-
var navs = document.querySelectorAll('#left-
|
|
8353
|
+
var navs = document.querySelectorAll('#left-tool-surface .left-nav-icon');
|
|
7926
8354
|
var navLabels = [t('left.newChat'), t('left.plugins'), t('left.memoryLab'), t('left.automation'), t('left.flow'), t('left.settings')];
|
|
7927
8355
|
for (var i = 0; i < navs.length && i < navLabels.length; i++) {
|
|
7928
8356
|
var span = navs[i].querySelector('span:last-child');
|
|
@@ -8395,6 +8823,30 @@ function syncTerminalShellOptions() {
|
|
|
8395
8823
|
|
|
8396
8824
|
// === API Bridge ===
|
|
8397
8825
|
var api = window.api || {
|
|
8826
|
+
queueAction: async function(action, input) {
|
|
8827
|
+
try {
|
|
8828
|
+
var target = normalizeQueuedConversationTarget(currentConversationTarget());
|
|
8829
|
+
if (!target) return { ok: false, error: 'No active conversation target' };
|
|
8830
|
+
var body = {
|
|
8831
|
+
workspaceId: target.workspaceId,
|
|
8832
|
+
conversationId: target.conversationId,
|
|
8833
|
+
action: action,
|
|
8834
|
+
id: String(input && input.id || ''),
|
|
8835
|
+
text: String(input && input.text || ''),
|
|
8836
|
+
value: String(input && input.text || ''),
|
|
8837
|
+
requestedMode: String(input && input.requestedMode || 'build'),
|
|
8838
|
+
goalObjective: String(input && input.goalObjective || ''),
|
|
8839
|
+
createdAt: String(input && input.createdAt || ''),
|
|
8840
|
+
};
|
|
8841
|
+
if (Array.isArray(input && input.orderedIds)) body.orderedIds = input.orderedIds;
|
|
8842
|
+
var r = await fetch('/api/queue-action', {
|
|
8843
|
+
method: 'POST',
|
|
8844
|
+
headers: { 'Content-Type': 'application/json' },
|
|
8845
|
+
body: JSON.stringify(body)
|
|
8846
|
+
});
|
|
8847
|
+
return r.json();
|
|
8848
|
+
} catch(e) { return { ok: false, error: e.message }; }
|
|
8849
|
+
},
|
|
8398
8850
|
sendMessage: async function(msg, conversationId) {
|
|
8399
8851
|
try {
|
|
8400
8852
|
var body = {message: msg, mode: state.mode, model: state.model, intelligence: state.intelligence, inputMode: state.inputMode};
|
|
@@ -9316,24 +9768,84 @@ window.openWorkReviewFile = async function(path) {
|
|
|
9316
9768
|
return true;
|
|
9317
9769
|
};
|
|
9318
9770
|
|
|
9319
|
-
function
|
|
9771
|
+
function workReviewRunIds(runId, aliases) {
|
|
9772
|
+
var ids = [];
|
|
9773
|
+
[runId].concat(Array.isArray(aliases) ? aliases : []).forEach(function(value) {
|
|
9774
|
+
var id = String(value || '');
|
|
9775
|
+
if (id && ids.indexOf(id) < 0) ids.push(id);
|
|
9776
|
+
});
|
|
9777
|
+
return ids;
|
|
9778
|
+
}
|
|
9779
|
+
|
|
9780
|
+
function upsertWorkReviewRecord(ui, diffs, runId, aliases) {
|
|
9781
|
+
if (!ui) return null;
|
|
9782
|
+
if (!Array.isArray(ui.workReviews)) ui.workReviews = [];
|
|
9783
|
+
var ids = workReviewRunIds(runId, aliases);
|
|
9784
|
+
var canonicalRunId = String(runId || ids[0] || '');
|
|
9785
|
+
var record = { diffs: diffs, runId: canonicalRunId };
|
|
9786
|
+
var matchIndex = -1;
|
|
9787
|
+
for (var index = 0; index < ui.workReviews.length; index++) {
|
|
9788
|
+
var itemRunId = String(ui.workReviews[index] && ui.workReviews[index].runId || '');
|
|
9789
|
+
if (ids.indexOf(itemRunId) < 0) continue;
|
|
9790
|
+
if (matchIndex < 0) {
|
|
9791
|
+
matchIndex = index;
|
|
9792
|
+
ui.workReviews[index] = record;
|
|
9793
|
+
} else {
|
|
9794
|
+
ui.workReviews.splice(index, 1);
|
|
9795
|
+
index--;
|
|
9796
|
+
}
|
|
9797
|
+
}
|
|
9798
|
+
if (matchIndex < 0) ui.workReviews.push(record);
|
|
9799
|
+
return record;
|
|
9800
|
+
}
|
|
9801
|
+
|
|
9802
|
+
function addWorkReview(diffs, runId, aliases) {
|
|
9320
9803
|
var files = normalizeWorkReviewDiffs(diffs);
|
|
9321
9804
|
if (!files.length) return null;
|
|
9805
|
+
var filesKey = JSON.stringify(files);
|
|
9806
|
+
var reviewRunId = String(runId || '');
|
|
9807
|
+
var reviewRunIds = workReviewRunIds(reviewRunId, aliases);
|
|
9808
|
+
var chatArea = els['chat-area'];
|
|
9809
|
+
var review = null;
|
|
9810
|
+
if (chatArea) {
|
|
9811
|
+
var existingReviews = chatArea.querySelectorAll('.work-review');
|
|
9812
|
+
for (var ri = 0; ri < existingReviews.length; ri++) {
|
|
9813
|
+
var sameFiles = existingReviews[ri].getAttribute('data-files') === filesKey;
|
|
9814
|
+
var existingRunId = String(existingReviews[ri].getAttribute('data-run-id') || '');
|
|
9815
|
+
var sameRun = reviewRunIds.length ? reviewRunIds.indexOf(existingRunId) >= 0 : sameFiles && !existingRunId;
|
|
9816
|
+
if (!sameRun) continue;
|
|
9817
|
+
if (!review) review = existingReviews[ri];
|
|
9818
|
+
else existingReviews[ri].remove();
|
|
9819
|
+
}
|
|
9820
|
+
}
|
|
9322
9821
|
var added = files.reduce(function(total, file) { return total + file.added; }, 0);
|
|
9323
9822
|
var deleted = files.reduce(function(total, file) { return total + file.deleted; }, 0);
|
|
9324
|
-
var
|
|
9325
|
-
|
|
9823
|
+
var wasCollapsed = review ? review.classList.contains('collapsed') : true;
|
|
9824
|
+
var wasExpanded = review ? review.classList.contains('expanded') : false;
|
|
9825
|
+
if (!review) review = document.createElement('div');
|
|
9826
|
+
review.className = 'work-review' + (wasCollapsed ? ' collapsed' : '') + (wasExpanded ? ' expanded' : '');
|
|
9326
9827
|
review.setAttribute('data-files', JSON.stringify(files));
|
|
9828
|
+
if (reviewRunId) review.setAttribute('data-run-id', reviewRunId);
|
|
9829
|
+
else review.removeAttribute('data-run-id');
|
|
9327
9830
|
var rows = files.map(function(file, index) {
|
|
9328
|
-
return '<div class="work-review-file"' + (index >= 3 ? ' style="display:none"' : '') + '><span class="work-review-path">' + esc(file.path) + '</span><span><span class="work-review-add">+' + file.added + '</span><span class="work-review-del">-' + file.deleted + '</span></span></div>';
|
|
9831
|
+
return '<div class="work-review-file"' + (!wasExpanded && index >= 3 ? ' style="display:none"' : '') + '><span class="work-review-path">' + esc(file.path) + '</span><span><span class="work-review-add">+' + file.added + '</span><span class="work-review-del">-' + file.deleted + '</span></span></div>';
|
|
9329
9832
|
}).join('');
|
|
9330
9833
|
var editedLabel = files.length === 1 ? t('review.editedOne') : t('review.editedMany').replace('{count}', files.length);
|
|
9331
9834
|
review.innerHTML = '<div class="work-review-head" onclick="window.toggleWorkReview(this)"><div class="work-review-mark">' + iconSvg('file-diff', t('review.fileChanges'), 'small') + '</div>' +
|
|
9332
9835
|
'<div><div class="work-review-title">' + esc(editedLabel) + '</div><div class="work-review-stats"><span class="work-review-add">+' + added + '</span><span class="work-review-del">-' + deleted + '</span></div></div>' +
|
|
9333
9836
|
'<div class="work-review-actions"><button class="work-review-btn" onclick="window.openWorkReview(this);event.stopPropagation()">' + esc(t('review.open')) + '</button>' +
|
|
9334
|
-
'<button type="button" class="work-review-toggle" aria-expanded="false" onclick="event.stopPropagation();window.toggleWorkReview(this)" title="' + escAttr(t('queue.expand')) + '"><span class="work-review-chevron" aria-hidden="true"></span><span class="icon-only-label">' + esc(t('queue.expand')) + '</span></button></div></div>' +
|
|
9335
|
-
'<div class="work-review-list">' + rows + (files.length > 3 ? '<button class="work-review-more" onclick="window.toggleWorkReviewFiles(this)">' + esc(t('review.showMore').replace('{count}', files.length - 3)) + '</button>' : '') + '</div>';
|
|
9336
|
-
|
|
9837
|
+
'<button type="button" class="work-review-toggle" aria-expanded="' + (wasCollapsed ? 'false' : 'true') + '" onclick="event.stopPropagation();window.toggleWorkReview(this)" title="' + escAttr(t('queue.expand')) + '"><span class="work-review-chevron" aria-hidden="true"></span><span class="icon-only-label">' + esc(t('queue.expand')) + '</span></button></div></div>' +
|
|
9838
|
+
'<div class="work-review-list">' + rows + (files.length > 3 ? '<button class="work-review-more" onclick="window.toggleWorkReviewFiles(this)">' + esc(wasExpanded ? t('queue.collapse') : t('review.showMore').replace('{count}', files.length - 3)) + '</button>' : '') + '</div>';
|
|
9839
|
+
var owningRun = null;
|
|
9840
|
+
var renderedRuns = reviewRunId ? els['chat-area'].querySelectorAll('.conversation-work-run[data-run-id]') : [];
|
|
9841
|
+
for (var wi = 0; wi < renderedRuns.length; wi++) {
|
|
9842
|
+
if (renderedRuns[wi].getAttribute('data-run-id') === reviewRunId) { owningRun = renderedRuns[wi]; break; }
|
|
9843
|
+
}
|
|
9844
|
+
var owningMessage = owningRun && owningRun.closest ? owningRun.closest('.work-run-message') : null;
|
|
9845
|
+
if (!review.parentNode) {
|
|
9846
|
+
if (owningMessage && owningMessage.parentNode === els['chat-area']) owningMessage.insertAdjacentElement('afterend', review);
|
|
9847
|
+
else els['chat-area'].appendChild(review);
|
|
9848
|
+
}
|
|
9337
9849
|
autoScrollIfAtBottom();
|
|
9338
9850
|
return review;
|
|
9339
9851
|
}
|
|
@@ -9701,12 +10213,25 @@ function renderPendingGuideMessages(target, persistedGuideIds) {
|
|
|
9701
10213
|
persistedGuideIds = persistedGuideIds || {};
|
|
9702
10214
|
var messages = guideMessagesForTarget(target);
|
|
9703
10215
|
var pending = Object.keys(messages).map(function(id) { return messages[id]; }).filter(function(item) {
|
|
9704
|
-
|
|
10216
|
+
if (!item || persistedGuideIds[item.clientMessageId]) return false;
|
|
10217
|
+
// A Guide already owned by its work run is rendered by the Build block
|
|
10218
|
+
// (expanded events or collapsed guide list), never as a separate trailing
|
|
10219
|
+
// message. Without this guard, collapse/expand rebuilds would re-add the
|
|
10220
|
+
// applied Guide at the end of the chat.
|
|
10221
|
+
if (workRunOwnsGuide(item.runId, item.clientMessageId, target)) return false;
|
|
10222
|
+
return true;
|
|
9705
10223
|
}).sort(function(a, b) {
|
|
9706
10224
|
return String(a.createdAt || '').localeCompare(String(b.createdAt || ''));
|
|
9707
10225
|
});
|
|
9708
10226
|
pending.forEach(function(item) {
|
|
9709
10227
|
var element = findGuideMessageElement(item.clientMessageId);
|
|
10228
|
+
if (element && !item.awaitingAck && item.runId && workRunOwnsGuide(item.runId, item.clientMessageId, target)) {
|
|
10229
|
+
// The Guide is owned by its work run but a stale standalone copy exists
|
|
10230
|
+
// (e.g. created before this guard). Remove it so the Build block stays
|
|
10231
|
+
// the single source of truth.
|
|
10232
|
+
element.remove();
|
|
10233
|
+
element = null;
|
|
10234
|
+
}
|
|
9710
10235
|
if (item.awaitingAck && item.runId) {
|
|
9711
10236
|
var runElement = findWorkRunElement({ runId: item.runId });
|
|
9712
10237
|
var runBody = runElement && runElement.querySelector('.conversation-work-run-body');
|
|
@@ -9738,6 +10263,22 @@ function renderPendingGuideMessages(target, persistedGuideIds) {
|
|
|
9738
10263
|
return pending.length;
|
|
9739
10264
|
}
|
|
9740
10265
|
|
|
10266
|
+
function workRunOwnsGuide(runId, clientMessageId, target) {
|
|
10267
|
+
if (!runId || !clientMessageId) return false;
|
|
10268
|
+
var runs = workRunsForTarget(target || currentConversationTarget());
|
|
10269
|
+
var run = runs.find(function(item) { return String(item.runId || '') === String(runId); });
|
|
10270
|
+
if (!run) return false;
|
|
10271
|
+
function matches(guide) {
|
|
10272
|
+
return guide && String(guide.clientMessageId || '') === String(clientMessageId);
|
|
10273
|
+
}
|
|
10274
|
+
if ((run.guides || []).some(matches)) return true;
|
|
10275
|
+
return (run.events || []).some(function(event) {
|
|
10276
|
+
if (!event) return false;
|
|
10277
|
+
if (matches(event.guide)) return true;
|
|
10278
|
+
return !!(event.clientMessageId && String(event.clientMessageId) === String(clientMessageId));
|
|
10279
|
+
});
|
|
10280
|
+
}
|
|
10281
|
+
|
|
9741
10282
|
function eventRuntimeKey(event) {
|
|
9742
10283
|
var target = {
|
|
9743
10284
|
workspaceId: event && (event.workspaceId || (event.target && event.target.workspaceId)),
|
|
@@ -10508,23 +11049,29 @@ function workRunGuideEvents(run) {
|
|
|
10508
11049
|
});
|
|
10509
11050
|
}
|
|
10510
11051
|
|
|
10511
|
-
function
|
|
11052
|
+
function presentedWorkRunEvents(run, includeGuides) {
|
|
10512
11053
|
var rawEvents = (run.events || []).filter(publicWorkEvent).filter(function(event) {
|
|
10513
11054
|
return includeGuides !== false || !(String(event && event.type || '').toLowerCase().indexOf('guide') === 0 || !!(event && event.guide));
|
|
10514
11055
|
}).sort(compareConversationWorkEvents);
|
|
10515
11056
|
var terminalInterrupted = ['interrupted', 'force_interrupted'].indexOf(String(run && run.status || '')) >= 0;
|
|
10516
11057
|
var events = [];
|
|
10517
11058
|
var publicText = '';
|
|
11059
|
+
// 公共文本段继承其最后一条 text 事件的 sequence:增量渲染依赖 presented
|
|
11060
|
+
// 顺序的单调性,无 sequence 的文本段会被排到列表前部,导致每次文本增量
|
|
11061
|
+
// 都从前部重建后续节点。
|
|
11062
|
+
var publicTextSequence = 0;
|
|
10518
11063
|
function flushPublicText() {
|
|
10519
11064
|
var content = publicText.trim().slice(0, 50000);
|
|
10520
11065
|
publicText = '';
|
|
10521
|
-
if (content) events.push({ type: terminalInterrupted ? 'partial_text' : 'public_text', content: content });
|
|
11066
|
+
if (content) events.push({ type: terminalInterrupted ? 'partial_text' : 'public_text', content: content, sequence: publicTextSequence });
|
|
11067
|
+
publicTextSequence = 0;
|
|
10522
11068
|
}
|
|
10523
11069
|
for (var eventIndex = 0; eventIndex < rawEvents.length; eventIndex++) {
|
|
10524
11070
|
var rawEvent = rawEvents[eventIndex];
|
|
10525
11071
|
var rawType = String(rawEvent && rawEvent.type || '').toLowerCase();
|
|
10526
11072
|
if (rawType === 'text') {
|
|
10527
11073
|
publicText += String(rawEvent.content || '');
|
|
11074
|
+
publicTextSequence = Math.max(publicTextSequence, Number(rawEvent.sequence || 0));
|
|
10528
11075
|
continue;
|
|
10529
11076
|
}
|
|
10530
11077
|
if (rawType === 'response') {
|
|
@@ -10596,46 +11143,120 @@ function renderWorkRunEvents(run, includeGuides) {
|
|
|
10596
11143
|
}
|
|
10597
11144
|
presentedEvents.push({ type: 'tool_group', count: count, completed: allCompleted, toolName: presented.toolName, items: activityItems });
|
|
10598
11145
|
}
|
|
10599
|
-
events
|
|
10600
|
-
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10604
|
-
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
|
|
10608
|
-
|
|
10609
|
-
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
return
|
|
10615
|
-
}
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
11146
|
+
return { events: presentedEvents, terminalInterrupted: terminalInterrupted };
|
|
11147
|
+
}
|
|
11148
|
+
|
|
11149
|
+
// 每个呈现节点的稳定 key:内容变化(增长/完成)时 key 变化,增量渲染据此
|
|
11150
|
+
// 只重建变化节点之后的 DOM,前缀节点原样保留。
|
|
11151
|
+
function workRunPresentedEventKey(event, eventIndex) {
|
|
11152
|
+
var type = String(event && event.type || 'status').toLowerCase();
|
|
11153
|
+
if (type === 'public_text' || type === 'partial_text' || type === 'response') {
|
|
11154
|
+
return type + ':' + String(event && event.content || '').length;
|
|
11155
|
+
}
|
|
11156
|
+
if (type === 'thought') {
|
|
11157
|
+
return 'thought:' + String(event && (event.id || event.toolCallId) || '') + '@' + String(event && event.content || '').length + ':' + (event && event.completed ? '1' : '0');
|
|
11158
|
+
}
|
|
11159
|
+
if (type === 'tool_group') {
|
|
11160
|
+
return 'tool_group:' + ((event && event.items || []).map(function(item) {
|
|
11161
|
+
return String(item && (item.id || item.toolCallId) || '') + ':' + (item && item.completed ? '1' : '0');
|
|
11162
|
+
}).join(',')) + '#n' + Number(event && event.count || 0);
|
|
11163
|
+
}
|
|
11164
|
+
if (type.indexOf('guide') === 0 || (event && event.guide)) {
|
|
11165
|
+
return 'guide:' + guideWorkEventKey(event) + ':' + guideWorkEventStatus(event);
|
|
11166
|
+
}
|
|
11167
|
+
var activityTail = type.indexOf('tool_') === 0 && type !== 'tool_call' && type !== 'tool_result'
|
|
11168
|
+
? ':' + String(event && event.activity && event.activity.key || '') + ':' + Number(event && event.count || 1)
|
|
11169
|
+
: '';
|
|
11170
|
+
return type + ':' + String(event && (event.id || [event && event.sequence || '', event && event.toolName || '', event && event.timestamp || ''].join('|')) || '') + ':' + (event && event.completed ? '1' : '0') + activityTail;
|
|
11171
|
+
}
|
|
11172
|
+
|
|
11173
|
+
function renderWorkRunEventNode(event, eventIndex, run, terminalInterrupted) {
|
|
11174
|
+
var type = String(event.type || 'status').toLowerCase();
|
|
11175
|
+
if (type === 'public_text' || type === 'partial_text' || type === 'response') {
|
|
11176
|
+
var responseLabel = type === 'response' && terminalInterrupted
|
|
11177
|
+
? (currentLang() === 'zh' ? '未完成回复片段\n' : 'Incomplete response fragment\n') + workEventLabel(event)
|
|
11178
|
+
: workEventLabel(event);
|
|
11179
|
+
// While a run is still receiving text, this view can be rebuilt once per
|
|
11180
|
+
// event. Avoid invoking the full Markdown parser for a large live
|
|
11181
|
+
// narrative; the final response path still gets the normal rich render.
|
|
11182
|
+
var liveNarrative = ['running', 'stopping', 'force_restarting'].indexOf(String(run && run.status || '').toLowerCase()) >= 0;
|
|
11183
|
+
var narrativeHtml = liveNarrative && String(responseLabel || '').length >= STREAMING_PLAIN_TEXT_THRESHOLD
|
|
11184
|
+
? esc(responseLabel)
|
|
11185
|
+
: renderMessageContent(responseLabel);
|
|
11186
|
+
return '<div class="conversation-work-event narrative"><div class="conversation-work-event-content">' + narrativeHtml + '</div></div>';
|
|
11187
|
+
}
|
|
11188
|
+
if (type.indexOf('guide') === 0 || event.guide) return renderWorkRunGuideMessage(event);
|
|
11189
|
+
if (type === 'tool_group') return renderWorkToolGroup(event, eventIndex);
|
|
11190
|
+
if (type === 'thought') return renderWorkThought(event, eventIndex);
|
|
11191
|
+
var activitySummary = type.indexOf('tool_') === 0 && type !== 'tool_call' && type !== 'tool_result';
|
|
11192
|
+
var cls = type.indexOf('guide') === 0 ? ' guide' : (type === 'error' ? ' error' : (activitySummary ? ' activity-summary' : ''));
|
|
11193
|
+
var label = activitySummary ? workToolActivityLabel(event.activity, Number(event.count || 1), !!event.completed) : workEventLabel(event) + (event.completed ? (currentLang() === 'zh' ? ' · 已完成' : ' · completed') : '');
|
|
11194
|
+
if (activitySummary) {
|
|
11195
|
+
var firstActivityItem = event.items && event.items[0] || {};
|
|
11196
|
+
var activityDomKey = String(firstActivityItem.id || firstActivityItem.toolCallId || ((event.activity && event.activity.key || type) + '-' + eventIndex));
|
|
11197
|
+
var detailRows = (event.items || []).map(function(item) {
|
|
11198
|
+
var detail = String(item.toolArgs || '').trim();
|
|
11199
|
+
return '<div class="conversation-work-activity-item">' + iconSvg(workEventIcon('tool_call'), 'tool_call', 'tiny') +
|
|
11200
|
+
'<div><div>' + esc(publicToolNameForUi(item.toolName)) + (item.completed ? (currentLang() === 'zh' ? ' · 已完成' : ' · completed') : '') + '</div>' +
|
|
11201
|
+
(detail ? '<div class="conversation-work-activity-detail">' + esc(detail) + '</div>' : '') + '</div></div>';
|
|
11202
|
+
}).join('');
|
|
11203
|
+
return '<details class="conversation-work-activity" data-activity-key="' + escAttr(activityDomKey) + '"><summary><span class="conversation-work-activity-chevron" aria-hidden="true"></span><div>' + esc(label) + '</div></summary>' +
|
|
11204
|
+
'<div class="conversation-work-activity-list">' + detailRows + '</div></details>';
|
|
11205
|
+
}
|
|
11206
|
+
return '<div class="conversation-work-event' + cls + '">' + iconSvg(workEventIcon(type), type, 'tiny') +
|
|
11207
|
+
'<div class="conversation-work-event-content">' + renderMessageContent(label) + '</div></div>';
|
|
11208
|
+
}
|
|
11209
|
+
|
|
11210
|
+
// 给单个事件节点的 HTML 根标签插入稳定 key,使首次渲染的节点与增量
|
|
11211
|
+
// diff 使用同一 key 体系(否则第一次增量更新会全量失配重建)。
|
|
11212
|
+
function withWorkEventKey(html, key) {
|
|
11213
|
+
var match = /^<([A-Za-z][\w-]*)([^>]*)>/.exec(String(html || ''));
|
|
11214
|
+
if (!match) return html;
|
|
11215
|
+
return '<' + match[1] + match[2] + ' data-work-event-key="' + escAttr(String(key || '')) + '">' + html.slice(match[0].length);
|
|
11216
|
+
}
|
|
11217
|
+
|
|
11218
|
+
function renderWorkRunEvents(run, includeGuides) {
|
|
11219
|
+
var presented = presentedWorkRunEvents(run, includeGuides);
|
|
11220
|
+
if (!presented.events.length) return '<div class="conversation-work-empty">' + esc(currentLang() === 'zh' ? '等待公开工作进度…' : 'Waiting for public progress…') + '</div>';
|
|
11221
|
+
return '<div class="conversation-work-run-events">' + presented.events.map(function(event, eventIndex) {
|
|
11222
|
+
return withWorkEventKey(renderWorkRunEventNode(event, eventIndex, run, presented.terminalInterrupted), workRunPresentedEventKey(event, eventIndex));
|
|
10636
11223
|
}).join('') + '</div>';
|
|
10637
11224
|
}
|
|
10638
11225
|
|
|
11226
|
+
// 增量更新 run body 内的事件列表:保留与新呈现列表 key 一致的前缀节点,
|
|
11227
|
+
// 从第一个分歧节点起重建尾部。运行中通常只有最后一个文本/thought 节点
|
|
11228
|
+
// 增长或尾部追加,因此不会整块重绘整个进程渲染。
|
|
11229
|
+
function updateWorkRunEventsContainer(run, body, expanded) {
|
|
11230
|
+
var presented = presentedWorkRunEvents(run, expanded);
|
|
11231
|
+
var container = body.querySelector ? body.querySelector('.conversation-work-run-events') : null;
|
|
11232
|
+
if (!container) {
|
|
11233
|
+
body.innerHTML = renderWorkRunEvents(run, expanded);
|
|
11234
|
+
return;
|
|
11235
|
+
}
|
|
11236
|
+
if (!presented.events.length) {
|
|
11237
|
+
var emptyHtml = '<div class="conversation-work-empty">' + esc(currentLang() === 'zh' ? '等待公开工作进度…' : 'Waiting for public progress…') + '</div>';
|
|
11238
|
+
if (container.innerHTML !== emptyHtml) container.innerHTML = emptyHtml;
|
|
11239
|
+
return;
|
|
11240
|
+
}
|
|
11241
|
+
var nodes = Array.prototype.slice.call(container.children);
|
|
11242
|
+
var keep = 0;
|
|
11243
|
+
while (keep < nodes.length && keep < presented.events.length) {
|
|
11244
|
+
if (String(nodes[keep].getAttribute('data-work-event-key') || '') === workRunPresentedEventKey(presented.events[keep], keep)) keep++;
|
|
11245
|
+
else break;
|
|
11246
|
+
}
|
|
11247
|
+
while (container.children.length > keep) container.removeChild(container.lastChild);
|
|
11248
|
+
for (var i = keep; i < presented.events.length; i++) {
|
|
11249
|
+
var key = workRunPresentedEventKey(presented.events[i], i);
|
|
11250
|
+
var holder = document.createElement('div');
|
|
11251
|
+
holder.innerHTML = renderWorkRunEventNode(presented.events[i], i, run, presented.terminalInterrupted);
|
|
11252
|
+
while (holder.firstChild) {
|
|
11253
|
+
var node = holder.firstChild;
|
|
11254
|
+
node.setAttribute('data-work-event-key', key);
|
|
11255
|
+
container.appendChild(node);
|
|
11256
|
+
}
|
|
11257
|
+
}
|
|
11258
|
+
}
|
|
11259
|
+
|
|
10639
11260
|
function findWorkRunElement(run) {
|
|
10640
11261
|
if (!els['chat-area']) return null;
|
|
10641
11262
|
var nodes = els['chat-area'].querySelectorAll('.conversation-work-run');
|
|
@@ -10648,6 +11269,11 @@ function findWorkRunElement(run) {
|
|
|
10648
11269
|
function updateConversationWorkRunElement(run, element) {
|
|
10649
11270
|
element = element || findWorkRunElement(run);
|
|
10650
11271
|
if (!element) return null;
|
|
11272
|
+
// 重建/增量更新前后保持聊天视窗滚动位置:贴底时跟随最新,未贴底时
|
|
11273
|
+
// 精确恢复 scrollTop,避免内容高度变化把视窗“强制拖向最新”。
|
|
11274
|
+
var chatEl = els['chat-area'];
|
|
11275
|
+
var wasAtBottom = !!(chatEl && shouldAutoScroll(chatEl));
|
|
11276
|
+
var scrollTopBefore = chatEl ? chatEl.scrollTop : 0;
|
|
10651
11277
|
var activityOpenStates = {};
|
|
10652
11278
|
var priorActivities = element.querySelectorAll ? element.querySelectorAll('details.conversation-work-activity[data-activity-key], details[data-work-detail-key]') : [];
|
|
10653
11279
|
for (var activityIndex = 0; activityIndex < priorActivities.length; activityIndex++) {
|
|
@@ -10662,12 +11288,51 @@ function updateConversationWorkRunElement(run, element) {
|
|
|
10662
11288
|
element.className = 'conversation-work-run ' + (running ? 'running ' : '') + (expanded ? 'expanded' : 'collapsed');
|
|
10663
11289
|
element.setAttribute('data-run-id', run.runId);
|
|
10664
11290
|
element.setAttribute('data-runtime-key', runtimeKeyFor(run.target && run.target.workspaceId, run.target && run.target.conversationId));
|
|
10665
|
-
element.
|
|
10666
|
-
|
|
10667
|
-
|
|
10668
|
-
|
|
10669
|
-
|
|
10670
|
-
|
|
11291
|
+
var head = element.querySelector ? element.querySelector('.conversation-work-run-head') : null;
|
|
11292
|
+
if (!head) {
|
|
11293
|
+
element.innerHTML = '<button class="conversation-work-run-head" type="button" onclick="window.toggleConversationWorkRun(this)">' +
|
|
11294
|
+
'<span class="conversation-work-run-title">' + esc(workRunTitle(run)) + '</span>' +
|
|
11295
|
+
workRunChangeBadge(run) +
|
|
11296
|
+
'<span class="conversation-work-run-status">' + esc(String(run.status || '')) + '</span>' +
|
|
11297
|
+
'<span class="conversation-work-run-chevron" aria-hidden="true"></span></button>' +
|
|
11298
|
+
'<div class="conversation-work-run-body">' + renderWorkRunEvents(run, expanded) + '</div>';
|
|
11299
|
+
} else {
|
|
11300
|
+
// 就地更新头部,不重建 head 按钮(保住聚焦/动画状态)。
|
|
11301
|
+
var titleEl = head.querySelector('.conversation-work-run-title');
|
|
11302
|
+
var nextTitle = workRunTitle(run);
|
|
11303
|
+
if (titleEl && titleEl.textContent !== nextTitle) titleEl.textContent = nextTitle;
|
|
11304
|
+
var statusEl = head.querySelector('.conversation-work-run-status');
|
|
11305
|
+
if (statusEl && statusEl.textContent !== String(run.status || '')) statusEl.textContent = String(run.status || '');
|
|
11306
|
+
// Keep the selector identical to workRunChangeBadge(). The old
|
|
11307
|
+
// `.conversation-work-run-change-badge` typo never found the existing
|
|
11308
|
+
// node, so every expand/collapse refresh inserted another review badge.
|
|
11309
|
+
var badgeHolder = head.querySelector('.conversation-work-change-badge');
|
|
11310
|
+
var nextBadge = workRunChangeBadge(run);
|
|
11311
|
+
if (nextBadge) {
|
|
11312
|
+
if (badgeHolder) {
|
|
11313
|
+
if (badgeHolder.getAttribute('data-badge-key') !== nextBadge) {
|
|
11314
|
+
var badgeShell = document.createElement('span');
|
|
11315
|
+
badgeShell.innerHTML = nextBadge;
|
|
11316
|
+
var replacementBadge = badgeShell.firstChild;
|
|
11317
|
+
replacementBadge.setAttribute('data-badge-key', nextBadge);
|
|
11318
|
+
badgeHolder.parentNode.replaceChild(replacementBadge, badgeHolder);
|
|
11319
|
+
}
|
|
11320
|
+
} else {
|
|
11321
|
+
var badgeShellNew = document.createElement('span');
|
|
11322
|
+
badgeShellNew.innerHTML = nextBadge;
|
|
11323
|
+
var badgeNode = badgeShellNew.firstChild;
|
|
11324
|
+
if (badgeNode) {
|
|
11325
|
+
badgeNode.setAttribute('data-badge-key', nextBadge);
|
|
11326
|
+
head.insertBefore(badgeNode, statusEl || null);
|
|
11327
|
+
}
|
|
11328
|
+
}
|
|
11329
|
+
} else if (badgeHolder) {
|
|
11330
|
+
badgeHolder.parentNode.removeChild(badgeHolder);
|
|
11331
|
+
}
|
|
11332
|
+
var body = element.querySelector('.conversation-work-run-body');
|
|
11333
|
+
if (body) updateWorkRunEventsContainer(run, body, expanded);
|
|
11334
|
+
else element.insertAdjacentHTML('beforeend', '<div class="conversation-work-run-body">' + renderWorkRunEvents(run, expanded) + '</div>');
|
|
11335
|
+
}
|
|
10671
11336
|
renderPendingGuideMessages(run.target, {});
|
|
10672
11337
|
var wrapper = element.closest ? element.closest('.work-run-message') : null;
|
|
10673
11338
|
if (wrapper) {
|
|
@@ -10698,9 +11363,16 @@ function updateConversationWorkRunElement(run, element) {
|
|
|
10698
11363
|
var refreshedKey = refreshedParentKey ? refreshedParentKey + '::' + refreshedDetailKey : '';
|
|
10699
11364
|
if (activityOpenStates[refreshedKey] !== undefined) refreshedActivities[refreshedIndex].open = activityOpenStates[refreshedKey];
|
|
10700
11365
|
}
|
|
10701
|
-
|
|
10702
|
-
|
|
10703
|
-
|
|
11366
|
+
if (chatEl) {
|
|
11367
|
+
if (wasAtBottom) {
|
|
11368
|
+
_chatShouldAutoScroll = true;
|
|
11369
|
+
chatEl.scrollTop = chatEl.scrollHeight;
|
|
11370
|
+
} else {
|
|
11371
|
+
chatEl.scrollTop = scrollTopBefore;
|
|
11372
|
+
}
|
|
11373
|
+
}
|
|
11374
|
+
return element;
|
|
11375
|
+
}
|
|
10704
11376
|
|
|
10705
11377
|
function updateConversationWorkRunDuration(run) {
|
|
10706
11378
|
var element = findWorkRunElement(run);
|
|
@@ -10933,7 +11605,8 @@ function conversationWorkUiState(conversationId, workspaceId) {
|
|
|
10933
11605
|
finalResponseMsg: null,
|
|
10934
11606
|
finalResponseText: '',
|
|
10935
11607
|
finalResponseAt: 0,
|
|
10936
|
-
pendingWorkReview: null
|
|
11608
|
+
pendingWorkReview: null,
|
|
11609
|
+
workReviews: []
|
|
10937
11610
|
};
|
|
10938
11611
|
}
|
|
10939
11612
|
return state.agentWorkUiByConversation[id];
|
|
@@ -10951,7 +11624,8 @@ function resetConversationWorkUi(conversationId, keepCompleted, workspaceId) {
|
|
|
10951
11624
|
finalResponseMsg: keepCompleted ? prior.finalResponseMsg : null,
|
|
10952
11625
|
finalResponseText: keepCompleted ? prior.finalResponseText : '',
|
|
10953
11626
|
finalResponseAt: keepCompleted ? prior.finalResponseAt : 0,
|
|
10954
|
-
pendingWorkReview: keepCompleted ? prior.pendingWorkReview : null
|
|
11627
|
+
pendingWorkReview: keepCompleted ? prior.pendingWorkReview : null,
|
|
11628
|
+
workReviews: keepCompleted ? (prior.workReviews || []).slice() : []
|
|
10955
11629
|
};
|
|
10956
11630
|
return state.agentWorkUiByConversation[id];
|
|
10957
11631
|
}
|
|
@@ -11233,9 +11907,12 @@ function renderChatMessages(messages, target) {
|
|
|
11233
11907
|
if (!orphanRoles.user && !orphanRoles.assistant) renderOwnedWorkRun(orphanRun, true);
|
|
11234
11908
|
}
|
|
11235
11909
|
}
|
|
11236
|
-
var
|
|
11910
|
+
var priorWorkUi = conversationWorkUiState(renderConversationId);
|
|
11911
|
+
var pendingWorkReview = priorWorkUi.pendingWorkReview;
|
|
11912
|
+
var retainedWorkReviews = Array.isArray(priorWorkUi.workReviews) ? priorWorkUi.workReviews.slice() : [];
|
|
11237
11913
|
resetConversationWorkUi(renderConversationId, false);
|
|
11238
11914
|
conversationWorkUiState(renderConversationId).pendingWorkReview = pendingWorkReview;
|
|
11915
|
+
conversationWorkUiState(renderConversationId).workReviews = retainedWorkReviews;
|
|
11239
11916
|
if (!normalizedMessages.length) {
|
|
11240
11917
|
persistedRuns.forEach(function(run) { renderOwnedWorkRun(run, true); });
|
|
11241
11918
|
var pendingGuideCount = renderPendingGuideMessages(renderTarget, persistedGuideIds);
|
|
@@ -11279,7 +11956,10 @@ function renderChatMessages(messages, target) {
|
|
|
11279
11956
|
finishToolBatch(renderConversationId);
|
|
11280
11957
|
persistedRuns.forEach(function(run) { renderOwnedWorkRun(run, true); });
|
|
11281
11958
|
renderPendingGuideMessages(renderTarget, persistedGuideIds);
|
|
11282
|
-
|
|
11959
|
+
retainedWorkReviews.forEach(function(item) {
|
|
11960
|
+
addWorkReview(item.diffs || item, item.runId || '');
|
|
11961
|
+
});
|
|
11962
|
+
if (pendingWorkReview) addWorkReview(pendingWorkReview.diffs || pendingWorkReview, pendingWorkReview.runId || '');
|
|
11283
11963
|
renderConversationBranchPagers();
|
|
11284
11964
|
renderAutoRouteRatingControls();
|
|
11285
11965
|
if (typeof renderPendingOptionsInChat === 'function') renderPendingOptionsInChat(state.pendingOptions || []);
|
|
@@ -11531,6 +12211,7 @@ function renderAgentWorkEvent(event) {
|
|
|
11531
12211
|
var eventQueueTarget = { workspaceId: eventWorkspaceId || runtimeWorkspaceId(''), conversationId: eventConversationId };
|
|
11532
12212
|
state.backendQueue = setBackendQueueForTarget(event.queue || { steering: [], followUp: [] }, eventQueueTarget);
|
|
11533
12213
|
if (window.syncNextQueueFromBackend) window.syncNextQueueFromBackend(state.backendQueue, eventQueueTarget);
|
|
12214
|
+
syncQueueItemsFromSnapshot(event, eventQueueTarget);
|
|
11534
12215
|
if (window.renderInputStack) window.renderInputStack();
|
|
11535
12216
|
} else {
|
|
11536
12217
|
return;
|
|
@@ -11588,6 +12269,9 @@ function appendAgentWorkEvent(event) {
|
|
|
11588
12269
|
eventRuntimeKey(event);
|
|
11589
12270
|
var active = isActiveConversationTarget(target);
|
|
11590
12271
|
var visibleRuntimeBranch = active && isViewingRuntimeConversationBranch(target);
|
|
12272
|
+
// 模型回退不是隐藏的参数回退:活跃对话发生回退时,把输入框下方的
|
|
12273
|
+
// 模型选择区同步为实际生效的模型。
|
|
12274
|
+
if (active && event.fallback && event.fallback.to) applyModelFallbackToInputSurface(event);
|
|
11591
12275
|
if (active) markConversationTracked(id, state.conversationTrackMs || 300000, workspaceId);
|
|
11592
12276
|
if (active || isConversationTracked(id, workspaceId)) cacheAgentWorkEvent(event);
|
|
11593
12277
|
var pendingProvisionalStop = (event.type === 'start' || event.status === 'running')
|
|
@@ -11756,6 +12440,7 @@ function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
|
11756
12440
|
state.backendQueue = setBackendQueueForTarget(s.queued, snapshotTarget);
|
|
11757
12441
|
if (window.syncNextQueueFromBackend) window.syncNextQueueFromBackend(state.backendQueue, snapshotTarget);
|
|
11758
12442
|
}
|
|
12443
|
+
syncQueueItemsFromSnapshot(s, snapshotTarget);
|
|
11759
12444
|
if (s && Array.isArray(s.workEvents)) {
|
|
11760
12445
|
var eventConversationId = runtimeKeyFor(snapshotTarget.workspaceId, s.conversationId || requestedConversationId || 'default');
|
|
11761
12446
|
if (!state.agentWorkEventsByConversation) state.agentWorkEventsByConversation = {};
|
|
@@ -11980,6 +12665,7 @@ window.optionManualSubmitted = function(element) {
|
|
|
11980
12665
|
|
|
11981
12666
|
// === Working State ===
|
|
11982
12667
|
function setWorking(working) {
|
|
12668
|
+
var changed = state.isWorking !== working;
|
|
11983
12669
|
state.isWorking = working;
|
|
11984
12670
|
state.activeAgentRunning = working;
|
|
11985
12671
|
var btn = els['submit-btn'];
|
|
@@ -11988,13 +12674,13 @@ function setWorking(working) {
|
|
|
11988
12674
|
var ws = els['ws-thumb'];
|
|
11989
12675
|
if (working) {
|
|
11990
12676
|
if (btn) btn.classList.add('working');
|
|
11991
|
-
if (input) input.classList.add('working-glow');
|
|
12677
|
+
if (input) input.classList.add('working-glow', 'marquee-border');
|
|
11992
12678
|
if (title) title.classList.add('animate');
|
|
11993
12679
|
if (ws) ws.classList.add('marquee-border');
|
|
11994
|
-
renderConversations();
|
|
12680
|
+
if (changed) renderConversations();
|
|
11995
12681
|
} else {
|
|
11996
12682
|
if (btn) btn.classList.remove('working');
|
|
11997
|
-
if (input) input.classList.remove('working-glow');
|
|
12683
|
+
if (input) input.classList.remove('working-glow', 'marquee-border');
|
|
11998
12684
|
if (title) title.classList.remove('animate');
|
|
11999
12685
|
if (ws) ws.classList.remove('marquee-border');
|
|
12000
12686
|
}
|
|
@@ -12534,6 +13220,7 @@ window.closeModelSelectMenu = function(options) {
|
|
|
12534
13220
|
if (shell) shell.classList.remove('open');
|
|
12535
13221
|
if (menu && typeof menu.hidePopover === 'function' && menu.matches(':popover-open')) menu.hidePopover();
|
|
12536
13222
|
if (button) button.setAttribute('aria-expanded', 'false');
|
|
13223
|
+
window.setLiquidSidebarGestureLock('popup:model-select', false);
|
|
12537
13224
|
if (options && options.focus && button) button.focus();
|
|
12538
13225
|
};
|
|
12539
13226
|
|
|
@@ -12550,6 +13237,7 @@ window.toggleModelSelectMenu = function(forceOpen) {
|
|
|
12550
13237
|
}
|
|
12551
13238
|
if (open) document.querySelectorAll('.newmark-select-shell.open').forEach(function(item) { window.closeNewmarkSelect(item); });
|
|
12552
13239
|
shell.classList.add('open');
|
|
13240
|
+
window.setLiquidSidebarGestureLock('popup:model-select', true);
|
|
12553
13241
|
button.setAttribute('aria-expanded', 'true');
|
|
12554
13242
|
if (open) {
|
|
12555
13243
|
if (menu && typeof menu.showPopover === 'function' && !menu.matches(':popover-open')) menu.showPopover();
|
|
@@ -12577,6 +13265,7 @@ window.closeConversationActionMenu = function(options) {
|
|
|
12577
13265
|
if (menu && typeof menu.hidePopover === 'function' && menu.matches(':popover-open')) menu.hidePopover();
|
|
12578
13266
|
document.querySelectorAll('.conv-more-btn[aria-expanded="true"]').forEach(function(item) { item.setAttribute('aria-expanded', 'false'); });
|
|
12579
13267
|
state.conversationActionAnchor = null;
|
|
13268
|
+
window.setLiquidSidebarGestureLock('popup:conversation-action', false);
|
|
12580
13269
|
if (options && options.focus && anchor && anchor.focus) anchor.focus();
|
|
12581
13270
|
};
|
|
12582
13271
|
|
|
@@ -12632,6 +13321,7 @@ window.openConversationActionMenu = function(anchor, conversationId, pos) {
|
|
|
12632
13321
|
document.querySelectorAll('.conv-more-btn[aria-expanded="true"]').forEach(function(item) { item.setAttribute('aria-expanded', 'false'); });
|
|
12633
13322
|
if (moreBtn) moreBtn.setAttribute('aria-expanded', 'true');
|
|
12634
13323
|
state.conversationActionAnchor = anchor || moreBtn || null;
|
|
13324
|
+
window.setLiquidSidebarGestureLock('popup:conversation-action', true);
|
|
12635
13325
|
if (typeof menu.showPopover === 'function' && !menu.matches(':popover-open')) menu.showPopover();
|
|
12636
13326
|
requestAnimationFrame(function() {
|
|
12637
13327
|
window.positionConversationActionMenu(state.conversationActionAnchor || moreBtn, menu, pos);
|
|
@@ -12695,6 +13385,967 @@ window.handleModelSelectButtonKey = function(event) {
|
|
|
12695
13385
|
}
|
|
12696
13386
|
};
|
|
12697
13387
|
|
|
13388
|
+
// 后端回退信号携带的是纯模型名(如 gpt-4o-mini),把名称映射回前端
|
|
13389
|
+
// 选择器的部署值(deployment:providerId:modelName),使选择区能直接选中。
|
|
13390
|
+
function modelDeploymentValueForName(name, providerId) {
|
|
13391
|
+
var modelName = String(name || '').trim();
|
|
13392
|
+
if (!modelName) return '';
|
|
13393
|
+
var providers = Array.isArray(state.providers) ? state.providers : [];
|
|
13394
|
+
var match = null;
|
|
13395
|
+
for (var pi = 0; pi < providers.length && !match; pi++) {
|
|
13396
|
+
var provider = providers[pi];
|
|
13397
|
+
if (providerId && String(provider.id || provider.name || '') !== String(providerId)) continue;
|
|
13398
|
+
var models = Array.isArray(provider.models) ? provider.models : [];
|
|
13399
|
+
for (var mi = 0; mi < models.length && !match; mi++) {
|
|
13400
|
+
var entryName = typeof models[mi] === 'string' ? models[mi] : (models[mi] && models[mi].name || '');
|
|
13401
|
+
if (entryName === modelName) match = { provider: provider, name: entryName };
|
|
13402
|
+
}
|
|
13403
|
+
}
|
|
13404
|
+
if (!match) return modelName;
|
|
13405
|
+
var providerIdValue = String(match.provider.id || match.provider.name || '');
|
|
13406
|
+
return 'deployment:' + encodeURIComponent(providerIdValue) + ':' + encodeURIComponent(match.name);
|
|
13407
|
+
}
|
|
13408
|
+
|
|
13409
|
+
// 不可用模型回退同步输入框下方选择区:固定模型选择被回退时,选择区
|
|
13410
|
+
// 显示实际生效的模型;Auto 模式的路由切换不污染选择区(保持 Auto 显示)。
|
|
13411
|
+
function applyModelFallbackToInputSurface(event) {
|
|
13412
|
+
var f = event && event.fallback;
|
|
13413
|
+
if (!f || !f.to) return;
|
|
13414
|
+
if (String(state.model || '') === 'auto') return;
|
|
13415
|
+
var nextValue = modelDeploymentValueForName(f.to, f.providerId || '');
|
|
13416
|
+
if (!nextValue || String(state.model || '') === nextValue) return;
|
|
13417
|
+
state.model = nextValue;
|
|
13418
|
+
state.modelLabel = '';
|
|
13419
|
+
// 不预置 _syncedModel:下一次发送前的 syncConversationExecutionState 会把
|
|
13420
|
+
// 回退模型正式同步到后端 host agent(并持久化为该对话的首选模型)。
|
|
13421
|
+
window.refreshModelSelect();
|
|
13422
|
+
window.syncModelSelectSurface();
|
|
13423
|
+
}
|
|
13424
|
+
|
|
13425
|
+
var liquidSidebarGestureLocks = new Set();
|
|
13426
|
+
window.setLiquidSidebarGestureLock = function(token, locked) {
|
|
13427
|
+
if (locked) liquidSidebarGestureLocks.add(String(token));
|
|
13428
|
+
else liquidSidebarGestureLocks.delete(String(token));
|
|
13429
|
+
document.body.classList.toggle('liquid-sidebar-gesture-locked', liquidSidebarGestureLocks.size > 0);
|
|
13430
|
+
};
|
|
13431
|
+
window.liquidSidebarGesturesLocked = function() { return liquidSidebarGestureLocks.size > 0; };
|
|
13432
|
+
|
|
13433
|
+
// One moving glass capsule per select menu. It starts at the existing
|
|
13434
|
+
// selection, fades in, follows pointer movement across options, and commits
|
|
13435
|
+
// the landing option on release. Static options retain their color capsule.
|
|
13436
|
+
var liquidBackdropFramePromise = null;
|
|
13437
|
+
var liquidBackdropCachedFrame = null;
|
|
13438
|
+
var liquidBackdropPrimeTimer = 0;
|
|
13439
|
+
window.__liquidBackdropPrimeReady = false;
|
|
13440
|
+
function decodeLiquidBackdropFrame(frame) {
|
|
13441
|
+
if (!frame) return Promise.resolve(null);
|
|
13442
|
+
if (frame.bitmap) return Promise.resolve(frame);
|
|
13443
|
+
var bytes = frame.bytes;
|
|
13444
|
+
if (bytes && Array.isArray(bytes.data)) bytes = new Uint8Array(bytes.data);
|
|
13445
|
+
var blobPromise = bytes
|
|
13446
|
+
? Promise.resolve(new Blob([bytes], { type: frame.mimeType || 'image/jpeg' }))
|
|
13447
|
+
: (frame.dataUrl ? fetch(frame.dataUrl).then(function(response) { return response.blob(); }) : Promise.resolve(null));
|
|
13448
|
+
return blobPromise.then(function(blob) {
|
|
13449
|
+
if (!blob) return null;
|
|
13450
|
+
return createImageBitmap(blob);
|
|
13451
|
+
}).then(function(bitmap) {
|
|
13452
|
+
if (!bitmap) return null;
|
|
13453
|
+
frame.bitmap = bitmap;
|
|
13454
|
+
frame.id = frame.id || String(Date.now()) + ':' + String(frame.width || bitmap.width) + 'x' + String(frame.height || bitmap.height);
|
|
13455
|
+
frame.capturedAt = performance.now();
|
|
13456
|
+
return frame;
|
|
13457
|
+
});
|
|
13458
|
+
}
|
|
13459
|
+
function captureLiquidBackdropFrame(options) {
|
|
13460
|
+
if (!window.api || typeof window.api.captureLiquidBackdrop !== 'function') return Promise.resolve(null);
|
|
13461
|
+
options = options || {};
|
|
13462
|
+
var maxAge = Number.isFinite(options.maxAge) ? options.maxAge : 5000;
|
|
13463
|
+
if (!options.force && liquidBackdropCachedFrame && performance.now() - liquidBackdropCachedFrame.capturedAt <= maxAge) {
|
|
13464
|
+
return Promise.resolve(liquidBackdropCachedFrame);
|
|
13465
|
+
}
|
|
13466
|
+
if (!liquidBackdropFramePromise) {
|
|
13467
|
+
window.__liquidBackdropPrimeReady = false;
|
|
13468
|
+
liquidBackdropFramePromise = window.api.captureLiquidBackdrop({ width: window.innerWidth, height: window.innerHeight })
|
|
13469
|
+
.then(decodeLiquidBackdropFrame)
|
|
13470
|
+
.then(function(frame) {
|
|
13471
|
+
if (frame) {
|
|
13472
|
+
var previous = liquidBackdropCachedFrame;
|
|
13473
|
+
liquidBackdropCachedFrame = frame;
|
|
13474
|
+
prewarmKyantRenderer(frame);
|
|
13475
|
+
window.__liquidBackdropPrimeReady = true;
|
|
13476
|
+
if (previous && previous !== frame && previous.bitmap && previous.bitmap.close) previous.bitmap.close();
|
|
13477
|
+
}
|
|
13478
|
+
return frame;
|
|
13479
|
+
})
|
|
13480
|
+
.catch(function(error) {
|
|
13481
|
+
window.__liquidErrors = window.__liquidErrors || [];
|
|
13482
|
+
window.__liquidErrors.push(String(error && (error.stack || error.message) || error));
|
|
13483
|
+
console.warn('[KyantGlass] backdrop capture failed:', error);
|
|
13484
|
+
return liquidBackdropCachedFrame;
|
|
13485
|
+
})
|
|
13486
|
+
.finally(function() { liquidBackdropFramePromise = null; });
|
|
13487
|
+
}
|
|
13488
|
+
return liquidBackdropFramePromise;
|
|
13489
|
+
}
|
|
13490
|
+
|
|
13491
|
+
function scheduleLiquidBackdropPrime() {
|
|
13492
|
+
if (liquidBackdropPrimeTimer || (liquidBackdropCachedFrame && performance.now() - liquidBackdropCachedFrame.capturedAt < 5000)) return;
|
|
13493
|
+
liquidBackdropPrimeTimer = setTimeout(function() {
|
|
13494
|
+
liquidBackdropPrimeTimer = 0;
|
|
13495
|
+
captureLiquidBackdropFrame({ maxAge: 5000 });
|
|
13496
|
+
}, liquidBackdropCachedFrame ? 220 : 48);
|
|
13497
|
+
}
|
|
13498
|
+
|
|
13499
|
+
if (window.requestIdleCallback) {
|
|
13500
|
+
window.requestIdleCallback(function() { captureLiquidBackdropFrame({ maxAge: 5000 }); }, { timeout: 900 });
|
|
13501
|
+
} else {
|
|
13502
|
+
setTimeout(function() { captureLiquidBackdropFrame({ maxAge: 5000 }); }, 600);
|
|
13503
|
+
}
|
|
13504
|
+
|
|
13505
|
+
document.addEventListener('pointerover', function(event) {
|
|
13506
|
+
var target = event.target && event.target.closest ? event.target.closest('.mode-toggle-btn[data-mode], .left-nav-icon, .lt-item, .left-ws-item, .tab-btn[data-tab], .model-select-menu-option, .newmark-select-option, .conv-item') : null;
|
|
13507
|
+
if (target) scheduleLiquidBackdropPrime();
|
|
13508
|
+
}, { passive: true });
|
|
13509
|
+
document.addEventListener('focusin', function(event) {
|
|
13510
|
+
var target = event.target && event.target.closest ? event.target.closest('.mode-toggle-btn[data-mode], .left-nav-icon, .lt-item, .left-ws-item, .tab-btn[data-tab], .model-select-menu-option, .newmark-select-option, .conv-item') : null;
|
|
13511
|
+
if (target) scheduleLiquidBackdropPrime();
|
|
13512
|
+
});
|
|
13513
|
+
|
|
13514
|
+
function compileLiquidShader(gl, type, source) {
|
|
13515
|
+
var shader = gl.createShader(type);
|
|
13516
|
+
gl.shaderSource(shader, source);
|
|
13517
|
+
gl.compileShader(shader);
|
|
13518
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
13519
|
+
console.error('[KyantGlass] shader compile failed:', gl.getShaderInfoLog(shader));
|
|
13520
|
+
gl.deleteShader(shader);
|
|
13521
|
+
return null;
|
|
13522
|
+
}
|
|
13523
|
+
return shader;
|
|
13524
|
+
}
|
|
13525
|
+
|
|
13526
|
+
function liquidGlassCssNumber(name, fallback) {
|
|
13527
|
+
var value = parseFloat(getComputedStyle(document.documentElement).getPropertyValue(name));
|
|
13528
|
+
return Number.isFinite(value) ? value : fallback;
|
|
13529
|
+
}
|
|
13530
|
+
|
|
13531
|
+
var liquidKyantRendererPool = [];
|
|
13532
|
+
var liquidKyantVertexSource = '#version 300 es\n' +
|
|
13533
|
+
'in vec2 p; out vec2 uv; void main(){uv=p*.5+.5;gl_Position=vec4(p,0.,1.);}';
|
|
13534
|
+
var liquidKyantFragmentSource = '#version 300 es\nprecision highp float;\n' +
|
|
13535
|
+
'uniform sampler2D content; uniform vec2 size; uniform vec2 viewport; uniform vec2 origin; uniform vec4 cornerRadii; uniform float refractionHeight; uniform float refractionAmount; uniform float depthEffect; uniform float chromaticAberration; in vec2 uv; out vec4 outColor;\n' +
|
|
13536
|
+
'float radiusAt(vec2 coord,vec4 radii){if(coord.x>=0.)return coord.y<=0.?radii.y:radii.z;return coord.y<=0.?radii.x:radii.w;}\n' +
|
|
13537
|
+
'float sdRoundedRect(vec2 coord,vec2 halfSize,float r){vec2 q=abs(coord)-(halfSize-vec2(r));return length(max(q,0.))-r+min(max(q.x,q.y),0.);}\n' +
|
|
13538
|
+
'vec2 gradSdRoundedRect(vec2 coord,vec2 halfSize,float r){vec2 q=abs(coord)-(halfSize-vec2(r));if(q.x>=0.||q.y>=0.)return sign(coord)*normalize(max(q,vec2(0.0001)));float gx=step(q.y,q.x);return sign(coord)*vec2(gx,1.-gx);}\n' +
|
|
13539
|
+
'float circleMap(float x){x=clamp(x,0.,1.);return 1.-sqrt(max(0.,1.-x*x));}\n' +
|
|
13540
|
+
'vec4 sampleBackdrop(vec2 cssCoord){vec2 t=vec2(cssCoord.x/viewport.x,1.-cssCoord.y/viewport.y);return texture(content,clamp(t,vec2(0.),vec2(1.)));}\n' +
|
|
13541
|
+
'void main(){vec2 local=vec2(uv.x*size.x,(1.-uv.y)*size.y);vec2 halfSize=size*.5;vec2 centered=local-halfSize;float radius=radiusAt(centered,cornerRadii);float sd=sdRoundedRect(centered,halfSize,radius);if(sd>0.)discard;vec2 base=origin+local;if(-sd>=refractionHeight){outColor=vec4(0.);return;}sd=min(sd,0.);float edge=clamp(1.-(-sd/refractionHeight),0.,1.);float d=circleMap(edge)*refractionAmount;float gradRadius=min(radius*1.5,min(halfSize.x,halfSize.y));vec2 grad=normalize(gradSdRoundedRect(centered,halfSize,gradRadius)+depthEffect*normalize(centered));vec2 refracted=base+d*grad;float dispersionIntensity=chromaticAberration*edge;vec2 dispersed=d*grad*dispersionIntensity;vec4 red=sampleBackdrop(refracted+dispersed);vec4 green=sampleBackdrop(refracted);vec4 blue=sampleBackdrop(refracted-dispersed);float alpha=mix(.14,.82,edge);outColor=vec4(vec3(red.r,green.g,blue.b)*alpha,alpha);}';
|
|
13542
|
+
|
|
13543
|
+
function createKyantLiquidRenderer() {
|
|
13544
|
+
var canvas = document.createElement('canvas');
|
|
13545
|
+
var displayCanvas = document.createElement('canvas');
|
|
13546
|
+
displayCanvas.className = 'liquid-selection-canvas';
|
|
13547
|
+
var displayContext = displayCanvas.getContext('2d', { alpha:true });
|
|
13548
|
+
if (!displayContext) return null;
|
|
13549
|
+
var gl = canvas.getContext('webgl2', { alpha:true, antialias:false, premultipliedAlpha:true, powerPreference:'high-performance' });
|
|
13550
|
+
if (!gl) return null;
|
|
13551
|
+
var vs = compileLiquidShader(gl, gl.VERTEX_SHADER, liquidKyantVertexSource);
|
|
13552
|
+
var fs = compileLiquidShader(gl, gl.FRAGMENT_SHADER, liquidKyantFragmentSource);
|
|
13553
|
+
if (!vs || !fs) return null;
|
|
13554
|
+
var program = gl.createProgram();
|
|
13555
|
+
gl.attachShader(program, vs); gl.attachShader(program, fs); gl.linkProgram(program);
|
|
13556
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
13557
|
+
console.error('[KyantGlass] program link failed:', gl.getProgramInfoLog(program));
|
|
13558
|
+
return null;
|
|
13559
|
+
}
|
|
13560
|
+
gl.deleteShader(vs);
|
|
13561
|
+
gl.deleteShader(fs);
|
|
13562
|
+
var buffer = gl.createBuffer();
|
|
13563
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
13564
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]), gl.STATIC_DRAW);
|
|
13565
|
+
var location = gl.getAttribLocation(program, 'p');
|
|
13566
|
+
gl.enableVertexAttribArray(location); gl.vertexAttribPointer(location,2,gl.FLOAT,false,0,0);
|
|
13567
|
+
var texture = gl.createTexture();
|
|
13568
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
13569
|
+
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);
|
|
13570
|
+
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR);
|
|
13571
|
+
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);
|
|
13572
|
+
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);
|
|
13573
|
+
var uniforms = {
|
|
13574
|
+
content: gl.getUniformLocation(program,'content'),
|
|
13575
|
+
size: gl.getUniformLocation(program,'size'),
|
|
13576
|
+
viewport: gl.getUniformLocation(program,'viewport'),
|
|
13577
|
+
origin: gl.getUniformLocation(program,'origin'),
|
|
13578
|
+
cornerRadii: gl.getUniformLocation(program,'cornerRadii'),
|
|
13579
|
+
refractionHeight: gl.getUniformLocation(program,'refractionHeight'),
|
|
13580
|
+
refractionAmount: gl.getUniformLocation(program,'refractionAmount'),
|
|
13581
|
+
depthEffect: gl.getUniformLocation(program,'depthEffect'),
|
|
13582
|
+
chromaticAberration: gl.getUniformLocation(program,'chromaticAberration')
|
|
13583
|
+
};
|
|
13584
|
+
gl.useProgram(program);
|
|
13585
|
+
gl.uniform1i(uniforms.content, 0);
|
|
13586
|
+
var renderer = {
|
|
13587
|
+
canvas: canvas,
|
|
13588
|
+
displayCanvas: displayCanvas,
|
|
13589
|
+
displayContext: displayContext,
|
|
13590
|
+
gl: gl,
|
|
13591
|
+
program: program,
|
|
13592
|
+
texture: texture,
|
|
13593
|
+
uniforms: uniforms,
|
|
13594
|
+
owner: null,
|
|
13595
|
+
textureReady: false,
|
|
13596
|
+
generation: 0,
|
|
13597
|
+
renderFrame: 0,
|
|
13598
|
+
frameId: '',
|
|
13599
|
+
lastGeometry: null,
|
|
13600
|
+
pendingGeometry: null,
|
|
13601
|
+
uniformState: Object.create(null),
|
|
13602
|
+
invalid: false
|
|
13603
|
+
};
|
|
13604
|
+
canvas.addEventListener('webglcontextlost', function(event) {
|
|
13605
|
+
event.preventDefault();
|
|
13606
|
+
renderer.invalid = true;
|
|
13607
|
+
renderer.textureReady = false;
|
|
13608
|
+
if (renderer.renderFrame) cancelAnimationFrame(renderer.renderFrame);
|
|
13609
|
+
renderer.renderFrame = 0;
|
|
13610
|
+
liquidKyantRendererPool = liquidKyantRendererPool.filter(function(item) { return item !== renderer; });
|
|
13611
|
+
});
|
|
13612
|
+
return renderer;
|
|
13613
|
+
}
|
|
13614
|
+
|
|
13615
|
+
function acquireKyantLiquidRenderer(float) {
|
|
13616
|
+
var renderer = liquidKyantRendererPool.find(function(item) {
|
|
13617
|
+
return !item.invalid && (!item.owner || !item.owner.isConnected);
|
|
13618
|
+
});
|
|
13619
|
+
if (!renderer) {
|
|
13620
|
+
renderer = createKyantLiquidRenderer();
|
|
13621
|
+
if (!renderer) return null;
|
|
13622
|
+
liquidKyantRendererPool.push(renderer);
|
|
13623
|
+
}
|
|
13624
|
+
renderer.owner = float;
|
|
13625
|
+
renderer.generation += 1;
|
|
13626
|
+
if (renderer.renderFrame) cancelAnimationFrame(renderer.renderFrame);
|
|
13627
|
+
renderer.renderFrame = 0;
|
|
13628
|
+
renderer.displayCanvas.remove();
|
|
13629
|
+
renderer.lastGeometry = null;
|
|
13630
|
+
renderer.pendingGeometry = null;
|
|
13631
|
+
return renderer;
|
|
13632
|
+
}
|
|
13633
|
+
|
|
13634
|
+
function uploadKyantLiquidFrame(renderer, frame) {
|
|
13635
|
+
if (!renderer || !frame || !frame.bitmap || renderer.frameId === frame.id) return false;
|
|
13636
|
+
var gl = renderer.gl;
|
|
13637
|
+
gl.bindTexture(gl.TEXTURE_2D, renderer.texture);
|
|
13638
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
|
|
13639
|
+
gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,frame.bitmap);
|
|
13640
|
+
renderer.textureReady = true;
|
|
13641
|
+
renderer.frameId = frame.id;
|
|
13642
|
+
return true;
|
|
13643
|
+
}
|
|
13644
|
+
|
|
13645
|
+
function prewarmKyantRenderer(frame) {
|
|
13646
|
+
if (!frame || !frame.bitmap) return;
|
|
13647
|
+
var renderer = liquidKyantRendererPool.find(function(item) { return !item.invalid && !item.owner; });
|
|
13648
|
+
if (!renderer) {
|
|
13649
|
+
if (liquidKyantRendererPool.some(function(item) { return !item.invalid; })) return;
|
|
13650
|
+
renderer = createKyantLiquidRenderer();
|
|
13651
|
+
if (!renderer) return;
|
|
13652
|
+
liquidKyantRendererPool.push(renderer);
|
|
13653
|
+
}
|
|
13654
|
+
uploadKyantLiquidFrame(renderer, frame);
|
|
13655
|
+
}
|
|
13656
|
+
|
|
13657
|
+
function attachKyantLiquidRenderer(float, frame) {
|
|
13658
|
+
var renderer = float._kyantRendererRef;
|
|
13659
|
+
if (!renderer || renderer.owner !== float) renderer = acquireKyantLiquidRenderer(float);
|
|
13660
|
+
if (!renderer) { float.dataset.kyantRenderer = 'webgl2-unavailable'; return; }
|
|
13661
|
+
float._kyantRendererRef = renderer;
|
|
13662
|
+
var canvas = renderer.canvas;
|
|
13663
|
+
var displayCanvas = renderer.displayCanvas;
|
|
13664
|
+
var displayContext = renderer.displayContext;
|
|
13665
|
+
var gl = renderer.gl;
|
|
13666
|
+
var program = renderer.program;
|
|
13667
|
+
var texture = renderer.texture;
|
|
13668
|
+
var uniforms = renderer.uniforms;
|
|
13669
|
+
var generation = renderer.generation;
|
|
13670
|
+
float.dataset.kyantRenderer = 'webgl2';
|
|
13671
|
+
float._kyantTextureReady = false;
|
|
13672
|
+
float._kyantDrawReady = false;
|
|
13673
|
+
delete float.dataset.kyantDrawReady;
|
|
13674
|
+
float._renderKyantGlass = function(geometry, immediate) {
|
|
13675
|
+
renderer.pendingGeometry = geometry || null;
|
|
13676
|
+
if (!renderer.textureReady || renderer.owner !== float || !float.isConnected) return;
|
|
13677
|
+
if (immediate && renderer.renderFrame) {
|
|
13678
|
+
cancelAnimationFrame(renderer.renderFrame);
|
|
13679
|
+
renderer.renderFrame = 0;
|
|
13680
|
+
}
|
|
13681
|
+
if (renderer.renderFrame) return;
|
|
13682
|
+
var draw = function() {
|
|
13683
|
+
renderer.renderFrame = 0;
|
|
13684
|
+
if (!renderer.textureReady || renderer.owner !== float || !float.isConnected) return;
|
|
13685
|
+
var geometry = renderer.pendingGeometry;
|
|
13686
|
+
renderer.pendingGeometry = null;
|
|
13687
|
+
var style = geometry ? null : getComputedStyle(float);
|
|
13688
|
+
var width = geometry ? geometry.width : (parseFloat(style.width) || float.offsetWidth || 1);
|
|
13689
|
+
var height = geometry ? geometry.height : (parseFloat(style.height) || float.offsetHeight || 1);
|
|
13690
|
+
var left = geometry ? geometry.left : (parseFloat(style.left) || 0);
|
|
13691
|
+
var top = geometry ? geometry.top : (parseFloat(style.top) || 0);
|
|
13692
|
+
var dpr = window.devicePixelRatio || 1;
|
|
13693
|
+
var renderScale = Math.min(dpr, 1.5);
|
|
13694
|
+
var pixelWidth = Math.max(1, Math.round(width*renderScale));
|
|
13695
|
+
var pixelHeight = Math.max(1, Math.round(height*renderScale));
|
|
13696
|
+
var geometryKey = [left.toFixed(2),top.toFixed(2),width.toFixed(2),height.toFixed(2),pixelWidth,pixelHeight].join(':');
|
|
13697
|
+
if (renderer.lastGeometry === geometryKey) return;
|
|
13698
|
+
renderer.lastGeometry = geometryKey;
|
|
13699
|
+
if (canvas.width !== pixelWidth) canvas.width = pixelWidth;
|
|
13700
|
+
if (canvas.height !== pixelHeight) canvas.height = pixelHeight;
|
|
13701
|
+
if (displayCanvas.width !== pixelWidth) displayCanvas.width = pixelWidth;
|
|
13702
|
+
if (displayCanvas.height !== pixelHeight) displayCanvas.height = pixelHeight;
|
|
13703
|
+
gl.viewport(0,0,pixelWidth,pixelHeight);
|
|
13704
|
+
gl.useProgram(program);
|
|
13705
|
+
function uniformChanged(key, values, apply) {
|
|
13706
|
+
var prior = renderer.uniformState[key];
|
|
13707
|
+
if (prior && prior.length === values.length && values.every(function(value,index) { return prior[index] === value; })) return;
|
|
13708
|
+
renderer.uniformState[key] = values;
|
|
13709
|
+
apply();
|
|
13710
|
+
}
|
|
13711
|
+
uniformChanged('size',[width,height],function() { gl.uniform2f(uniforms.size,width,height); });
|
|
13712
|
+
uniformChanged('viewport',[window.innerWidth,window.innerHeight],function() { gl.uniform2f(uniforms.viewport,window.innerWidth,window.innerHeight); });
|
|
13713
|
+
uniformChanged('origin',[left,top],function() { gl.uniform2f(uniforms.origin,left,top); });
|
|
13714
|
+
var capsuleRadius = Math.min(width,height)*.5;
|
|
13715
|
+
uniformChanged('cornerRadii',[capsuleRadius],function() { gl.uniform4f(uniforms.cornerRadii,capsuleRadius,capsuleRadius,capsuleRadius,capsuleRadius); });
|
|
13716
|
+
var configuredHeight = liquidGlassCssNumber('--liquid-interaction-edge', 6);
|
|
13717
|
+
var configuredAmount = liquidGlassCssNumber('--liquid-float-refraction-amount', 24);
|
|
13718
|
+
var configuredChromatic = liquidGlassCssNumber('--liquid-float-chromatic-aberration', 1);
|
|
13719
|
+
var refractionHeight = Math.max(0.001,Math.min(configuredHeight,height*.48));
|
|
13720
|
+
uniformChanged('refractionHeight',[refractionHeight],function() { gl.uniform1f(uniforms.refractionHeight,refractionHeight); });
|
|
13721
|
+
uniformChanged('refractionAmount',[-configuredAmount],function() { gl.uniform1f(uniforms.refractionAmount,-configuredAmount); });
|
|
13722
|
+
uniformChanged('depthEffect',[1],function() { gl.uniform1f(uniforms.depthEffect,1); });
|
|
13723
|
+
uniformChanged('chromaticAberration',[configuredChromatic],function() { gl.uniform1f(uniforms.chromaticAberration,configuredChromatic); });
|
|
13724
|
+
gl.drawArrays(gl.TRIANGLES,0,6);
|
|
13725
|
+
displayContext.clearRect(0,0,pixelWidth,pixelHeight);
|
|
13726
|
+
displayContext.drawImage(canvas,0,0);
|
|
13727
|
+
float._kyantDrawReady = true;
|
|
13728
|
+
float.dataset.kyantDrawReady = 'true';
|
|
13729
|
+
if (displayCanvas.parentNode !== float) float.appendChild(displayCanvas);
|
|
13730
|
+
};
|
|
13731
|
+
if (immediate) draw();
|
|
13732
|
+
else renderer.renderFrame = requestAnimationFrame(draw);
|
|
13733
|
+
};
|
|
13734
|
+
float._releaseKyantGlass = function() {
|
|
13735
|
+
if (renderer.owner !== float) return;
|
|
13736
|
+
renderer.generation += 1;
|
|
13737
|
+
renderer.owner = null;
|
|
13738
|
+
if (renderer.renderFrame) cancelAnimationFrame(renderer.renderFrame);
|
|
13739
|
+
renderer.renderFrame = 0;
|
|
13740
|
+
renderer.lastGeometry = null;
|
|
13741
|
+
renderer.pendingGeometry = null;
|
|
13742
|
+
displayCanvas.remove();
|
|
13743
|
+
delete float._kyantRendererRef;
|
|
13744
|
+
};
|
|
13745
|
+
if (frame && frame.bitmap && renderer.owner === float && renderer.generation === generation && float.isConnected) {
|
|
13746
|
+
uploadKyantLiquidFrame(renderer, frame);
|
|
13747
|
+
float._kyantTextureReady = true;
|
|
13748
|
+
if (float.style.width && float.style.height) float._renderKyantGlass(null, true);
|
|
13749
|
+
} else if (renderer.textureReady) {
|
|
13750
|
+
float._kyantTextureReady = true;
|
|
13751
|
+
if (float.style.width && float.style.height) float._renderKyantGlass(null, true);
|
|
13752
|
+
} else {
|
|
13753
|
+
float.dataset.kyantRenderer = 'waiting-backdrop';
|
|
13754
|
+
}
|
|
13755
|
+
}
|
|
13756
|
+
|
|
13757
|
+
function mountLiquidViewportFloat(float) {
|
|
13758
|
+
if (liquidBackdropPrimeTimer) clearTimeout(liquidBackdropPrimeTimer);
|
|
13759
|
+
liquidBackdropPrimeTimer = 0;
|
|
13760
|
+
float.classList.add('liquid-selection-viewport');
|
|
13761
|
+
float.setAttribute('popover', 'manual');
|
|
13762
|
+
document.body.appendChild(float);
|
|
13763
|
+
startLiquidMotionTracking(float);
|
|
13764
|
+
attachKyantLiquidRenderer(float, liquidBackdropCachedFrame);
|
|
13765
|
+
}
|
|
13766
|
+
|
|
13767
|
+
function showLiquidViewportFloat(float) {
|
|
13768
|
+
try { float.showPopover(); } catch (_) {}
|
|
13769
|
+
}
|
|
13770
|
+
|
|
13771
|
+
function keepLiquidFloatExpansionInsideViewport(float, rect) {
|
|
13772
|
+
if (!float || !rect) return;
|
|
13773
|
+
var edge = 28;
|
|
13774
|
+
float.style.transformOrigin = rect.left < edge ? 'left center' : (rect.right > window.innerWidth - edge ? 'right center' : 'center');
|
|
13775
|
+
}
|
|
13776
|
+
|
|
13777
|
+
function liquidInteractionEdgePx() {
|
|
13778
|
+
return liquidGlassCssNumber('--liquid-interaction-edge', 6);
|
|
13779
|
+
}
|
|
13780
|
+
|
|
13781
|
+
function startLiquidMotionTracking(float) {
|
|
13782
|
+
if (!float || float._liquidMotionFrame) return;
|
|
13783
|
+
var lastTime = 0;
|
|
13784
|
+
var lastX = 0;
|
|
13785
|
+
var lastY = 0;
|
|
13786
|
+
var velocityX = 0;
|
|
13787
|
+
var velocityY = 0;
|
|
13788
|
+
var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
13789
|
+
function frame(now) {
|
|
13790
|
+
if (!float || !float.isConnected) {
|
|
13791
|
+
if (float) float._liquidMotionFrame = 0;
|
|
13792
|
+
return;
|
|
13793
|
+
}
|
|
13794
|
+
var rect = float.getBoundingClientRect();
|
|
13795
|
+
var left = rect.left;
|
|
13796
|
+
var top = rect.top;
|
|
13797
|
+
var width = rect.width;
|
|
13798
|
+
var height = rect.height;
|
|
13799
|
+
var centerX = left + width * .5;
|
|
13800
|
+
var centerY = top + height * .5;
|
|
13801
|
+
var moved = lastTime > 0 && (Math.abs(centerX - lastX) > .01 || Math.abs(centerY - lastY) > .01);
|
|
13802
|
+
if (lastTime > 0) {
|
|
13803
|
+
var seconds = Math.max(.001, Math.min(.05, (now - lastTime) / 1000));
|
|
13804
|
+
var instantaneousX = (centerX - lastX) / seconds;
|
|
13805
|
+
var instantaneousY = (centerY - lastY) / seconds;
|
|
13806
|
+
velocityX = velocityX * .45 + instantaneousX * .55;
|
|
13807
|
+
velocityY = velocityY * .45 + instantaneousY * .55;
|
|
13808
|
+
}
|
|
13809
|
+
lastTime = now;
|
|
13810
|
+
lastX = centerX;
|
|
13811
|
+
lastY = centerY;
|
|
13812
|
+
var speed = reducedMotion ? 0 : Math.hypot(velocityX, velocityY);
|
|
13813
|
+
var amount = Math.max(0, Math.min(1, (speed - 8) / 700));
|
|
13814
|
+
var angle = speed > 1 ? Math.atan2(velocityY, velocityX) : 0;
|
|
13815
|
+
float.style.setProperty('--liquid-motion-angle', angle + 'rad');
|
|
13816
|
+
float.style.setProperty('--liquid-motion-stretch', String(1 + amount * .075));
|
|
13817
|
+
float.style.setProperty('--liquid-motion-squash', String(1 - amount * .035));
|
|
13818
|
+
if (window.__liquidPerfCounts) float.dataset.liquidMotionAmount = amount.toFixed(4);
|
|
13819
|
+
if (moved && float._renderKyantGlass) float._renderKyantGlass({ left:left, top:top, width:width, height:height }, true);
|
|
13820
|
+
float._liquidMotionFrame = requestAnimationFrame(frame);
|
|
13821
|
+
}
|
|
13822
|
+
float._liquidMotionFrame = requestAnimationFrame(frame);
|
|
13823
|
+
}
|
|
13824
|
+
|
|
13825
|
+
function stopLiquidMotionTracking(float) {
|
|
13826
|
+
if (!float) return;
|
|
13827
|
+
if (float._liquidArrivalFrame) cancelAnimationFrame(float._liquidArrivalFrame);
|
|
13828
|
+
float._liquidArrivalFrame = 0;
|
|
13829
|
+
if (float._liquidMotionFrame) cancelAnimationFrame(float._liquidMotionFrame);
|
|
13830
|
+
float._liquidMotionFrame = 0;
|
|
13831
|
+
float.style.removeProperty('--liquid-motion-angle');
|
|
13832
|
+
float.style.removeProperty('--liquid-motion-stretch');
|
|
13833
|
+
float.style.removeProperty('--liquid-motion-squash');
|
|
13834
|
+
}
|
|
13835
|
+
|
|
13836
|
+
function waitForLiquidSelectionArrival(float, onArrive) {
|
|
13837
|
+
if (!float || !float.isConnected) { onArrive(); return; }
|
|
13838
|
+
var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
13839
|
+
if (reducedMotion) { onArrive(); return; }
|
|
13840
|
+
if (float._liquidArrivalFrame) cancelAnimationFrame(float._liquidArrivalFrame);
|
|
13841
|
+
var startedAt = performance.now();
|
|
13842
|
+
function settled() {
|
|
13843
|
+
if (!float || !float.isConnected) return true;
|
|
13844
|
+
var target = float._liquidTargetRect;
|
|
13845
|
+
if (!target) return true;
|
|
13846
|
+
var style = getComputedStyle(float);
|
|
13847
|
+
var left = parseFloat(style.left) || 0;
|
|
13848
|
+
var top = parseFloat(style.top) || 0;
|
|
13849
|
+
var width = parseFloat(style.width) || 0;
|
|
13850
|
+
var height = parseFloat(style.height) || 0;
|
|
13851
|
+
return Math.abs(left - target.left) < .75 && Math.abs(top - target.top) < .75 &&
|
|
13852
|
+
Math.abs(width - target.width) < .75 && Math.abs(height - target.height) < .75 &&
|
|
13853
|
+
parseFloat(style.opacity || '0') >= .995;
|
|
13854
|
+
}
|
|
13855
|
+
function inspect() {
|
|
13856
|
+
if (settled() || performance.now() - startedAt > 300) {
|
|
13857
|
+
float._liquidArrivalFrame = 0;
|
|
13858
|
+
onArrive();
|
|
13859
|
+
return;
|
|
13860
|
+
}
|
|
13861
|
+
float._liquidArrivalFrame = requestAnimationFrame(inspect);
|
|
13862
|
+
}
|
|
13863
|
+
float._liquidArrivalFrame = requestAnimationFrame(inspect);
|
|
13864
|
+
}
|
|
13865
|
+
|
|
13866
|
+
function landLiquidSelectionFloat(float, cleanup, onComplete) {
|
|
13867
|
+
if (!float) { cleanup(); if (onComplete) onComplete(); return; }
|
|
13868
|
+
var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
13869
|
+
if (!float.isConnected) { cleanup(); if (onComplete) onComplete(); return; }
|
|
13870
|
+
stopLiquidMotionTracking(float);
|
|
13871
|
+
float.classList.add('liquid-selection-landing');
|
|
13872
|
+
float._liquidLandingTimer = setTimeout(function() {
|
|
13873
|
+
cleanup();
|
|
13874
|
+
scheduleLiquidBackdropPrime();
|
|
13875
|
+
if (onComplete) onComplete();
|
|
13876
|
+
}, reducedMotion ? 0 : 120);
|
|
13877
|
+
}
|
|
13878
|
+
|
|
13879
|
+
function wireLiquidMenuInteractions(menu) {
|
|
13880
|
+
if (!menu || menu.dataset.liquidInteractions === 'true') return;
|
|
13881
|
+
menu.dataset.liquidInteractions = 'true';
|
|
13882
|
+
var pointer = null;
|
|
13883
|
+
var source = null;
|
|
13884
|
+
var target = null;
|
|
13885
|
+
var float = null;
|
|
13886
|
+
var generation = 0;
|
|
13887
|
+
var pressX = 0;
|
|
13888
|
+
var pressY = 0;
|
|
13889
|
+
var gestureMoved = false;
|
|
13890
|
+
function optionAt(event) {
|
|
13891
|
+
var hit = event && document.elementFromPoint ? document.elementFromPoint(event.clientX, event.clientY) : event.target;
|
|
13892
|
+
var node = hit && hit.closest ? hit.closest('button.model-select-menu-option, button.newmark-select-option') : null;
|
|
13893
|
+
return node && menu.contains(node) && !node.disabled ? node : null;
|
|
13894
|
+
}
|
|
13895
|
+
function position(option) {
|
|
13896
|
+
if (!option || !float) return;
|
|
13897
|
+
if (float.classList.contains('liquid-selection-viewport')) {
|
|
13898
|
+
var rect = option.getBoundingClientRect();
|
|
13899
|
+
var edge = liquidInteractionEdgePx();
|
|
13900
|
+
keepLiquidFloatExpansionInsideViewport(float, rect);
|
|
13901
|
+
float._liquidTargetRect = { left: rect.left - edge, top: rect.top - edge, width: rect.width + edge * 2, height: rect.height + edge * 2 };
|
|
13902
|
+
float.style.left = (rect.left - edge) + 'px';
|
|
13903
|
+
float.style.top = (rect.top - edge) + 'px';
|
|
13904
|
+
float.style.width = (rect.width + edge * 2) + 'px';
|
|
13905
|
+
float.style.height = (rect.height + edge * 2) + 'px';
|
|
13906
|
+
if (float._renderKyantGlass) float._renderKyantGlass({
|
|
13907
|
+
left: rect.left - edge, top: rect.top - edge,
|
|
13908
|
+
width: rect.width + edge * 2, height: rect.height + edge * 2
|
|
13909
|
+
}, true);
|
|
13910
|
+
return;
|
|
13911
|
+
}
|
|
13912
|
+
var edge = liquidInteractionEdgePx();
|
|
13913
|
+
float.style.left = (option.offsetLeft - edge) + 'px';
|
|
13914
|
+
float.style.top = (option.offsetTop - edge) + 'px';
|
|
13915
|
+
float.style.width = (option.offsetWidth + edge * 2) + 'px';
|
|
13916
|
+
float.style.height = (option.offsetHeight + edge * 2) + 'px';
|
|
13917
|
+
}
|
|
13918
|
+
function positionAlongPointer(event) {
|
|
13919
|
+
if (!float || !event) return;
|
|
13920
|
+
var menuRect = menu.getBoundingClientRect();
|
|
13921
|
+
var clipTop = Math.max(0, menuRect.top + menu.clientTop + 7);
|
|
13922
|
+
var clipBottom = Math.min(window.innerHeight, menuRect.bottom - menu.clientTop - 7);
|
|
13923
|
+
var allOptions = Array.from(menu.querySelectorAll('button.model-select-menu-option, button.newmark-select-option'))
|
|
13924
|
+
.filter(function(option) { return !option.disabled && option.getClientRects().length; });
|
|
13925
|
+
var options = allOptions.filter(function(option) {
|
|
13926
|
+
var rect = option.getBoundingClientRect();
|
|
13927
|
+
return rect.bottom > clipTop && rect.top < clipBottom;
|
|
13928
|
+
});
|
|
13929
|
+
if (!options.length) return;
|
|
13930
|
+
var nearest = options[0];
|
|
13931
|
+
var nearestDistance = Infinity;
|
|
13932
|
+
options.forEach(function(option) {
|
|
13933
|
+
var rect = option.getBoundingClientRect();
|
|
13934
|
+
var distance = Math.abs(event.clientY - (rect.top + rect.height / 2));
|
|
13935
|
+
if (distance < nearestDistance) { nearest = option; nearestDistance = distance; }
|
|
13936
|
+
});
|
|
13937
|
+
target = nearest;
|
|
13938
|
+
var rect = nearest.getBoundingClientRect();
|
|
13939
|
+
var firstRect = options[0].getBoundingClientRect();
|
|
13940
|
+
var lastRect = options[options.length - 1].getBoundingClientRect();
|
|
13941
|
+
var minCenter = Math.min(firstRect.top + firstRect.height / 2, lastRect.top + lastRect.height / 2);
|
|
13942
|
+
var maxCenter = Math.max(firstRect.top + firstRect.height / 2, lastRect.top + lastRect.height / 2);
|
|
13943
|
+
if (maxCenter < minCenter) maxCenter = minCenter;
|
|
13944
|
+
var centerY = Math.max(minCenter, Math.min(maxCenter, event.clientY));
|
|
13945
|
+
var edge = liquidInteractionEdgePx();
|
|
13946
|
+
float.style.left = (rect.left - edge) + 'px';
|
|
13947
|
+
float.style.top = (centerY - rect.height / 2 - edge) + 'px';
|
|
13948
|
+
float.style.width = (rect.width + edge * 2) + 'px';
|
|
13949
|
+
float.style.height = (rect.height + edge * 2) + 'px';
|
|
13950
|
+
}
|
|
13951
|
+
function detachWindowTracking() {
|
|
13952
|
+
window.removeEventListener('pointermove', onPointerMove, true);
|
|
13953
|
+
window.removeEventListener('pointerup', onPointerUp, true);
|
|
13954
|
+
window.removeEventListener('pointercancel', onPointerCancel, true);
|
|
13955
|
+
}
|
|
13956
|
+
function clear() {
|
|
13957
|
+
detachWindowTracking();
|
|
13958
|
+
if (source) source.classList.remove('liquid-selection-source');
|
|
13959
|
+
stopLiquidMotionTracking(float);
|
|
13960
|
+
if (float && float._liquidLandingTimer) clearTimeout(float._liquidLandingTimer);
|
|
13961
|
+
if (float && float._releaseKyantGlass) float._releaseKyantGlass();
|
|
13962
|
+
if (float && float.parentNode) float.parentNode.removeChild(float);
|
|
13963
|
+
generation += 1;
|
|
13964
|
+
pointer = source = target = float = null;
|
|
13965
|
+
}
|
|
13966
|
+
function onPointerMove(event) {
|
|
13967
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
13968
|
+
if (!gestureMoved && Math.hypot(event.clientX - pressX, event.clientY - pressY) >= 4) {
|
|
13969
|
+
gestureMoved = true;
|
|
13970
|
+
}
|
|
13971
|
+
event.preventDefault();
|
|
13972
|
+
positionAlongPointer(event);
|
|
13973
|
+
}
|
|
13974
|
+
function onPointerUp(event) {
|
|
13975
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
13976
|
+
var commit = target;
|
|
13977
|
+
detachWindowTracking();
|
|
13978
|
+
try { menu.releasePointerCapture(pointer); } catch (_) {}
|
|
13979
|
+
pointer = null;
|
|
13980
|
+
if (commit) position(commit);
|
|
13981
|
+
waitForLiquidSelectionArrival(float, function() {
|
|
13982
|
+
landLiquidSelectionFloat(float, clear, function() {
|
|
13983
|
+
if (commit && commit.isConnected) commit.click();
|
|
13984
|
+
});
|
|
13985
|
+
});
|
|
13986
|
+
}
|
|
13987
|
+
function onPointerCancel(event) {
|
|
13988
|
+
if (pointer === null || event.pointerId === pointer) clear();
|
|
13989
|
+
}
|
|
13990
|
+
menu.addEventListener('pointerdown', function(event) {
|
|
13991
|
+
var pressed = optionAt(event);
|
|
13992
|
+
if (!pressed || event.button !== 0) return;
|
|
13993
|
+
event.preventDefault();
|
|
13994
|
+
target = pressed;
|
|
13995
|
+
pointer = event.pointerId;
|
|
13996
|
+
pressX = event.clientX;
|
|
13997
|
+
pressY = event.clientY;
|
|
13998
|
+
gestureMoved = false;
|
|
13999
|
+
var captureGeneration = ++generation;
|
|
14000
|
+
menu.setPointerCapture(pointer);
|
|
14001
|
+
window.addEventListener('pointermove', onPointerMove, true);
|
|
14002
|
+
window.addEventListener('pointerup', onPointerUp, true);
|
|
14003
|
+
window.addEventListener('pointercancel', onPointerCancel, true);
|
|
14004
|
+
if (float && float.isConnected) {
|
|
14005
|
+
if (float._liquidLandingTimer) clearTimeout(float._liquidLandingTimer);
|
|
14006
|
+
float._liquidLandingTimer = 0;
|
|
14007
|
+
float.classList.remove('liquid-selection-landing');
|
|
14008
|
+
float.classList.add('visible');
|
|
14009
|
+
position(target);
|
|
14010
|
+
} else {
|
|
14011
|
+
var selected = menu.querySelector('button.model-select-menu-option.selected, button.newmark-select-option.selected');
|
|
14012
|
+
source = selected || pressed;
|
|
14013
|
+
source.classList.add('liquid-selection-source');
|
|
14014
|
+
float = document.createElement('div');
|
|
14015
|
+
float.className = 'liquid-selection-float liquid-selection-primed';
|
|
14016
|
+
mountLiquidViewportFloat(float);
|
|
14017
|
+
position(source);
|
|
14018
|
+
showLiquidViewportFloat(float);
|
|
14019
|
+
position(source);
|
|
14020
|
+
float.getBoundingClientRect();
|
|
14021
|
+
requestAnimationFrame(function() {
|
|
14022
|
+
if (!float) return;
|
|
14023
|
+
float.classList.remove('liquid-selection-primed');
|
|
14024
|
+
float.classList.add('visible');
|
|
14025
|
+
requestAnimationFrame(function() { if (float) position(target); });
|
|
14026
|
+
});
|
|
14027
|
+
}
|
|
14028
|
+
captureLiquidBackdropFrame().then(function(backdropFrame) {
|
|
14029
|
+
if (generation === captureGeneration && float && float.isConnected) attachKyantLiquidRenderer(float, backdropFrame);
|
|
14030
|
+
}).catch(function() {});
|
|
14031
|
+
});
|
|
14032
|
+
menu.addEventListener('click', function(event) {
|
|
14033
|
+
if (!event.isTrusted || !float || !float.isConnected) return;
|
|
14034
|
+
event.preventDefault();
|
|
14035
|
+
event.stopImmediatePropagation();
|
|
14036
|
+
}, true);
|
|
14037
|
+
}
|
|
14038
|
+
|
|
14039
|
+
// The same single floating capsule is used by the left workspace rail and
|
|
14040
|
+
// right sidebar tabs. Delegation keeps it valid while either rail rerenders.
|
|
14041
|
+
function wireLiquidRailInteractions(container, selector) {
|
|
14042
|
+
if (!container || container.dataset.liquidRailInteractions === 'true') return;
|
|
14043
|
+
container.dataset.liquidRailInteractions = 'true';
|
|
14044
|
+
var pointer = null;
|
|
14045
|
+
var source = null;
|
|
14046
|
+
var target = null;
|
|
14047
|
+
var float = null;
|
|
14048
|
+
var generation = 0;
|
|
14049
|
+
var pressX = 0;
|
|
14050
|
+
var pressY = 0;
|
|
14051
|
+
var gestureMoved = false;
|
|
14052
|
+
function itemAt(event) {
|
|
14053
|
+
var hit = event && document.elementFromPoint ? document.elementFromPoint(event.clientX, event.clientY) : event.target;
|
|
14054
|
+
var node = hit && hit.closest ? hit.closest(selector) : null;
|
|
14055
|
+
return node && container.contains(node) && !node.disabled ? node : null;
|
|
14056
|
+
}
|
|
14057
|
+
function position(item) {
|
|
14058
|
+
if (!item || !float) return;
|
|
14059
|
+
if (float.classList.contains('liquid-selection-viewport')) {
|
|
14060
|
+
var rect = item.getBoundingClientRect();
|
|
14061
|
+
var edge = liquidInteractionEdgePx();
|
|
14062
|
+
keepLiquidFloatExpansionInsideViewport(float, rect);
|
|
14063
|
+
float._liquidTargetRect = { left: rect.left - edge, top: rect.top - edge, width: rect.width + edge * 2, height: rect.height + edge * 2 };
|
|
14064
|
+
float.style.left = (rect.left - edge) + 'px';
|
|
14065
|
+
float.style.top = (rect.top - edge) + 'px';
|
|
14066
|
+
float.style.width = (rect.width + edge * 2) + 'px';
|
|
14067
|
+
float.style.height = (rect.height + edge * 2) + 'px';
|
|
14068
|
+
if (float._renderKyantGlass) float._renderKyantGlass({
|
|
14069
|
+
left: rect.left - edge, top: rect.top - edge,
|
|
14070
|
+
width: rect.width + edge * 2, height: rect.height + edge * 2
|
|
14071
|
+
}, true);
|
|
14072
|
+
return;
|
|
14073
|
+
}
|
|
14074
|
+
var edge = liquidInteractionEdgePx();
|
|
14075
|
+
float.style.left = (item.offsetLeft - edge) + 'px';
|
|
14076
|
+
float.style.top = (item.offsetTop - edge) + 'px';
|
|
14077
|
+
float.style.width = (item.offsetWidth + edge * 2) + 'px';
|
|
14078
|
+
float.style.height = (item.offsetHeight + edge * 2) + 'px';
|
|
14079
|
+
}
|
|
14080
|
+
function positionAlongPointer(event) {
|
|
14081
|
+
if (!float || !event) return;
|
|
14082
|
+
var items = Array.from(container.querySelectorAll(selector))
|
|
14083
|
+
.filter(function(item) { return !item.disabled && item.getClientRects().length; });
|
|
14084
|
+
if (!items.length) return;
|
|
14085
|
+
var rects = items.map(function(item) { return item.getBoundingClientRect(); });
|
|
14086
|
+
var centersX = rects.map(function(rect) { return rect.left + rect.width / 2; });
|
|
14087
|
+
var centersY = rects.map(function(rect) { return rect.top + rect.height / 2; });
|
|
14088
|
+
var horizontal = Math.max.apply(null, centersX) - Math.min.apply(null, centersX) >
|
|
14089
|
+
Math.max.apply(null, centersY) - Math.min.apply(null, centersY);
|
|
14090
|
+
var pointerAxis = horizontal ? event.clientX : event.clientY;
|
|
14091
|
+
var centers = horizontal ? centersX : centersY;
|
|
14092
|
+
var nearestIndex = 0;
|
|
14093
|
+
var nearestDistance = Infinity;
|
|
14094
|
+
centers.forEach(function(center, index) {
|
|
14095
|
+
var distance = Math.abs(pointerAxis - center);
|
|
14096
|
+
if (distance < nearestDistance) { nearestIndex = index; nearestDistance = distance; }
|
|
14097
|
+
});
|
|
14098
|
+
target = items[nearestIndex];
|
|
14099
|
+
var rect = rects[nearestIndex];
|
|
14100
|
+
var minCenter = Math.min.apply(null, centers);
|
|
14101
|
+
var maxCenter = Math.max.apply(null, centers);
|
|
14102
|
+
var projectedCenter = Math.max(minCenter, Math.min(maxCenter, pointerAxis));
|
|
14103
|
+
var edge = liquidInteractionEdgePx();
|
|
14104
|
+
var left = horizontal ? projectedCenter - rect.width / 2 - edge : rect.left - edge;
|
|
14105
|
+
var top = horizontal ? rect.top - edge : projectedCenter - rect.height / 2 - edge;
|
|
14106
|
+
float.style.left = left + 'px';
|
|
14107
|
+
float.style.top = top + 'px';
|
|
14108
|
+
float.style.width = (rect.width + edge * 2) + 'px';
|
|
14109
|
+
float.style.height = (rect.height + edge * 2) + 'px';
|
|
14110
|
+
}
|
|
14111
|
+
function detachWindowTracking() {
|
|
14112
|
+
window.removeEventListener('pointermove', onPointerMove, true);
|
|
14113
|
+
window.removeEventListener('pointerup', onPointerUp, true);
|
|
14114
|
+
window.removeEventListener('pointercancel', onPointerCancel, true);
|
|
14115
|
+
}
|
|
14116
|
+
function clear() {
|
|
14117
|
+
detachWindowTracking();
|
|
14118
|
+
if (source) source.classList.remove('liquid-selection-source');
|
|
14119
|
+
stopLiquidMotionTracking(float);
|
|
14120
|
+
if (float && float._liquidLandingTimer) clearTimeout(float._liquidLandingTimer);
|
|
14121
|
+
if (float && float._releaseKyantGlass) float._releaseKyantGlass();
|
|
14122
|
+
if (float && float.parentNode) float.parentNode.removeChild(float);
|
|
14123
|
+
generation += 1;
|
|
14124
|
+
pointer = source = target = float = null;
|
|
14125
|
+
window.setLiquidSidebarGestureLock('rail:' + (container.id || selector), false);
|
|
14126
|
+
}
|
|
14127
|
+
function onPointerMove(event) {
|
|
14128
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
14129
|
+
if (!gestureMoved && Math.hypot(event.clientX - pressX, event.clientY - pressY) >= 4) {
|
|
14130
|
+
gestureMoved = true;
|
|
14131
|
+
}
|
|
14132
|
+
event.preventDefault();
|
|
14133
|
+
positionAlongPointer(event);
|
|
14134
|
+
}
|
|
14135
|
+
function onPointerUp(event) {
|
|
14136
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
14137
|
+
var commit = target;
|
|
14138
|
+
detachWindowTracking();
|
|
14139
|
+
try { container.releasePointerCapture(pointer); } catch (_) {}
|
|
14140
|
+
pointer = null;
|
|
14141
|
+
if (commit) {
|
|
14142
|
+
position(commit);
|
|
14143
|
+
}
|
|
14144
|
+
waitForLiquidSelectionArrival(float, function() {
|
|
14145
|
+
landLiquidSelectionFloat(float, clear, function() {
|
|
14146
|
+
if (!commit || !commit.isConnected) return;
|
|
14147
|
+
if (container.id === 'left-tool-surface' || container.id === 'left-thumb') {
|
|
14148
|
+
container.querySelectorAll('.liquid-tool-active').forEach(function(item) { item.classList.remove('liquid-tool-active'); });
|
|
14149
|
+
commit.classList.add('liquid-tool-active');
|
|
14150
|
+
}
|
|
14151
|
+
commit.click();
|
|
14152
|
+
});
|
|
14153
|
+
});
|
|
14154
|
+
}
|
|
14155
|
+
function onPointerCancel(event) {
|
|
14156
|
+
if (pointer === null || event.pointerId === pointer) clear();
|
|
14157
|
+
}
|
|
14158
|
+
container.addEventListener('pointerdown', function(event) {
|
|
14159
|
+
if (window.liquidSidebarGesturesLocked() && !(float && float.isConnected)) return;
|
|
14160
|
+
var pressed = itemAt(event);
|
|
14161
|
+
if (!pressed || event.button !== 0) return;
|
|
14162
|
+
event.preventDefault();
|
|
14163
|
+
target = pressed;
|
|
14164
|
+
pointer = event.pointerId;
|
|
14165
|
+
pressX = event.clientX;
|
|
14166
|
+
pressY = event.clientY;
|
|
14167
|
+
gestureMoved = false;
|
|
14168
|
+
var captureGeneration = ++generation;
|
|
14169
|
+
window.setLiquidSidebarGestureLock('rail:' + (container.id || selector), true);
|
|
14170
|
+
container.setPointerCapture(pointer);
|
|
14171
|
+
window.addEventListener('pointermove', onPointerMove, true);
|
|
14172
|
+
window.addEventListener('pointerup', onPointerUp, true);
|
|
14173
|
+
window.addEventListener('pointercancel', onPointerCancel, true);
|
|
14174
|
+
if (float && float.isConnected) {
|
|
14175
|
+
if (float._liquidLandingTimer) clearTimeout(float._liquidLandingTimer);
|
|
14176
|
+
float._liquidLandingTimer = 0;
|
|
14177
|
+
float.classList.remove('liquid-selection-landing');
|
|
14178
|
+
float.classList.add('visible');
|
|
14179
|
+
position(target);
|
|
14180
|
+
} else {
|
|
14181
|
+
source = container.querySelector('.liquid-tool-active') || container.querySelector(selector + '.active') || pressed;
|
|
14182
|
+
source.classList.add('liquid-selection-source');
|
|
14183
|
+
float = document.createElement('div');
|
|
14184
|
+
float.className = 'liquid-selection-float liquid-selection-primed';
|
|
14185
|
+
mountLiquidViewportFloat(float);
|
|
14186
|
+
position(source);
|
|
14187
|
+
showLiquidViewportFloat(float);
|
|
14188
|
+
position(source);
|
|
14189
|
+
float.getBoundingClientRect();
|
|
14190
|
+
requestAnimationFrame(function() {
|
|
14191
|
+
if (!float) return;
|
|
14192
|
+
float.classList.remove('liquid-selection-primed');
|
|
14193
|
+
float.classList.add('visible');
|
|
14194
|
+
requestAnimationFrame(function() { if (float) position(target); });
|
|
14195
|
+
});
|
|
14196
|
+
}
|
|
14197
|
+
captureLiquidBackdropFrame().then(function(backdropFrame) {
|
|
14198
|
+
if (generation === captureGeneration && float && float.isConnected) attachKyantLiquidRenderer(float, backdropFrame);
|
|
14199
|
+
}).catch(function() {});
|
|
14200
|
+
});
|
|
14201
|
+
container.addEventListener('click', function(event) {
|
|
14202
|
+
if (!event.isTrusted || !float || !float.isConnected) return;
|
|
14203
|
+
event.preventDefault();
|
|
14204
|
+
event.stopImmediatePropagation();
|
|
14205
|
+
}, true);
|
|
14206
|
+
}
|
|
14207
|
+
|
|
14208
|
+
requestAnimationFrame(function() {
|
|
14209
|
+
wireLiquidRailInteractions(document.getElementById('mode-toggle'), '.mode-toggle-btn[data-mode]');
|
|
14210
|
+
wireLiquidRailInteractions(document.getElementById('left-tool-surface'), '.left-nav-icon');
|
|
14211
|
+
wireLiquidRailInteractions(document.getElementById('left-thumb'), '.lt-item');
|
|
14212
|
+
wireLiquidRailInteractions(document.getElementById('left-ws-list'), '.left-ws-item');
|
|
14213
|
+
wireLiquidRailInteractions(document.getElementById('right-tabs'), '.tab-btn[data-tab]');
|
|
14214
|
+
});
|
|
14215
|
+
|
|
14216
|
+
// Checkbox switches use one pointer-captured glass thumb that follows the
|
|
14217
|
+
// pointer continuously, then commits to the nearest binary stop on release.
|
|
14218
|
+
document.addEventListener('pointerdown', function(event) {
|
|
14219
|
+
var input = event.target && event.target.closest ? event.target.closest('input[type="checkbox"]') : null;
|
|
14220
|
+
if (!input || input.disabled || event.button !== 0) return;
|
|
14221
|
+
event.preventDefault();
|
|
14222
|
+
var rect = input.getBoundingClientRect();
|
|
14223
|
+
var pointer = event.pointerId;
|
|
14224
|
+
var fraction = input.checked ? 1 : 0;
|
|
14225
|
+
var startX = event.clientX;
|
|
14226
|
+
var startY = event.clientY;
|
|
14227
|
+
var dragging = false;
|
|
14228
|
+
var canceledToScroll = false;
|
|
14229
|
+
var dragThreshold = event.pointerType === 'mouse' ? 4 : 8;
|
|
14230
|
+
input.dataset.liquidSuppressClick = 'true';
|
|
14231
|
+
clearTimeout(input._liquidSuppressClickTimer);
|
|
14232
|
+
input._liquidSuppressClickTimer = setTimeout(function() {
|
|
14233
|
+
delete input.dataset.liquidSuppressClick;
|
|
14234
|
+
delete input._liquidSuppressClickTimer;
|
|
14235
|
+
}, 700);
|
|
14236
|
+
function update(clientX) {
|
|
14237
|
+
fraction = ((clientX - rect.left - 12) / Math.max(1, rect.width - 24));
|
|
14238
|
+
fraction = Math.max(0, Math.min(1, fraction));
|
|
14239
|
+
input.style.setProperty('--liquid-switch-x', (18 * fraction) + 'px');
|
|
14240
|
+
input.style.setProperty('--liquid-switch-progress', (100 * fraction) + '%');
|
|
14241
|
+
}
|
|
14242
|
+
function finish(commit) {
|
|
14243
|
+
input.removeEventListener('pointermove', move);
|
|
14244
|
+
input.removeEventListener('pointerup', up);
|
|
14245
|
+
input.removeEventListener('pointercancel', cancel);
|
|
14246
|
+
input.classList.remove('liquid-switch-dragging');
|
|
14247
|
+
input.style.removeProperty('--liquid-switch-x');
|
|
14248
|
+
input.style.removeProperty('--liquid-switch-progress');
|
|
14249
|
+
try { input.releasePointerCapture(pointer); } catch (_) {}
|
|
14250
|
+
if (!commit || canceledToScroll) return;
|
|
14251
|
+
var next = dragging ? fraction >= 0.5 : !input.checked;
|
|
14252
|
+
var changed = input.checked !== next;
|
|
14253
|
+
input.checked = next;
|
|
14254
|
+
if (changed) {
|
|
14255
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
14256
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
14257
|
+
}
|
|
14258
|
+
}
|
|
14259
|
+
function move(moveEvent) {
|
|
14260
|
+
if (moveEvent.pointerId !== pointer) return;
|
|
14261
|
+
if (!dragging) {
|
|
14262
|
+
var deltaX = moveEvent.clientX - startX;
|
|
14263
|
+
var deltaY = moveEvent.clientY - startY;
|
|
14264
|
+
if (Math.hypot(deltaX, deltaY) < dragThreshold) return;
|
|
14265
|
+
if (Math.abs(deltaY) > Math.abs(deltaX)) {
|
|
14266
|
+
canceledToScroll = true;
|
|
14267
|
+
return;
|
|
14268
|
+
}
|
|
14269
|
+
dragging = true;
|
|
14270
|
+
}
|
|
14271
|
+
moveEvent.preventDefault();
|
|
14272
|
+
update(moveEvent.clientX);
|
|
14273
|
+
}
|
|
14274
|
+
function up(upEvent) {
|
|
14275
|
+
if (upEvent.pointerId === pointer) finish(true);
|
|
14276
|
+
}
|
|
14277
|
+
function cancel(cancelEvent) {
|
|
14278
|
+
if (cancelEvent.pointerId === pointer) finish(false);
|
|
14279
|
+
}
|
|
14280
|
+
input.classList.add('liquid-switch-dragging');
|
|
14281
|
+
input.setPointerCapture(pointer);
|
|
14282
|
+
input.addEventListener('pointermove', move);
|
|
14283
|
+
input.addEventListener('pointerup', up);
|
|
14284
|
+
input.addEventListener('pointercancel', cancel);
|
|
14285
|
+
}, true);
|
|
14286
|
+
document.addEventListener('click', function(event) {
|
|
14287
|
+
var input = event.target && event.target.closest ? event.target.closest('input[type="checkbox"]') : null;
|
|
14288
|
+
if (!input || input.dataset.liquidSuppressClick !== 'true') return;
|
|
14289
|
+
clearTimeout(input._liquidSuppressClickTimer);
|
|
14290
|
+
delete input.dataset.liquidSuppressClick;
|
|
14291
|
+
delete input._liquidSuppressClickTimer;
|
|
14292
|
+
event.preventDefault();
|
|
14293
|
+
event.stopImmediatePropagation();
|
|
14294
|
+
}, true);
|
|
14295
|
+
|
|
14296
|
+
// Glass-strength range mirrors the binary liquid switch: its existing color
|
|
14297
|
+
// thumb lifts into one glass capsule, follows the pointer continuously, then
|
|
14298
|
+
// falls back to the color thumb and persists the released value.
|
|
14299
|
+
document.addEventListener('pointerdown', function(event) {
|
|
14300
|
+
var input = event.target && event.target.closest ? event.target.closest('#settings-glass-opacity') : null;
|
|
14301
|
+
if (!input || input.disabled || event.button !== 0) return;
|
|
14302
|
+
event.preventDefault();
|
|
14303
|
+
var rect = input.getBoundingClientRect();
|
|
14304
|
+
var pointer = event.pointerId;
|
|
14305
|
+
var minimum = Number(input.min || 0);
|
|
14306
|
+
var maximum = Number(input.max || 100);
|
|
14307
|
+
var step = Math.max(0.0001, Number(input.step || 1));
|
|
14308
|
+
var initialValue = input.value;
|
|
14309
|
+
function update(clientX) {
|
|
14310
|
+
var fraction = Math.max(0, Math.min(1, (clientX - rect.left) / Math.max(1, rect.width)));
|
|
14311
|
+
var raw = minimum + fraction * (maximum - minimum);
|
|
14312
|
+
var next = Math.min(maximum, Math.max(minimum, Math.round((raw - minimum) / step) * step + minimum));
|
|
14313
|
+
input.value = String(next);
|
|
14314
|
+
input.style.setProperty('--liquid-range-progress', (fraction * 100) + '%');
|
|
14315
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
14316
|
+
}
|
|
14317
|
+
function finish(commit) {
|
|
14318
|
+
input.removeEventListener('pointermove', move);
|
|
14319
|
+
input.removeEventListener('pointerup', up);
|
|
14320
|
+
input.removeEventListener('pointercancel', cancel);
|
|
14321
|
+
input.classList.remove('liquid-range-dragging');
|
|
14322
|
+
try { input.releasePointerCapture(pointer); } catch (_) {}
|
|
14323
|
+
if (!commit) {
|
|
14324
|
+
input.value = initialValue;
|
|
14325
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
14326
|
+
return;
|
|
14327
|
+
}
|
|
14328
|
+
input.dataset.liquidSuppressRangeClick = 'true';
|
|
14329
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
14330
|
+
}
|
|
14331
|
+
function move(moveEvent) { if (moveEvent.pointerId === pointer) { moveEvent.preventDefault(); update(moveEvent.clientX); } }
|
|
14332
|
+
function up(upEvent) { if (upEvent.pointerId === pointer) finish(true); }
|
|
14333
|
+
function cancel(cancelEvent) { if (cancelEvent.pointerId === pointer) finish(false); }
|
|
14334
|
+
input.classList.add('liquid-range-dragging');
|
|
14335
|
+
update(event.clientX);
|
|
14336
|
+
input.setPointerCapture(pointer);
|
|
14337
|
+
input.addEventListener('pointermove', move);
|
|
14338
|
+
input.addEventListener('pointerup', up);
|
|
14339
|
+
input.addEventListener('pointercancel', cancel);
|
|
14340
|
+
}, true);
|
|
14341
|
+
document.addEventListener('click', function(event) {
|
|
14342
|
+
var input = event.target && event.target.closest ? event.target.closest('#settings-glass-opacity') : null;
|
|
14343
|
+
if (!input || input.dataset.liquidSuppressRangeClick !== 'true') return;
|
|
14344
|
+
delete input.dataset.liquidSuppressRangeClick;
|
|
14345
|
+
event.preventDefault();
|
|
14346
|
+
event.stopImmediatePropagation();
|
|
14347
|
+
}, true);
|
|
14348
|
+
|
|
12698
14349
|
window.syncModelSelectSurface = function() {
|
|
12699
14350
|
var sel = els['model-select'] || document.getElementById('model-select');
|
|
12700
14351
|
var label = document.getElementById('model-select-label');
|
|
@@ -12716,6 +14367,7 @@ window.syncModelSelectSurface = function() {
|
|
|
12716
14367
|
}
|
|
12717
14368
|
});
|
|
12718
14369
|
menu.innerHTML = html;
|
|
14370
|
+
wireLiquidMenuInteractions(menu);
|
|
12719
14371
|
};
|
|
12720
14372
|
|
|
12721
14373
|
document.addEventListener('pointerdown', function(event) {
|
|
@@ -12727,6 +14379,7 @@ window.closeNewmarkSelect = function(shell, options) {
|
|
|
12727
14379
|
shell = shell || document.querySelector('.newmark-select-shell.open');
|
|
12728
14380
|
if (!shell) return;
|
|
12729
14381
|
shell.classList.remove('open');
|
|
14382
|
+
window.setLiquidSidebarGestureLock('popup:select:' + (shell.dataset.selectId || 'anonymous'), false);
|
|
12730
14383
|
var menu = shell.querySelector('.newmark-select-menu');
|
|
12731
14384
|
if (menu && typeof menu.hidePopover === 'function' && menu.matches(':popover-open')) menu.hidePopover();
|
|
12732
14385
|
var button = shell.querySelector('.newmark-select-button');
|
|
@@ -12810,6 +14463,7 @@ window.syncNewmarkSelect = function(select) {
|
|
|
12810
14463
|
}
|
|
12811
14464
|
});
|
|
12812
14465
|
menu.innerHTML = html;
|
|
14466
|
+
wireLiquidMenuInteractions(menu);
|
|
12813
14467
|
};
|
|
12814
14468
|
|
|
12815
14469
|
window.enhanceNewmarkSelect = function(select) {
|
|
@@ -12850,7 +14504,7 @@ window.enhanceNewmarkSelect = function(select) {
|
|
|
12850
14504
|
button.setAttribute('aria-label', String(accessibleLabel).trim());
|
|
12851
14505
|
button.innerHTML = '<span class="newmark-select-button-label"></span>' + iconSvg('chevron-down', '', 'tiny');
|
|
12852
14506
|
var menu = document.createElement('div');
|
|
12853
|
-
menu.className = 'model-select-menu newmark-select-menu';
|
|
14507
|
+
menu.className = 'model-select-menu newmark-select-menu liquid-glass liquid-glass-carrier';
|
|
12854
14508
|
menu.setAttribute('role', 'listbox');
|
|
12855
14509
|
menu.setAttribute('popover', 'manual');
|
|
12856
14510
|
select.parentNode.insertBefore(shell, select);
|
|
@@ -12871,6 +14525,7 @@ window.enhanceNewmarkSelect = function(select) {
|
|
|
12871
14525
|
}
|
|
12872
14526
|
window.syncNewmarkSelect(select);
|
|
12873
14527
|
shell.classList.add('open');
|
|
14528
|
+
window.setLiquidSidebarGestureLock('popup:select:' + (shell.dataset.selectId || 'anonymous'), true);
|
|
12874
14529
|
button.setAttribute('aria-expanded', 'true');
|
|
12875
14530
|
if (opening) {
|
|
12876
14531
|
if (typeof menu.showPopover === 'function' && !menu.matches(':popover-open')) menu.showPopover();
|
|
@@ -13169,6 +14824,47 @@ function backendQueueForTarget(target) {
|
|
|
13169
14824
|
return normalizeBackendQueue(state.backendQueuesByTarget[runtimeKeyFor(owner.workspaceId, owner.conversationId)]);
|
|
13170
14825
|
}
|
|
13171
14826
|
|
|
14827
|
+
function queueItemsForTarget(target) {
|
|
14828
|
+
var owner = normalizeQueuedConversationTarget(target || currentConversationTarget());
|
|
14829
|
+
if (!owner) return [];
|
|
14830
|
+
if (!state.queueItemsByTarget) state.queueItemsByTarget = {};
|
|
14831
|
+
return state.queueItemsByTarget[runtimeKeyFor(owner.workspaceId, owner.conversationId)] || [];
|
|
14832
|
+
}
|
|
14833
|
+
|
|
14834
|
+
function setQueueItemsForTarget(items, target) {
|
|
14835
|
+
var owner = normalizeQueuedConversationTarget(target || currentConversationTarget());
|
|
14836
|
+
if (!owner) return;
|
|
14837
|
+
if (!state.queueItemsByTarget) state.queueItemsByTarget = {};
|
|
14838
|
+
state.queueItemsByTarget[runtimeKeyFor(owner.workspaceId, owner.conversationId)] = (Array.isArray(items) ? items : []).map(function(item) {
|
|
14839
|
+
return {
|
|
14840
|
+
id: String(item && item.id || ''),
|
|
14841
|
+
text: String(item && item.text || ''),
|
|
14842
|
+
queueMode: String(item && item.queueMode || 'followUp'),
|
|
14843
|
+
requestedMode: String(item && item.requestedMode || 'build'),
|
|
14844
|
+
goalObjective: String(item && item.goalObjective || ''),
|
|
14845
|
+
runId: String(item && item.runId || ''),
|
|
14846
|
+
createdAt: String(item && item.createdAt || ''),
|
|
14847
|
+
};
|
|
14848
|
+
});
|
|
14849
|
+
}
|
|
14850
|
+
|
|
14851
|
+
function queueItemIdForText(text, target) {
|
|
14852
|
+
var items = queueItemsForTarget(target);
|
|
14853
|
+
var normalized = String(text || '');
|
|
14854
|
+
for (var i = 0; i < items.length; i++) {
|
|
14855
|
+
if (String(items[i].text || '') === normalized) return items[i].id;
|
|
14856
|
+
}
|
|
14857
|
+
return '';
|
|
14858
|
+
}
|
|
14859
|
+
|
|
14860
|
+
function syncQueueItemsFromSnapshot(snapshot, target) {
|
|
14861
|
+
if (snapshot && Array.isArray(snapshot.queueItems)) setQueueItemsForTarget(snapshot.queueItems, target);
|
|
14862
|
+
}
|
|
14863
|
+
|
|
14864
|
+
window.queueAction = function(action, input) {
|
|
14865
|
+
if (!api.queueAction) return Promise.resolve({ ok: false, error: 'queueAction unavailable' });
|
|
14866
|
+
return api.queueAction(action, input);
|
|
14867
|
+
};
|
|
13172
14868
|
function setBackendQueueForTarget(queue, target) {
|
|
13173
14869
|
var owner = normalizeQueuedConversationTarget(target || currentConversationTarget());
|
|
13174
14870
|
var normalized = normalizeBackendQueue(queue);
|
|
@@ -13248,19 +14944,22 @@ window.renderQueuePanel = function() {
|
|
|
13248
14944
|
if (queueIndex === state._editingQueueIndex) continue;
|
|
13249
14945
|
var request = state.nextQueueRequests[queueIndex];
|
|
13250
14946
|
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
var
|
|
13255
|
-
|
|
13256
|
-
|
|
13257
|
-
|
|
13258
|
-
|
|
13259
|
-
|
|
13260
|
-
|
|
14947
|
+
// dev-0.5.5: a backend-managed item is editable/deletable when the kernel
|
|
14948
|
+
// exposed its stable queueItem id (mobile-originated queue entries now
|
|
14949
|
+
// carry ids through queueItems). Items without an id stay locked.
|
|
14950
|
+
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[queueIndex] || '', currentConversationTarget()) : '';
|
|
14951
|
+
var operable = !backendManaged || !!queueItemId;
|
|
14952
|
+
var dragAttributes = operable
|
|
14953
|
+
? ' draggable="true" ondragstart="window.startQueueDrag(event,' + queueIndex + ')" ondragover="window.overQueueDrag(event,' + queueIndex + ')" ondrop="window.dropQueueDrag(event,' + queueIndex + ')" ondragend="window.endQueueDrag(event)"'
|
|
14954
|
+
: ' draggable="false"';
|
|
14955
|
+
var dragHandle = operable
|
|
14956
|
+
? '<button class="queue-drag" title="' + escAttr(t('queue.title')) + '">' + iconSvg('grip-vertical', t('queue.title'), 'tiny') + '</button>'
|
|
14957
|
+
: '<span class="queue-drag" aria-hidden="true">' + iconSvg('grip-vertical', t('queue.title'), 'tiny') + '</span>';
|
|
14958
|
+
var inputAttributes = ' readonly aria-readonly="true"';
|
|
14959
|
+
var actions = operable ?
|
|
13261
14960
|
'<button type="button" class="stack-icon-btn queue-guide-btn" onclick="window.guideQueueItem(' + queueIndex + ')" title="' + escAttr(t('queue.guideNow')) + '">' + iconSvg('corner-down-right', t('queue.guideNow'), 'tiny') + '<span>' + esc(t('queue.guideAction')) + '</span></button>' +
|
|
13262
14961
|
'<button class="stack-icon-btn" onclick="window.focusQueueItem(' + queueIndex + ')" title="' + escAttr(t('common.edit')) + '">' + iconSvg('square-pen', t('common.edit'), 'tiny') + '</button>' +
|
|
13263
|
-
'<button class="stack-icon-btn danger" onclick="window.deleteQueueItem(' + queueIndex + ')" title="' + escAttr(t('common.delete')) + '">' + iconSvg('trash', t('common.delete'), 'tiny') + '</button>';
|
|
14962
|
+
'<button class="stack-icon-btn danger" onclick="window.deleteQueueItem(' + queueIndex + ')" title="' + escAttr(t('common.delete')) + '">' + iconSvg('trash', t('common.delete'), 'tiny') + '</button>' : '';
|
|
13264
14963
|
html += '<div class="queue-item"' + dragAttributes + '>' +
|
|
13265
14964
|
dragHandle +
|
|
13266
14965
|
'<input class="queue-edit" value="' + escAttr(state.nextQueue[queueIndex] || '') + '"' + inputAttributes + '>' +
|
|
@@ -13293,9 +14992,11 @@ window.editQueueItem = function(idx, value) {
|
|
|
13293
14992
|
|
|
13294
14993
|
window.focusQueueItem = function(idx) {
|
|
13295
14994
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
13296
|
-
if (
|
|
14995
|
+
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) return;
|
|
14996
|
+
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
14997
|
+
if (backendManaged && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget())) return;
|
|
13297
14998
|
state._editingQueueIndex = idx;
|
|
13298
|
-
state.pendingInputEdit = { kind: 'queue', index: idx, request: request, target: currentConversationTarget() };
|
|
14999
|
+
state.pendingInputEdit = { kind: 'queue', index: idx, request: request, target: currentConversationTarget(), originalText: String(state.nextQueue[idx] || '') };
|
|
13299
15000
|
els.prompt.value = String(state.nextQueue[idx] || '');
|
|
13300
15001
|
state.promptAttachments = (request.images || []).map(function(image, imageIndex) {
|
|
13301
15002
|
return { id: 'queue-edit-' + imageIndex + '-' + Date.now(), name: image.name || 'queued image', type: image.type || 'image/png', dataUrl: image.dataUrl };
|
|
@@ -13309,13 +15010,23 @@ window.focusQueueItem = function(idx) {
|
|
|
13309
15010
|
window.deleteQueueItem = function(idx) {
|
|
13310
15011
|
if (!state.nextQueue) return;
|
|
13311
15012
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
13312
|
-
if (
|
|
15013
|
+
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) return;
|
|
15014
|
+
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
15015
|
+
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget()) : '';
|
|
15016
|
+
if (backendManaged && !queueItemId) return;
|
|
13313
15017
|
if (!state.queueHiddenItems) state.queueHiddenItems = {};
|
|
13314
15018
|
if (state.nextQueue[idx]) state.queueHiddenItems[queueHiddenItemKey(state.nextQueue[idx], currentConversationTarget())] = true;
|
|
13315
15019
|
state.nextQueue.splice(idx, 1);
|
|
13316
15020
|
if (state.nextQueueRequests) state.nextQueueRequests.splice(idx, 1);
|
|
13317
15021
|
refreshNextPromptForTarget(currentConversationTarget());
|
|
13318
15022
|
window.renderInputStack();
|
|
15023
|
+
if (backendManaged) {
|
|
15024
|
+
window.queueAction('queue_delete', { id: queueItemId }).then(function(result) {
|
|
15025
|
+
if (!result || result.ok === false) {
|
|
15026
|
+
showUiNotice(result && result.error ? result.error : (currentLang() === 'zh' ? '删除队列项失败' : 'Failed to delete queue item'), 'error', 'queue-delete-' + queueItemId);
|
|
15027
|
+
}
|
|
15028
|
+
});
|
|
15029
|
+
}
|
|
13319
15030
|
};
|
|
13320
15031
|
|
|
13321
15032
|
function restoreQueueItemAfterGuideFailure(idx, text, request, target, reuseClientMessageId) {
|
|
@@ -13332,7 +15043,10 @@ function restoreQueueItemAfterGuideFailure(idx, text, request, target, reuseClie
|
|
|
13332
15043
|
window.guideQueueItem = function(idx) {
|
|
13333
15044
|
if (!state.nextQueue || !state.nextQueue[idx]) return;
|
|
13334
15045
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
13335
|
-
if (
|
|
15046
|
+
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) return;
|
|
15047
|
+
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
15048
|
+
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget()) : '';
|
|
15049
|
+
if (backendManaged && !queueItemId) return;
|
|
13336
15050
|
var target = normalizeQueuedConversationTarget(request.target) || currentConversationTarget();
|
|
13337
15051
|
var deliverDuringRun = isCurrentConversationRunning();
|
|
13338
15052
|
var next = state.nextQueue.splice(idx, 1)[0];
|
|
@@ -13341,6 +15055,20 @@ window.guideQueueItem = function(idx) {
|
|
|
13341
15055
|
if (next) state.queueHiddenItems[queueHiddenItemKey(next, target)] = true;
|
|
13342
15056
|
refreshNextPromptForTarget(currentConversationTarget());
|
|
13343
15057
|
window.renderInputStack();
|
|
15058
|
+
if (backendManaged) {
|
|
15059
|
+
// The kernel owns this entry; guide it there so the mobile side stays
|
|
15060
|
+
// in sync. On failure restore the local row.
|
|
15061
|
+
return window.queueAction('queue_guide', { id: queueItemId }).then(function(outcome) {
|
|
15062
|
+
if (!outcome || outcome.ok === false) {
|
|
15063
|
+
restoreQueueItemAfterGuideFailure(idx, next, nextRequest, target, true);
|
|
15064
|
+
showUiNotice(outcome && outcome.error ? outcome.error : (currentLang() === 'zh' ? 'Guide 投递失败' : 'Guide delivery failed'), 'error', 'queue-guide-' + queueItemId);
|
|
15065
|
+
}
|
|
15066
|
+
return outcome || { ok: false };
|
|
15067
|
+
}, function(error) {
|
|
15068
|
+
restoreQueueItemAfterGuideFailure(idx, next, nextRequest, target, true);
|
|
15069
|
+
return { ok: false, error: error && error.message ? error.message : String(error) };
|
|
15070
|
+
});
|
|
15071
|
+
}
|
|
13344
15072
|
if (next) {
|
|
13345
15073
|
var delivery = window.sendMessage('guide', next, {
|
|
13346
15074
|
fromQueue: true,
|
|
@@ -13365,7 +15093,11 @@ window.guideQueueItem = function(idx) {
|
|
|
13365
15093
|
|
|
13366
15094
|
window.startQueueDrag = function(event, idx) {
|
|
13367
15095
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
13368
|
-
if (
|
|
15096
|
+
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) {
|
|
15097
|
+
state.queueDragIndex = -1;
|
|
15098
|
+
return;
|
|
15099
|
+
}
|
|
15100
|
+
if (queuedRequestIsBackendManaged(request) && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget())) {
|
|
13369
15101
|
state.queueDragIndex = -1;
|
|
13370
15102
|
return;
|
|
13371
15103
|
}
|
|
@@ -13386,9 +15118,12 @@ window.dropQueueDrag = function(event, idx) {
|
|
|
13386
15118
|
if (!state.nextQueue || from < 0 || from === idx || from >= state.nextQueue.length) return;
|
|
13387
15119
|
var sourceRequest = state.nextQueueRequests && state.nextQueueRequests[from];
|
|
13388
15120
|
var destinationRequest = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
13389
|
-
if (
|
|
13390
|
-
|| !queuedRequestMatchesTarget(sourceRequest, currentConversationTarget())
|
|
15121
|
+
if (!queuedRequestMatchesTarget(sourceRequest, currentConversationTarget())
|
|
13391
15122
|
|| !queuedRequestMatchesTarget(destinationRequest, currentConversationTarget())) return;
|
|
15123
|
+
var sourceBackend = queuedRequestIsBackendManaged(sourceRequest);
|
|
15124
|
+
var destinationBackend = queuedRequestIsBackendManaged(destinationRequest);
|
|
15125
|
+
if ((sourceBackend && !queueItemIdForText(state.nextQueue[from] || '', currentConversationTarget()))
|
|
15126
|
+
|| (destinationBackend && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget()))) return;
|
|
13392
15127
|
var item = state.nextQueue.splice(from, 1)[0];
|
|
13393
15128
|
var requestItem = state.nextQueueRequests ? state.nextQueueRequests.splice(from, 1)[0] : null;
|
|
13394
15129
|
state.nextQueue.splice(idx, 0, item);
|
|
@@ -13396,6 +15131,17 @@ window.dropQueueDrag = function(event, idx) {
|
|
|
13396
15131
|
state.queueDragIndex = -1;
|
|
13397
15132
|
refreshNextPromptForTarget(currentConversationTarget());
|
|
13398
15133
|
window.renderInputStack();
|
|
15134
|
+
if (sourceBackend || destinationBackend) {
|
|
15135
|
+
// The kernel owns at least one affected entry: forward the complete
|
|
15136
|
+
// ordered id list so mobile and desktop stay in sync.
|
|
15137
|
+
var visibleIndexes = queueIndexesForTarget(currentConversationTarget());
|
|
15138
|
+
var orderedIds = visibleIndexes.map(function(vi) {
|
|
15139
|
+
return queueItemIdForText(state.nextQueue[vi] || '', currentConversationTarget());
|
|
15140
|
+
}).filter(function(id) { return !!id; });
|
|
15141
|
+
if (orderedIds.length) {
|
|
15142
|
+
window.queueAction('queue_reorder', { orderedIds: orderedIds });
|
|
15143
|
+
}
|
|
15144
|
+
}
|
|
13399
15145
|
};
|
|
13400
15146
|
|
|
13401
15147
|
window.endQueueDrag = function(event) {
|
|
@@ -13445,7 +15191,24 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13445
15191
|
els.prompt.dispatchEvent(new Event('input', { bubbles: true }));
|
|
13446
15192
|
if (pendingEdit.kind === 'queue') {
|
|
13447
15193
|
var editIndex = Number(pendingEdit.index);
|
|
15194
|
+
var editRequest = pendingEdit.request || (state.nextQueueRequests && state.nextQueueRequests[editIndex]);
|
|
15195
|
+
var editBackendManaged = queuedRequestIsBackendManaged(editRequest);
|
|
15196
|
+
var editQueueItemId = editBackendManaged ? queueItemIdForText(String(state.nextQueue[editIndex] || pendingEdit.originalText || ''), pendingEdit.target) : '';
|
|
15197
|
+
if (editBackendManaged && !editQueueItemId) {
|
|
15198
|
+
showUiNotice(currentLang() === 'zh' ? '无法编辑该队列项:缺少后端 id' : 'Cannot edit queue item: backend id missing', 'error', 'queue-edit-' + editIndex);
|
|
15199
|
+
return { ok: false, error: 'missing backend queue id' };
|
|
15200
|
+
}
|
|
13448
15201
|
if (effectiveInputMode === 'next') {
|
|
15202
|
+
if (editBackendManaged) {
|
|
15203
|
+
// The kernel owns this entry: forward the edit and let the next
|
|
15204
|
+
// snapshot reconcile the local row.
|
|
15205
|
+
window.queueAction('queue_update', { id: editQueueItemId, text: editedValue, requestedMode: String(editRequest && editRequest.requestedMode || 'build'), goalObjective: String(editRequest && editRequest.goalObjective || ''), createdAt: String(editRequest && editRequest.createdAt || '') });
|
|
15206
|
+
state.nextQueue[editIndex] = editedValue;
|
|
15207
|
+
if (state.nextQueueRequests[editIndex]) state.nextQueueRequests[editIndex].text = editedValue;
|
|
15208
|
+
refreshNextPromptForTarget(pendingEdit.target);
|
|
15209
|
+
window.renderInputStack();
|
|
15210
|
+
return { ok: true, queued: true };
|
|
15211
|
+
}
|
|
13449
15212
|
state.nextQueue[editIndex] = editedValue;
|
|
13450
15213
|
if (state.nextQueueRequests[editIndex]) {
|
|
13451
15214
|
state.nextQueueRequests[editIndex].text = editedValue;
|
|
@@ -13808,7 +15571,11 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13808
15571
|
}));
|
|
13809
15572
|
var lockedUi = conversationWorkUiState(lockedConversationId, lockedTarget.workspaceId);
|
|
13810
15573
|
if (stillActive && r && Array.isArray(r.diffs) && r.diffs.length) {
|
|
13811
|
-
|
|
15574
|
+
var reviewRecord = {
|
|
15575
|
+
diffs: r.diffs,
|
|
15576
|
+
runId: String(r.runId || localRunId || '')
|
|
15577
|
+
};
|
|
15578
|
+
upsertWorkReviewRecord(lockedUi, reviewRecord.diffs, reviewRecord.runId, [localRunId]);
|
|
13812
15579
|
}
|
|
13813
15580
|
if (stillActive && !responseOwnedByRun) {
|
|
13814
15581
|
responseMsg = lockedUi.activeWorkflowMsg;
|
|
@@ -13837,9 +15604,13 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13837
15604
|
hydrateConversationBranchState(r);
|
|
13838
15605
|
renderChatMessages(r.chatMessages);
|
|
13839
15606
|
}
|
|
13840
|
-
|
|
15607
|
+
// Re-render every retained completed-run review after a transcript rebuild.
|
|
15608
|
+
// addWorkReview dedupes by run identity and normalized file totals.
|
|
15609
|
+
if (stillActive) {
|
|
13841
15610
|
var completedReviewUi = conversationWorkUiState(lockedConversationId);
|
|
13842
|
-
|
|
15611
|
+
(completedReviewUi.workReviews || []).forEach(function(item) {
|
|
15612
|
+
addWorkReview(item.diffs || item, item.runId || '', [localRunId]);
|
|
15613
|
+
});
|
|
13843
15614
|
}
|
|
13844
15615
|
if (r && r.conversations) {
|
|
13845
15616
|
applyBackendConversations(r.conversations || [], stillActive ? lockedConversationId : activeConversationId(), lockedTarget.workspaceId);
|
|
@@ -13860,6 +15631,7 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13860
15631
|
if (r && r.queued) {
|
|
13861
15632
|
var responseQueue = setBackendQueueForTarget(r.queued, lockedTarget);
|
|
13862
15633
|
window.syncNextQueueFromBackend(responseQueue, lockedTarget);
|
|
15634
|
+
syncQueueItemsFromSnapshot(r, lockedTarget);
|
|
13863
15635
|
if (stillActive) state.backendQueue = responseQueue;
|
|
13864
15636
|
}
|
|
13865
15637
|
if (r && r.options) {
|
|
@@ -13905,6 +15677,7 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13905
15677
|
if (s && s.queued) {
|
|
13906
15678
|
var refreshedQueue = setBackendQueueForTarget(s.queued, lockedTarget);
|
|
13907
15679
|
window.syncNextQueueFromBackend(refreshedQueue, lockedTarget);
|
|
15680
|
+
syncQueueItemsFromSnapshot(s, lockedTarget);
|
|
13908
15681
|
if (stillActiveAfterRefresh) state.backendQueue = refreshedQueue;
|
|
13909
15682
|
}
|
|
13910
15683
|
if (s && s.conversations) {
|
|
@@ -14349,6 +16122,7 @@ window.switchRightTab = function(tab) {
|
|
|
14349
16122
|
var rightTabs = els['right-tabs'] || document.getElementById('right-tabs');
|
|
14350
16123
|
var rightContent = els['right-content'] || document.getElementById('right-content');
|
|
14351
16124
|
if (!rightTabs || !rightContent) return;
|
|
16125
|
+
wireLiquidRailInteractions(rightTabs, '.tab-btn[data-tab]');
|
|
14352
16126
|
var btns = rightTabs.querySelectorAll('.tab-btn[data-tab]');
|
|
14353
16127
|
for (var i = 0; i < btns.length; i++) {
|
|
14354
16128
|
var selected = btns[i].getAttribute('data-tab') === tab;
|
|
@@ -14753,6 +16527,57 @@ var _terminalCounter = 0;
|
|
|
14753
16527
|
var _terminalTakeoverTabs = {};
|
|
14754
16528
|
var _terminalTakeoverRefreshToken = 0;
|
|
14755
16529
|
var _terminalTakeoverDetached = {};
|
|
16530
|
+
var _terminalOutputLimit = 256 * 1024;
|
|
16531
|
+
|
|
16532
|
+
window.appendTerminalOutput = function(output, text, style) {
|
|
16533
|
+
if (!output || !text) return;
|
|
16534
|
+
var state = output._terminalRenderState;
|
|
16535
|
+
if (!state) state = output._terminalRenderState = { pending: [], frame: 0 };
|
|
16536
|
+
state.pending.push({ text: String(text), style: style || null });
|
|
16537
|
+
if (state.frame) return;
|
|
16538
|
+
state.frame = requestAnimationFrame(function() {
|
|
16539
|
+
state.frame = 0;
|
|
16540
|
+
if (!output.isConnected) { state.pending.length = 0; return; }
|
|
16541
|
+
var nearBottom = output.scrollHeight - output.scrollTop - output.clientHeight <= 32;
|
|
16542
|
+
var fragment = document.createDocumentFragment();
|
|
16543
|
+
for (var i = 0; i < state.pending.length; i++) {
|
|
16544
|
+
var item = state.pending[i];
|
|
16545
|
+
if (item.style) {
|
|
16546
|
+
var span = document.createElement('span');
|
|
16547
|
+
span.textContent = item.text;
|
|
16548
|
+
if (item.style.color) span.style.color = item.style.color;
|
|
16549
|
+
if (item.style.opacity) span.style.opacity = item.style.opacity;
|
|
16550
|
+
fragment.appendChild(span);
|
|
16551
|
+
} else {
|
|
16552
|
+
fragment.appendChild(document.createTextNode(item.text));
|
|
16553
|
+
}
|
|
16554
|
+
}
|
|
16555
|
+
state.pending.length = 0;
|
|
16556
|
+
output.appendChild(fragment);
|
|
16557
|
+
var overflow = output.textContent.length - _terminalOutputLimit;
|
|
16558
|
+
while (overflow > 0 && output.firstChild) {
|
|
16559
|
+
var first = output.firstChild;
|
|
16560
|
+
var length = first.textContent.length;
|
|
16561
|
+
if (length <= overflow) {
|
|
16562
|
+
output.removeChild(first);
|
|
16563
|
+
overflow -= length;
|
|
16564
|
+
} else {
|
|
16565
|
+
first.textContent = first.textContent.slice(overflow);
|
|
16566
|
+
overflow = 0;
|
|
16567
|
+
}
|
|
16568
|
+
}
|
|
16569
|
+
if (nearBottom) output.scrollTop = output.scrollHeight;
|
|
16570
|
+
});
|
|
16571
|
+
};
|
|
16572
|
+
|
|
16573
|
+
window.setTerminalOutput = function(output, text, style) {
|
|
16574
|
+
if (!output) return;
|
|
16575
|
+
var state = output._terminalRenderState;
|
|
16576
|
+
if (state && state.frame) cancelAnimationFrame(state.frame);
|
|
16577
|
+
output._terminalRenderState = { pending: [], frame: 0 };
|
|
16578
|
+
output.textContent = '';
|
|
16579
|
+
window.appendTerminalOutput(output, text, style);
|
|
16580
|
+
};
|
|
14756
16581
|
|
|
14757
16582
|
window.portableTerminalWorkspacePath = function(input) {
|
|
14758
16583
|
var raw = String(input || '').trim().replace(/\\/g, '/').replace(/\/+$/g, '');
|
|
@@ -14835,14 +16660,14 @@ window.terminalSend = function() {
|
|
|
14835
16660
|
if (takeoverSession) {
|
|
14836
16661
|
input.value = '';
|
|
14837
16662
|
if (!api.terminalTakeoverWrite) {
|
|
14838
|
-
if (output) output
|
|
16663
|
+
if (output) window.appendTerminalOutput(output, '\r\n[' + t('terminal.notConnected') + ']', { color:'#ff6666' });
|
|
14839
16664
|
return;
|
|
14840
16665
|
}
|
|
14841
16666
|
var takeoverConversation = pane.getAttribute('data-takeover-conversation') || activeConversationId();
|
|
14842
16667
|
api.terminalTakeoverWrite(takeoverSession, cmd, takeoverConversation).then(function(result) {
|
|
14843
16668
|
if (result && result.ok === false) throw new Error(result.error || 'Terminal write failed');
|
|
14844
16669
|
}).catch(function(err) {
|
|
14845
|
-
if (output) output
|
|
16670
|
+
if (output) window.appendTerminalOutput(output, '\r\n[' + t('common.error') + '] ' + err.message, { color:'#ff6666' });
|
|
14846
16671
|
});
|
|
14847
16672
|
return;
|
|
14848
16673
|
}
|
|
@@ -14861,12 +16686,12 @@ window.terminalSend = function() {
|
|
|
14861
16686
|
input.disabled = false;
|
|
14862
16687
|
var currentPane = window.getActiveTerminalPane();
|
|
14863
16688
|
var currentOutput = (currentPane && currentPane.querySelector('.terminal-output')) || output;
|
|
14864
|
-
if (currentOutput) currentOutput
|
|
16689
|
+
if (currentOutput) window.appendTerminalOutput(currentOutput, '\r\n[' + t('common.error') + '] ' + (err.message || String(err)), { color:'#ff6666' });
|
|
14865
16690
|
});
|
|
14866
16691
|
}
|
|
14867
16692
|
input.value = '';
|
|
14868
16693
|
api.terminalWrite(sessionId, cmd + '\r').catch(function(err) {
|
|
14869
|
-
if (output) output
|
|
16694
|
+
if (output) window.appendTerminalOutput(output, '\r\n[' + t('common.error') + '] ' + err.message, { color:'#ff6666' });
|
|
14870
16695
|
});
|
|
14871
16696
|
};
|
|
14872
16697
|
|
|
@@ -14880,7 +16705,7 @@ window.clearTerminal = function() {
|
|
|
14880
16705
|
api.terminalKill(sessionId, state.terminalInterruptTimeoutMs || 0).catch(function(){});
|
|
14881
16706
|
pane.setAttribute('data-session', '');
|
|
14882
16707
|
}
|
|
14883
|
-
if (output) output
|
|
16708
|
+
if (output) window.setTerminalOutput(output, '');
|
|
14884
16709
|
if (prompt) prompt.textContent = '>';
|
|
14885
16710
|
};
|
|
14886
16711
|
|
|
@@ -14990,12 +16815,12 @@ window.addTerminalTab = function(shellId, options) {
|
|
|
14990
16815
|
var terminalReadyPromise = terminalSpawnPromise.then(function(resp) {
|
|
14991
16816
|
pane.setAttribute('data-session', resp.sessionId);
|
|
14992
16817
|
var out = pane.querySelector('.terminal-output');
|
|
14993
|
-
if (out)
|
|
16818
|
+
if (out) window.setTerminalOutput(out, t('terminal.connected') + ' (' + shellId + ')\r\n', { color:'var(--accent2)' });
|
|
14994
16819
|
state._terminalActiveSession = resp.sessionId;
|
|
14995
16820
|
return resp;
|
|
14996
16821
|
}).catch(function(err) {
|
|
14997
16822
|
var out = pane.querySelector('.terminal-output');
|
|
14998
|
-
if (out) out
|
|
16823
|
+
if (out) window.setTerminalOutput(out, t('terminal.failed') + ': ' + err.message, { color:'#ff6666' });
|
|
14999
16824
|
if (options.required === true) throw err;
|
|
15000
16825
|
return null;
|
|
15001
16826
|
});
|
|
@@ -15110,13 +16935,12 @@ window.applyTerminalTakeoverEvent = function(payload) {
|
|
|
15110
16935
|
var output = pane.querySelector('.terminal-output');
|
|
15111
16936
|
if (!output) return;
|
|
15112
16937
|
if (payload.type === 'started' && session.buffer) {
|
|
15113
|
-
output
|
|
16938
|
+
window.setTerminalOutput(output, session.buffer);
|
|
15114
16939
|
} else if (payload.data) {
|
|
15115
|
-
output
|
|
16940
|
+
window.appendTerminalOutput(output, payload.data);
|
|
15116
16941
|
} else if (payload.type === 'stopped') {
|
|
15117
|
-
output
|
|
16942
|
+
window.appendTerminalOutput(output, '\r\n[Agent takeover stopped]', { color:'var(--text-dim)', opacity:'0.65' });
|
|
15118
16943
|
}
|
|
15119
|
-
output.scrollTop = output.scrollHeight;
|
|
15120
16944
|
if (!active) window.pruneTerminalTakeoverEndedPanes(session.id);
|
|
15121
16945
|
};
|
|
15122
16946
|
|
|
@@ -15236,16 +17060,14 @@ if (api.onTerminalData) {
|
|
|
15236
17060
|
.replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, '')
|
|
15237
17061
|
.replace(/\x1b\[[0-?]*[ -\/]*[@-~]/g, '')
|
|
15238
17062
|
.replace(/\r(?!\n)/g, '');
|
|
15239
|
-
|
|
15240
|
-
output.innerHTML += text;
|
|
15241
|
-
output.scrollTop = output.scrollHeight;
|
|
17063
|
+
window.appendTerminalOutput(output, clean);
|
|
15242
17064
|
});
|
|
15243
17065
|
api.onTerminalExit(function(event, sessionId, code) {
|
|
15244
17066
|
var pane = document.querySelector('.terminal-pane[data-session="' + sessionId + '"]');
|
|
15245
17067
|
if (!pane) return;
|
|
15246
17068
|
pane.setAttribute('data-session', '');
|
|
15247
17069
|
var output = pane.querySelector('.terminal-output');
|
|
15248
|
-
if (output) output
|
|
17070
|
+
if (output) window.appendTerminalOutput(output, '\r\n[Process exited code=' + code + ']', { color:'var(--text-dim)', opacity:'0.5' });
|
|
15249
17071
|
});
|
|
15250
17072
|
}
|
|
15251
17073
|
|
|
@@ -15257,7 +17079,7 @@ if (api.onTerminalTakeover) {
|
|
|
15257
17079
|
if (api.terminalTakeoverState) window.refreshTerminalTakeoverPanes();
|
|
15258
17080
|
|
|
15259
17081
|
// === Scroll to Bottom ===
|
|
15260
|
-
var _chatScrollNearBottomPx =
|
|
17082
|
+
var _chatScrollNearBottomPx = 24;
|
|
15261
17083
|
var _chatShouldAutoScroll = true;
|
|
15262
17084
|
function shouldAutoScroll(el) { return el.scrollHeight - el.scrollTop - el.clientHeight <= _chatScrollNearBottomPx; }
|
|
15263
17085
|
function autoScrollIfAtBottom() { var el = els['chat-area']; if (!el) return; if (_chatShouldAutoScroll) el.scrollTop = el.scrollHeight; }
|
|
@@ -15547,6 +17369,7 @@ window.openSubWin = function(title, html) {
|
|
|
15547
17369
|
els['sub-win-title'].textContent = title;
|
|
15548
17370
|
els['sub-win-body'].innerHTML = html;
|
|
15549
17371
|
els['sub-win'].classList.toggle('memory-lab-window', !!(state.activeSubWindowView && state.activeSubWindowView.name === 'memoryLab'));
|
|
17372
|
+
els['sub-win'].classList.toggle('settings-window', !!(state.activeSubWindowView && state.activeSubWindowView.name === 'settings'));
|
|
15550
17373
|
els['sub-win-overlay'].classList.add('open');
|
|
15551
17374
|
updateApplicationInertState();
|
|
15552
17375
|
requestAnimationFrame(focusSubWindowInitial);
|
|
@@ -15577,6 +17400,7 @@ window.closeSubWin = function() {
|
|
|
15577
17400
|
els['sub-win-title'].textContent = previous.title;
|
|
15578
17401
|
els['sub-win-body'].innerHTML = previous.html;
|
|
15579
17402
|
els['sub-win'].classList.toggle('memory-lab-window', !!(previous.view && previous.view.name === 'memoryLab'));
|
|
17403
|
+
els['sub-win'].classList.toggle('settings-window', !!(previous.view && previous.view.name === 'settings'));
|
|
15580
17404
|
requestAnimationFrame(function() {
|
|
15581
17405
|
els['sub-win-body'].scrollTop = previous.scrollTop || 0;
|
|
15582
17406
|
state.restoringSubWindow = false;
|
|
@@ -15587,7 +17411,7 @@ window.closeSubWin = function() {
|
|
|
15587
17411
|
els['sub-win-overlay'].classList.remove('open');
|
|
15588
17412
|
state.activeSubWindowView = null;
|
|
15589
17413
|
var win = els['sub-win'];
|
|
15590
|
-
if (win) { win.classList.remove('memory-lab-window'); win.style.left = ''; win.style.top = ''; win.style.margin = ''; }
|
|
17414
|
+
if (win) { win.classList.remove('memory-lab-window', 'settings-window'); win.style.left = ''; win.style.top = ''; win.style.margin = ''; }
|
|
15591
17415
|
var restoreFocus = subWindowOriginFocus;
|
|
15592
17416
|
subWindowOriginFocus = null;
|
|
15593
17417
|
updateApplicationInertState();
|
|
@@ -15660,7 +17484,10 @@ window.openSettings = function(tab) {
|
|
|
15660
17484
|
if (refreshingSettings) state.restoringSubWindow = true;
|
|
15661
17485
|
window.openSubWin(t('settings.title'), html);
|
|
15662
17486
|
state.restoringSubWindow = false;
|
|
15663
|
-
requestAnimationFrame(function() {
|
|
17487
|
+
requestAnimationFrame(function() {
|
|
17488
|
+
window.settingsTab(activeTab);
|
|
17489
|
+
wireLiquidRailInteractions(document.querySelector('.settings-tabs:not(.plugin-tabs)'), '.stab-btn[data-stab]');
|
|
17490
|
+
});
|
|
15664
17491
|
};
|
|
15665
17492
|
|
|
15666
17493
|
function renderGeneralSettings() {
|
|
@@ -15740,7 +17567,7 @@ function renderGeneralSettings() {
|
|
|
15740
17567
|
'</div>' +
|
|
15741
17568
|
'<div class="setting-row">' +
|
|
15742
17569
|
'<label class="setting-label" for="settings-glass-opacity">' + esc(t('settings.glass')) + '</label>' +
|
|
15743
|
-
'<div class="setting-control"><input id="settings-glass-opacity" type="range" min="0" max="100" step="1" value="' + glassVal + '" aria-describedby="glass-desc" aria-valuetext="' + escAttr(glassDescription) + '" oninput="window.previewGlassOpacity(this.value)" onchange="window.commitGlassOpacity(this.value)">' +
|
|
17570
|
+
'<div class="setting-control"><div class="settings-glass-range-line"><input id="settings-glass-opacity" type="range" min="0" max="100" step="1" value="' + glassVal + '" style="--liquid-range-progress:' + glassVal + '%" aria-describedby="glass-desc settings-glass-value" aria-valuetext="' + escAttr(glassDescription) + '" oninput="window.previewGlassOpacity(this.value)" onchange="window.commitGlassOpacity(this.value)"><output id="settings-glass-value" for="settings-glass-opacity">' + glassNumberText(glassVal) + '%</output></div>' +
|
|
15744
17571
|
'<div class="setting-desc" id="glass-desc" aria-live="polite">' + esc(glassDescription) + '</div></div>' +
|
|
15745
17572
|
'</div>' +
|
|
15746
17573
|
'<div class="setting-row">' +
|
|
@@ -16240,6 +18067,7 @@ function glassPresentationForOpacity(value) {
|
|
|
16240
18067
|
var alpha = opacityPercent / 100;
|
|
16241
18068
|
var transparencyPercent = 100 - opacityPercent;
|
|
16242
18069
|
var glassWidth = 20 * transparencyPercent / 100;
|
|
18070
|
+
var opticalStrength = Math.min(2, transparencyPercent / 15);
|
|
16243
18071
|
var rounded = function(number) { return Math.round(number * 10000) / 10000; };
|
|
16244
18072
|
return {
|
|
16245
18073
|
opacityPercent: rounded(opacityPercent),
|
|
@@ -16248,6 +18076,12 @@ function glassPresentationForOpacity(value) {
|
|
|
16248
18076
|
blur1: rounded(0.4 * glassWidth),
|
|
16249
18077
|
blur2: rounded(0.8 * glassWidth),
|
|
16250
18078
|
blur3: rounded(glassWidth),
|
|
18079
|
+
carrierBlur: rounded(glassWidth * 2.2),
|
|
18080
|
+
carrierAlpha: rounded(Math.max(0.08, 0.44 * alpha)),
|
|
18081
|
+
floatRefractionHeight: rounded(12 * opticalStrength),
|
|
18082
|
+
floatRefractionAmount: rounded(24 * opticalStrength),
|
|
18083
|
+
floatChromaticAberration: rounded(opticalStrength),
|
|
18084
|
+
floatOpticalOpacity: rounded(Math.min(1, opticalStrength)),
|
|
16251
18085
|
alpha1: rounded(0.75 * alpha),
|
|
16252
18086
|
alpha2: rounded(0.80 * alpha),
|
|
16253
18087
|
alpha3: rounded(0.85 * alpha),
|
|
@@ -16280,11 +18114,21 @@ function applyGlassPresentation(presentation) {
|
|
|
16280
18114
|
root.style.setProperty('--glass-blur-2', presentation.blur2 + 'px');
|
|
16281
18115
|
root.style.setProperty('--glass-blur-3', presentation.blur3 + 'px');
|
|
16282
18116
|
}
|
|
18117
|
+
root.style.setProperty('--carrier-glass-blur', presentation.carrierBlur + 'px');
|
|
18118
|
+
root.style.setProperty('--carrier-glass-alpha', String(presentation.carrierAlpha));
|
|
18119
|
+
root.style.setProperty('--liquid-float-refraction-amount', presentation.floatRefractionAmount + 'px');
|
|
18120
|
+
root.style.setProperty('--liquid-float-chromatic-aberration', String(presentation.floatChromaticAberration));
|
|
18121
|
+
root.style.setProperty('--liquid-float-optical-opacity', String(presentation.floatOpticalOpacity));
|
|
16283
18122
|
var description = glassDescriptionText(presentation);
|
|
16284
18123
|
var desc = document.getElementById('glass-desc');
|
|
16285
18124
|
if (desc) desc.textContent = description;
|
|
16286
18125
|
var slider = document.getElementById('settings-glass-opacity');
|
|
16287
|
-
if (slider)
|
|
18126
|
+
if (slider) {
|
|
18127
|
+
slider.setAttribute('aria-valuetext', description);
|
|
18128
|
+
slider.style.setProperty('--liquid-range-progress', presentation.opacityPercent + '%');
|
|
18129
|
+
}
|
|
18130
|
+
var valueOutput = document.getElementById('settings-glass-value');
|
|
18131
|
+
if (valueOutput) valueOutput.textContent = glassNumberText(presentation.opacityPercent) + '%';
|
|
16288
18132
|
return presentation;
|
|
16289
18133
|
}
|
|
16290
18134
|
|
|
@@ -19431,14 +21275,25 @@ function pendingConversationActivation(target) {
|
|
|
19431
21275
|
}
|
|
19432
21276
|
|
|
19433
21277
|
function applyBackendConversations(items, activeId, workspaceId) {
|
|
19434
|
-
if (!state.currentWorkspace
|
|
21278
|
+
if (!state.currentWorkspace) return;
|
|
19435
21279
|
var key = currentWorkspaceKey(workspaceId);
|
|
19436
21280
|
var isForegroundWorkspace = key === currentWorkspaceKey();
|
|
19437
21281
|
var existingConversations = state.workspaceConversations[key] || [];
|
|
19438
|
-
var convs = [];
|
|
19439
|
-
var requestedActiveId = String(activeId || '');
|
|
19440
21282
|
var holdId = String(state.foregroundConversationHoldId || '');
|
|
19441
21283
|
var useHold = !!(isForegroundWorkspace && holdId && Date.now() < Number(state.foregroundConversationHoldUntil || 0));
|
|
21284
|
+
if ((!items || !items.length) && !useHold) {
|
|
21285
|
+
// 后端权威列表为空:工作区对话确实已清空。保持为空,并移除本地
|
|
21286
|
+
// 自动兜底的 default 对话,跨重启/切换工作区后也不会复活。
|
|
21287
|
+
state.workspaceConversations[key] = [];
|
|
21288
|
+
state.workspaceActiveConversation[key] = -1;
|
|
21289
|
+
if (isForegroundWorkspace) {
|
|
21290
|
+
state.conversations = [];
|
|
21291
|
+
state.activeConversation = -1;
|
|
21292
|
+
}
|
|
21293
|
+
return;
|
|
21294
|
+
}
|
|
21295
|
+
var convs = [];
|
|
21296
|
+
var requestedActiveId = String(activeId || '');
|
|
19442
21297
|
var seenIds = {};
|
|
19443
21298
|
for (var i = 0; i < items.length; i++) {
|
|
19444
21299
|
var item = items[i] || {};
|
|
@@ -19564,16 +21419,244 @@ function appendDomNodesInBatches(container, nodes, batchKey, batchSize) {
|
|
|
19564
21419
|
appendBatch();
|
|
19565
21420
|
}
|
|
19566
21421
|
|
|
21422
|
+
var conversationGlassFlight = { float: null, source: null, covered: null, timer: 0, generation: 0 };
|
|
21423
|
+
var conversationNativeDrag = {
|
|
21424
|
+
row: null,
|
|
21425
|
+
float: null,
|
|
21426
|
+
arrived: false,
|
|
21427
|
+
grabX: 0,
|
|
21428
|
+
grabY: 0,
|
|
21429
|
+
clientX: 0,
|
|
21430
|
+
clientY: 0
|
|
21431
|
+
};
|
|
21432
|
+
|
|
21433
|
+
function positionConversationGlassAroundRow(float, row) {
|
|
21434
|
+
if (!float || !row) return;
|
|
21435
|
+
var rect = row.getBoundingClientRect();
|
|
21436
|
+
var edge = liquidInteractionEdgePx();
|
|
21437
|
+
keepLiquidFloatExpansionInsideViewport(float, rect);
|
|
21438
|
+
float._liquidTargetRect = { left: rect.left - edge, top: rect.top - edge, width: rect.width + edge * 2, height: rect.height + edge * 2 };
|
|
21439
|
+
float.style.left = (rect.left - edge) + 'px';
|
|
21440
|
+
float.style.top = (rect.top - edge) + 'px';
|
|
21441
|
+
float.style.width = (rect.width + edge * 2) + 'px';
|
|
21442
|
+
float.style.height = (rect.height + edge * 2) + 'px';
|
|
21443
|
+
if (float._renderKyantGlass) float._renderKyantGlass({
|
|
21444
|
+
left: rect.left - edge, top: rect.top - edge,
|
|
21445
|
+
width: rect.width + edge * 2, height: rect.height + edge * 2
|
|
21446
|
+
}, true);
|
|
21447
|
+
}
|
|
21448
|
+
|
|
21449
|
+
function followConversationDragPointer() {
|
|
21450
|
+
var drag = conversationNativeDrag;
|
|
21451
|
+
if (!drag.arrived || !drag.float) return;
|
|
21452
|
+
var list = els['conversation-list'];
|
|
21453
|
+
if (!list || !drag.row) return;
|
|
21454
|
+
var conversations = currentWorkspaceConversations();
|
|
21455
|
+
var draggedId = String(drag.row.getAttribute('data-conversation-id') || '');
|
|
21456
|
+
var draggedConversation = conversations.find(function(item) { return String(item && item.id || '') === draggedId; });
|
|
21457
|
+
var trackRows = Array.from(list.querySelectorAll('.conv-item')).filter(function(row) {
|
|
21458
|
+
var id = String(row.getAttribute('data-conversation-id') || '');
|
|
21459
|
+
var conversation = conversations.find(function(item) { return String(item && item.id || '') === id; });
|
|
21460
|
+
return conversation && draggedConversation && !!conversation.pinned === !!draggedConversation.pinned;
|
|
21461
|
+
});
|
|
21462
|
+
if (!trackRows.length) trackRows = [drag.row];
|
|
21463
|
+
var firstRect = trackRows[0].getBoundingClientRect();
|
|
21464
|
+
var lastRect = trackRows[trackRows.length - 1].getBoundingClientRect();
|
|
21465
|
+
var rowRect = drag.row.getBoundingClientRect();
|
|
21466
|
+
var projectedTop = drag.clientY - drag.grabY;
|
|
21467
|
+
var minTop = firstRect.top - rowRect.height;
|
|
21468
|
+
var maxTop = Math.max(minTop, lastRect.bottom);
|
|
21469
|
+
var trackTop = Math.max(minTop, Math.min(maxTop, projectedTop));
|
|
21470
|
+
var edge = liquidInteractionEdgePx();
|
|
21471
|
+
drag.float.style.left = (rowRect.left - edge) + 'px';
|
|
21472
|
+
drag.float.style.top = (trackTop - edge) + 'px';
|
|
21473
|
+
if (drag.float._renderKyantGlass) drag.float._renderKyantGlass();
|
|
21474
|
+
}
|
|
21475
|
+
|
|
21476
|
+
function wrapConversationDragContent(row, float) {
|
|
21477
|
+
if (!row || !float || !row.isConnected) return;
|
|
21478
|
+
var content = document.createElement('div');
|
|
21479
|
+
content.className = 'conversation-drag-content';
|
|
21480
|
+
Array.prototype.slice.call(row.childNodes).forEach(function(node) { content.appendChild(node.cloneNode(true)); });
|
|
21481
|
+
float.appendChild(content);
|
|
21482
|
+
row.classList.add('dragging', 'conversation-glass-dragging');
|
|
21483
|
+
float.classList.add('conversation-drag-following');
|
|
21484
|
+
conversationNativeDrag.arrived = true;
|
|
21485
|
+
conversationNativeDrag.float = float;
|
|
21486
|
+
followConversationDragPointer();
|
|
21487
|
+
}
|
|
21488
|
+
|
|
21489
|
+
window.updateConversationNativeDrag = function(clientX, clientY) {
|
|
21490
|
+
conversationNativeDrag.clientX = clientX;
|
|
21491
|
+
conversationNativeDrag.clientY = clientY;
|
|
21492
|
+
var drag = conversationNativeDrag;
|
|
21493
|
+
var list = els['conversation-list'];
|
|
21494
|
+
if (drag.row && list) {
|
|
21495
|
+
var conversations = currentWorkspaceConversations();
|
|
21496
|
+
var draggedId = String(drag.row.getAttribute('data-conversation-id') || '');
|
|
21497
|
+
var draggedConversation = conversations.find(function(item) { return String(item && item.id || '') === draggedId; });
|
|
21498
|
+
var peers = Array.from(list.querySelectorAll('.conv-item')).filter(function(row) {
|
|
21499
|
+
if (row === drag.row) return false;
|
|
21500
|
+
var id = String(row.getAttribute('data-conversation-id') || '');
|
|
21501
|
+
var conversation = conversations.find(function(item) { return String(item && item.id || '') === id; });
|
|
21502
|
+
return conversation && draggedConversation && !!conversation.pinned === !!draggedConversation.pinned;
|
|
21503
|
+
});
|
|
21504
|
+
if (peers.length) {
|
|
21505
|
+
var firstPeerRect = peers[0].getBoundingClientRect();
|
|
21506
|
+
var lastPeerRect = peers[peers.length - 1].getBoundingClientRect();
|
|
21507
|
+
if (clientY <= firstPeerRect.top) {
|
|
21508
|
+
list.insertBefore(drag.row, peers[0]);
|
|
21509
|
+
} else if (clientY >= lastPeerRect.bottom) {
|
|
21510
|
+
list.insertBefore(drag.row, peers[peers.length - 1].nextSibling);
|
|
21511
|
+
}
|
|
21512
|
+
}
|
|
21513
|
+
}
|
|
21514
|
+
followConversationDragPointer();
|
|
21515
|
+
};
|
|
21516
|
+
|
|
21517
|
+
// Native conversation drags can leave the list while the mouse button remains
|
|
21518
|
+
// held. Track at document capture scope so the carried glass never freezes at
|
|
21519
|
+
// the list boundary; drop/reorder ownership remains scoped to the list itself.
|
|
21520
|
+
document.addEventListener('dragover', function(event) {
|
|
21521
|
+
if (conversationNativeDrag.row) window.updateConversationNativeDrag(event.clientX, event.clientY);
|
|
21522
|
+
}, true);
|
|
21523
|
+
|
|
21524
|
+
window.cancelConversationGlassFlight = function() {
|
|
21525
|
+
conversationGlassFlight.generation += 1;
|
|
21526
|
+
if (conversationGlassFlight.timer) clearTimeout(conversationGlassFlight.timer);
|
|
21527
|
+
conversationGlassFlight.timer = 0;
|
|
21528
|
+
if (conversationGlassFlight.source) conversationGlassFlight.source.classList.remove('liquid-selection-source');
|
|
21529
|
+
conversationGlassFlight.source = null;
|
|
21530
|
+
if (conversationGlassFlight.covered) conversationGlassFlight.covered.classList.remove('liquid-selection-covered');
|
|
21531
|
+
conversationGlassFlight.covered = null;
|
|
21532
|
+
stopLiquidMotionTracking(conversationGlassFlight.float);
|
|
21533
|
+
if (conversationGlassFlight.float && conversationGlassFlight.float._releaseKyantGlass) {
|
|
21534
|
+
conversationGlassFlight.float._releaseKyantGlass();
|
|
21535
|
+
}
|
|
21536
|
+
if (conversationGlassFlight.float && conversationGlassFlight.float.parentNode) {
|
|
21537
|
+
conversationGlassFlight.float.parentNode.removeChild(conversationGlassFlight.float);
|
|
21538
|
+
}
|
|
21539
|
+
conversationGlassFlight.float = null;
|
|
21540
|
+
if (conversationNativeDrag.row) conversationNativeDrag.row.classList.remove('dragging', 'conversation-glass-dragging');
|
|
21541
|
+
conversationNativeDrag.row = null;
|
|
21542
|
+
conversationNativeDrag.float = null;
|
|
21543
|
+
conversationNativeDrag.arrived = false;
|
|
21544
|
+
window.setLiquidSidebarGestureLock('conversation-flight', false);
|
|
21545
|
+
};
|
|
21546
|
+
|
|
21547
|
+
window.animateConversationGlassTo = function(target, options) {
|
|
21548
|
+
options = options || {};
|
|
21549
|
+
var list = els['conversation-list'];
|
|
21550
|
+
if (!list || !target) { if (options.onArrive) options.onArrive(); return; }
|
|
21551
|
+
var source = list.querySelector('.conv-item.active') || target;
|
|
21552
|
+
var float = conversationGlassFlight.float;
|
|
21553
|
+
var redirecting = !!(float && float.isConnected);
|
|
21554
|
+
if (!redirecting) {
|
|
21555
|
+
float = document.createElement('div');
|
|
21556
|
+
float.className = 'liquid-selection-float conversation-selection-float liquid-selection-primed';
|
|
21557
|
+
mountLiquidViewportFloat(float);
|
|
21558
|
+
captureLiquidBackdropFrame().then(function(frame) {
|
|
21559
|
+
if (float && float.isConnected) attachKyantLiquidRenderer(float, frame);
|
|
21560
|
+
});
|
|
21561
|
+
conversationGlassFlight.float = float;
|
|
21562
|
+
conversationGlassFlight.source = source;
|
|
21563
|
+
source.classList.add('liquid-selection-source');
|
|
21564
|
+
}
|
|
21565
|
+
function place(row) {
|
|
21566
|
+
if (conversationGlassFlight.covered && conversationGlassFlight.covered !== row) {
|
|
21567
|
+
conversationGlassFlight.covered.classList.remove('liquid-selection-covered');
|
|
21568
|
+
}
|
|
21569
|
+
conversationGlassFlight.covered = row;
|
|
21570
|
+
row.classList.add('liquid-selection-covered');
|
|
21571
|
+
positionConversationGlassAroundRow(float, row);
|
|
21572
|
+
}
|
|
21573
|
+
var generation = ++conversationGlassFlight.generation;
|
|
21574
|
+
if (!redirecting) {
|
|
21575
|
+
place(source);
|
|
21576
|
+
showLiquidViewportFloat(float);
|
|
21577
|
+
place(source);
|
|
21578
|
+
// Lock the true source geometry before enabling lift and movement together.
|
|
21579
|
+
float.getBoundingClientRect();
|
|
21580
|
+
requestAnimationFrame(function() {
|
|
21581
|
+
if (conversationGlassFlight.generation !== generation || !float.isConnected) return;
|
|
21582
|
+
float.classList.remove('liquid-selection-primed');
|
|
21583
|
+
float.classList.add('visible');
|
|
21584
|
+
place(target);
|
|
21585
|
+
});
|
|
21586
|
+
window.setLiquidSidebarGestureLock('conversation-flight', true);
|
|
21587
|
+
} else {
|
|
21588
|
+
// Updating top/left while the transition is running makes CSS continue
|
|
21589
|
+
// from the currently rendered position; no second glass block is born.
|
|
21590
|
+
float.classList.remove('liquid-selection-landing');
|
|
21591
|
+
place(target);
|
|
21592
|
+
}
|
|
21593
|
+
if (conversationGlassFlight.timer) clearTimeout(conversationGlassFlight.timer);
|
|
21594
|
+
waitForLiquidSelectionArrival(float, function() {
|
|
21595
|
+
if (conversationGlassFlight.generation !== generation) return;
|
|
21596
|
+
conversationGlassFlight.timer = 0;
|
|
21597
|
+
if (options.dragging && target.isConnected) {
|
|
21598
|
+
wrapConversationDragContent(target, float);
|
|
21599
|
+
window.setLiquidSidebarGestureLock('conversation-flight', false);
|
|
21600
|
+
if (options.onArrive) options.onArrive();
|
|
21601
|
+
return;
|
|
21602
|
+
}
|
|
21603
|
+
landLiquidSelectionFloat(float, function() {
|
|
21604
|
+
if (conversationGlassFlight.generation !== generation) return;
|
|
21605
|
+
if (float._releaseKyantGlass) float._releaseKyantGlass();
|
|
21606
|
+
if (float.parentNode) float.parentNode.removeChild(float);
|
|
21607
|
+
conversationGlassFlight.float = null;
|
|
21608
|
+
if (conversationGlassFlight.source) conversationGlassFlight.source.classList.remove('liquid-selection-source');
|
|
21609
|
+
conversationGlassFlight.source = null;
|
|
21610
|
+
if (conversationGlassFlight.covered) conversationGlassFlight.covered.classList.remove('liquid-selection-covered');
|
|
21611
|
+
conversationGlassFlight.covered = null;
|
|
21612
|
+
window.setLiquidSidebarGestureLock('conversation-flight', false);
|
|
21613
|
+
if (options.onArrive) options.onArrive();
|
|
21614
|
+
});
|
|
21615
|
+
});
|
|
21616
|
+
};
|
|
21617
|
+
|
|
21618
|
+
window.activateConversationWithGlass = function(idx, row) {
|
|
21619
|
+
var conversations = currentWorkspaceConversations();
|
|
21620
|
+
if (!row || !conversations[idx]) {
|
|
21621
|
+
window.switchConversation(idx);
|
|
21622
|
+
return;
|
|
21623
|
+
}
|
|
21624
|
+
var targetId = String(conversations[idx].id || '');
|
|
21625
|
+
window.animateConversationGlassTo(row, { onArrive: function() {
|
|
21626
|
+
var current = currentWorkspaceConversations();
|
|
21627
|
+
var targetIndex = current.findIndex(function(item) { return String(item.id || '') === targetId; });
|
|
21628
|
+
if (targetIndex >= 0) window.switchConversation(targetIndex);
|
|
21629
|
+
} });
|
|
21630
|
+
};
|
|
21631
|
+
|
|
19567
21632
|
function renderConversations() {
|
|
19568
21633
|
var list = els['conversation-list'];
|
|
19569
21634
|
if (!list) return;
|
|
19570
|
-
list.
|
|
21635
|
+
if (list.dataset.conversationDropWired !== 'true') {
|
|
21636
|
+
list.dataset.conversationDropWired = 'true';
|
|
21637
|
+
list.addEventListener('dragover', function(event) {
|
|
21638
|
+
window.updateConversationNativeDrag(event.clientX, event.clientY);
|
|
21639
|
+
if (conversationNativeDrag.row) event.preventDefault();
|
|
21640
|
+
});
|
|
21641
|
+
list.addEventListener('drop', function(event) {
|
|
21642
|
+
if (!list.querySelector('.conv-item.dragging')) return;
|
|
21643
|
+
event.preventDefault();
|
|
21644
|
+
list.dataset.conversationDropCommitted = 'true';
|
|
21645
|
+
window.commitConversationDomOrder();
|
|
21646
|
+
});
|
|
21647
|
+
}
|
|
19571
21648
|
if (!state.currentWorkspace) {
|
|
19572
21649
|
list.innerHTML = '<div style="padding:14px 10px;font-size:12px;color:var(--text-dim);line-height:1.5;text-align:center;">' + esc(t('workspace.selectFirst')) + '</div>';
|
|
19573
21650
|
updateWorkspaceGate();
|
|
19574
21651
|
return;
|
|
19575
21652
|
}
|
|
19576
21653
|
var conversations = currentWorkspaceConversations();
|
|
21654
|
+
// 工作区对话被清空后保持为空:只显示空态提示,不自动创建对话。
|
|
21655
|
+
if (!conversations.length) {
|
|
21656
|
+
list.innerHTML = '<div style="padding:14px 10px;font-size:12px;color:var(--text-dim);line-height:1.5;text-align:center;">' + esc(t('conversation.emptyWorkspace')) + '</div>';
|
|
21657
|
+
updateWorkspaceGate();
|
|
21658
|
+
return;
|
|
21659
|
+
}
|
|
19577
21660
|
var summaryCounts = {};
|
|
19578
21661
|
for (var ci = 0; ci < conversations.length; ci++) {
|
|
19579
21662
|
var summaryKey = String(conversations[ci] && conversations[ci].summary || '');
|
|
@@ -19582,7 +21665,17 @@ function renderConversations() {
|
|
|
19582
21665
|
var activeBeforeRender = (conversations.find(function(c) { return c && c.active; }) || {}).id;
|
|
19583
21666
|
if (activeBeforeRender) setActiveWorkspaceConversationById(activeBeforeRender);
|
|
19584
21667
|
else setActiveWorkspaceConversation(state.workspaceActiveConversation[currentWorkspaceKey()] || 0);
|
|
19585
|
-
|
|
21668
|
+
// 增量更新:复用已存在的行节点,就地更新 class 与内容。行节点不被替换,
|
|
21669
|
+
// 运行中对话的 marquee 跑马灯 CSS 动画就不会因重建 DOM 而从头开始。
|
|
21670
|
+
Array.prototype.slice.call(list.children).forEach(function(child) {
|
|
21671
|
+
if (!child || !child.classList || !child.classList.contains('conv-item')) child.parentNode.removeChild(child);
|
|
21672
|
+
});
|
|
21673
|
+
var existingRows = {};
|
|
21674
|
+
Array.prototype.slice.call(list.querySelectorAll('.conv-item')).forEach(function(row) {
|
|
21675
|
+
existingRows[String(row.getAttribute('data-conversation-id') || '')] = row;
|
|
21676
|
+
});
|
|
21677
|
+
var keepRows = {};
|
|
21678
|
+
var orderedRows = [];
|
|
19586
21679
|
for (var i = 0; i < conversations.length; i++) {
|
|
19587
21680
|
var conv = conversations[i];
|
|
19588
21681
|
var runtimeKey = currentRuntimeKey(conv.id);
|
|
@@ -19591,53 +21684,114 @@ function renderConversations() {
|
|
|
19591
21684
|
if (summaryCounts[String(conv.summary || '')] > 1) {
|
|
19592
21685
|
displaySummary += ' · ' + String(conv.id || '').slice(-8);
|
|
19593
21686
|
}
|
|
19594
|
-
var
|
|
19595
|
-
div.className = 'conv-item' + (conv.active ? ' active' : '');
|
|
19596
|
-
div.setAttribute('role', 'listitem');
|
|
19597
|
-
div.setAttribute('tabindex', conv.active ? '0' : '-1');
|
|
19598
|
-
div.setAttribute('aria-current', conv.active ? 'true' : 'false');
|
|
19599
|
-
div.setAttribute('draggable', 'true');
|
|
19600
|
-
div.setAttribute('data-conversation-id', String(conv.id || ''));
|
|
19601
|
-
if (runtimeState && ['running', 'stopping', 'force_restarting'].indexOf(String(runtimeState.status || '')) >= 0) div.classList.add('marquee-border');
|
|
21687
|
+
var running = !!(runtimeState && ['running', 'stopping', 'force_restarting'].indexOf(String(runtimeState.status || '')) >= 0);
|
|
19602
21688
|
var runtimeBadge = runtimeState && runtimeState.status && runtimeState.status !== 'idle'
|
|
19603
21689
|
? '<span class="conv-runtime-badge ' + escAttr(String(runtimeState.status)) + '">' + esc(String(runtimeState.status)) + '</span>' : '';
|
|
19604
21690
|
var branchCommIcon = conv.branchCommunication
|
|
19605
21691
|
? '<span class="conv-branch-comm-icon" title="' + escAttr(t('conversation.branchCommunicationBadge')) + '" aria-label="' + escAttr(t('conversation.branchCommunicationBadge')) + '">' + iconSvg('git-branch', t('conversation.branchCommunicationBadge')) + '</span>' : '';
|
|
19606
|
-
|
|
21692
|
+
var innerKey = [String(displaySummary), String(conv.messageCount || ''), String(runtimeState && runtimeState.status || ''), conv.branchCommunication ? '1' : '0'].join('|');
|
|
21693
|
+
var nextInner = '<span class="conv-summary" title="' + escAttr(String(conv.id || '')) + '">' + esc(displaySummary) + (conv.messageCount ? ' (' + esc(String(conv.messageCount)) + ')' : '') + '</span>' + branchCommIcon + runtimeBadge +
|
|
19607
21694
|
'<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>';
|
|
19608
|
-
div
|
|
19609
|
-
div
|
|
19610
|
-
|
|
19611
|
-
|
|
19612
|
-
|
|
19613
|
-
|
|
19614
|
-
|
|
19615
|
-
|
|
19616
|
-
|
|
19617
|
-
|
|
19618
|
-
|
|
19619
|
-
|
|
19620
|
-
|
|
19621
|
-
|
|
19622
|
-
|
|
19623
|
-
|
|
19624
|
-
|
|
19625
|
-
|
|
19626
|
-
|
|
19627
|
-
|
|
19628
|
-
|
|
19629
|
-
|
|
19630
|
-
|
|
19631
|
-
|
|
19632
|
-
|
|
19633
|
-
|
|
19634
|
-
|
|
19635
|
-
|
|
19636
|
-
|
|
19637
|
-
|
|
19638
|
-
|
|
21695
|
+
var div = existingRows[String(conv.id || '')];
|
|
21696
|
+
if (div) {
|
|
21697
|
+
keepRows[String(conv.id || '')] = true;
|
|
21698
|
+
} else {
|
|
21699
|
+
div = document.createElement('div');
|
|
21700
|
+
div.setAttribute('role', 'listitem');
|
|
21701
|
+
div.setAttribute('draggable', 'true');
|
|
21702
|
+
div.setAttribute('data-conversation-id', String(conv.id || ''));
|
|
21703
|
+
div.addEventListener('keydown', window.handleConversationKey);
|
|
21704
|
+
div.addEventListener('contextmenu', function(event) {
|
|
21705
|
+
event.preventDefault();
|
|
21706
|
+
event.stopPropagation();
|
|
21707
|
+
window.openConversationActionMenu(event.currentTarget, this.getAttribute('data-conversation-id') || '', { x: event.clientX, y: event.clientY });
|
|
21708
|
+
});
|
|
21709
|
+
div.addEventListener('dragstart', function(event) {
|
|
21710
|
+
window.cancelConversationGlassFlight();
|
|
21711
|
+
var rect = this.getBoundingClientRect();
|
|
21712
|
+
conversationNativeDrag.row = this;
|
|
21713
|
+
conversationNativeDrag.arrived = false;
|
|
21714
|
+
conversationNativeDrag.grabX = Math.max(0, Math.min(rect.width, event.clientX - rect.left));
|
|
21715
|
+
conversationNativeDrag.grabY = Math.max(0, Math.min(rect.height, event.clientY - rect.top));
|
|
21716
|
+
conversationNativeDrag.clientX = event.clientX;
|
|
21717
|
+
conversationNativeDrag.clientY = event.clientY;
|
|
21718
|
+
if (event.dataTransfer) {
|
|
21719
|
+
var transparentDragImage = document.createElement('canvas');
|
|
21720
|
+
transparentDragImage.width = transparentDragImage.height = 1;
|
|
21721
|
+
document.body.appendChild(transparentDragImage);
|
|
21722
|
+
event.dataTransfer.setDragImage(transparentDragImage, 0, 0);
|
|
21723
|
+
setTimeout(function() { transparentDragImage.remove(); }, 0);
|
|
21724
|
+
}
|
|
21725
|
+
list.classList.add('conversation-reordering');
|
|
21726
|
+
list.dataset.conversationDropCommitted = 'false';
|
|
21727
|
+
window.setLiquidSidebarGestureLock('conversation-reorder', true);
|
|
21728
|
+
window.animateConversationGlassTo(this, { dragging: true });
|
|
21729
|
+
if (event.dataTransfer) { event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.setData('text/plain', this.getAttribute('data-conversation-id') || ''); }
|
|
21730
|
+
});
|
|
21731
|
+
div.addEventListener('drag', function(event) {
|
|
21732
|
+
if (conversationNativeDrag.row === this && (event.clientX || event.clientY)) {
|
|
21733
|
+
window.updateConversationNativeDrag(event.clientX, event.clientY);
|
|
21734
|
+
}
|
|
21735
|
+
});
|
|
21736
|
+
div.addEventListener('dragend', function() {
|
|
21737
|
+
var releasedRow = this;
|
|
21738
|
+
list.classList.remove('conversation-reordering');
|
|
21739
|
+
Array.from(list.querySelectorAll('.conv-item')).forEach(function(row) { row.classList.remove('drag-over'); });
|
|
21740
|
+
window.animateConversationGlassTo(releasedRow, { onArrive: function() {
|
|
21741
|
+
releasedRow.classList.remove('dragging', 'conversation-glass-dragging');
|
|
21742
|
+
conversationNativeDrag.row = null;
|
|
21743
|
+
conversationNativeDrag.float = null;
|
|
21744
|
+
conversationNativeDrag.arrived = false;
|
|
21745
|
+
window.setLiquidSidebarGestureLock('conversation-reorder', false);
|
|
21746
|
+
renderConversations();
|
|
21747
|
+
} });
|
|
21748
|
+
});
|
|
21749
|
+
div.addEventListener('dragover', function(event) {
|
|
21750
|
+
window.updateConversationNativeDrag(event.clientX, event.clientY);
|
|
21751
|
+
var dragging = list.querySelector('.conv-item.dragging');
|
|
21752
|
+
if (!dragging || dragging === this) return;
|
|
21753
|
+
var convsNow = currentWorkspaceConversations();
|
|
21754
|
+
var draggingConv = convsNow.find(function(item) { return String(item.id) === String(dragging.getAttribute('data-conversation-id')); });
|
|
21755
|
+
var targetConv = convsNow.find(function(item) { return String(item.id) === String(this.getAttribute('data-conversation-id')); }, this);
|
|
21756
|
+
if (!draggingConv || !targetConv || !!draggingConv.pinned !== !!targetConv.pinned) return;
|
|
21757
|
+
event.preventDefault();
|
|
21758
|
+
var targetRect = this.getBoundingClientRect();
|
|
21759
|
+
var after = event.clientY >= targetRect.top + targetRect.height / 2;
|
|
21760
|
+
var anchor = after ? this.nextSibling : this;
|
|
21761
|
+
if (anchor !== dragging) list.insertBefore(dragging, anchor);
|
|
21762
|
+
Array.from(list.querySelectorAll('.conv-item')).forEach(function(row) { row.classList.remove('drag-over'); });
|
|
21763
|
+
});
|
|
21764
|
+
div.addEventListener('dragleave', function() { this.classList.remove('drag-over'); });
|
|
21765
|
+
div.addEventListener('drop', function(event) {
|
|
21766
|
+
event.preventDefault();
|
|
21767
|
+
event.stopPropagation();
|
|
21768
|
+
this.classList.remove('drag-over');
|
|
21769
|
+
list.dataset.conversationDropCommitted = 'true';
|
|
21770
|
+
window.commitConversationDomOrder();
|
|
21771
|
+
});
|
|
21772
|
+
existingRows[String(conv.id || '')] = div;
|
|
21773
|
+
}
|
|
21774
|
+
div.className = 'conv-item' + (conv.active ? ' active' : '');
|
|
21775
|
+
if (running) div.classList.add('marquee-border');
|
|
21776
|
+
div.setAttribute('tabindex', conv.active ? '0' : '-1');
|
|
21777
|
+
div.setAttribute('aria-current', conv.active ? 'true' : 'false');
|
|
21778
|
+
if (div.getAttribute('data-inner-key') !== innerKey) {
|
|
21779
|
+
div.setAttribute('data-inner-key', innerKey);
|
|
21780
|
+
div.innerHTML = nextInner;
|
|
21781
|
+
}
|
|
21782
|
+
div.onclick = function(idx) { return function(event) { window.activateConversationWithGlass(idx, event.currentTarget); }; }(i);
|
|
21783
|
+
orderedRows.push(div);
|
|
19639
21784
|
}
|
|
19640
|
-
|
|
21785
|
+
Object.keys(existingRows).forEach(function(id) {
|
|
21786
|
+
if (keepRows[id]) return;
|
|
21787
|
+
var stale = existingRows[id];
|
|
21788
|
+
if (stale && stale.parentNode === list) stale.parentNode.removeChild(stale);
|
|
21789
|
+
});
|
|
21790
|
+
orderedRows.forEach(function(row, idx) {
|
|
21791
|
+
var current = list.children[idx];
|
|
21792
|
+
if (current === row) return;
|
|
21793
|
+
list.insertBefore(row, current || null);
|
|
21794
|
+
});
|
|
19641
21795
|
updateWorkspaceGate();
|
|
19642
21796
|
}
|
|
19643
21797
|
|
|
@@ -19925,16 +22079,28 @@ window.archiveConv = function(conversationId) {
|
|
|
19925
22079
|
var currentConvs = currentWorkspaceConversations();
|
|
19926
22080
|
var currentIdx = currentConvs.findIndex(function(item) { return String(item && item.id || 'default') === targetId; });
|
|
19927
22081
|
if (currentIdx >= 0) currentConvs.splice(currentIdx, 1);
|
|
19928
|
-
if (!currentConvs.length)
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
|
|
19932
|
-
|
|
19933
|
-
|
|
19934
|
-
|
|
19935
|
-
|
|
19936
|
-
|
|
19937
|
-
|
|
22082
|
+
if (!currentConvs.length) {
|
|
22083
|
+
// 工作区所有对话被清空后保持为空:不再自动创建 default 对话。
|
|
22084
|
+
for (var emptyI = 0; emptyI < currentConvs.length; emptyI++) currentConvs[emptyI].active = false;
|
|
22085
|
+
state.workspaceActiveConversation[workspaceKey] = -1;
|
|
22086
|
+
state.activeConversation = -1;
|
|
22087
|
+
state.conversations = currentConvs;
|
|
22088
|
+
if (priorActiveId === targetId) {
|
|
22089
|
+
state.activeBackendConversationId = '';
|
|
22090
|
+
if (els['chat-area']) els['chat-area'].innerHTML = '';
|
|
22091
|
+
if (window.renderFlowTakeover) window.renderFlowTakeover(false, '', { target: targetRuntime });
|
|
22092
|
+
}
|
|
22093
|
+
} else {
|
|
22094
|
+
var nextActiveId = priorActiveId && priorActiveId !== targetId ? priorActiveId : String(currentConvs[Math.min(Math.max(currentIdx, 0), currentConvs.length - 1)].id || 'default');
|
|
22095
|
+
var nextActiveIndex = Math.max(0, currentConvs.findIndex(function(item) { return String(item && item.id || '') === nextActiveId; }));
|
|
22096
|
+
for (var i = 0; i < currentConvs.length; i++) currentConvs[i].active = i === nextActiveIndex;
|
|
22097
|
+
state.workspaceActiveConversation[workspaceKey] = nextActiveIndex;
|
|
22098
|
+
state.activeConversation = nextActiveIndex;
|
|
22099
|
+
state.conversations = currentConvs;
|
|
22100
|
+
if (priorActiveId === targetId) {
|
|
22101
|
+
state.activeBackendConversationId = nextActiveId;
|
|
22102
|
+
if (els['chat-area']) els['chat-area'].innerHTML = '';
|
|
22103
|
+
}
|
|
19938
22104
|
}
|
|
19939
22105
|
if (state.flowTakeovers) {
|
|
19940
22106
|
var archivedFlowKey = runtimeKeyFor(targetRuntime.workspaceId, targetRuntime.conversationId);
|
|
@@ -20138,6 +22304,7 @@ window.renderMemoryLab = function() {
|
|
|
20138
22304
|
html += '</div>';
|
|
20139
22305
|
panel.innerHTML = html;
|
|
20140
22306
|
if (state.memoryLabSearchQuery) window.updateMemoryLabSearch(state.memoryLabSearchQuery);
|
|
22307
|
+
wireLiquidRailInteractions(panel.querySelector('.memory-lab-view-menu'), 'button[data-memory-view]');
|
|
20141
22308
|
if (state.memoryLabView === 'overview') requestAnimationFrame(function() { window.initMemoryLabOverview(); });
|
|
20142
22309
|
state.memoryLabNavDirection = 'fade';
|
|
20143
22310
|
};
|
|
@@ -20146,8 +22313,8 @@ window.memoryLabTopbar = function(lab) {
|
|
|
20146
22313
|
var view = state.memoryLabView || 'overview';
|
|
20147
22314
|
return '<div class="memory-lab-panel"><div class="memory-lab-topbar">' +
|
|
20148
22315
|
'<div class="memory-lab-view-menu" role="tablist">' +
|
|
20149
|
-
'<button class="' + (view === 'overview' ? 'active' : '') + '" onclick="window.switchMemoryLabView(\'overview\')">' + esc(t('memoryLab.overview')) + '</button>' +
|
|
20150
|
-
'<button class="' + (view === 'detail' ? 'active' : '') + '" onclick="window.switchMemoryLabView(\'detail\')">' + esc(t('memoryLab.detail')) + '</button>' +
|
|
22316
|
+
'<button data-memory-view="overview" class="' + (view === 'overview' ? 'active' : '') + '" onclick="window.switchMemoryLabView(\'overview\')">' + esc(t('memoryLab.overview')) + '</button>' +
|
|
22317
|
+
'<button data-memory-view="detail" class="' + (view === 'detail' ? 'active' : '') + '" onclick="window.switchMemoryLabView(\'detail\')">' + esc(t('memoryLab.detail')) + '</button>' +
|
|
20151
22318
|
'</div>' +
|
|
20152
22319
|
'<div class="memory-lab-search">' +
|
|
20153
22320
|
'<input id="memory-lab-search-input" value="' + escAttr(state.memoryLabSearchQuery || '') + '" placeholder="' + escAttr(t('memoryLab.search')) + '" oninput="window.updateMemoryLabSearch(this.value)" onfocus="window.updateMemoryLabSearch(this.value)" />' +
|
|
@@ -20885,6 +23052,7 @@ window.showPluginList = function(tab, options) {
|
|
|
20885
23052
|
state.activeSubWindowView = { name: 'plugins', tab: requested };
|
|
20886
23053
|
}
|
|
20887
23054
|
window.syncPluginTabs(!pluginOpen || !!(options && options.focusTab));
|
|
23055
|
+
wireLiquidRailInteractions(document.querySelector('.plugin-tabs[role="tablist"]'), '.stab-btn[data-plugin-tab]');
|
|
20888
23056
|
var panel = document.getElementById('plugin-panel');
|
|
20889
23057
|
if (panel) panel.replaceChildren();
|
|
20890
23058
|
if (requested === 'mcp') window.renderMcpManager();
|
|
@@ -21798,6 +23966,30 @@ window.renderLeftWsList = function() {
|
|
|
21798
23966
|
var holder = document.createElement('div');
|
|
21799
23967
|
holder.innerHTML = html;
|
|
21800
23968
|
appendDomNodesInBatches(container, Array.from(holder.children), 'workspace-list', 24);
|
|
23969
|
+
wireLiquidRailInteractions(container, '.left-ws-item');
|
|
23970
|
+
};
|
|
23971
|
+
|
|
23972
|
+
window.commitConversationDomOrder = function() {
|
|
23973
|
+
var list = els['conversation-list'];
|
|
23974
|
+
var convs = currentWorkspaceConversations();
|
|
23975
|
+
if (!list || !convs.length) return;
|
|
23976
|
+
window.cancelConversationGlassFlight();
|
|
23977
|
+
var byId = {};
|
|
23978
|
+
convs.forEach(function(item) { byId[String(item.id)] = item; });
|
|
23979
|
+
var ids = Array.from(list.querySelectorAll('.conv-item')).map(function(row) {
|
|
23980
|
+
return String(row.getAttribute('data-conversation-id') || '');
|
|
23981
|
+
}).filter(function(id) { return !!byId[id]; });
|
|
23982
|
+
if (ids.length !== convs.length) return renderConversations();
|
|
23983
|
+
var activeId = activeConversationId();
|
|
23984
|
+
convs.splice.apply(convs, [0, convs.length].concat(ids.map(function(id) { return byId[id]; })));
|
|
23985
|
+
setActiveWorkspaceConversationById(activeId);
|
|
23986
|
+
renderConversations();
|
|
23987
|
+
Promise.resolve(api.reorderConversations && api.reorderConversations(ids)).then(function(saved) {
|
|
23988
|
+
if (!saved) throw new Error(t('conversation.reorderFailed'));
|
|
23989
|
+
}).catch(function() {
|
|
23990
|
+
showUiNotice(t('conversation.reorderFailed'), 'error', 'reorder-conversations');
|
|
23991
|
+
if (api.getState) api.getState(currentConversationTarget(activeId)).then(function(s) { if (s) applyWorkspaceStateFromBackend(s); });
|
|
23992
|
+
});
|
|
21801
23993
|
};
|
|
21802
23994
|
|
|
21803
23995
|
window.handleWorkspaceKey = function(event) {
|
|
@@ -21916,10 +24108,10 @@ window.showNewWorkspaceDialog = function() {
|
|
|
21916
24108
|
state.activeSubWindowView = { name: 'workspaceNew' };
|
|
21917
24109
|
var html = '<div style="padding:10px;">' +
|
|
21918
24110
|
'<label style="display:block;font-size:12px;color:var(--text-dim);margin-bottom:6px;">' + esc(t('workspace.type')) + '</label>' +
|
|
21919
|
-
'<div
|
|
21920
|
-
'<label
|
|
21921
|
-
'<label
|
|
21922
|
-
'<label
|
|
24111
|
+
'<div class="workspace-type-pager" role="radiogroup" aria-label="' + escAttr(t('workspace.type')) + '">' +
|
|
24112
|
+
'<label class="workspace-type-option active"><input type="radio" name="ws-type" value="internal" checked onchange="window.toggleWsType()">' + esc(t('workspace.internal')) + '</label>' +
|
|
24113
|
+
'<label class="workspace-type-option"><input type="radio" name="ws-type" value="external" onchange="window.toggleWsType()">' + esc(t('workspace.external')) + '</label>' +
|
|
24114
|
+
'<label class="workspace-type-option"><input type="radio" name="ws-type" value="ssh" onchange="window.toggleWsType()">' + esc(t('workspace.ssh')) + '</label>' +
|
|
21923
24115
|
'</div>' +
|
|
21924
24116
|
'<label id="ws-name-label-dlg" style="display:block;font-size:12px;color:var(--text-dim);margin-bottom:8px;">' + esc(t('workspace.name')) + '</label>' +
|
|
21925
24117
|
'<input id="new-ws-input" style="width:100%;padding:8px 10px;border:1px solid var(--glass-border-2);border-radius:var(--radius-sm);background:var(--glass-bg-1);color:var(--text);font-size:13px;outline:none;box-sizing:border-box;" placeholder="' + escAttr(t('workspace.enterName')) + '" onkeydown="if(event.key===\'Enter\'&&!event.isComposing&&event.key!==\'Process\'&&event.keyCode!==229)window.doCreateWorkspace()">' +
|
|
@@ -21944,6 +24136,7 @@ window.showNewWorkspaceDialog = function() {
|
|
|
21944
24136
|
'<button class="sec-btn" onclick="window.closeSubWin()">' + esc(t('common.cancel')) + '</button>' +
|
|
21945
24137
|
'</div></div>';
|
|
21946
24138
|
window.openSubWin(t('workspace.new'), html);
|
|
24139
|
+
wireLiquidRailInteractions(document.querySelector('.workspace-type-pager'), '.workspace-type-option');
|
|
21947
24140
|
setTimeout(function() {
|
|
21948
24141
|
var inp = document.getElementById('new-ws-input');
|
|
21949
24142
|
if (inp) inp.focus();
|
|
@@ -21955,6 +24148,11 @@ window.toggleWsType = function() {
|
|
|
21955
24148
|
for (var i = 0; i < radios.length; i++) {
|
|
21956
24149
|
if (radios[i].checked) { selected = radios[i].value; break; }
|
|
21957
24150
|
}
|
|
24151
|
+
var options = document.querySelectorAll('.workspace-type-option');
|
|
24152
|
+
for (var oi = 0; oi < options.length; oi++) {
|
|
24153
|
+
var radio = options[oi].querySelector('input[name="ws-type"]');
|
|
24154
|
+
options[oi].classList.toggle('active', !!(radio && radio.checked));
|
|
24155
|
+
}
|
|
21958
24156
|
var extPath = document.getElementById('ws-ext-path');
|
|
21959
24157
|
var sshFields = document.getElementById('ws-ssh-fields');
|
|
21960
24158
|
var nameLabel = document.getElementById('ws-name-label-dlg');
|
|
@@ -23058,6 +25256,7 @@ function schedulePostStartupUiRendering() {
|
|
|
23058
25256
|
this.setAttribute('aria-valuenow', String(Math.round(size)));
|
|
23059
25257
|
});
|
|
23060
25258
|
handles[i].addEventListener('mousedown', function(e) {
|
|
25259
|
+
if (window.liquidSidebarGesturesLocked && window.liquidSidebarGesturesLocked()) return;
|
|
23061
25260
|
e.preventDefault();
|
|
23062
25261
|
var handle = this;
|
|
23063
25262
|
var target = this.getAttribute('data-target');
|