newmark-agent 0.5.4 → 0.5.7
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 +71 -26
- package/dist/core/agent.js +48 -24
- package/dist/core/agentKernelRunner.js +12 -1
- 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/types.d.ts +21 -0
- package/dist/core/wslAgentRuntimePool.js +6 -2
- package/dist/main.js +34 -0
- package/dist/preload.js +1 -0
- package/dist/providers/provider-events.d.ts +6 -0
- package/dist/providers/provider-events.js +15 -4
- package/dist/server.js +21 -0
- package/dist/ui/index.html +2345 -197
- package/dist/ui/lucide-sprite.svg +4 -0
- package/dist/wsl-agent-host.bundle.cjs +71 -26
- package/package.json +8 -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 {
|
|
@@ -3296,8 +3373,7 @@ button.todo-item { background: transparent; }
|
|
|
3296
3373
|
padding: 12px 16px;
|
|
3297
3374
|
border-top: 1px solid var(--glass-border-2);
|
|
3298
3375
|
background: var(--glass-bg-2);
|
|
3299
|
-
|
|
3300
|
-
-webkit-backdrop-filter: blur(var(--glass-blur-3)) saturate(140%);
|
|
3376
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow);
|
|
3301
3377
|
}
|
|
3302
3378
|
|
|
3303
3379
|
#input-area textarea {
|
|
@@ -3443,10 +3519,9 @@ button.todo-item { background: transparent; }
|
|
|
3443
3519
|
overflow: auto;
|
|
3444
3520
|
padding: 7px;
|
|
3445
3521
|
border: 1px solid var(--glass-border-2);
|
|
3446
|
-
border-radius: var(--radius-
|
|
3522
|
+
border-radius: var(--radius-xl);
|
|
3447
3523
|
background: var(--modal-surface);
|
|
3448
|
-
box-shadow: var(--shadow-lg);
|
|
3449
|
-
backdrop-filter: blur(var(--glass-blur-3)) saturate(1.08);
|
|
3524
|
+
box-shadow: var(--shadow-lg), var(--liquid-top-highlight), var(--liquid-inner-shadow);
|
|
3450
3525
|
}
|
|
3451
3526
|
.model-select-menu:popover-open { display: block; animation: model-menu-in 150ms var(--ease-out-expo); }
|
|
3452
3527
|
|
|
@@ -3470,6 +3545,85 @@ button.todo-item { background: transparent; }
|
|
|
3470
3545
|
.model-select-menu-option:hover,
|
|
3471
3546
|
.model-select-menu-option:focus-visible { background: var(--control-hover-bg); color: var(--text-bright); outline: none; }
|
|
3472
3547
|
.model-select-menu-option.selected { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--text-bright); }
|
|
3548
|
+
.liquid-selection-source { background: transparent !important; border-color: transparent !important; }
|
|
3549
|
+
.liquid-selection-source::after { display: none !important; opacity: 0 !important; }
|
|
3550
|
+
.conv-item.liquid-selection-covered { border-color: transparent !important; }
|
|
3551
|
+
.conv-item.liquid-selection-source.marquee-border::before,
|
|
3552
|
+
.conv-item.liquid-selection-covered.marquee-border::before { opacity: 0 !important; }
|
|
3553
|
+
/* Rails and selectable menus are governed by one shared moving glass block.
|
|
3554
|
+
Suppress the generic pressed pseudo-glass so the destination never creates
|
|
3555
|
+
a second block while the source block is flying toward it. */
|
|
3556
|
+
.model-select-menu-option::after,
|
|
3557
|
+
.newmark-select-option::after,
|
|
3558
|
+
.left-nav-icon::after,
|
|
3559
|
+
.lt-item::after,
|
|
3560
|
+
.left-ws-item::after,
|
|
3561
|
+
.tab-btn::after,
|
|
3562
|
+
.stab-btn[data-stab]::after,
|
|
3563
|
+
.stab-btn[data-plugin-tab]::after,
|
|
3564
|
+
.mode-toggle-btn[data-mode]::after,
|
|
3565
|
+
.memory-lab-view-menu button[data-memory-view]::after {
|
|
3566
|
+
display: none !important;
|
|
3567
|
+
opacity: 0 !important;
|
|
3568
|
+
}
|
|
3569
|
+
.left-ws-item.liquid-selection-source,
|
|
3570
|
+
.tab-btn.liquid-selection-source { background: transparent !important; }
|
|
3571
|
+
#left-ws-list,
|
|
3572
|
+
#right-tabs { position: relative; }
|
|
3573
|
+
.left-nav-icon.liquid-tool-active,
|
|
3574
|
+
.lt-item.liquid-tool-active { background: var(--control-hover-bg); color: var(--text-bright); }
|
|
3575
|
+
.liquid-selection-float {
|
|
3576
|
+
--liquid-lift-scale: .94;
|
|
3577
|
+
--liquid-motion-angle: 0rad;
|
|
3578
|
+
--liquid-motion-stretch: 1;
|
|
3579
|
+
--liquid-motion-squash: 1;
|
|
3580
|
+
position: absolute;
|
|
3581
|
+
z-index: 4;
|
|
3582
|
+
display: flex;
|
|
3583
|
+
align-items: center;
|
|
3584
|
+
padding: 6px 10px;
|
|
3585
|
+
box-sizing: border-box;
|
|
3586
|
+
border-radius: var(--radius-full);
|
|
3587
|
+
color: var(--text-bright);
|
|
3588
|
+
pointer-events: none;
|
|
3589
|
+
overflow: visible;
|
|
3590
|
+
opacity: 0;
|
|
3591
|
+
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)));
|
|
3592
|
+
transform-origin: center;
|
|
3593
|
+
background: transparent;
|
|
3594
|
+
background-color: transparent !important;
|
|
3595
|
+
background-image: none !important;
|
|
3596
|
+
backdrop-filter: none;
|
|
3597
|
+
-webkit-backdrop-filter: none;
|
|
3598
|
+
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);
|
|
3599
|
+
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;
|
|
3600
|
+
will-change: top, left, width, height, transform;
|
|
3601
|
+
}
|
|
3602
|
+
.liquid-selection-float.visible { opacity: 1; --liquid-lift-scale: 1; }
|
|
3603
|
+
.liquid-selection-float.liquid-selection-landing {
|
|
3604
|
+
opacity: 0;
|
|
3605
|
+
--liquid-lift-scale: .86;
|
|
3606
|
+
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)));
|
|
3607
|
+
transition: opacity 120ms var(--ease-out-expo), transform 120ms var(--ease-out-expo) !important;
|
|
3608
|
+
}
|
|
3609
|
+
.liquid-selection-float.liquid-selection-viewport {
|
|
3610
|
+
position: fixed;
|
|
3611
|
+
inset: auto;
|
|
3612
|
+
z-index: 1000;
|
|
3613
|
+
margin: 0;
|
|
3614
|
+
border: 0;
|
|
3615
|
+
}
|
|
3616
|
+
.liquid-selection-float.liquid-selection-primed { transition: none !important; }
|
|
3617
|
+
.liquid-selection-float.liquid-selection-viewport:popover-open { display: flex; }
|
|
3618
|
+
.liquid-selection-float::-webkit-scrollbar { display: none; }
|
|
3619
|
+
.liquid-selection-canvas {
|
|
3620
|
+
position: absolute;
|
|
3621
|
+
inset: 0;
|
|
3622
|
+
width: 100%;
|
|
3623
|
+
height: 100%;
|
|
3624
|
+
border-radius: inherit;
|
|
3625
|
+
pointer-events: none;
|
|
3626
|
+
}
|
|
3473
3627
|
@keyframes model-menu-in { from { opacity: 0; transform: translateY(5px) scale(.985); } to { opacity: 1; transform: none; } }
|
|
3474
3628
|
|
|
3475
3629
|
.newmark-select-shell {
|
|
@@ -3508,8 +3662,7 @@ button.todo-item { background: transparent; }
|
|
|
3508
3662
|
border: 1px solid var(--glass-border-2);
|
|
3509
3663
|
border-radius: var(--radius-lg);
|
|
3510
3664
|
background: var(--modal-surface);
|
|
3511
|
-
box-shadow: var(--shadow-lg);
|
|
3512
|
-
backdrop-filter: blur(var(--glass-blur-3)) saturate(1.08);
|
|
3665
|
+
box-shadow: var(--shadow-lg), var(--liquid-top-highlight), var(--liquid-inner-shadow);
|
|
3513
3666
|
}
|
|
3514
3667
|
.newmark-select-shell.open .newmark-select-menu { display: block; animation: model-menu-in 150ms var(--ease-out-expo); }
|
|
3515
3668
|
.newmark-select-menu-group { padding: 7px 9px 4px; color: var(--text-accent); font: 700 11px var(--font-ui); }
|
|
@@ -3530,6 +3683,7 @@ button.todo-item { background: transparent; }
|
|
|
3530
3683
|
.newmark-select-option:hover,
|
|
3531
3684
|
.newmark-select-option:focus-visible { background: var(--control-hover-bg); color: var(--text-bright); outline: none; }
|
|
3532
3685
|
.newmark-select-option.selected { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--text-bright); }
|
|
3686
|
+
.newmark-select-option.liquid-selection-source { background: transparent !important; }
|
|
3533
3687
|
.newmark-select-option:disabled { opacity: .42; cursor: not-allowed; }
|
|
3534
3688
|
.setting-control > .newmark-select-shell,
|
|
3535
3689
|
.auto-input-group > .newmark-select-shell,
|
|
@@ -3668,9 +3822,11 @@ button.todo-item { background: transparent; }
|
|
|
3668
3822
|
.mode-toggle {
|
|
3669
3823
|
display: flex;
|
|
3670
3824
|
border: 1px solid var(--border);
|
|
3671
|
-
border-radius: var(--radius-
|
|
3672
|
-
overflow:
|
|
3825
|
+
border-radius: var(--radius-full);
|
|
3826
|
+
overflow: visible;
|
|
3673
3827
|
flex-shrink: 0;
|
|
3828
|
+
position: relative;
|
|
3829
|
+
padding: 2px;
|
|
3674
3830
|
}
|
|
3675
3831
|
|
|
3676
3832
|
.mode-toggle-btn {
|
|
@@ -3686,9 +3842,25 @@ button.todo-item { background: transparent; }
|
|
|
3686
3842
|
}
|
|
3687
3843
|
|
|
3688
3844
|
.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); }
|
|
3845
|
+
.mode-toggle-btn.active { background: rgba(91,120,255,0.15); color: var(--accent); border-radius: var(--radius-full); }
|
|
3690
3846
|
.mode-toggle-btn:active { transform: scale(0.95) translateZ(0); }
|
|
3691
3847
|
|
|
3848
|
+
.workspace-type-pager {
|
|
3849
|
+
display: grid;
|
|
3850
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
3851
|
+
position: relative;
|
|
3852
|
+
gap: 2px;
|
|
3853
|
+
padding: 3px;
|
|
3854
|
+
margin-bottom: 12px;
|
|
3855
|
+
border: 1px solid var(--border);
|
|
3856
|
+
border-radius: var(--radius-full);
|
|
3857
|
+
overflow: visible;
|
|
3858
|
+
background: var(--control-bg);
|
|
3859
|
+
}
|
|
3860
|
+
.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; }
|
|
3861
|
+
.workspace-type-option.active { background:var(--control-hover-bg); color:var(--text-bright); }
|
|
3862
|
+
.workspace-type-option input { position:absolute; opacity:0; pointer-events:none; }
|
|
3863
|
+
|
|
3692
3864
|
#submit-btn {
|
|
3693
3865
|
width: 34px; height: 34px;
|
|
3694
3866
|
border-radius: 50%;
|
|
@@ -3809,12 +3981,12 @@ button.todo-item { background: transparent; }
|
|
|
3809
3981
|
linear-gradient(#fff 0 0) content-box,
|
|
3810
3982
|
linear-gradient(#fff 0 0);
|
|
3811
3983
|
mask-composite: exclude;
|
|
3812
|
-
/*
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
animation:
|
|
3984
|
+
/* Live marquee rotation. The @property-registered --marquee-angle animates
|
|
3985
|
+
the conic-gradient sweep; Chromium promotes the masked layer so the border
|
|
3986
|
+
stays composited. Release smoke tests verify the animation is active while
|
|
3987
|
+
ordinary text input remains responsive (see release-ui-render-performance
|
|
3988
|
+
and release-ui-icon smoke). */
|
|
3989
|
+
animation: marquee-rotate var(--marquee-speed) linear infinite;
|
|
3818
3990
|
z-index: -1;
|
|
3819
3991
|
pointer-events: none;
|
|
3820
3992
|
transform: translateZ(0);
|
|
@@ -3825,7 +3997,7 @@ button.todo-item { background: transparent; }
|
|
|
3825
3997
|
background: linear-gradient(var(--marquee-angle, 0deg), var(--g1), var(--g2), var(--g3), var(--g4), var(--g1));
|
|
3826
3998
|
}
|
|
3827
3999
|
@keyframes marquee-rotate-fallback { 0% { filter: hue-rotate(0deg); } 100% { filter: hue-rotate(360deg); } }
|
|
3828
|
-
.marquee-border::before { animation:
|
|
4000
|
+
.marquee-border::before { animation: marquee-rotate-fallback var(--marquee-speed) linear infinite; }
|
|
3829
4001
|
}
|
|
3830
4002
|
|
|
3831
4003
|
.working-glow {
|
|
@@ -4071,16 +4243,24 @@ button.todo-item { background: transparent; }
|
|
|
4071
4243
|
============================================================ */
|
|
4072
4244
|
.settings-tabs {
|
|
4073
4245
|
display: flex;
|
|
4246
|
+
position: relative;
|
|
4074
4247
|
gap: 4px;
|
|
4075
|
-
|
|
4076
|
-
|
|
4248
|
+
width: fit-content;
|
|
4249
|
+
max-width: 100%;
|
|
4250
|
+
padding: 4px;
|
|
4251
|
+
border: 1px solid var(--glass-border-1);
|
|
4252
|
+
border-radius: var(--radius-full);
|
|
4253
|
+
background: var(--glass-bg-1);
|
|
4077
4254
|
margin-bottom: 14px;
|
|
4255
|
+
overflow-x: auto;
|
|
4256
|
+
scrollbar-width: none;
|
|
4078
4257
|
}
|
|
4258
|
+
.settings-tabs::-webkit-scrollbar { display: none; }
|
|
4079
4259
|
|
|
4080
4260
|
.stab-btn {
|
|
4081
4261
|
padding: 6px 14px;
|
|
4082
4262
|
border: 1px solid var(--border);
|
|
4083
|
-
border-radius: var(--radius-
|
|
4263
|
+
border-radius: var(--radius-full);
|
|
4084
4264
|
background: transparent;
|
|
4085
4265
|
color: var(--text-dim);
|
|
4086
4266
|
font-size: 12px;
|
|
@@ -4391,6 +4571,42 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4391
4571
|
box-shadow: 0 0 4px rgba(0,0,0,0.3);
|
|
4392
4572
|
}
|
|
4393
4573
|
|
|
4574
|
+
#settings-glass-opacity {
|
|
4575
|
+
--liquid-range-progress: 85%;
|
|
4576
|
+
height: 6px;
|
|
4577
|
+
border-radius: var(--radius-full);
|
|
4578
|
+
background: linear-gradient(90deg, var(--accent) 0 var(--liquid-range-progress), var(--glass-border-2) var(--liquid-range-progress) 100%);
|
|
4579
|
+
cursor: pointer;
|
|
4580
|
+
}
|
|
4581
|
+
#settings-glass-opacity::-webkit-slider-thumb {
|
|
4582
|
+
width: 16px;
|
|
4583
|
+
height: 16px;
|
|
4584
|
+
border: 0;
|
|
4585
|
+
background: var(--accent);
|
|
4586
|
+
box-shadow: 0 2px 6px rgba(0,0,0,.3);
|
|
4587
|
+
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);
|
|
4588
|
+
}
|
|
4589
|
+
#settings-glass-opacity.liquid-range-dragging::-webkit-slider-thumb {
|
|
4590
|
+
width: 26px;
|
|
4591
|
+
height: 20px;
|
|
4592
|
+
border-radius: var(--radius-full);
|
|
4593
|
+
background: var(--liquid-refraction);
|
|
4594
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%);
|
|
4595
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%);
|
|
4596
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), 0 8px 20px rgba(0,0,0,.34);
|
|
4597
|
+
transform: translateY(-2px) scale(1.22);
|
|
4598
|
+
transition: none;
|
|
4599
|
+
}
|
|
4600
|
+
.settings-glass-range-line { display:flex; align-items:center; gap:10px; }
|
|
4601
|
+
.settings-glass-range-line #settings-glass-opacity { flex:1; min-width:0; }
|
|
4602
|
+
#settings-glass-value {
|
|
4603
|
+
min-width: 42px;
|
|
4604
|
+
color: var(--text-bright);
|
|
4605
|
+
font: 650 11px var(--font-mono);
|
|
4606
|
+
text-align: right;
|
|
4607
|
+
font-variant-numeric: tabular-nums;
|
|
4608
|
+
}
|
|
4609
|
+
|
|
4394
4610
|
.setting-desc {
|
|
4395
4611
|
font-size: 10px;
|
|
4396
4612
|
color: var(--text-dim);
|
|
@@ -4504,9 +4720,13 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4504
4720
|
}
|
|
4505
4721
|
|
|
4506
4722
|
[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(
|
|
4723
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 2.2)) saturate(165%) brightness(1.01);
|
|
4724
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 2.2)) saturate(165%) brightness(1.01);
|
|
4725
|
+
border-color: rgba(54,68,106,0.14);
|
|
4726
|
+
box-shadow: var(--modal-shadow);
|
|
4727
|
+
}
|
|
4728
|
+
[data-theme="light"] .sub-win.liquid-glass-carrier {
|
|
4729
|
+
background: var(--settings-modal-surface) !important;
|
|
4510
4730
|
}
|
|
4511
4731
|
.memory-lab-search-results[hidden] {
|
|
4512
4732
|
display: none;
|
|
@@ -4521,7 +4741,7 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4521
4741
|
align-items: center;
|
|
4522
4742
|
gap: 4px;
|
|
4523
4743
|
padding: 3px;
|
|
4524
|
-
border-radius: var(--radius-
|
|
4744
|
+
border-radius: var(--radius-full);
|
|
4525
4745
|
border: 1px solid var(--glass-border-1);
|
|
4526
4746
|
background: var(--glass-bg-1);
|
|
4527
4747
|
}
|
|
@@ -4572,7 +4792,7 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4572
4792
|
.memory-lab-overview-actions select {
|
|
4573
4793
|
height: 26px;
|
|
4574
4794
|
padding: 0 9px;
|
|
4575
|
-
border-radius: var(--radius-
|
|
4795
|
+
border-radius: var(--radius-full);
|
|
4576
4796
|
border: 1px solid transparent;
|
|
4577
4797
|
background: transparent;
|
|
4578
4798
|
color: var(--text-dim);
|
|
@@ -5400,6 +5620,202 @@ html.newmark-memory-overview-viewer body {
|
|
|
5400
5620
|
opacity: 0.8;
|
|
5401
5621
|
}
|
|
5402
5622
|
|
|
5623
|
+
|
|
5624
|
+
/* ============================================================
|
|
5625
|
+
LIQUID GLASS (Kyant AndroidLiquidGlass backdrop parity)
|
|
5626
|
+
CSS equivalent of the library lens/edge refraction, top
|
|
5627
|
+
highlight, inner shadow, outer shadow and vibrancy boost.
|
|
5628
|
+
============================================================ */
|
|
5629
|
+
.liquid-glass {
|
|
5630
|
+
position: relative;
|
|
5631
|
+
backdrop-filter: blur(var(--glass-blur-3)) saturate(140%) brightness(1.02);
|
|
5632
|
+
-webkit-backdrop-filter: blur(var(--glass-blur-3)) saturate(140%) brightness(1.02);
|
|
5633
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), var(--liquid-outer-shadow);
|
|
5634
|
+
}
|
|
5635
|
+
.liquid-glass::before {
|
|
5636
|
+
content: '';
|
|
5637
|
+
position: absolute;
|
|
5638
|
+
inset: 0;
|
|
5639
|
+
pointer-events: none;
|
|
5640
|
+
border-radius: inherit;
|
|
5641
|
+
background: var(--liquid-refraction);
|
|
5642
|
+
opacity: 0.9;
|
|
5643
|
+
mix-blend-mode: screen;
|
|
5644
|
+
}
|
|
5645
|
+
.liquid-glass::after {
|
|
5646
|
+
content: '';
|
|
5647
|
+
position: absolute;
|
|
5648
|
+
inset: 0;
|
|
5649
|
+
pointer-events: none;
|
|
5650
|
+
border-radius: inherit;
|
|
5651
|
+
border: 1px solid var(--liquid-edge-glow);
|
|
5652
|
+
box-shadow: inset 0 0 0 0.5px var(--liquid-edge-glow-strong);
|
|
5653
|
+
}
|
|
5654
|
+
.liquid-glass-strong {
|
|
5655
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 1.5)) saturate(150%) brightness(1.04);
|
|
5656
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 1.5)) saturate(150%) brightness(1.04);
|
|
5657
|
+
}
|
|
5658
|
+
[data-theme='light'] .liquid-glass {
|
|
5659
|
+
backdrop-filter: none;
|
|
5660
|
+
-webkit-backdrop-filter: none;
|
|
5661
|
+
}
|
|
5662
|
+
[data-theme='light'] .liquid-glass::before {
|
|
5663
|
+
opacity: 0.45;
|
|
5664
|
+
mix-blend-mode: normal;
|
|
5665
|
+
}
|
|
5666
|
+
.liquid-glass-carrier {
|
|
5667
|
+
background: rgb(var(--glass-rgb-2) / var(--carrier-glass-alpha)) !important;
|
|
5668
|
+
backdrop-filter: blur(var(--carrier-glass-blur)) saturate(150%) brightness(1.02) !important;
|
|
5669
|
+
-webkit-backdrop-filter: blur(var(--carrier-glass-blur)) saturate(150%) brightness(1.02) !important;
|
|
5670
|
+
}
|
|
5671
|
+
/* List popups use the live carrier refraction only. The old static outer
|
|
5672
|
+
glow/shadow painted a second fixed background behind the popup. */
|
|
5673
|
+
.conv-action-menu.liquid-glass,
|
|
5674
|
+
.model-select-menu.liquid-glass,
|
|
5675
|
+
.newmark-select-menu.liquid-glass {
|
|
5676
|
+
box-shadow: none;
|
|
5677
|
+
}
|
|
5678
|
+
.conv-action-menu.liquid-glass::before,
|
|
5679
|
+
.model-select-menu.liquid-glass::before,
|
|
5680
|
+
.newmark-select-menu.liquid-glass::before {
|
|
5681
|
+
background: none;
|
|
5682
|
+
opacity: 0;
|
|
5683
|
+
mix-blend-mode: normal;
|
|
5684
|
+
}
|
|
5685
|
+
.conv-action-menu.liquid-glass::after,
|
|
5686
|
+
.model-select-menu.liquid-glass::after,
|
|
5687
|
+
.newmark-select-menu.liquid-glass::after {
|
|
5688
|
+
box-shadow: none;
|
|
5689
|
+
}
|
|
5690
|
+
.liquid-selection-canvas { opacity: var(--liquid-float-optical-opacity); }
|
|
5691
|
+
|
|
5692
|
+
/* Buttons retain their component-owned static surface. The Kyant-like glass
|
|
5693
|
+
layer exists only during press and falls away with the color block. */
|
|
5694
|
+
button {
|
|
5695
|
+
position: relative;
|
|
5696
|
+
isolation: isolate;
|
|
5697
|
+
transition-property: scale, filter, border-color, background-color, box-shadow, color;
|
|
5698
|
+
transition-duration: 120ms;
|
|
5699
|
+
transition-timing-function: var(--ease-out-expo);
|
|
5700
|
+
}
|
|
5701
|
+
[data-theme="light"] .conv-action-menu,
|
|
5702
|
+
[data-theme="light"] .model-select-menu,
|
|
5703
|
+
[data-theme="light"] .newmark-select-menu,
|
|
5704
|
+
[data-theme="light"] .sub-win {
|
|
5705
|
+
background-color: transparent;
|
|
5706
|
+
}
|
|
5707
|
+
button::after {
|
|
5708
|
+
content: '';
|
|
5709
|
+
position: absolute;
|
|
5710
|
+
inset: -2px;
|
|
5711
|
+
z-index: -1;
|
|
5712
|
+
pointer-events: none;
|
|
5713
|
+
border-radius: inherit;
|
|
5714
|
+
opacity: 0;
|
|
5715
|
+
background: var(--liquid-refraction);
|
|
5716
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%) brightness(1.04);
|
|
5717
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%) brightness(1.04);
|
|
5718
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), 0 4px 14px rgba(0,0,0,0.18);
|
|
5719
|
+
transition: opacity 90ms var(--ease-out-expo);
|
|
5720
|
+
}
|
|
5721
|
+
button:hover:not(:disabled) {
|
|
5722
|
+
filter: brightness(1.06);
|
|
5723
|
+
}
|
|
5724
|
+
button:active:not(:disabled) {
|
|
5725
|
+
scale: 1.03;
|
|
5726
|
+
filter: brightness(1.04);
|
|
5727
|
+
background-color: transparent !important;
|
|
5728
|
+
box-shadow: none;
|
|
5729
|
+
}
|
|
5730
|
+
button:active:not(:disabled)::after { opacity: 1; }
|
|
5731
|
+
button:disabled { opacity: 0.5; }
|
|
5732
|
+
|
|
5733
|
+
/* Borderless utility actions stay visually quiet; they never gain a glass plate. */
|
|
5734
|
+
.msg-action-btn::after,
|
|
5735
|
+
.top-btn::after,
|
|
5736
|
+
.conv-more-btn::after,
|
|
5737
|
+
.provider-icon-btn::after,
|
|
5738
|
+
.work-review-toggle::after,
|
|
5739
|
+
button.conversation-work-run-head::after,
|
|
5740
|
+
button.shell-block-header::after,
|
|
5741
|
+
button.diff-block-header::after,
|
|
5742
|
+
button.work-review-head::after { display: none !important; }
|
|
5743
|
+
button.conversation-work-run-head:active:not(:disabled),
|
|
5744
|
+
button.shell-block-header:active:not(:disabled),
|
|
5745
|
+
button.diff-block-header:active:not(:disabled),
|
|
5746
|
+
button.work-review-head:active:not(:disabled) {
|
|
5747
|
+
scale: 1;
|
|
5748
|
+
filter: none;
|
|
5749
|
+
background-color: revert !important;
|
|
5750
|
+
}
|
|
5751
|
+
|
|
5752
|
+
/* Two-position liquid track. The thumb becomes glass only while held. */
|
|
5753
|
+
input[type="checkbox"] {
|
|
5754
|
+
--liquid-switch-x: 0px;
|
|
5755
|
+
--liquid-switch-progress: 0%;
|
|
5756
|
+
appearance: none;
|
|
5757
|
+
-webkit-appearance: none;
|
|
5758
|
+
width: 42px !important;
|
|
5759
|
+
height: 24px !important;
|
|
5760
|
+
min-width: 42px;
|
|
5761
|
+
margin: 0;
|
|
5762
|
+
padding: 0;
|
|
5763
|
+
border: 1px solid var(--glass-border-2);
|
|
5764
|
+
border-radius: var(--radius-full);
|
|
5765
|
+
background: linear-gradient(90deg, var(--accent) 0 var(--liquid-switch-progress), var(--glass-bg-3) var(--liquid-switch-progress) 100%);
|
|
5766
|
+
cursor: pointer;
|
|
5767
|
+
position: relative;
|
|
5768
|
+
vertical-align: middle;
|
|
5769
|
+
transition: background 160ms var(--ease-out-expo), border-color 160ms var(--ease-out-expo);
|
|
5770
|
+
}
|
|
5771
|
+
input[type="checkbox"]:checked {
|
|
5772
|
+
--liquid-switch-x: 18px;
|
|
5773
|
+
--liquid-switch-progress: 100%;
|
|
5774
|
+
border-color: color-mix(in srgb, var(--accent) 72%, white);
|
|
5775
|
+
}
|
|
5776
|
+
input[type="checkbox"]::before {
|
|
5777
|
+
content: '';
|
|
5778
|
+
position: absolute;
|
|
5779
|
+
left: 2px;
|
|
5780
|
+
top: 2px;
|
|
5781
|
+
width: 18px;
|
|
5782
|
+
height: 18px;
|
|
5783
|
+
border-radius: 50%;
|
|
5784
|
+
background: var(--text-bright);
|
|
5785
|
+
box-shadow: 0 2px 5px rgba(0,0,0,.28);
|
|
5786
|
+
transform: translateX(var(--liquid-switch-x)) scale(1);
|
|
5787
|
+
transition: transform 160ms var(--ease-out-expo), box-shadow 100ms var(--ease-out-expo), background 100ms var(--ease-out-expo);
|
|
5788
|
+
}
|
|
5789
|
+
input[type="checkbox"].liquid-switch-dragging::before {
|
|
5790
|
+
left: 1px;
|
|
5791
|
+
width: 22px;
|
|
5792
|
+
border-radius: var(--radius-full);
|
|
5793
|
+
background: var(--liquid-refraction);
|
|
5794
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%);
|
|
5795
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%);
|
|
5796
|
+
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), 0 7px 18px rgba(0,0,0,.32);
|
|
5797
|
+
transform: translateX(var(--liquid-switch-x)) translateY(-2px) scale(1.22);
|
|
5798
|
+
transition: none;
|
|
5799
|
+
}
|
|
5800
|
+
|
|
5801
|
+
/* Center composer and terminal stay solid. Only their left/right edge catches
|
|
5802
|
+
light so the glass sidebars remain the visual frame. */
|
|
5803
|
+
#input-area,
|
|
5804
|
+
#bottom {
|
|
5805
|
+
backdrop-filter: none;
|
|
5806
|
+
-webkit-backdrop-filter: none;
|
|
5807
|
+
box-shadow: inset 1px 0 0 var(--liquid-edge-glow), inset -1px 0 0 var(--liquid-edge-glow);
|
|
5808
|
+
}
|
|
5809
|
+
|
|
5810
|
+
/* The live indicator is strictly a continuous black/white/black/white border.
|
|
5811
|
+
Keep it above generic glass pseudo-elements when classes are combined. */
|
|
5812
|
+
.marquee-border::before,
|
|
5813
|
+
.liquid-glass.marquee-border::before {
|
|
5814
|
+
background: conic-gradient(from var(--marquee-angle, 0deg), #000 0deg, #fff 90deg, #000 180deg, #fff 270deg, #000 360deg);
|
|
5815
|
+
opacity: 1;
|
|
5816
|
+
mix-blend-mode: normal;
|
|
5817
|
+
animation: marquee-rotate var(--marquee-speed) linear infinite;
|
|
5818
|
+
}
|
|
5403
5819
|
</style>
|
|
5404
5820
|
</head>
|
|
5405
5821
|
<body>
|
|
@@ -5625,6 +6041,10 @@ html.newmark-memory-overview-viewer body {
|
|
|
5625
6041
|
<symbol id="minus" viewBox="0 0 24 24">
|
|
5626
6042
|
<path d="M5 12h14" />
|
|
5627
6043
|
</symbol>
|
|
6044
|
+
<symbol id="mouse" viewBox="0 0 24 24">
|
|
6045
|
+
<rect x="5" y="2" width="14" height="20" rx="7" />
|
|
6046
|
+
<path d="M12 6v4" />
|
|
6047
|
+
</symbol>
|
|
5628
6048
|
<symbol id="move" viewBox="0 0 24 24">
|
|
5629
6049
|
<path d="M12 2v20" />
|
|
5630
6050
|
<path d="m15 19-3 3-3-3" />
|
|
@@ -5875,6 +6295,7 @@ html.newmark-memory-overview-viewer body {
|
|
|
5875
6295
|
<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
6296
|
</div>
|
|
5877
6297
|
<div id="left-content">
|
|
6298
|
+
<div id="left-tool-surface">
|
|
5878
6299
|
<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
6300
|
<button type="button" class="left-nav-icon" onclick="window.showNewConversationPage()" title="New conversation">
|
|
5880
6301
|
<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 +6315,7 @@ html.newmark-memory-overview-viewer body {
|
|
|
5894
6315
|
<button type="button" class="left-nav-icon" onclick="window.openSettings()">
|
|
5895
6316
|
<span class="icon"><svg class="nm-icon small" aria-hidden="true" focusable="false"><use href="#settings"></use></svg></span><span>Settings</span>
|
|
5896
6317
|
</button>
|
|
6318
|
+
</div>
|
|
5897
6319
|
<div id="left-ws-section">
|
|
5898
6320
|
<div id="left-ws-header">Workspaces</div>
|
|
5899
6321
|
<div id="left-ws-list" role="list" aria-label="Workspaces"></div>
|
|
@@ -5993,9 +6415,9 @@ html.newmark-memory-overview-viewer body {
|
|
|
5993
6415
|
<div class="model-select-shell" id="model-select-shell">
|
|
5994
6416
|
<select class="model-select-native" id="model-select" tabindex="-1" aria-hidden="true"></select>
|
|
5995
6417
|
<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>
|
|
6418
|
+
<div class="model-select-menu liquid-glass liquid-glass-carrier" id="model-select-menu" role="listbox" aria-label="Model" popover="manual"></div>
|
|
5997
6419
|
</div>
|
|
5998
|
-
<div class="conv-action-menu" id="conversation-action-menu" role="menu" aria-label="Conversation actions" popover="manual"></div>
|
|
6420
|
+
<div class="conv-action-menu liquid-glass liquid-glass-carrier" id="conversation-action-menu" role="menu" aria-label="Conversation actions" popover="manual"></div>
|
|
5999
6421
|
<div class="context-token-wrap">
|
|
6000
6422
|
<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
6423
|
<div id="context-token-tooltip"></div>
|
|
@@ -6113,7 +6535,7 @@ html.newmark-memory-overview-viewer body {
|
|
|
6113
6535
|
|
|
6114
6536
|
<!-- ========== SUB-WINDOW OVERLAY ========== -->
|
|
6115
6537
|
<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">
|
|
6538
|
+
<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
6539
|
<div class="sub-win-header" id="sub-win-header">
|
|
6118
6540
|
<span class="sub-win-title" id="sub-win-title">Window</span>
|
|
6119
6541
|
<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 +6714,7 @@ var state = {
|
|
|
6292
6714
|
activeSendCallsByTarget: {},
|
|
6293
6715
|
backendQueue: { steering: [], followUp: [] },
|
|
6294
6716
|
backendQueuesByTarget: {},
|
|
6717
|
+
queueItemsByTarget: {},
|
|
6295
6718
|
defaultFlow: '',
|
|
6296
6719
|
settingsActiveTab: 'general',
|
|
6297
6720
|
globalAgentPrompt: '',
|
|
@@ -7077,6 +7500,7 @@ var NEWMARK_I18N = {
|
|
|
7077
7500
|
'conversation.branchCommunicationBadge': 'Branch communication',
|
|
7078
7501
|
'conversation.loadingIsolated': 'Loading isolated conversation...',
|
|
7079
7502
|
'conversation.locked': 'Current conversation is locked while the agent is working.',
|
|
7503
|
+
'conversation.emptyWorkspace': 'No conversations in this workspace yet.',
|
|
7080
7504
|
'queue.next': 'Next',
|
|
7081
7505
|
'terminal.notConnected': 'Terminal not connected',
|
|
7082
7506
|
'common.error': 'Error',
|
|
@@ -7808,6 +8232,7 @@ var NEWMARK_I18N = {
|
|
|
7808
8232
|
'conversation.branchCommunicationBadge': '分支交流',
|
|
7809
8233
|
'conversation.loadingIsolated': '正在加载隔离对话...',
|
|
7810
8234
|
'conversation.locked': 'Agent 工作中,当前对话已锁定。',
|
|
8235
|
+
'conversation.emptyWorkspace': '当前工作区还没有对话。',
|
|
7811
8236
|
'queue.next': '下一轮',
|
|
7812
8237
|
'terminal.notConnected': '终端未连接',
|
|
7813
8238
|
'common.error': '错误',
|
|
@@ -7920,9 +8345,9 @@ function applyLanguageToUi() {
|
|
|
7920
8345
|
var nextBtn = document.querySelector('.mode-toggle-btn[data-mode="next"]');
|
|
7921
8346
|
if (guideBtn) guideBtn.textContent = t('input.guide');
|
|
7922
8347
|
if (nextBtn) nextBtn.textContent = t('input.next');
|
|
7923
|
-
var toolsHead = document.querySelector('#left-
|
|
8348
|
+
var toolsHead = document.querySelector('#left-tool-surface > div:first-child');
|
|
7924
8349
|
if (toolsHead) toolsHead.textContent = t('left.tools');
|
|
7925
|
-
var navs = document.querySelectorAll('#left-
|
|
8350
|
+
var navs = document.querySelectorAll('#left-tool-surface .left-nav-icon');
|
|
7926
8351
|
var navLabels = [t('left.newChat'), t('left.plugins'), t('left.memoryLab'), t('left.automation'), t('left.flow'), t('left.settings')];
|
|
7927
8352
|
for (var i = 0; i < navs.length && i < navLabels.length; i++) {
|
|
7928
8353
|
var span = navs[i].querySelector('span:last-child');
|
|
@@ -8395,6 +8820,30 @@ function syncTerminalShellOptions() {
|
|
|
8395
8820
|
|
|
8396
8821
|
// === API Bridge ===
|
|
8397
8822
|
var api = window.api || {
|
|
8823
|
+
queueAction: async function(action, input) {
|
|
8824
|
+
try {
|
|
8825
|
+
var target = normalizeQueuedConversationTarget(currentConversationTarget());
|
|
8826
|
+
if (!target) return { ok: false, error: 'No active conversation target' };
|
|
8827
|
+
var body = {
|
|
8828
|
+
workspaceId: target.workspaceId,
|
|
8829
|
+
conversationId: target.conversationId,
|
|
8830
|
+
action: action,
|
|
8831
|
+
id: String(input && input.id || ''),
|
|
8832
|
+
text: String(input && input.text || ''),
|
|
8833
|
+
value: String(input && input.text || ''),
|
|
8834
|
+
requestedMode: String(input && input.requestedMode || 'build'),
|
|
8835
|
+
goalObjective: String(input && input.goalObjective || ''),
|
|
8836
|
+
createdAt: String(input && input.createdAt || ''),
|
|
8837
|
+
};
|
|
8838
|
+
if (Array.isArray(input && input.orderedIds)) body.orderedIds = input.orderedIds;
|
|
8839
|
+
var r = await fetch('/api/queue-action', {
|
|
8840
|
+
method: 'POST',
|
|
8841
|
+
headers: { 'Content-Type': 'application/json' },
|
|
8842
|
+
body: JSON.stringify(body)
|
|
8843
|
+
});
|
|
8844
|
+
return r.json();
|
|
8845
|
+
} catch(e) { return { ok: false, error: e.message }; }
|
|
8846
|
+
},
|
|
8398
8847
|
sendMessage: async function(msg, conversationId) {
|
|
8399
8848
|
try {
|
|
8400
8849
|
var body = {message: msg, mode: state.mode, model: state.model, intelligence: state.intelligence, inputMode: state.inputMode};
|
|
@@ -9316,24 +9765,84 @@ window.openWorkReviewFile = async function(path) {
|
|
|
9316
9765
|
return true;
|
|
9317
9766
|
};
|
|
9318
9767
|
|
|
9319
|
-
function
|
|
9768
|
+
function workReviewRunIds(runId, aliases) {
|
|
9769
|
+
var ids = [];
|
|
9770
|
+
[runId].concat(Array.isArray(aliases) ? aliases : []).forEach(function(value) {
|
|
9771
|
+
var id = String(value || '');
|
|
9772
|
+
if (id && ids.indexOf(id) < 0) ids.push(id);
|
|
9773
|
+
});
|
|
9774
|
+
return ids;
|
|
9775
|
+
}
|
|
9776
|
+
|
|
9777
|
+
function upsertWorkReviewRecord(ui, diffs, runId, aliases) {
|
|
9778
|
+
if (!ui) return null;
|
|
9779
|
+
if (!Array.isArray(ui.workReviews)) ui.workReviews = [];
|
|
9780
|
+
var ids = workReviewRunIds(runId, aliases);
|
|
9781
|
+
var canonicalRunId = String(runId || ids[0] || '');
|
|
9782
|
+
var record = { diffs: diffs, runId: canonicalRunId };
|
|
9783
|
+
var matchIndex = -1;
|
|
9784
|
+
for (var index = 0; index < ui.workReviews.length; index++) {
|
|
9785
|
+
var itemRunId = String(ui.workReviews[index] && ui.workReviews[index].runId || '');
|
|
9786
|
+
if (ids.indexOf(itemRunId) < 0) continue;
|
|
9787
|
+
if (matchIndex < 0) {
|
|
9788
|
+
matchIndex = index;
|
|
9789
|
+
ui.workReviews[index] = record;
|
|
9790
|
+
} else {
|
|
9791
|
+
ui.workReviews.splice(index, 1);
|
|
9792
|
+
index--;
|
|
9793
|
+
}
|
|
9794
|
+
}
|
|
9795
|
+
if (matchIndex < 0) ui.workReviews.push(record);
|
|
9796
|
+
return record;
|
|
9797
|
+
}
|
|
9798
|
+
|
|
9799
|
+
function addWorkReview(diffs, runId, aliases) {
|
|
9320
9800
|
var files = normalizeWorkReviewDiffs(diffs);
|
|
9321
9801
|
if (!files.length) return null;
|
|
9802
|
+
var filesKey = JSON.stringify(files);
|
|
9803
|
+
var reviewRunId = String(runId || '');
|
|
9804
|
+
var reviewRunIds = workReviewRunIds(reviewRunId, aliases);
|
|
9805
|
+
var chatArea = els['chat-area'];
|
|
9806
|
+
var review = null;
|
|
9807
|
+
if (chatArea) {
|
|
9808
|
+
var existingReviews = chatArea.querySelectorAll('.work-review');
|
|
9809
|
+
for (var ri = 0; ri < existingReviews.length; ri++) {
|
|
9810
|
+
var sameFiles = existingReviews[ri].getAttribute('data-files') === filesKey;
|
|
9811
|
+
var existingRunId = String(existingReviews[ri].getAttribute('data-run-id') || '');
|
|
9812
|
+
var sameRun = reviewRunIds.length ? reviewRunIds.indexOf(existingRunId) >= 0 : sameFiles && !existingRunId;
|
|
9813
|
+
if (!sameRun) continue;
|
|
9814
|
+
if (!review) review = existingReviews[ri];
|
|
9815
|
+
else existingReviews[ri].remove();
|
|
9816
|
+
}
|
|
9817
|
+
}
|
|
9322
9818
|
var added = files.reduce(function(total, file) { return total + file.added; }, 0);
|
|
9323
9819
|
var deleted = files.reduce(function(total, file) { return total + file.deleted; }, 0);
|
|
9324
|
-
var
|
|
9325
|
-
|
|
9820
|
+
var wasCollapsed = review ? review.classList.contains('collapsed') : true;
|
|
9821
|
+
var wasExpanded = review ? review.classList.contains('expanded') : false;
|
|
9822
|
+
if (!review) review = document.createElement('div');
|
|
9823
|
+
review.className = 'work-review' + (wasCollapsed ? ' collapsed' : '') + (wasExpanded ? ' expanded' : '');
|
|
9326
9824
|
review.setAttribute('data-files', JSON.stringify(files));
|
|
9825
|
+
if (reviewRunId) review.setAttribute('data-run-id', reviewRunId);
|
|
9826
|
+
else review.removeAttribute('data-run-id');
|
|
9327
9827
|
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>';
|
|
9828
|
+
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
9829
|
}).join('');
|
|
9330
9830
|
var editedLabel = files.length === 1 ? t('review.editedOne') : t('review.editedMany').replace('{count}', files.length);
|
|
9331
9831
|
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
9832
|
'<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
9833
|
'<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
|
-
|
|
9834
|
+
'<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>' +
|
|
9835
|
+
'<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>';
|
|
9836
|
+
var owningRun = null;
|
|
9837
|
+
var renderedRuns = reviewRunId ? els['chat-area'].querySelectorAll('.conversation-work-run[data-run-id]') : [];
|
|
9838
|
+
for (var wi = 0; wi < renderedRuns.length; wi++) {
|
|
9839
|
+
if (renderedRuns[wi].getAttribute('data-run-id') === reviewRunId) { owningRun = renderedRuns[wi]; break; }
|
|
9840
|
+
}
|
|
9841
|
+
var owningMessage = owningRun && owningRun.closest ? owningRun.closest('.work-run-message') : null;
|
|
9842
|
+
if (!review.parentNode) {
|
|
9843
|
+
if (owningMessage && owningMessage.parentNode === els['chat-area']) owningMessage.insertAdjacentElement('afterend', review);
|
|
9844
|
+
else els['chat-area'].appendChild(review);
|
|
9845
|
+
}
|
|
9337
9846
|
autoScrollIfAtBottom();
|
|
9338
9847
|
return review;
|
|
9339
9848
|
}
|
|
@@ -9701,12 +10210,25 @@ function renderPendingGuideMessages(target, persistedGuideIds) {
|
|
|
9701
10210
|
persistedGuideIds = persistedGuideIds || {};
|
|
9702
10211
|
var messages = guideMessagesForTarget(target);
|
|
9703
10212
|
var pending = Object.keys(messages).map(function(id) { return messages[id]; }).filter(function(item) {
|
|
9704
|
-
|
|
10213
|
+
if (!item || persistedGuideIds[item.clientMessageId]) return false;
|
|
10214
|
+
// A Guide already owned by its work run is rendered by the Build block
|
|
10215
|
+
// (expanded events or collapsed guide list), never as a separate trailing
|
|
10216
|
+
// message. Without this guard, collapse/expand rebuilds would re-add the
|
|
10217
|
+
// applied Guide at the end of the chat.
|
|
10218
|
+
if (workRunOwnsGuide(item.runId, item.clientMessageId, target)) return false;
|
|
10219
|
+
return true;
|
|
9705
10220
|
}).sort(function(a, b) {
|
|
9706
10221
|
return String(a.createdAt || '').localeCompare(String(b.createdAt || ''));
|
|
9707
10222
|
});
|
|
9708
10223
|
pending.forEach(function(item) {
|
|
9709
10224
|
var element = findGuideMessageElement(item.clientMessageId);
|
|
10225
|
+
if (element && !item.awaitingAck && item.runId && workRunOwnsGuide(item.runId, item.clientMessageId, target)) {
|
|
10226
|
+
// The Guide is owned by its work run but a stale standalone copy exists
|
|
10227
|
+
// (e.g. created before this guard). Remove it so the Build block stays
|
|
10228
|
+
// the single source of truth.
|
|
10229
|
+
element.remove();
|
|
10230
|
+
element = null;
|
|
10231
|
+
}
|
|
9710
10232
|
if (item.awaitingAck && item.runId) {
|
|
9711
10233
|
var runElement = findWorkRunElement({ runId: item.runId });
|
|
9712
10234
|
var runBody = runElement && runElement.querySelector('.conversation-work-run-body');
|
|
@@ -9738,6 +10260,22 @@ function renderPendingGuideMessages(target, persistedGuideIds) {
|
|
|
9738
10260
|
return pending.length;
|
|
9739
10261
|
}
|
|
9740
10262
|
|
|
10263
|
+
function workRunOwnsGuide(runId, clientMessageId, target) {
|
|
10264
|
+
if (!runId || !clientMessageId) return false;
|
|
10265
|
+
var runs = workRunsForTarget(target || currentConversationTarget());
|
|
10266
|
+
var run = runs.find(function(item) { return String(item.runId || '') === String(runId); });
|
|
10267
|
+
if (!run) return false;
|
|
10268
|
+
function matches(guide) {
|
|
10269
|
+
return guide && String(guide.clientMessageId || '') === String(clientMessageId);
|
|
10270
|
+
}
|
|
10271
|
+
if ((run.guides || []).some(matches)) return true;
|
|
10272
|
+
return (run.events || []).some(function(event) {
|
|
10273
|
+
if (!event) return false;
|
|
10274
|
+
if (matches(event.guide)) return true;
|
|
10275
|
+
return !!(event.clientMessageId && String(event.clientMessageId) === String(clientMessageId));
|
|
10276
|
+
});
|
|
10277
|
+
}
|
|
10278
|
+
|
|
9741
10279
|
function eventRuntimeKey(event) {
|
|
9742
10280
|
var target = {
|
|
9743
10281
|
workspaceId: event && (event.workspaceId || (event.target && event.target.workspaceId)),
|
|
@@ -10508,23 +11046,29 @@ function workRunGuideEvents(run) {
|
|
|
10508
11046
|
});
|
|
10509
11047
|
}
|
|
10510
11048
|
|
|
10511
|
-
function
|
|
11049
|
+
function presentedWorkRunEvents(run, includeGuides) {
|
|
10512
11050
|
var rawEvents = (run.events || []).filter(publicWorkEvent).filter(function(event) {
|
|
10513
11051
|
return includeGuides !== false || !(String(event && event.type || '').toLowerCase().indexOf('guide') === 0 || !!(event && event.guide));
|
|
10514
11052
|
}).sort(compareConversationWorkEvents);
|
|
10515
11053
|
var terminalInterrupted = ['interrupted', 'force_interrupted'].indexOf(String(run && run.status || '')) >= 0;
|
|
10516
11054
|
var events = [];
|
|
10517
11055
|
var publicText = '';
|
|
11056
|
+
// 公共文本段继承其最后一条 text 事件的 sequence:增量渲染依赖 presented
|
|
11057
|
+
// 顺序的单调性,无 sequence 的文本段会被排到列表前部,导致每次文本增量
|
|
11058
|
+
// 都从前部重建后续节点。
|
|
11059
|
+
var publicTextSequence = 0;
|
|
10518
11060
|
function flushPublicText() {
|
|
10519
11061
|
var content = publicText.trim().slice(0, 50000);
|
|
10520
11062
|
publicText = '';
|
|
10521
|
-
if (content) events.push({ type: terminalInterrupted ? 'partial_text' : 'public_text', content: content });
|
|
11063
|
+
if (content) events.push({ type: terminalInterrupted ? 'partial_text' : 'public_text', content: content, sequence: publicTextSequence });
|
|
11064
|
+
publicTextSequence = 0;
|
|
10522
11065
|
}
|
|
10523
11066
|
for (var eventIndex = 0; eventIndex < rawEvents.length; eventIndex++) {
|
|
10524
11067
|
var rawEvent = rawEvents[eventIndex];
|
|
10525
11068
|
var rawType = String(rawEvent && rawEvent.type || '').toLowerCase();
|
|
10526
11069
|
if (rawType === 'text') {
|
|
10527
11070
|
publicText += String(rawEvent.content || '');
|
|
11071
|
+
publicTextSequence = Math.max(publicTextSequence, Number(rawEvent.sequence || 0));
|
|
10528
11072
|
continue;
|
|
10529
11073
|
}
|
|
10530
11074
|
if (rawType === 'response') {
|
|
@@ -10596,46 +11140,120 @@ function renderWorkRunEvents(run, includeGuides) {
|
|
|
10596
11140
|
}
|
|
10597
11141
|
presentedEvents.push({ type: 'tool_group', count: count, completed: allCompleted, toolName: presented.toolName, items: activityItems });
|
|
10598
11142
|
}
|
|
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
|
-
|
|
11143
|
+
return { events: presentedEvents, terminalInterrupted: terminalInterrupted };
|
|
11144
|
+
}
|
|
11145
|
+
|
|
11146
|
+
// 每个呈现节点的稳定 key:内容变化(增长/完成)时 key 变化,增量渲染据此
|
|
11147
|
+
// 只重建变化节点之后的 DOM,前缀节点原样保留。
|
|
11148
|
+
function workRunPresentedEventKey(event, eventIndex) {
|
|
11149
|
+
var type = String(event && event.type || 'status').toLowerCase();
|
|
11150
|
+
if (type === 'public_text' || type === 'partial_text' || type === 'response') {
|
|
11151
|
+
return type + ':' + String(event && event.content || '').length;
|
|
11152
|
+
}
|
|
11153
|
+
if (type === 'thought') {
|
|
11154
|
+
return 'thought:' + String(event && (event.id || event.toolCallId) || '') + '@' + String(event && event.content || '').length + ':' + (event && event.completed ? '1' : '0');
|
|
11155
|
+
}
|
|
11156
|
+
if (type === 'tool_group') {
|
|
11157
|
+
return 'tool_group:' + ((event && event.items || []).map(function(item) {
|
|
11158
|
+
return String(item && (item.id || item.toolCallId) || '') + ':' + (item && item.completed ? '1' : '0');
|
|
11159
|
+
}).join(',')) + '#n' + Number(event && event.count || 0);
|
|
11160
|
+
}
|
|
11161
|
+
if (type.indexOf('guide') === 0 || (event && event.guide)) {
|
|
11162
|
+
return 'guide:' + guideWorkEventKey(event) + ':' + guideWorkEventStatus(event);
|
|
11163
|
+
}
|
|
11164
|
+
var activityTail = type.indexOf('tool_') === 0 && type !== 'tool_call' && type !== 'tool_result'
|
|
11165
|
+
? ':' + String(event && event.activity && event.activity.key || '') + ':' + Number(event && event.count || 1)
|
|
11166
|
+
: '';
|
|
11167
|
+
return type + ':' + String(event && (event.id || [event && event.sequence || '', event && event.toolName || '', event && event.timestamp || ''].join('|')) || '') + ':' + (event && event.completed ? '1' : '0') + activityTail;
|
|
11168
|
+
}
|
|
11169
|
+
|
|
11170
|
+
function renderWorkRunEventNode(event, eventIndex, run, terminalInterrupted) {
|
|
11171
|
+
var type = String(event.type || 'status').toLowerCase();
|
|
11172
|
+
if (type === 'public_text' || type === 'partial_text' || type === 'response') {
|
|
11173
|
+
var responseLabel = type === 'response' && terminalInterrupted
|
|
11174
|
+
? (currentLang() === 'zh' ? '未完成回复片段\n' : 'Incomplete response fragment\n') + workEventLabel(event)
|
|
11175
|
+
: workEventLabel(event);
|
|
11176
|
+
// While a run is still receiving text, this view can be rebuilt once per
|
|
11177
|
+
// event. Avoid invoking the full Markdown parser for a large live
|
|
11178
|
+
// narrative; the final response path still gets the normal rich render.
|
|
11179
|
+
var liveNarrative = ['running', 'stopping', 'force_restarting'].indexOf(String(run && run.status || '').toLowerCase()) >= 0;
|
|
11180
|
+
var narrativeHtml = liveNarrative && String(responseLabel || '').length >= STREAMING_PLAIN_TEXT_THRESHOLD
|
|
11181
|
+
? esc(responseLabel)
|
|
11182
|
+
: renderMessageContent(responseLabel);
|
|
11183
|
+
return '<div class="conversation-work-event narrative"><div class="conversation-work-event-content">' + narrativeHtml + '</div></div>';
|
|
11184
|
+
}
|
|
11185
|
+
if (type.indexOf('guide') === 0 || event.guide) return renderWorkRunGuideMessage(event);
|
|
11186
|
+
if (type === 'tool_group') return renderWorkToolGroup(event, eventIndex);
|
|
11187
|
+
if (type === 'thought') return renderWorkThought(event, eventIndex);
|
|
11188
|
+
var activitySummary = type.indexOf('tool_') === 0 && type !== 'tool_call' && type !== 'tool_result';
|
|
11189
|
+
var cls = type.indexOf('guide') === 0 ? ' guide' : (type === 'error' ? ' error' : (activitySummary ? ' activity-summary' : ''));
|
|
11190
|
+
var label = activitySummary ? workToolActivityLabel(event.activity, Number(event.count || 1), !!event.completed) : workEventLabel(event) + (event.completed ? (currentLang() === 'zh' ? ' · 已完成' : ' · completed') : '');
|
|
11191
|
+
if (activitySummary) {
|
|
11192
|
+
var firstActivityItem = event.items && event.items[0] || {};
|
|
11193
|
+
var activityDomKey = String(firstActivityItem.id || firstActivityItem.toolCallId || ((event.activity && event.activity.key || type) + '-' + eventIndex));
|
|
11194
|
+
var detailRows = (event.items || []).map(function(item) {
|
|
11195
|
+
var detail = String(item.toolArgs || '').trim();
|
|
11196
|
+
return '<div class="conversation-work-activity-item">' + iconSvg(workEventIcon('tool_call'), 'tool_call', 'tiny') +
|
|
11197
|
+
'<div><div>' + esc(publicToolNameForUi(item.toolName)) + (item.completed ? (currentLang() === 'zh' ? ' · 已完成' : ' · completed') : '') + '</div>' +
|
|
11198
|
+
(detail ? '<div class="conversation-work-activity-detail">' + esc(detail) + '</div>' : '') + '</div></div>';
|
|
11199
|
+
}).join('');
|
|
11200
|
+
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>' +
|
|
11201
|
+
'<div class="conversation-work-activity-list">' + detailRows + '</div></details>';
|
|
11202
|
+
}
|
|
11203
|
+
return '<div class="conversation-work-event' + cls + '">' + iconSvg(workEventIcon(type), type, 'tiny') +
|
|
11204
|
+
'<div class="conversation-work-event-content">' + renderMessageContent(label) + '</div></div>';
|
|
11205
|
+
}
|
|
11206
|
+
|
|
11207
|
+
// 给单个事件节点的 HTML 根标签插入稳定 key,使首次渲染的节点与增量
|
|
11208
|
+
// diff 使用同一 key 体系(否则第一次增量更新会全量失配重建)。
|
|
11209
|
+
function withWorkEventKey(html, key) {
|
|
11210
|
+
var match = /^<([A-Za-z][\w-]*)([^>]*)>/.exec(String(html || ''));
|
|
11211
|
+
if (!match) return html;
|
|
11212
|
+
return '<' + match[1] + match[2] + ' data-work-event-key="' + escAttr(String(key || '')) + '">' + html.slice(match[0].length);
|
|
11213
|
+
}
|
|
11214
|
+
|
|
11215
|
+
function renderWorkRunEvents(run, includeGuides) {
|
|
11216
|
+
var presented = presentedWorkRunEvents(run, includeGuides);
|
|
11217
|
+
if (!presented.events.length) return '<div class="conversation-work-empty">' + esc(currentLang() === 'zh' ? '等待公开工作进度…' : 'Waiting for public progress…') + '</div>';
|
|
11218
|
+
return '<div class="conversation-work-run-events">' + presented.events.map(function(event, eventIndex) {
|
|
11219
|
+
return withWorkEventKey(renderWorkRunEventNode(event, eventIndex, run, presented.terminalInterrupted), workRunPresentedEventKey(event, eventIndex));
|
|
10636
11220
|
}).join('') + '</div>';
|
|
10637
11221
|
}
|
|
10638
11222
|
|
|
11223
|
+
// 增量更新 run body 内的事件列表:保留与新呈现列表 key 一致的前缀节点,
|
|
11224
|
+
// 从第一个分歧节点起重建尾部。运行中通常只有最后一个文本/thought 节点
|
|
11225
|
+
// 增长或尾部追加,因此不会整块重绘整个进程渲染。
|
|
11226
|
+
function updateWorkRunEventsContainer(run, body, expanded) {
|
|
11227
|
+
var presented = presentedWorkRunEvents(run, expanded);
|
|
11228
|
+
var container = body.querySelector ? body.querySelector('.conversation-work-run-events') : null;
|
|
11229
|
+
if (!container) {
|
|
11230
|
+
body.innerHTML = renderWorkRunEvents(run, expanded);
|
|
11231
|
+
return;
|
|
11232
|
+
}
|
|
11233
|
+
if (!presented.events.length) {
|
|
11234
|
+
var emptyHtml = '<div class="conversation-work-empty">' + esc(currentLang() === 'zh' ? '等待公开工作进度…' : 'Waiting for public progress…') + '</div>';
|
|
11235
|
+
if (container.innerHTML !== emptyHtml) container.innerHTML = emptyHtml;
|
|
11236
|
+
return;
|
|
11237
|
+
}
|
|
11238
|
+
var nodes = Array.prototype.slice.call(container.children);
|
|
11239
|
+
var keep = 0;
|
|
11240
|
+
while (keep < nodes.length && keep < presented.events.length) {
|
|
11241
|
+
if (String(nodes[keep].getAttribute('data-work-event-key') || '') === workRunPresentedEventKey(presented.events[keep], keep)) keep++;
|
|
11242
|
+
else break;
|
|
11243
|
+
}
|
|
11244
|
+
while (container.children.length > keep) container.removeChild(container.lastChild);
|
|
11245
|
+
for (var i = keep; i < presented.events.length; i++) {
|
|
11246
|
+
var key = workRunPresentedEventKey(presented.events[i], i);
|
|
11247
|
+
var holder = document.createElement('div');
|
|
11248
|
+
holder.innerHTML = renderWorkRunEventNode(presented.events[i], i, run, presented.terminalInterrupted);
|
|
11249
|
+
while (holder.firstChild) {
|
|
11250
|
+
var node = holder.firstChild;
|
|
11251
|
+
node.setAttribute('data-work-event-key', key);
|
|
11252
|
+
container.appendChild(node);
|
|
11253
|
+
}
|
|
11254
|
+
}
|
|
11255
|
+
}
|
|
11256
|
+
|
|
10639
11257
|
function findWorkRunElement(run) {
|
|
10640
11258
|
if (!els['chat-area']) return null;
|
|
10641
11259
|
var nodes = els['chat-area'].querySelectorAll('.conversation-work-run');
|
|
@@ -10648,6 +11266,11 @@ function findWorkRunElement(run) {
|
|
|
10648
11266
|
function updateConversationWorkRunElement(run, element) {
|
|
10649
11267
|
element = element || findWorkRunElement(run);
|
|
10650
11268
|
if (!element) return null;
|
|
11269
|
+
// 重建/增量更新前后保持聊天视窗滚动位置:贴底时跟随最新,未贴底时
|
|
11270
|
+
// 精确恢复 scrollTop,避免内容高度变化把视窗“强制拖向最新”。
|
|
11271
|
+
var chatEl = els['chat-area'];
|
|
11272
|
+
var wasAtBottom = !!(chatEl && shouldAutoScroll(chatEl));
|
|
11273
|
+
var scrollTopBefore = chatEl ? chatEl.scrollTop : 0;
|
|
10651
11274
|
var activityOpenStates = {};
|
|
10652
11275
|
var priorActivities = element.querySelectorAll ? element.querySelectorAll('details.conversation-work-activity[data-activity-key], details[data-work-detail-key]') : [];
|
|
10653
11276
|
for (var activityIndex = 0; activityIndex < priorActivities.length; activityIndex++) {
|
|
@@ -10662,12 +11285,51 @@ function updateConversationWorkRunElement(run, element) {
|
|
|
10662
11285
|
element.className = 'conversation-work-run ' + (running ? 'running ' : '') + (expanded ? 'expanded' : 'collapsed');
|
|
10663
11286
|
element.setAttribute('data-run-id', run.runId);
|
|
10664
11287
|
element.setAttribute('data-runtime-key', runtimeKeyFor(run.target && run.target.workspaceId, run.target && run.target.conversationId));
|
|
10665
|
-
element.
|
|
10666
|
-
|
|
10667
|
-
|
|
10668
|
-
|
|
10669
|
-
|
|
10670
|
-
|
|
11288
|
+
var head = element.querySelector ? element.querySelector('.conversation-work-run-head') : null;
|
|
11289
|
+
if (!head) {
|
|
11290
|
+
element.innerHTML = '<button class="conversation-work-run-head" type="button" onclick="window.toggleConversationWorkRun(this)">' +
|
|
11291
|
+
'<span class="conversation-work-run-title">' + esc(workRunTitle(run)) + '</span>' +
|
|
11292
|
+
workRunChangeBadge(run) +
|
|
11293
|
+
'<span class="conversation-work-run-status">' + esc(String(run.status || '')) + '</span>' +
|
|
11294
|
+
'<span class="conversation-work-run-chevron" aria-hidden="true"></span></button>' +
|
|
11295
|
+
'<div class="conversation-work-run-body">' + renderWorkRunEvents(run, expanded) + '</div>';
|
|
11296
|
+
} else {
|
|
11297
|
+
// 就地更新头部,不重建 head 按钮(保住聚焦/动画状态)。
|
|
11298
|
+
var titleEl = head.querySelector('.conversation-work-run-title');
|
|
11299
|
+
var nextTitle = workRunTitle(run);
|
|
11300
|
+
if (titleEl && titleEl.textContent !== nextTitle) titleEl.textContent = nextTitle;
|
|
11301
|
+
var statusEl = head.querySelector('.conversation-work-run-status');
|
|
11302
|
+
if (statusEl && statusEl.textContent !== String(run.status || '')) statusEl.textContent = String(run.status || '');
|
|
11303
|
+
// Keep the selector identical to workRunChangeBadge(). The old
|
|
11304
|
+
// `.conversation-work-run-change-badge` typo never found the existing
|
|
11305
|
+
// node, so every expand/collapse refresh inserted another review badge.
|
|
11306
|
+
var badgeHolder = head.querySelector('.conversation-work-change-badge');
|
|
11307
|
+
var nextBadge = workRunChangeBadge(run);
|
|
11308
|
+
if (nextBadge) {
|
|
11309
|
+
if (badgeHolder) {
|
|
11310
|
+
if (badgeHolder.getAttribute('data-badge-key') !== nextBadge) {
|
|
11311
|
+
var badgeShell = document.createElement('span');
|
|
11312
|
+
badgeShell.innerHTML = nextBadge;
|
|
11313
|
+
var replacementBadge = badgeShell.firstChild;
|
|
11314
|
+
replacementBadge.setAttribute('data-badge-key', nextBadge);
|
|
11315
|
+
badgeHolder.parentNode.replaceChild(replacementBadge, badgeHolder);
|
|
11316
|
+
}
|
|
11317
|
+
} else {
|
|
11318
|
+
var badgeShellNew = document.createElement('span');
|
|
11319
|
+
badgeShellNew.innerHTML = nextBadge;
|
|
11320
|
+
var badgeNode = badgeShellNew.firstChild;
|
|
11321
|
+
if (badgeNode) {
|
|
11322
|
+
badgeNode.setAttribute('data-badge-key', nextBadge);
|
|
11323
|
+
head.insertBefore(badgeNode, statusEl || null);
|
|
11324
|
+
}
|
|
11325
|
+
}
|
|
11326
|
+
} else if (badgeHolder) {
|
|
11327
|
+
badgeHolder.parentNode.removeChild(badgeHolder);
|
|
11328
|
+
}
|
|
11329
|
+
var body = element.querySelector('.conversation-work-run-body');
|
|
11330
|
+
if (body) updateWorkRunEventsContainer(run, body, expanded);
|
|
11331
|
+
else element.insertAdjacentHTML('beforeend', '<div class="conversation-work-run-body">' + renderWorkRunEvents(run, expanded) + '</div>');
|
|
11332
|
+
}
|
|
10671
11333
|
renderPendingGuideMessages(run.target, {});
|
|
10672
11334
|
var wrapper = element.closest ? element.closest('.work-run-message') : null;
|
|
10673
11335
|
if (wrapper) {
|
|
@@ -10698,7 +11360,14 @@ function updateConversationWorkRunElement(run, element) {
|
|
|
10698
11360
|
var refreshedKey = refreshedParentKey ? refreshedParentKey + '::' + refreshedDetailKey : '';
|
|
10699
11361
|
if (activityOpenStates[refreshedKey] !== undefined) refreshedActivities[refreshedIndex].open = activityOpenStates[refreshedKey];
|
|
10700
11362
|
}
|
|
10701
|
-
|
|
11363
|
+
if (chatEl) {
|
|
11364
|
+
if (wasAtBottom) {
|
|
11365
|
+
_chatShouldAutoScroll = true;
|
|
11366
|
+
chatEl.scrollTop = chatEl.scrollHeight;
|
|
11367
|
+
} else {
|
|
11368
|
+
chatEl.scrollTop = scrollTopBefore;
|
|
11369
|
+
}
|
|
11370
|
+
}
|
|
10702
11371
|
return element;
|
|
10703
11372
|
}
|
|
10704
11373
|
|
|
@@ -10933,7 +11602,8 @@ function conversationWorkUiState(conversationId, workspaceId) {
|
|
|
10933
11602
|
finalResponseMsg: null,
|
|
10934
11603
|
finalResponseText: '',
|
|
10935
11604
|
finalResponseAt: 0,
|
|
10936
|
-
pendingWorkReview: null
|
|
11605
|
+
pendingWorkReview: null,
|
|
11606
|
+
workReviews: []
|
|
10937
11607
|
};
|
|
10938
11608
|
}
|
|
10939
11609
|
return state.agentWorkUiByConversation[id];
|
|
@@ -10951,7 +11621,8 @@ function resetConversationWorkUi(conversationId, keepCompleted, workspaceId) {
|
|
|
10951
11621
|
finalResponseMsg: keepCompleted ? prior.finalResponseMsg : null,
|
|
10952
11622
|
finalResponseText: keepCompleted ? prior.finalResponseText : '',
|
|
10953
11623
|
finalResponseAt: keepCompleted ? prior.finalResponseAt : 0,
|
|
10954
|
-
pendingWorkReview: keepCompleted ? prior.pendingWorkReview : null
|
|
11624
|
+
pendingWorkReview: keepCompleted ? prior.pendingWorkReview : null,
|
|
11625
|
+
workReviews: keepCompleted ? (prior.workReviews || []).slice() : []
|
|
10955
11626
|
};
|
|
10956
11627
|
return state.agentWorkUiByConversation[id];
|
|
10957
11628
|
}
|
|
@@ -11233,9 +11904,12 @@ function renderChatMessages(messages, target) {
|
|
|
11233
11904
|
if (!orphanRoles.user && !orphanRoles.assistant) renderOwnedWorkRun(orphanRun, true);
|
|
11234
11905
|
}
|
|
11235
11906
|
}
|
|
11236
|
-
var
|
|
11907
|
+
var priorWorkUi = conversationWorkUiState(renderConversationId);
|
|
11908
|
+
var pendingWorkReview = priorWorkUi.pendingWorkReview;
|
|
11909
|
+
var retainedWorkReviews = Array.isArray(priorWorkUi.workReviews) ? priorWorkUi.workReviews.slice() : [];
|
|
11237
11910
|
resetConversationWorkUi(renderConversationId, false);
|
|
11238
11911
|
conversationWorkUiState(renderConversationId).pendingWorkReview = pendingWorkReview;
|
|
11912
|
+
conversationWorkUiState(renderConversationId).workReviews = retainedWorkReviews;
|
|
11239
11913
|
if (!normalizedMessages.length) {
|
|
11240
11914
|
persistedRuns.forEach(function(run) { renderOwnedWorkRun(run, true); });
|
|
11241
11915
|
var pendingGuideCount = renderPendingGuideMessages(renderTarget, persistedGuideIds);
|
|
@@ -11279,7 +11953,10 @@ function renderChatMessages(messages, target) {
|
|
|
11279
11953
|
finishToolBatch(renderConversationId);
|
|
11280
11954
|
persistedRuns.forEach(function(run) { renderOwnedWorkRun(run, true); });
|
|
11281
11955
|
renderPendingGuideMessages(renderTarget, persistedGuideIds);
|
|
11282
|
-
|
|
11956
|
+
retainedWorkReviews.forEach(function(item) {
|
|
11957
|
+
addWorkReview(item.diffs || item, item.runId || '');
|
|
11958
|
+
});
|
|
11959
|
+
if (pendingWorkReview) addWorkReview(pendingWorkReview.diffs || pendingWorkReview, pendingWorkReview.runId || '');
|
|
11283
11960
|
renderConversationBranchPagers();
|
|
11284
11961
|
renderAutoRouteRatingControls();
|
|
11285
11962
|
if (typeof renderPendingOptionsInChat === 'function') renderPendingOptionsInChat(state.pendingOptions || []);
|
|
@@ -11531,6 +12208,7 @@ function renderAgentWorkEvent(event) {
|
|
|
11531
12208
|
var eventQueueTarget = { workspaceId: eventWorkspaceId || runtimeWorkspaceId(''), conversationId: eventConversationId };
|
|
11532
12209
|
state.backendQueue = setBackendQueueForTarget(event.queue || { steering: [], followUp: [] }, eventQueueTarget);
|
|
11533
12210
|
if (window.syncNextQueueFromBackend) window.syncNextQueueFromBackend(state.backendQueue, eventQueueTarget);
|
|
12211
|
+
syncQueueItemsFromSnapshot(event, eventQueueTarget);
|
|
11534
12212
|
if (window.renderInputStack) window.renderInputStack();
|
|
11535
12213
|
} else {
|
|
11536
12214
|
return;
|
|
@@ -11588,6 +12266,9 @@ function appendAgentWorkEvent(event) {
|
|
|
11588
12266
|
eventRuntimeKey(event);
|
|
11589
12267
|
var active = isActiveConversationTarget(target);
|
|
11590
12268
|
var visibleRuntimeBranch = active && isViewingRuntimeConversationBranch(target);
|
|
12269
|
+
// 模型回退不是隐藏的参数回退:活跃对话发生回退时,把输入框下方的
|
|
12270
|
+
// 模型选择区同步为实际生效的模型。
|
|
12271
|
+
if (active && event.fallback && event.fallback.to) applyModelFallbackToInputSurface(event);
|
|
11591
12272
|
if (active) markConversationTracked(id, state.conversationTrackMs || 300000, workspaceId);
|
|
11592
12273
|
if (active || isConversationTracked(id, workspaceId)) cacheAgentWorkEvent(event);
|
|
11593
12274
|
var pendingProvisionalStop = (event.type === 'start' || event.status === 'running')
|
|
@@ -11756,6 +12437,7 @@ function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
|
11756
12437
|
state.backendQueue = setBackendQueueForTarget(s.queued, snapshotTarget);
|
|
11757
12438
|
if (window.syncNextQueueFromBackend) window.syncNextQueueFromBackend(state.backendQueue, snapshotTarget);
|
|
11758
12439
|
}
|
|
12440
|
+
syncQueueItemsFromSnapshot(s, snapshotTarget);
|
|
11759
12441
|
if (s && Array.isArray(s.workEvents)) {
|
|
11760
12442
|
var eventConversationId = runtimeKeyFor(snapshotTarget.workspaceId, s.conversationId || requestedConversationId || 'default');
|
|
11761
12443
|
if (!state.agentWorkEventsByConversation) state.agentWorkEventsByConversation = {};
|
|
@@ -11980,6 +12662,7 @@ window.optionManualSubmitted = function(element) {
|
|
|
11980
12662
|
|
|
11981
12663
|
// === Working State ===
|
|
11982
12664
|
function setWorking(working) {
|
|
12665
|
+
var changed = state.isWorking !== working;
|
|
11983
12666
|
state.isWorking = working;
|
|
11984
12667
|
state.activeAgentRunning = working;
|
|
11985
12668
|
var btn = els['submit-btn'];
|
|
@@ -11988,13 +12671,13 @@ function setWorking(working) {
|
|
|
11988
12671
|
var ws = els['ws-thumb'];
|
|
11989
12672
|
if (working) {
|
|
11990
12673
|
if (btn) btn.classList.add('working');
|
|
11991
|
-
if (input) input.classList.add('working-glow');
|
|
12674
|
+
if (input) input.classList.add('working-glow', 'marquee-border');
|
|
11992
12675
|
if (title) title.classList.add('animate');
|
|
11993
12676
|
if (ws) ws.classList.add('marquee-border');
|
|
11994
|
-
renderConversations();
|
|
12677
|
+
if (changed) renderConversations();
|
|
11995
12678
|
} else {
|
|
11996
12679
|
if (btn) btn.classList.remove('working');
|
|
11997
|
-
if (input) input.classList.remove('working-glow');
|
|
12680
|
+
if (input) input.classList.remove('working-glow', 'marquee-border');
|
|
11998
12681
|
if (title) title.classList.remove('animate');
|
|
11999
12682
|
if (ws) ws.classList.remove('marquee-border');
|
|
12000
12683
|
}
|
|
@@ -12534,6 +13217,7 @@ window.closeModelSelectMenu = function(options) {
|
|
|
12534
13217
|
if (shell) shell.classList.remove('open');
|
|
12535
13218
|
if (menu && typeof menu.hidePopover === 'function' && menu.matches(':popover-open')) menu.hidePopover();
|
|
12536
13219
|
if (button) button.setAttribute('aria-expanded', 'false');
|
|
13220
|
+
window.setLiquidSidebarGestureLock('popup:model-select', false);
|
|
12537
13221
|
if (options && options.focus && button) button.focus();
|
|
12538
13222
|
};
|
|
12539
13223
|
|
|
@@ -12550,6 +13234,7 @@ window.toggleModelSelectMenu = function(forceOpen) {
|
|
|
12550
13234
|
}
|
|
12551
13235
|
if (open) document.querySelectorAll('.newmark-select-shell.open').forEach(function(item) { window.closeNewmarkSelect(item); });
|
|
12552
13236
|
shell.classList.add('open');
|
|
13237
|
+
window.setLiquidSidebarGestureLock('popup:model-select', true);
|
|
12553
13238
|
button.setAttribute('aria-expanded', 'true');
|
|
12554
13239
|
if (open) {
|
|
12555
13240
|
if (menu && typeof menu.showPopover === 'function' && !menu.matches(':popover-open')) menu.showPopover();
|
|
@@ -12577,6 +13262,7 @@ window.closeConversationActionMenu = function(options) {
|
|
|
12577
13262
|
if (menu && typeof menu.hidePopover === 'function' && menu.matches(':popover-open')) menu.hidePopover();
|
|
12578
13263
|
document.querySelectorAll('.conv-more-btn[aria-expanded="true"]').forEach(function(item) { item.setAttribute('aria-expanded', 'false'); });
|
|
12579
13264
|
state.conversationActionAnchor = null;
|
|
13265
|
+
window.setLiquidSidebarGestureLock('popup:conversation-action', false);
|
|
12580
13266
|
if (options && options.focus && anchor && anchor.focus) anchor.focus();
|
|
12581
13267
|
};
|
|
12582
13268
|
|
|
@@ -12632,6 +13318,7 @@ window.openConversationActionMenu = function(anchor, conversationId, pos) {
|
|
|
12632
13318
|
document.querySelectorAll('.conv-more-btn[aria-expanded="true"]').forEach(function(item) { item.setAttribute('aria-expanded', 'false'); });
|
|
12633
13319
|
if (moreBtn) moreBtn.setAttribute('aria-expanded', 'true');
|
|
12634
13320
|
state.conversationActionAnchor = anchor || moreBtn || null;
|
|
13321
|
+
window.setLiquidSidebarGestureLock('popup:conversation-action', true);
|
|
12635
13322
|
if (typeof menu.showPopover === 'function' && !menu.matches(':popover-open')) menu.showPopover();
|
|
12636
13323
|
requestAnimationFrame(function() {
|
|
12637
13324
|
window.positionConversationActionMenu(state.conversationActionAnchor || moreBtn, menu, pos);
|
|
@@ -12695,6 +13382,967 @@ window.handleModelSelectButtonKey = function(event) {
|
|
|
12695
13382
|
}
|
|
12696
13383
|
};
|
|
12697
13384
|
|
|
13385
|
+
// 后端回退信号携带的是纯模型名(如 gpt-4o-mini),把名称映射回前端
|
|
13386
|
+
// 选择器的部署值(deployment:providerId:modelName),使选择区能直接选中。
|
|
13387
|
+
function modelDeploymentValueForName(name, providerId) {
|
|
13388
|
+
var modelName = String(name || '').trim();
|
|
13389
|
+
if (!modelName) return '';
|
|
13390
|
+
var providers = Array.isArray(state.providers) ? state.providers : [];
|
|
13391
|
+
var match = null;
|
|
13392
|
+
for (var pi = 0; pi < providers.length && !match; pi++) {
|
|
13393
|
+
var provider = providers[pi];
|
|
13394
|
+
if (providerId && String(provider.id || provider.name || '') !== String(providerId)) continue;
|
|
13395
|
+
var models = Array.isArray(provider.models) ? provider.models : [];
|
|
13396
|
+
for (var mi = 0; mi < models.length && !match; mi++) {
|
|
13397
|
+
var entryName = typeof models[mi] === 'string' ? models[mi] : (models[mi] && models[mi].name || '');
|
|
13398
|
+
if (entryName === modelName) match = { provider: provider, name: entryName };
|
|
13399
|
+
}
|
|
13400
|
+
}
|
|
13401
|
+
if (!match) return modelName;
|
|
13402
|
+
var providerIdValue = String(match.provider.id || match.provider.name || '');
|
|
13403
|
+
return 'deployment:' + encodeURIComponent(providerIdValue) + ':' + encodeURIComponent(match.name);
|
|
13404
|
+
}
|
|
13405
|
+
|
|
13406
|
+
// 不可用模型回退同步输入框下方选择区:固定模型选择被回退时,选择区
|
|
13407
|
+
// 显示实际生效的模型;Auto 模式的路由切换不污染选择区(保持 Auto 显示)。
|
|
13408
|
+
function applyModelFallbackToInputSurface(event) {
|
|
13409
|
+
var f = event && event.fallback;
|
|
13410
|
+
if (!f || !f.to) return;
|
|
13411
|
+
if (String(state.model || '') === 'auto') return;
|
|
13412
|
+
var nextValue = modelDeploymentValueForName(f.to, f.providerId || '');
|
|
13413
|
+
if (!nextValue || String(state.model || '') === nextValue) return;
|
|
13414
|
+
state.model = nextValue;
|
|
13415
|
+
state.modelLabel = '';
|
|
13416
|
+
// 不预置 _syncedModel:下一次发送前的 syncConversationExecutionState 会把
|
|
13417
|
+
// 回退模型正式同步到后端 host agent(并持久化为该对话的首选模型)。
|
|
13418
|
+
window.refreshModelSelect();
|
|
13419
|
+
window.syncModelSelectSurface();
|
|
13420
|
+
}
|
|
13421
|
+
|
|
13422
|
+
var liquidSidebarGestureLocks = new Set();
|
|
13423
|
+
window.setLiquidSidebarGestureLock = function(token, locked) {
|
|
13424
|
+
if (locked) liquidSidebarGestureLocks.add(String(token));
|
|
13425
|
+
else liquidSidebarGestureLocks.delete(String(token));
|
|
13426
|
+
document.body.classList.toggle('liquid-sidebar-gesture-locked', liquidSidebarGestureLocks.size > 0);
|
|
13427
|
+
};
|
|
13428
|
+
window.liquidSidebarGesturesLocked = function() { return liquidSidebarGestureLocks.size > 0; };
|
|
13429
|
+
|
|
13430
|
+
// One moving glass capsule per select menu. It starts at the existing
|
|
13431
|
+
// selection, fades in, follows pointer movement across options, and commits
|
|
13432
|
+
// the landing option on release. Static options retain their color capsule.
|
|
13433
|
+
var liquidBackdropFramePromise = null;
|
|
13434
|
+
var liquidBackdropCachedFrame = null;
|
|
13435
|
+
var liquidBackdropPrimeTimer = 0;
|
|
13436
|
+
window.__liquidBackdropPrimeReady = false;
|
|
13437
|
+
function decodeLiquidBackdropFrame(frame) {
|
|
13438
|
+
if (!frame) return Promise.resolve(null);
|
|
13439
|
+
if (frame.bitmap) return Promise.resolve(frame);
|
|
13440
|
+
var bytes = frame.bytes;
|
|
13441
|
+
if (bytes && Array.isArray(bytes.data)) bytes = new Uint8Array(bytes.data);
|
|
13442
|
+
var blobPromise = bytes
|
|
13443
|
+
? Promise.resolve(new Blob([bytes], { type: frame.mimeType || 'image/jpeg' }))
|
|
13444
|
+
: (frame.dataUrl ? fetch(frame.dataUrl).then(function(response) { return response.blob(); }) : Promise.resolve(null));
|
|
13445
|
+
return blobPromise.then(function(blob) {
|
|
13446
|
+
if (!blob) return null;
|
|
13447
|
+
return createImageBitmap(blob);
|
|
13448
|
+
}).then(function(bitmap) {
|
|
13449
|
+
if (!bitmap) return null;
|
|
13450
|
+
frame.bitmap = bitmap;
|
|
13451
|
+
frame.id = frame.id || String(Date.now()) + ':' + String(frame.width || bitmap.width) + 'x' + String(frame.height || bitmap.height);
|
|
13452
|
+
frame.capturedAt = performance.now();
|
|
13453
|
+
return frame;
|
|
13454
|
+
});
|
|
13455
|
+
}
|
|
13456
|
+
function captureLiquidBackdropFrame(options) {
|
|
13457
|
+
if (!window.api || typeof window.api.captureLiquidBackdrop !== 'function') return Promise.resolve(null);
|
|
13458
|
+
options = options || {};
|
|
13459
|
+
var maxAge = Number.isFinite(options.maxAge) ? options.maxAge : 5000;
|
|
13460
|
+
if (!options.force && liquidBackdropCachedFrame && performance.now() - liquidBackdropCachedFrame.capturedAt <= maxAge) {
|
|
13461
|
+
return Promise.resolve(liquidBackdropCachedFrame);
|
|
13462
|
+
}
|
|
13463
|
+
if (!liquidBackdropFramePromise) {
|
|
13464
|
+
window.__liquidBackdropPrimeReady = false;
|
|
13465
|
+
liquidBackdropFramePromise = window.api.captureLiquidBackdrop({ width: window.innerWidth, height: window.innerHeight })
|
|
13466
|
+
.then(decodeLiquidBackdropFrame)
|
|
13467
|
+
.then(function(frame) {
|
|
13468
|
+
if (frame) {
|
|
13469
|
+
var previous = liquidBackdropCachedFrame;
|
|
13470
|
+
liquidBackdropCachedFrame = frame;
|
|
13471
|
+
prewarmKyantRenderer(frame);
|
|
13472
|
+
window.__liquidBackdropPrimeReady = true;
|
|
13473
|
+
if (previous && previous !== frame && previous.bitmap && previous.bitmap.close) previous.bitmap.close();
|
|
13474
|
+
}
|
|
13475
|
+
return frame;
|
|
13476
|
+
})
|
|
13477
|
+
.catch(function(error) {
|
|
13478
|
+
window.__liquidErrors = window.__liquidErrors || [];
|
|
13479
|
+
window.__liquidErrors.push(String(error && (error.stack || error.message) || error));
|
|
13480
|
+
console.warn('[KyantGlass] backdrop capture failed:', error);
|
|
13481
|
+
return liquidBackdropCachedFrame;
|
|
13482
|
+
})
|
|
13483
|
+
.finally(function() { liquidBackdropFramePromise = null; });
|
|
13484
|
+
}
|
|
13485
|
+
return liquidBackdropFramePromise;
|
|
13486
|
+
}
|
|
13487
|
+
|
|
13488
|
+
function scheduleLiquidBackdropPrime() {
|
|
13489
|
+
if (liquidBackdropPrimeTimer || (liquidBackdropCachedFrame && performance.now() - liquidBackdropCachedFrame.capturedAt < 5000)) return;
|
|
13490
|
+
liquidBackdropPrimeTimer = setTimeout(function() {
|
|
13491
|
+
liquidBackdropPrimeTimer = 0;
|
|
13492
|
+
captureLiquidBackdropFrame({ maxAge: 5000 });
|
|
13493
|
+
}, liquidBackdropCachedFrame ? 220 : 48);
|
|
13494
|
+
}
|
|
13495
|
+
|
|
13496
|
+
if (window.requestIdleCallback) {
|
|
13497
|
+
window.requestIdleCallback(function() { captureLiquidBackdropFrame({ maxAge: 5000 }); }, { timeout: 900 });
|
|
13498
|
+
} else {
|
|
13499
|
+
setTimeout(function() { captureLiquidBackdropFrame({ maxAge: 5000 }); }, 600);
|
|
13500
|
+
}
|
|
13501
|
+
|
|
13502
|
+
document.addEventListener('pointerover', function(event) {
|
|
13503
|
+
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;
|
|
13504
|
+
if (target) scheduleLiquidBackdropPrime();
|
|
13505
|
+
}, { passive: true });
|
|
13506
|
+
document.addEventListener('focusin', function(event) {
|
|
13507
|
+
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;
|
|
13508
|
+
if (target) scheduleLiquidBackdropPrime();
|
|
13509
|
+
});
|
|
13510
|
+
|
|
13511
|
+
function compileLiquidShader(gl, type, source) {
|
|
13512
|
+
var shader = gl.createShader(type);
|
|
13513
|
+
gl.shaderSource(shader, source);
|
|
13514
|
+
gl.compileShader(shader);
|
|
13515
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
13516
|
+
console.error('[KyantGlass] shader compile failed:', gl.getShaderInfoLog(shader));
|
|
13517
|
+
gl.deleteShader(shader);
|
|
13518
|
+
return null;
|
|
13519
|
+
}
|
|
13520
|
+
return shader;
|
|
13521
|
+
}
|
|
13522
|
+
|
|
13523
|
+
function liquidGlassCssNumber(name, fallback) {
|
|
13524
|
+
var value = parseFloat(getComputedStyle(document.documentElement).getPropertyValue(name));
|
|
13525
|
+
return Number.isFinite(value) ? value : fallback;
|
|
13526
|
+
}
|
|
13527
|
+
|
|
13528
|
+
var liquidKyantRendererPool = [];
|
|
13529
|
+
var liquidKyantVertexSource = '#version 300 es\n' +
|
|
13530
|
+
'in vec2 p; out vec2 uv; void main(){uv=p*.5+.5;gl_Position=vec4(p,0.,1.);}';
|
|
13531
|
+
var liquidKyantFragmentSource = '#version 300 es\nprecision highp float;\n' +
|
|
13532
|
+
'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' +
|
|
13533
|
+
'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' +
|
|
13534
|
+
'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' +
|
|
13535
|
+
'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' +
|
|
13536
|
+
'float circleMap(float x){x=clamp(x,0.,1.);return 1.-sqrt(max(0.,1.-x*x));}\n' +
|
|
13537
|
+
'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' +
|
|
13538
|
+
'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);}';
|
|
13539
|
+
|
|
13540
|
+
function createKyantLiquidRenderer() {
|
|
13541
|
+
var canvas = document.createElement('canvas');
|
|
13542
|
+
var displayCanvas = document.createElement('canvas');
|
|
13543
|
+
displayCanvas.className = 'liquid-selection-canvas';
|
|
13544
|
+
var displayContext = displayCanvas.getContext('2d', { alpha:true });
|
|
13545
|
+
if (!displayContext) return null;
|
|
13546
|
+
var gl = canvas.getContext('webgl2', { alpha:true, antialias:false, premultipliedAlpha:true, powerPreference:'high-performance' });
|
|
13547
|
+
if (!gl) return null;
|
|
13548
|
+
var vs = compileLiquidShader(gl, gl.VERTEX_SHADER, liquidKyantVertexSource);
|
|
13549
|
+
var fs = compileLiquidShader(gl, gl.FRAGMENT_SHADER, liquidKyantFragmentSource);
|
|
13550
|
+
if (!vs || !fs) return null;
|
|
13551
|
+
var program = gl.createProgram();
|
|
13552
|
+
gl.attachShader(program, vs); gl.attachShader(program, fs); gl.linkProgram(program);
|
|
13553
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
13554
|
+
console.error('[KyantGlass] program link failed:', gl.getProgramInfoLog(program));
|
|
13555
|
+
return null;
|
|
13556
|
+
}
|
|
13557
|
+
gl.deleteShader(vs);
|
|
13558
|
+
gl.deleteShader(fs);
|
|
13559
|
+
var buffer = gl.createBuffer();
|
|
13560
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
13561
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]), gl.STATIC_DRAW);
|
|
13562
|
+
var location = gl.getAttribLocation(program, 'p');
|
|
13563
|
+
gl.enableVertexAttribArray(location); gl.vertexAttribPointer(location,2,gl.FLOAT,false,0,0);
|
|
13564
|
+
var texture = gl.createTexture();
|
|
13565
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
13566
|
+
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);
|
|
13567
|
+
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR);
|
|
13568
|
+
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);
|
|
13569
|
+
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);
|
|
13570
|
+
var uniforms = {
|
|
13571
|
+
content: gl.getUniformLocation(program,'content'),
|
|
13572
|
+
size: gl.getUniformLocation(program,'size'),
|
|
13573
|
+
viewport: gl.getUniformLocation(program,'viewport'),
|
|
13574
|
+
origin: gl.getUniformLocation(program,'origin'),
|
|
13575
|
+
cornerRadii: gl.getUniformLocation(program,'cornerRadii'),
|
|
13576
|
+
refractionHeight: gl.getUniformLocation(program,'refractionHeight'),
|
|
13577
|
+
refractionAmount: gl.getUniformLocation(program,'refractionAmount'),
|
|
13578
|
+
depthEffect: gl.getUniformLocation(program,'depthEffect'),
|
|
13579
|
+
chromaticAberration: gl.getUniformLocation(program,'chromaticAberration')
|
|
13580
|
+
};
|
|
13581
|
+
gl.useProgram(program);
|
|
13582
|
+
gl.uniform1i(uniforms.content, 0);
|
|
13583
|
+
var renderer = {
|
|
13584
|
+
canvas: canvas,
|
|
13585
|
+
displayCanvas: displayCanvas,
|
|
13586
|
+
displayContext: displayContext,
|
|
13587
|
+
gl: gl,
|
|
13588
|
+
program: program,
|
|
13589
|
+
texture: texture,
|
|
13590
|
+
uniforms: uniforms,
|
|
13591
|
+
owner: null,
|
|
13592
|
+
textureReady: false,
|
|
13593
|
+
generation: 0,
|
|
13594
|
+
renderFrame: 0,
|
|
13595
|
+
frameId: '',
|
|
13596
|
+
lastGeometry: null,
|
|
13597
|
+
pendingGeometry: null,
|
|
13598
|
+
uniformState: Object.create(null),
|
|
13599
|
+
invalid: false
|
|
13600
|
+
};
|
|
13601
|
+
canvas.addEventListener('webglcontextlost', function(event) {
|
|
13602
|
+
event.preventDefault();
|
|
13603
|
+
renderer.invalid = true;
|
|
13604
|
+
renderer.textureReady = false;
|
|
13605
|
+
if (renderer.renderFrame) cancelAnimationFrame(renderer.renderFrame);
|
|
13606
|
+
renderer.renderFrame = 0;
|
|
13607
|
+
liquidKyantRendererPool = liquidKyantRendererPool.filter(function(item) { return item !== renderer; });
|
|
13608
|
+
});
|
|
13609
|
+
return renderer;
|
|
13610
|
+
}
|
|
13611
|
+
|
|
13612
|
+
function acquireKyantLiquidRenderer(float) {
|
|
13613
|
+
var renderer = liquidKyantRendererPool.find(function(item) {
|
|
13614
|
+
return !item.invalid && (!item.owner || !item.owner.isConnected);
|
|
13615
|
+
});
|
|
13616
|
+
if (!renderer) {
|
|
13617
|
+
renderer = createKyantLiquidRenderer();
|
|
13618
|
+
if (!renderer) return null;
|
|
13619
|
+
liquidKyantRendererPool.push(renderer);
|
|
13620
|
+
}
|
|
13621
|
+
renderer.owner = float;
|
|
13622
|
+
renderer.generation += 1;
|
|
13623
|
+
if (renderer.renderFrame) cancelAnimationFrame(renderer.renderFrame);
|
|
13624
|
+
renderer.renderFrame = 0;
|
|
13625
|
+
renderer.displayCanvas.remove();
|
|
13626
|
+
renderer.lastGeometry = null;
|
|
13627
|
+
renderer.pendingGeometry = null;
|
|
13628
|
+
return renderer;
|
|
13629
|
+
}
|
|
13630
|
+
|
|
13631
|
+
function uploadKyantLiquidFrame(renderer, frame) {
|
|
13632
|
+
if (!renderer || !frame || !frame.bitmap || renderer.frameId === frame.id) return false;
|
|
13633
|
+
var gl = renderer.gl;
|
|
13634
|
+
gl.bindTexture(gl.TEXTURE_2D, renderer.texture);
|
|
13635
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
|
|
13636
|
+
gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,frame.bitmap);
|
|
13637
|
+
renderer.textureReady = true;
|
|
13638
|
+
renderer.frameId = frame.id;
|
|
13639
|
+
return true;
|
|
13640
|
+
}
|
|
13641
|
+
|
|
13642
|
+
function prewarmKyantRenderer(frame) {
|
|
13643
|
+
if (!frame || !frame.bitmap) return;
|
|
13644
|
+
var renderer = liquidKyantRendererPool.find(function(item) { return !item.invalid && !item.owner; });
|
|
13645
|
+
if (!renderer) {
|
|
13646
|
+
if (liquidKyantRendererPool.some(function(item) { return !item.invalid; })) return;
|
|
13647
|
+
renderer = createKyantLiquidRenderer();
|
|
13648
|
+
if (!renderer) return;
|
|
13649
|
+
liquidKyantRendererPool.push(renderer);
|
|
13650
|
+
}
|
|
13651
|
+
uploadKyantLiquidFrame(renderer, frame);
|
|
13652
|
+
}
|
|
13653
|
+
|
|
13654
|
+
function attachKyantLiquidRenderer(float, frame) {
|
|
13655
|
+
var renderer = float._kyantRendererRef;
|
|
13656
|
+
if (!renderer || renderer.owner !== float) renderer = acquireKyantLiquidRenderer(float);
|
|
13657
|
+
if (!renderer) { float.dataset.kyantRenderer = 'webgl2-unavailable'; return; }
|
|
13658
|
+
float._kyantRendererRef = renderer;
|
|
13659
|
+
var canvas = renderer.canvas;
|
|
13660
|
+
var displayCanvas = renderer.displayCanvas;
|
|
13661
|
+
var displayContext = renderer.displayContext;
|
|
13662
|
+
var gl = renderer.gl;
|
|
13663
|
+
var program = renderer.program;
|
|
13664
|
+
var texture = renderer.texture;
|
|
13665
|
+
var uniforms = renderer.uniforms;
|
|
13666
|
+
var generation = renderer.generation;
|
|
13667
|
+
float.dataset.kyantRenderer = 'webgl2';
|
|
13668
|
+
float._kyantTextureReady = false;
|
|
13669
|
+
float._kyantDrawReady = false;
|
|
13670
|
+
delete float.dataset.kyantDrawReady;
|
|
13671
|
+
float._renderKyantGlass = function(geometry, immediate) {
|
|
13672
|
+
renderer.pendingGeometry = geometry || null;
|
|
13673
|
+
if (!renderer.textureReady || renderer.owner !== float || !float.isConnected) return;
|
|
13674
|
+
if (immediate && renderer.renderFrame) {
|
|
13675
|
+
cancelAnimationFrame(renderer.renderFrame);
|
|
13676
|
+
renderer.renderFrame = 0;
|
|
13677
|
+
}
|
|
13678
|
+
if (renderer.renderFrame) return;
|
|
13679
|
+
var draw = function() {
|
|
13680
|
+
renderer.renderFrame = 0;
|
|
13681
|
+
if (!renderer.textureReady || renderer.owner !== float || !float.isConnected) return;
|
|
13682
|
+
var geometry = renderer.pendingGeometry;
|
|
13683
|
+
renderer.pendingGeometry = null;
|
|
13684
|
+
var style = geometry ? null : getComputedStyle(float);
|
|
13685
|
+
var width = geometry ? geometry.width : (parseFloat(style.width) || float.offsetWidth || 1);
|
|
13686
|
+
var height = geometry ? geometry.height : (parseFloat(style.height) || float.offsetHeight || 1);
|
|
13687
|
+
var left = geometry ? geometry.left : (parseFloat(style.left) || 0);
|
|
13688
|
+
var top = geometry ? geometry.top : (parseFloat(style.top) || 0);
|
|
13689
|
+
var dpr = window.devicePixelRatio || 1;
|
|
13690
|
+
var renderScale = Math.min(dpr, 1.5);
|
|
13691
|
+
var pixelWidth = Math.max(1, Math.round(width*renderScale));
|
|
13692
|
+
var pixelHeight = Math.max(1, Math.round(height*renderScale));
|
|
13693
|
+
var geometryKey = [left.toFixed(2),top.toFixed(2),width.toFixed(2),height.toFixed(2),pixelWidth,pixelHeight].join(':');
|
|
13694
|
+
if (renderer.lastGeometry === geometryKey) return;
|
|
13695
|
+
renderer.lastGeometry = geometryKey;
|
|
13696
|
+
if (canvas.width !== pixelWidth) canvas.width = pixelWidth;
|
|
13697
|
+
if (canvas.height !== pixelHeight) canvas.height = pixelHeight;
|
|
13698
|
+
if (displayCanvas.width !== pixelWidth) displayCanvas.width = pixelWidth;
|
|
13699
|
+
if (displayCanvas.height !== pixelHeight) displayCanvas.height = pixelHeight;
|
|
13700
|
+
gl.viewport(0,0,pixelWidth,pixelHeight);
|
|
13701
|
+
gl.useProgram(program);
|
|
13702
|
+
function uniformChanged(key, values, apply) {
|
|
13703
|
+
var prior = renderer.uniformState[key];
|
|
13704
|
+
if (prior && prior.length === values.length && values.every(function(value,index) { return prior[index] === value; })) return;
|
|
13705
|
+
renderer.uniformState[key] = values;
|
|
13706
|
+
apply();
|
|
13707
|
+
}
|
|
13708
|
+
uniformChanged('size',[width,height],function() { gl.uniform2f(uniforms.size,width,height); });
|
|
13709
|
+
uniformChanged('viewport',[window.innerWidth,window.innerHeight],function() { gl.uniform2f(uniforms.viewport,window.innerWidth,window.innerHeight); });
|
|
13710
|
+
uniformChanged('origin',[left,top],function() { gl.uniform2f(uniforms.origin,left,top); });
|
|
13711
|
+
var capsuleRadius = Math.min(width,height)*.5;
|
|
13712
|
+
uniformChanged('cornerRadii',[capsuleRadius],function() { gl.uniform4f(uniforms.cornerRadii,capsuleRadius,capsuleRadius,capsuleRadius,capsuleRadius); });
|
|
13713
|
+
var configuredHeight = liquidGlassCssNumber('--liquid-interaction-edge', 6);
|
|
13714
|
+
var configuredAmount = liquidGlassCssNumber('--liquid-float-refraction-amount', 24);
|
|
13715
|
+
var configuredChromatic = liquidGlassCssNumber('--liquid-float-chromatic-aberration', 1);
|
|
13716
|
+
var refractionHeight = Math.max(0.001,Math.min(configuredHeight,height*.48));
|
|
13717
|
+
uniformChanged('refractionHeight',[refractionHeight],function() { gl.uniform1f(uniforms.refractionHeight,refractionHeight); });
|
|
13718
|
+
uniformChanged('refractionAmount',[-configuredAmount],function() { gl.uniform1f(uniforms.refractionAmount,-configuredAmount); });
|
|
13719
|
+
uniformChanged('depthEffect',[1],function() { gl.uniform1f(uniforms.depthEffect,1); });
|
|
13720
|
+
uniformChanged('chromaticAberration',[configuredChromatic],function() { gl.uniform1f(uniforms.chromaticAberration,configuredChromatic); });
|
|
13721
|
+
gl.drawArrays(gl.TRIANGLES,0,6);
|
|
13722
|
+
displayContext.clearRect(0,0,pixelWidth,pixelHeight);
|
|
13723
|
+
displayContext.drawImage(canvas,0,0);
|
|
13724
|
+
float._kyantDrawReady = true;
|
|
13725
|
+
float.dataset.kyantDrawReady = 'true';
|
|
13726
|
+
if (displayCanvas.parentNode !== float) float.appendChild(displayCanvas);
|
|
13727
|
+
};
|
|
13728
|
+
if (immediate) draw();
|
|
13729
|
+
else renderer.renderFrame = requestAnimationFrame(draw);
|
|
13730
|
+
};
|
|
13731
|
+
float._releaseKyantGlass = function() {
|
|
13732
|
+
if (renderer.owner !== float) return;
|
|
13733
|
+
renderer.generation += 1;
|
|
13734
|
+
renderer.owner = null;
|
|
13735
|
+
if (renderer.renderFrame) cancelAnimationFrame(renderer.renderFrame);
|
|
13736
|
+
renderer.renderFrame = 0;
|
|
13737
|
+
renderer.lastGeometry = null;
|
|
13738
|
+
renderer.pendingGeometry = null;
|
|
13739
|
+
displayCanvas.remove();
|
|
13740
|
+
delete float._kyantRendererRef;
|
|
13741
|
+
};
|
|
13742
|
+
if (frame && frame.bitmap && renderer.owner === float && renderer.generation === generation && float.isConnected) {
|
|
13743
|
+
uploadKyantLiquidFrame(renderer, frame);
|
|
13744
|
+
float._kyantTextureReady = true;
|
|
13745
|
+
if (float.style.width && float.style.height) float._renderKyantGlass(null, true);
|
|
13746
|
+
} else if (renderer.textureReady) {
|
|
13747
|
+
float._kyantTextureReady = true;
|
|
13748
|
+
if (float.style.width && float.style.height) float._renderKyantGlass(null, true);
|
|
13749
|
+
} else {
|
|
13750
|
+
float.dataset.kyantRenderer = 'waiting-backdrop';
|
|
13751
|
+
}
|
|
13752
|
+
}
|
|
13753
|
+
|
|
13754
|
+
function mountLiquidViewportFloat(float) {
|
|
13755
|
+
if (liquidBackdropPrimeTimer) clearTimeout(liquidBackdropPrimeTimer);
|
|
13756
|
+
liquidBackdropPrimeTimer = 0;
|
|
13757
|
+
float.classList.add('liquid-selection-viewport');
|
|
13758
|
+
float.setAttribute('popover', 'manual');
|
|
13759
|
+
document.body.appendChild(float);
|
|
13760
|
+
startLiquidMotionTracking(float);
|
|
13761
|
+
attachKyantLiquidRenderer(float, liquidBackdropCachedFrame);
|
|
13762
|
+
}
|
|
13763
|
+
|
|
13764
|
+
function showLiquidViewportFloat(float) {
|
|
13765
|
+
try { float.showPopover(); } catch (_) {}
|
|
13766
|
+
}
|
|
13767
|
+
|
|
13768
|
+
function keepLiquidFloatExpansionInsideViewport(float, rect) {
|
|
13769
|
+
if (!float || !rect) return;
|
|
13770
|
+
var edge = 28;
|
|
13771
|
+
float.style.transformOrigin = rect.left < edge ? 'left center' : (rect.right > window.innerWidth - edge ? 'right center' : 'center');
|
|
13772
|
+
}
|
|
13773
|
+
|
|
13774
|
+
function liquidInteractionEdgePx() {
|
|
13775
|
+
return liquidGlassCssNumber('--liquid-interaction-edge', 6);
|
|
13776
|
+
}
|
|
13777
|
+
|
|
13778
|
+
function startLiquidMotionTracking(float) {
|
|
13779
|
+
if (!float || float._liquidMotionFrame) return;
|
|
13780
|
+
var lastTime = 0;
|
|
13781
|
+
var lastX = 0;
|
|
13782
|
+
var lastY = 0;
|
|
13783
|
+
var velocityX = 0;
|
|
13784
|
+
var velocityY = 0;
|
|
13785
|
+
var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
13786
|
+
function frame(now) {
|
|
13787
|
+
if (!float || !float.isConnected) {
|
|
13788
|
+
if (float) float._liquidMotionFrame = 0;
|
|
13789
|
+
return;
|
|
13790
|
+
}
|
|
13791
|
+
var rect = float.getBoundingClientRect();
|
|
13792
|
+
var left = rect.left;
|
|
13793
|
+
var top = rect.top;
|
|
13794
|
+
var width = rect.width;
|
|
13795
|
+
var height = rect.height;
|
|
13796
|
+
var centerX = left + width * .5;
|
|
13797
|
+
var centerY = top + height * .5;
|
|
13798
|
+
var moved = lastTime > 0 && (Math.abs(centerX - lastX) > .01 || Math.abs(centerY - lastY) > .01);
|
|
13799
|
+
if (lastTime > 0) {
|
|
13800
|
+
var seconds = Math.max(.001, Math.min(.05, (now - lastTime) / 1000));
|
|
13801
|
+
var instantaneousX = (centerX - lastX) / seconds;
|
|
13802
|
+
var instantaneousY = (centerY - lastY) / seconds;
|
|
13803
|
+
velocityX = velocityX * .45 + instantaneousX * .55;
|
|
13804
|
+
velocityY = velocityY * .45 + instantaneousY * .55;
|
|
13805
|
+
}
|
|
13806
|
+
lastTime = now;
|
|
13807
|
+
lastX = centerX;
|
|
13808
|
+
lastY = centerY;
|
|
13809
|
+
var speed = reducedMotion ? 0 : Math.hypot(velocityX, velocityY);
|
|
13810
|
+
var amount = Math.max(0, Math.min(1, (speed - 8) / 700));
|
|
13811
|
+
var angle = speed > 1 ? Math.atan2(velocityY, velocityX) : 0;
|
|
13812
|
+
float.style.setProperty('--liquid-motion-angle', angle + 'rad');
|
|
13813
|
+
float.style.setProperty('--liquid-motion-stretch', String(1 + amount * .075));
|
|
13814
|
+
float.style.setProperty('--liquid-motion-squash', String(1 - amount * .035));
|
|
13815
|
+
if (window.__liquidPerfCounts) float.dataset.liquidMotionAmount = amount.toFixed(4);
|
|
13816
|
+
if (moved && float._renderKyantGlass) float._renderKyantGlass({ left:left, top:top, width:width, height:height }, true);
|
|
13817
|
+
float._liquidMotionFrame = requestAnimationFrame(frame);
|
|
13818
|
+
}
|
|
13819
|
+
float._liquidMotionFrame = requestAnimationFrame(frame);
|
|
13820
|
+
}
|
|
13821
|
+
|
|
13822
|
+
function stopLiquidMotionTracking(float) {
|
|
13823
|
+
if (!float) return;
|
|
13824
|
+
if (float._liquidArrivalFrame) cancelAnimationFrame(float._liquidArrivalFrame);
|
|
13825
|
+
float._liquidArrivalFrame = 0;
|
|
13826
|
+
if (float._liquidMotionFrame) cancelAnimationFrame(float._liquidMotionFrame);
|
|
13827
|
+
float._liquidMotionFrame = 0;
|
|
13828
|
+
float.style.removeProperty('--liquid-motion-angle');
|
|
13829
|
+
float.style.removeProperty('--liquid-motion-stretch');
|
|
13830
|
+
float.style.removeProperty('--liquid-motion-squash');
|
|
13831
|
+
}
|
|
13832
|
+
|
|
13833
|
+
function waitForLiquidSelectionArrival(float, onArrive) {
|
|
13834
|
+
if (!float || !float.isConnected) { onArrive(); return; }
|
|
13835
|
+
var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
13836
|
+
if (reducedMotion) { onArrive(); return; }
|
|
13837
|
+
if (float._liquidArrivalFrame) cancelAnimationFrame(float._liquidArrivalFrame);
|
|
13838
|
+
var startedAt = performance.now();
|
|
13839
|
+
function settled() {
|
|
13840
|
+
if (!float || !float.isConnected) return true;
|
|
13841
|
+
var target = float._liquidTargetRect;
|
|
13842
|
+
if (!target) return true;
|
|
13843
|
+
var style = getComputedStyle(float);
|
|
13844
|
+
var left = parseFloat(style.left) || 0;
|
|
13845
|
+
var top = parseFloat(style.top) || 0;
|
|
13846
|
+
var width = parseFloat(style.width) || 0;
|
|
13847
|
+
var height = parseFloat(style.height) || 0;
|
|
13848
|
+
return Math.abs(left - target.left) < .75 && Math.abs(top - target.top) < .75 &&
|
|
13849
|
+
Math.abs(width - target.width) < .75 && Math.abs(height - target.height) < .75 &&
|
|
13850
|
+
parseFloat(style.opacity || '0') >= .995;
|
|
13851
|
+
}
|
|
13852
|
+
function inspect() {
|
|
13853
|
+
if (settled() || performance.now() - startedAt > 300) {
|
|
13854
|
+
float._liquidArrivalFrame = 0;
|
|
13855
|
+
onArrive();
|
|
13856
|
+
return;
|
|
13857
|
+
}
|
|
13858
|
+
float._liquidArrivalFrame = requestAnimationFrame(inspect);
|
|
13859
|
+
}
|
|
13860
|
+
float._liquidArrivalFrame = requestAnimationFrame(inspect);
|
|
13861
|
+
}
|
|
13862
|
+
|
|
13863
|
+
function landLiquidSelectionFloat(float, cleanup, onComplete) {
|
|
13864
|
+
if (!float) { cleanup(); if (onComplete) onComplete(); return; }
|
|
13865
|
+
var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
13866
|
+
if (!float.isConnected) { cleanup(); if (onComplete) onComplete(); return; }
|
|
13867
|
+
stopLiquidMotionTracking(float);
|
|
13868
|
+
float.classList.add('liquid-selection-landing');
|
|
13869
|
+
float._liquidLandingTimer = setTimeout(function() {
|
|
13870
|
+
cleanup();
|
|
13871
|
+
scheduleLiquidBackdropPrime();
|
|
13872
|
+
if (onComplete) onComplete();
|
|
13873
|
+
}, reducedMotion ? 0 : 120);
|
|
13874
|
+
}
|
|
13875
|
+
|
|
13876
|
+
function wireLiquidMenuInteractions(menu) {
|
|
13877
|
+
if (!menu || menu.dataset.liquidInteractions === 'true') return;
|
|
13878
|
+
menu.dataset.liquidInteractions = 'true';
|
|
13879
|
+
var pointer = null;
|
|
13880
|
+
var source = null;
|
|
13881
|
+
var target = null;
|
|
13882
|
+
var float = null;
|
|
13883
|
+
var generation = 0;
|
|
13884
|
+
var pressX = 0;
|
|
13885
|
+
var pressY = 0;
|
|
13886
|
+
var gestureMoved = false;
|
|
13887
|
+
function optionAt(event) {
|
|
13888
|
+
var hit = event && document.elementFromPoint ? document.elementFromPoint(event.clientX, event.clientY) : event.target;
|
|
13889
|
+
var node = hit && hit.closest ? hit.closest('button.model-select-menu-option, button.newmark-select-option') : null;
|
|
13890
|
+
return node && menu.contains(node) && !node.disabled ? node : null;
|
|
13891
|
+
}
|
|
13892
|
+
function position(option) {
|
|
13893
|
+
if (!option || !float) return;
|
|
13894
|
+
if (float.classList.contains('liquid-selection-viewport')) {
|
|
13895
|
+
var rect = option.getBoundingClientRect();
|
|
13896
|
+
var edge = liquidInteractionEdgePx();
|
|
13897
|
+
keepLiquidFloatExpansionInsideViewport(float, rect);
|
|
13898
|
+
float._liquidTargetRect = { left: rect.left - edge, top: rect.top - edge, width: rect.width + edge * 2, height: rect.height + edge * 2 };
|
|
13899
|
+
float.style.left = (rect.left - edge) + 'px';
|
|
13900
|
+
float.style.top = (rect.top - edge) + 'px';
|
|
13901
|
+
float.style.width = (rect.width + edge * 2) + 'px';
|
|
13902
|
+
float.style.height = (rect.height + edge * 2) + 'px';
|
|
13903
|
+
if (float._renderKyantGlass) float._renderKyantGlass({
|
|
13904
|
+
left: rect.left - edge, top: rect.top - edge,
|
|
13905
|
+
width: rect.width + edge * 2, height: rect.height + edge * 2
|
|
13906
|
+
}, true);
|
|
13907
|
+
return;
|
|
13908
|
+
}
|
|
13909
|
+
var edge = liquidInteractionEdgePx();
|
|
13910
|
+
float.style.left = (option.offsetLeft - edge) + 'px';
|
|
13911
|
+
float.style.top = (option.offsetTop - edge) + 'px';
|
|
13912
|
+
float.style.width = (option.offsetWidth + edge * 2) + 'px';
|
|
13913
|
+
float.style.height = (option.offsetHeight + edge * 2) + 'px';
|
|
13914
|
+
}
|
|
13915
|
+
function positionAlongPointer(event) {
|
|
13916
|
+
if (!float || !event) return;
|
|
13917
|
+
var menuRect = menu.getBoundingClientRect();
|
|
13918
|
+
var clipTop = Math.max(0, menuRect.top + menu.clientTop + 7);
|
|
13919
|
+
var clipBottom = Math.min(window.innerHeight, menuRect.bottom - menu.clientTop - 7);
|
|
13920
|
+
var allOptions = Array.from(menu.querySelectorAll('button.model-select-menu-option, button.newmark-select-option'))
|
|
13921
|
+
.filter(function(option) { return !option.disabled && option.getClientRects().length; });
|
|
13922
|
+
var options = allOptions.filter(function(option) {
|
|
13923
|
+
var rect = option.getBoundingClientRect();
|
|
13924
|
+
return rect.bottom > clipTop && rect.top < clipBottom;
|
|
13925
|
+
});
|
|
13926
|
+
if (!options.length) return;
|
|
13927
|
+
var nearest = options[0];
|
|
13928
|
+
var nearestDistance = Infinity;
|
|
13929
|
+
options.forEach(function(option) {
|
|
13930
|
+
var rect = option.getBoundingClientRect();
|
|
13931
|
+
var distance = Math.abs(event.clientY - (rect.top + rect.height / 2));
|
|
13932
|
+
if (distance < nearestDistance) { nearest = option; nearestDistance = distance; }
|
|
13933
|
+
});
|
|
13934
|
+
target = nearest;
|
|
13935
|
+
var rect = nearest.getBoundingClientRect();
|
|
13936
|
+
var firstRect = options[0].getBoundingClientRect();
|
|
13937
|
+
var lastRect = options[options.length - 1].getBoundingClientRect();
|
|
13938
|
+
var minCenter = Math.min(firstRect.top + firstRect.height / 2, lastRect.top + lastRect.height / 2);
|
|
13939
|
+
var maxCenter = Math.max(firstRect.top + firstRect.height / 2, lastRect.top + lastRect.height / 2);
|
|
13940
|
+
if (maxCenter < minCenter) maxCenter = minCenter;
|
|
13941
|
+
var centerY = Math.max(minCenter, Math.min(maxCenter, event.clientY));
|
|
13942
|
+
var edge = liquidInteractionEdgePx();
|
|
13943
|
+
float.style.left = (rect.left - edge) + 'px';
|
|
13944
|
+
float.style.top = (centerY - rect.height / 2 - edge) + 'px';
|
|
13945
|
+
float.style.width = (rect.width + edge * 2) + 'px';
|
|
13946
|
+
float.style.height = (rect.height + edge * 2) + 'px';
|
|
13947
|
+
}
|
|
13948
|
+
function detachWindowTracking() {
|
|
13949
|
+
window.removeEventListener('pointermove', onPointerMove, true);
|
|
13950
|
+
window.removeEventListener('pointerup', onPointerUp, true);
|
|
13951
|
+
window.removeEventListener('pointercancel', onPointerCancel, true);
|
|
13952
|
+
}
|
|
13953
|
+
function clear() {
|
|
13954
|
+
detachWindowTracking();
|
|
13955
|
+
if (source) source.classList.remove('liquid-selection-source');
|
|
13956
|
+
stopLiquidMotionTracking(float);
|
|
13957
|
+
if (float && float._liquidLandingTimer) clearTimeout(float._liquidLandingTimer);
|
|
13958
|
+
if (float && float._releaseKyantGlass) float._releaseKyantGlass();
|
|
13959
|
+
if (float && float.parentNode) float.parentNode.removeChild(float);
|
|
13960
|
+
generation += 1;
|
|
13961
|
+
pointer = source = target = float = null;
|
|
13962
|
+
}
|
|
13963
|
+
function onPointerMove(event) {
|
|
13964
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
13965
|
+
if (!gestureMoved && Math.hypot(event.clientX - pressX, event.clientY - pressY) >= 4) {
|
|
13966
|
+
gestureMoved = true;
|
|
13967
|
+
}
|
|
13968
|
+
event.preventDefault();
|
|
13969
|
+
positionAlongPointer(event);
|
|
13970
|
+
}
|
|
13971
|
+
function onPointerUp(event) {
|
|
13972
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
13973
|
+
var commit = target;
|
|
13974
|
+
detachWindowTracking();
|
|
13975
|
+
try { menu.releasePointerCapture(pointer); } catch (_) {}
|
|
13976
|
+
pointer = null;
|
|
13977
|
+
if (commit) position(commit);
|
|
13978
|
+
waitForLiquidSelectionArrival(float, function() {
|
|
13979
|
+
landLiquidSelectionFloat(float, clear, function() {
|
|
13980
|
+
if (commit && commit.isConnected) commit.click();
|
|
13981
|
+
});
|
|
13982
|
+
});
|
|
13983
|
+
}
|
|
13984
|
+
function onPointerCancel(event) {
|
|
13985
|
+
if (pointer === null || event.pointerId === pointer) clear();
|
|
13986
|
+
}
|
|
13987
|
+
menu.addEventListener('pointerdown', function(event) {
|
|
13988
|
+
var pressed = optionAt(event);
|
|
13989
|
+
if (!pressed || event.button !== 0) return;
|
|
13990
|
+
event.preventDefault();
|
|
13991
|
+
target = pressed;
|
|
13992
|
+
pointer = event.pointerId;
|
|
13993
|
+
pressX = event.clientX;
|
|
13994
|
+
pressY = event.clientY;
|
|
13995
|
+
gestureMoved = false;
|
|
13996
|
+
var captureGeneration = ++generation;
|
|
13997
|
+
menu.setPointerCapture(pointer);
|
|
13998
|
+
window.addEventListener('pointermove', onPointerMove, true);
|
|
13999
|
+
window.addEventListener('pointerup', onPointerUp, true);
|
|
14000
|
+
window.addEventListener('pointercancel', onPointerCancel, true);
|
|
14001
|
+
if (float && float.isConnected) {
|
|
14002
|
+
if (float._liquidLandingTimer) clearTimeout(float._liquidLandingTimer);
|
|
14003
|
+
float._liquidLandingTimer = 0;
|
|
14004
|
+
float.classList.remove('liquid-selection-landing');
|
|
14005
|
+
float.classList.add('visible');
|
|
14006
|
+
position(target);
|
|
14007
|
+
} else {
|
|
14008
|
+
var selected = menu.querySelector('button.model-select-menu-option.selected, button.newmark-select-option.selected');
|
|
14009
|
+
source = selected || pressed;
|
|
14010
|
+
source.classList.add('liquid-selection-source');
|
|
14011
|
+
float = document.createElement('div');
|
|
14012
|
+
float.className = 'liquid-selection-float liquid-selection-primed';
|
|
14013
|
+
mountLiquidViewportFloat(float);
|
|
14014
|
+
position(source);
|
|
14015
|
+
showLiquidViewportFloat(float);
|
|
14016
|
+
position(source);
|
|
14017
|
+
float.getBoundingClientRect();
|
|
14018
|
+
requestAnimationFrame(function() {
|
|
14019
|
+
if (!float) return;
|
|
14020
|
+
float.classList.remove('liquid-selection-primed');
|
|
14021
|
+
float.classList.add('visible');
|
|
14022
|
+
requestAnimationFrame(function() { if (float) position(target); });
|
|
14023
|
+
});
|
|
14024
|
+
}
|
|
14025
|
+
captureLiquidBackdropFrame().then(function(backdropFrame) {
|
|
14026
|
+
if (generation === captureGeneration && float && float.isConnected) attachKyantLiquidRenderer(float, backdropFrame);
|
|
14027
|
+
}).catch(function() {});
|
|
14028
|
+
});
|
|
14029
|
+
menu.addEventListener('click', function(event) {
|
|
14030
|
+
if (!event.isTrusted || !float || !float.isConnected) return;
|
|
14031
|
+
event.preventDefault();
|
|
14032
|
+
event.stopImmediatePropagation();
|
|
14033
|
+
}, true);
|
|
14034
|
+
}
|
|
14035
|
+
|
|
14036
|
+
// The same single floating capsule is used by the left workspace rail and
|
|
14037
|
+
// right sidebar tabs. Delegation keeps it valid while either rail rerenders.
|
|
14038
|
+
function wireLiquidRailInteractions(container, selector) {
|
|
14039
|
+
if (!container || container.dataset.liquidRailInteractions === 'true') return;
|
|
14040
|
+
container.dataset.liquidRailInteractions = 'true';
|
|
14041
|
+
var pointer = null;
|
|
14042
|
+
var source = null;
|
|
14043
|
+
var target = null;
|
|
14044
|
+
var float = null;
|
|
14045
|
+
var generation = 0;
|
|
14046
|
+
var pressX = 0;
|
|
14047
|
+
var pressY = 0;
|
|
14048
|
+
var gestureMoved = false;
|
|
14049
|
+
function itemAt(event) {
|
|
14050
|
+
var hit = event && document.elementFromPoint ? document.elementFromPoint(event.clientX, event.clientY) : event.target;
|
|
14051
|
+
var node = hit && hit.closest ? hit.closest(selector) : null;
|
|
14052
|
+
return node && container.contains(node) && !node.disabled ? node : null;
|
|
14053
|
+
}
|
|
14054
|
+
function position(item) {
|
|
14055
|
+
if (!item || !float) return;
|
|
14056
|
+
if (float.classList.contains('liquid-selection-viewport')) {
|
|
14057
|
+
var rect = item.getBoundingClientRect();
|
|
14058
|
+
var edge = liquidInteractionEdgePx();
|
|
14059
|
+
keepLiquidFloatExpansionInsideViewport(float, rect);
|
|
14060
|
+
float._liquidTargetRect = { left: rect.left - edge, top: rect.top - edge, width: rect.width + edge * 2, height: rect.height + edge * 2 };
|
|
14061
|
+
float.style.left = (rect.left - edge) + 'px';
|
|
14062
|
+
float.style.top = (rect.top - edge) + 'px';
|
|
14063
|
+
float.style.width = (rect.width + edge * 2) + 'px';
|
|
14064
|
+
float.style.height = (rect.height + edge * 2) + 'px';
|
|
14065
|
+
if (float._renderKyantGlass) float._renderKyantGlass({
|
|
14066
|
+
left: rect.left - edge, top: rect.top - edge,
|
|
14067
|
+
width: rect.width + edge * 2, height: rect.height + edge * 2
|
|
14068
|
+
}, true);
|
|
14069
|
+
return;
|
|
14070
|
+
}
|
|
14071
|
+
var edge = liquidInteractionEdgePx();
|
|
14072
|
+
float.style.left = (item.offsetLeft - edge) + 'px';
|
|
14073
|
+
float.style.top = (item.offsetTop - edge) + 'px';
|
|
14074
|
+
float.style.width = (item.offsetWidth + edge * 2) + 'px';
|
|
14075
|
+
float.style.height = (item.offsetHeight + edge * 2) + 'px';
|
|
14076
|
+
}
|
|
14077
|
+
function positionAlongPointer(event) {
|
|
14078
|
+
if (!float || !event) return;
|
|
14079
|
+
var items = Array.from(container.querySelectorAll(selector))
|
|
14080
|
+
.filter(function(item) { return !item.disabled && item.getClientRects().length; });
|
|
14081
|
+
if (!items.length) return;
|
|
14082
|
+
var rects = items.map(function(item) { return item.getBoundingClientRect(); });
|
|
14083
|
+
var centersX = rects.map(function(rect) { return rect.left + rect.width / 2; });
|
|
14084
|
+
var centersY = rects.map(function(rect) { return rect.top + rect.height / 2; });
|
|
14085
|
+
var horizontal = Math.max.apply(null, centersX) - Math.min.apply(null, centersX) >
|
|
14086
|
+
Math.max.apply(null, centersY) - Math.min.apply(null, centersY);
|
|
14087
|
+
var pointerAxis = horizontal ? event.clientX : event.clientY;
|
|
14088
|
+
var centers = horizontal ? centersX : centersY;
|
|
14089
|
+
var nearestIndex = 0;
|
|
14090
|
+
var nearestDistance = Infinity;
|
|
14091
|
+
centers.forEach(function(center, index) {
|
|
14092
|
+
var distance = Math.abs(pointerAxis - center);
|
|
14093
|
+
if (distance < nearestDistance) { nearestIndex = index; nearestDistance = distance; }
|
|
14094
|
+
});
|
|
14095
|
+
target = items[nearestIndex];
|
|
14096
|
+
var rect = rects[nearestIndex];
|
|
14097
|
+
var minCenter = Math.min.apply(null, centers);
|
|
14098
|
+
var maxCenter = Math.max.apply(null, centers);
|
|
14099
|
+
var projectedCenter = Math.max(minCenter, Math.min(maxCenter, pointerAxis));
|
|
14100
|
+
var edge = liquidInteractionEdgePx();
|
|
14101
|
+
var left = horizontal ? projectedCenter - rect.width / 2 - edge : rect.left - edge;
|
|
14102
|
+
var top = horizontal ? rect.top - edge : projectedCenter - rect.height / 2 - edge;
|
|
14103
|
+
float.style.left = left + 'px';
|
|
14104
|
+
float.style.top = top + 'px';
|
|
14105
|
+
float.style.width = (rect.width + edge * 2) + 'px';
|
|
14106
|
+
float.style.height = (rect.height + edge * 2) + 'px';
|
|
14107
|
+
}
|
|
14108
|
+
function detachWindowTracking() {
|
|
14109
|
+
window.removeEventListener('pointermove', onPointerMove, true);
|
|
14110
|
+
window.removeEventListener('pointerup', onPointerUp, true);
|
|
14111
|
+
window.removeEventListener('pointercancel', onPointerCancel, true);
|
|
14112
|
+
}
|
|
14113
|
+
function clear() {
|
|
14114
|
+
detachWindowTracking();
|
|
14115
|
+
if (source) source.classList.remove('liquid-selection-source');
|
|
14116
|
+
stopLiquidMotionTracking(float);
|
|
14117
|
+
if (float && float._liquidLandingTimer) clearTimeout(float._liquidLandingTimer);
|
|
14118
|
+
if (float && float._releaseKyantGlass) float._releaseKyantGlass();
|
|
14119
|
+
if (float && float.parentNode) float.parentNode.removeChild(float);
|
|
14120
|
+
generation += 1;
|
|
14121
|
+
pointer = source = target = float = null;
|
|
14122
|
+
window.setLiquidSidebarGestureLock('rail:' + (container.id || selector), false);
|
|
14123
|
+
}
|
|
14124
|
+
function onPointerMove(event) {
|
|
14125
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
14126
|
+
if (!gestureMoved && Math.hypot(event.clientX - pressX, event.clientY - pressY) >= 4) {
|
|
14127
|
+
gestureMoved = true;
|
|
14128
|
+
}
|
|
14129
|
+
event.preventDefault();
|
|
14130
|
+
positionAlongPointer(event);
|
|
14131
|
+
}
|
|
14132
|
+
function onPointerUp(event) {
|
|
14133
|
+
if (pointer === null || event.pointerId !== pointer) return;
|
|
14134
|
+
var commit = target;
|
|
14135
|
+
detachWindowTracking();
|
|
14136
|
+
try { container.releasePointerCapture(pointer); } catch (_) {}
|
|
14137
|
+
pointer = null;
|
|
14138
|
+
if (commit) {
|
|
14139
|
+
position(commit);
|
|
14140
|
+
}
|
|
14141
|
+
waitForLiquidSelectionArrival(float, function() {
|
|
14142
|
+
landLiquidSelectionFloat(float, clear, function() {
|
|
14143
|
+
if (!commit || !commit.isConnected) return;
|
|
14144
|
+
if (container.id === 'left-tool-surface' || container.id === 'left-thumb') {
|
|
14145
|
+
container.querySelectorAll('.liquid-tool-active').forEach(function(item) { item.classList.remove('liquid-tool-active'); });
|
|
14146
|
+
commit.classList.add('liquid-tool-active');
|
|
14147
|
+
}
|
|
14148
|
+
commit.click();
|
|
14149
|
+
});
|
|
14150
|
+
});
|
|
14151
|
+
}
|
|
14152
|
+
function onPointerCancel(event) {
|
|
14153
|
+
if (pointer === null || event.pointerId === pointer) clear();
|
|
14154
|
+
}
|
|
14155
|
+
container.addEventListener('pointerdown', function(event) {
|
|
14156
|
+
if (window.liquidSidebarGesturesLocked() && !(float && float.isConnected)) return;
|
|
14157
|
+
var pressed = itemAt(event);
|
|
14158
|
+
if (!pressed || event.button !== 0) return;
|
|
14159
|
+
event.preventDefault();
|
|
14160
|
+
target = pressed;
|
|
14161
|
+
pointer = event.pointerId;
|
|
14162
|
+
pressX = event.clientX;
|
|
14163
|
+
pressY = event.clientY;
|
|
14164
|
+
gestureMoved = false;
|
|
14165
|
+
var captureGeneration = ++generation;
|
|
14166
|
+
window.setLiquidSidebarGestureLock('rail:' + (container.id || selector), true);
|
|
14167
|
+
container.setPointerCapture(pointer);
|
|
14168
|
+
window.addEventListener('pointermove', onPointerMove, true);
|
|
14169
|
+
window.addEventListener('pointerup', onPointerUp, true);
|
|
14170
|
+
window.addEventListener('pointercancel', onPointerCancel, true);
|
|
14171
|
+
if (float && float.isConnected) {
|
|
14172
|
+
if (float._liquidLandingTimer) clearTimeout(float._liquidLandingTimer);
|
|
14173
|
+
float._liquidLandingTimer = 0;
|
|
14174
|
+
float.classList.remove('liquid-selection-landing');
|
|
14175
|
+
float.classList.add('visible');
|
|
14176
|
+
position(target);
|
|
14177
|
+
} else {
|
|
14178
|
+
source = container.querySelector('.liquid-tool-active') || container.querySelector(selector + '.active') || pressed;
|
|
14179
|
+
source.classList.add('liquid-selection-source');
|
|
14180
|
+
float = document.createElement('div');
|
|
14181
|
+
float.className = 'liquid-selection-float liquid-selection-primed';
|
|
14182
|
+
mountLiquidViewportFloat(float);
|
|
14183
|
+
position(source);
|
|
14184
|
+
showLiquidViewportFloat(float);
|
|
14185
|
+
position(source);
|
|
14186
|
+
float.getBoundingClientRect();
|
|
14187
|
+
requestAnimationFrame(function() {
|
|
14188
|
+
if (!float) return;
|
|
14189
|
+
float.classList.remove('liquid-selection-primed');
|
|
14190
|
+
float.classList.add('visible');
|
|
14191
|
+
requestAnimationFrame(function() { if (float) position(target); });
|
|
14192
|
+
});
|
|
14193
|
+
}
|
|
14194
|
+
captureLiquidBackdropFrame().then(function(backdropFrame) {
|
|
14195
|
+
if (generation === captureGeneration && float && float.isConnected) attachKyantLiquidRenderer(float, backdropFrame);
|
|
14196
|
+
}).catch(function() {});
|
|
14197
|
+
});
|
|
14198
|
+
container.addEventListener('click', function(event) {
|
|
14199
|
+
if (!event.isTrusted || !float || !float.isConnected) return;
|
|
14200
|
+
event.preventDefault();
|
|
14201
|
+
event.stopImmediatePropagation();
|
|
14202
|
+
}, true);
|
|
14203
|
+
}
|
|
14204
|
+
|
|
14205
|
+
requestAnimationFrame(function() {
|
|
14206
|
+
wireLiquidRailInteractions(document.getElementById('mode-toggle'), '.mode-toggle-btn[data-mode]');
|
|
14207
|
+
wireLiquidRailInteractions(document.getElementById('left-tool-surface'), '.left-nav-icon');
|
|
14208
|
+
wireLiquidRailInteractions(document.getElementById('left-thumb'), '.lt-item');
|
|
14209
|
+
wireLiquidRailInteractions(document.getElementById('left-ws-list'), '.left-ws-item');
|
|
14210
|
+
wireLiquidRailInteractions(document.getElementById('right-tabs'), '.tab-btn[data-tab]');
|
|
14211
|
+
});
|
|
14212
|
+
|
|
14213
|
+
// Checkbox switches use one pointer-captured glass thumb that follows the
|
|
14214
|
+
// pointer continuously, then commits to the nearest binary stop on release.
|
|
14215
|
+
document.addEventListener('pointerdown', function(event) {
|
|
14216
|
+
var input = event.target && event.target.closest ? event.target.closest('input[type="checkbox"]') : null;
|
|
14217
|
+
if (!input || input.disabled || event.button !== 0) return;
|
|
14218
|
+
event.preventDefault();
|
|
14219
|
+
var rect = input.getBoundingClientRect();
|
|
14220
|
+
var pointer = event.pointerId;
|
|
14221
|
+
var fraction = input.checked ? 1 : 0;
|
|
14222
|
+
var startX = event.clientX;
|
|
14223
|
+
var startY = event.clientY;
|
|
14224
|
+
var dragging = false;
|
|
14225
|
+
var canceledToScroll = false;
|
|
14226
|
+
var dragThreshold = event.pointerType === 'mouse' ? 4 : 8;
|
|
14227
|
+
input.dataset.liquidSuppressClick = 'true';
|
|
14228
|
+
clearTimeout(input._liquidSuppressClickTimer);
|
|
14229
|
+
input._liquidSuppressClickTimer = setTimeout(function() {
|
|
14230
|
+
delete input.dataset.liquidSuppressClick;
|
|
14231
|
+
delete input._liquidSuppressClickTimer;
|
|
14232
|
+
}, 700);
|
|
14233
|
+
function update(clientX) {
|
|
14234
|
+
fraction = ((clientX - rect.left - 12) / Math.max(1, rect.width - 24));
|
|
14235
|
+
fraction = Math.max(0, Math.min(1, fraction));
|
|
14236
|
+
input.style.setProperty('--liquid-switch-x', (18 * fraction) + 'px');
|
|
14237
|
+
input.style.setProperty('--liquid-switch-progress', (100 * fraction) + '%');
|
|
14238
|
+
}
|
|
14239
|
+
function finish(commit) {
|
|
14240
|
+
input.removeEventListener('pointermove', move);
|
|
14241
|
+
input.removeEventListener('pointerup', up);
|
|
14242
|
+
input.removeEventListener('pointercancel', cancel);
|
|
14243
|
+
input.classList.remove('liquid-switch-dragging');
|
|
14244
|
+
input.style.removeProperty('--liquid-switch-x');
|
|
14245
|
+
input.style.removeProperty('--liquid-switch-progress');
|
|
14246
|
+
try { input.releasePointerCapture(pointer); } catch (_) {}
|
|
14247
|
+
if (!commit || canceledToScroll) return;
|
|
14248
|
+
var next = dragging ? fraction >= 0.5 : !input.checked;
|
|
14249
|
+
var changed = input.checked !== next;
|
|
14250
|
+
input.checked = next;
|
|
14251
|
+
if (changed) {
|
|
14252
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
14253
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
14254
|
+
}
|
|
14255
|
+
}
|
|
14256
|
+
function move(moveEvent) {
|
|
14257
|
+
if (moveEvent.pointerId !== pointer) return;
|
|
14258
|
+
if (!dragging) {
|
|
14259
|
+
var deltaX = moveEvent.clientX - startX;
|
|
14260
|
+
var deltaY = moveEvent.clientY - startY;
|
|
14261
|
+
if (Math.hypot(deltaX, deltaY) < dragThreshold) return;
|
|
14262
|
+
if (Math.abs(deltaY) > Math.abs(deltaX)) {
|
|
14263
|
+
canceledToScroll = true;
|
|
14264
|
+
return;
|
|
14265
|
+
}
|
|
14266
|
+
dragging = true;
|
|
14267
|
+
}
|
|
14268
|
+
moveEvent.preventDefault();
|
|
14269
|
+
update(moveEvent.clientX);
|
|
14270
|
+
}
|
|
14271
|
+
function up(upEvent) {
|
|
14272
|
+
if (upEvent.pointerId === pointer) finish(true);
|
|
14273
|
+
}
|
|
14274
|
+
function cancel(cancelEvent) {
|
|
14275
|
+
if (cancelEvent.pointerId === pointer) finish(false);
|
|
14276
|
+
}
|
|
14277
|
+
input.classList.add('liquid-switch-dragging');
|
|
14278
|
+
input.setPointerCapture(pointer);
|
|
14279
|
+
input.addEventListener('pointermove', move);
|
|
14280
|
+
input.addEventListener('pointerup', up);
|
|
14281
|
+
input.addEventListener('pointercancel', cancel);
|
|
14282
|
+
}, true);
|
|
14283
|
+
document.addEventListener('click', function(event) {
|
|
14284
|
+
var input = event.target && event.target.closest ? event.target.closest('input[type="checkbox"]') : null;
|
|
14285
|
+
if (!input || input.dataset.liquidSuppressClick !== 'true') return;
|
|
14286
|
+
clearTimeout(input._liquidSuppressClickTimer);
|
|
14287
|
+
delete input.dataset.liquidSuppressClick;
|
|
14288
|
+
delete input._liquidSuppressClickTimer;
|
|
14289
|
+
event.preventDefault();
|
|
14290
|
+
event.stopImmediatePropagation();
|
|
14291
|
+
}, true);
|
|
14292
|
+
|
|
14293
|
+
// Glass-strength range mirrors the binary liquid switch: its existing color
|
|
14294
|
+
// thumb lifts into one glass capsule, follows the pointer continuously, then
|
|
14295
|
+
// falls back to the color thumb and persists the released value.
|
|
14296
|
+
document.addEventListener('pointerdown', function(event) {
|
|
14297
|
+
var input = event.target && event.target.closest ? event.target.closest('#settings-glass-opacity') : null;
|
|
14298
|
+
if (!input || input.disabled || event.button !== 0) return;
|
|
14299
|
+
event.preventDefault();
|
|
14300
|
+
var rect = input.getBoundingClientRect();
|
|
14301
|
+
var pointer = event.pointerId;
|
|
14302
|
+
var minimum = Number(input.min || 0);
|
|
14303
|
+
var maximum = Number(input.max || 100);
|
|
14304
|
+
var step = Math.max(0.0001, Number(input.step || 1));
|
|
14305
|
+
var initialValue = input.value;
|
|
14306
|
+
function update(clientX) {
|
|
14307
|
+
var fraction = Math.max(0, Math.min(1, (clientX - rect.left) / Math.max(1, rect.width)));
|
|
14308
|
+
var raw = minimum + fraction * (maximum - minimum);
|
|
14309
|
+
var next = Math.min(maximum, Math.max(minimum, Math.round((raw - minimum) / step) * step + minimum));
|
|
14310
|
+
input.value = String(next);
|
|
14311
|
+
input.style.setProperty('--liquid-range-progress', (fraction * 100) + '%');
|
|
14312
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
14313
|
+
}
|
|
14314
|
+
function finish(commit) {
|
|
14315
|
+
input.removeEventListener('pointermove', move);
|
|
14316
|
+
input.removeEventListener('pointerup', up);
|
|
14317
|
+
input.removeEventListener('pointercancel', cancel);
|
|
14318
|
+
input.classList.remove('liquid-range-dragging');
|
|
14319
|
+
try { input.releasePointerCapture(pointer); } catch (_) {}
|
|
14320
|
+
if (!commit) {
|
|
14321
|
+
input.value = initialValue;
|
|
14322
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
14323
|
+
return;
|
|
14324
|
+
}
|
|
14325
|
+
input.dataset.liquidSuppressRangeClick = 'true';
|
|
14326
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
14327
|
+
}
|
|
14328
|
+
function move(moveEvent) { if (moveEvent.pointerId === pointer) { moveEvent.preventDefault(); update(moveEvent.clientX); } }
|
|
14329
|
+
function up(upEvent) { if (upEvent.pointerId === pointer) finish(true); }
|
|
14330
|
+
function cancel(cancelEvent) { if (cancelEvent.pointerId === pointer) finish(false); }
|
|
14331
|
+
input.classList.add('liquid-range-dragging');
|
|
14332
|
+
update(event.clientX);
|
|
14333
|
+
input.setPointerCapture(pointer);
|
|
14334
|
+
input.addEventListener('pointermove', move);
|
|
14335
|
+
input.addEventListener('pointerup', up);
|
|
14336
|
+
input.addEventListener('pointercancel', cancel);
|
|
14337
|
+
}, true);
|
|
14338
|
+
document.addEventListener('click', function(event) {
|
|
14339
|
+
var input = event.target && event.target.closest ? event.target.closest('#settings-glass-opacity') : null;
|
|
14340
|
+
if (!input || input.dataset.liquidSuppressRangeClick !== 'true') return;
|
|
14341
|
+
delete input.dataset.liquidSuppressRangeClick;
|
|
14342
|
+
event.preventDefault();
|
|
14343
|
+
event.stopImmediatePropagation();
|
|
14344
|
+
}, true);
|
|
14345
|
+
|
|
12698
14346
|
window.syncModelSelectSurface = function() {
|
|
12699
14347
|
var sel = els['model-select'] || document.getElementById('model-select');
|
|
12700
14348
|
var label = document.getElementById('model-select-label');
|
|
@@ -12716,6 +14364,7 @@ window.syncModelSelectSurface = function() {
|
|
|
12716
14364
|
}
|
|
12717
14365
|
});
|
|
12718
14366
|
menu.innerHTML = html;
|
|
14367
|
+
wireLiquidMenuInteractions(menu);
|
|
12719
14368
|
};
|
|
12720
14369
|
|
|
12721
14370
|
document.addEventListener('pointerdown', function(event) {
|
|
@@ -12727,6 +14376,7 @@ window.closeNewmarkSelect = function(shell, options) {
|
|
|
12727
14376
|
shell = shell || document.querySelector('.newmark-select-shell.open');
|
|
12728
14377
|
if (!shell) return;
|
|
12729
14378
|
shell.classList.remove('open');
|
|
14379
|
+
window.setLiquidSidebarGestureLock('popup:select:' + (shell.dataset.selectId || 'anonymous'), false);
|
|
12730
14380
|
var menu = shell.querySelector('.newmark-select-menu');
|
|
12731
14381
|
if (menu && typeof menu.hidePopover === 'function' && menu.matches(':popover-open')) menu.hidePopover();
|
|
12732
14382
|
var button = shell.querySelector('.newmark-select-button');
|
|
@@ -12810,6 +14460,7 @@ window.syncNewmarkSelect = function(select) {
|
|
|
12810
14460
|
}
|
|
12811
14461
|
});
|
|
12812
14462
|
menu.innerHTML = html;
|
|
14463
|
+
wireLiquidMenuInteractions(menu);
|
|
12813
14464
|
};
|
|
12814
14465
|
|
|
12815
14466
|
window.enhanceNewmarkSelect = function(select) {
|
|
@@ -12850,7 +14501,7 @@ window.enhanceNewmarkSelect = function(select) {
|
|
|
12850
14501
|
button.setAttribute('aria-label', String(accessibleLabel).trim());
|
|
12851
14502
|
button.innerHTML = '<span class="newmark-select-button-label"></span>' + iconSvg('chevron-down', '', 'tiny');
|
|
12852
14503
|
var menu = document.createElement('div');
|
|
12853
|
-
menu.className = 'model-select-menu newmark-select-menu';
|
|
14504
|
+
menu.className = 'model-select-menu newmark-select-menu liquid-glass liquid-glass-carrier';
|
|
12854
14505
|
menu.setAttribute('role', 'listbox');
|
|
12855
14506
|
menu.setAttribute('popover', 'manual');
|
|
12856
14507
|
select.parentNode.insertBefore(shell, select);
|
|
@@ -12871,6 +14522,7 @@ window.enhanceNewmarkSelect = function(select) {
|
|
|
12871
14522
|
}
|
|
12872
14523
|
window.syncNewmarkSelect(select);
|
|
12873
14524
|
shell.classList.add('open');
|
|
14525
|
+
window.setLiquidSidebarGestureLock('popup:select:' + (shell.dataset.selectId || 'anonymous'), true);
|
|
12874
14526
|
button.setAttribute('aria-expanded', 'true');
|
|
12875
14527
|
if (opening) {
|
|
12876
14528
|
if (typeof menu.showPopover === 'function' && !menu.matches(':popover-open')) menu.showPopover();
|
|
@@ -13169,6 +14821,47 @@ function backendQueueForTarget(target) {
|
|
|
13169
14821
|
return normalizeBackendQueue(state.backendQueuesByTarget[runtimeKeyFor(owner.workspaceId, owner.conversationId)]);
|
|
13170
14822
|
}
|
|
13171
14823
|
|
|
14824
|
+
function queueItemsForTarget(target) {
|
|
14825
|
+
var owner = normalizeQueuedConversationTarget(target || currentConversationTarget());
|
|
14826
|
+
if (!owner) return [];
|
|
14827
|
+
if (!state.queueItemsByTarget) state.queueItemsByTarget = {};
|
|
14828
|
+
return state.queueItemsByTarget[runtimeKeyFor(owner.workspaceId, owner.conversationId)] || [];
|
|
14829
|
+
}
|
|
14830
|
+
|
|
14831
|
+
function setQueueItemsForTarget(items, target) {
|
|
14832
|
+
var owner = normalizeQueuedConversationTarget(target || currentConversationTarget());
|
|
14833
|
+
if (!owner) return;
|
|
14834
|
+
if (!state.queueItemsByTarget) state.queueItemsByTarget = {};
|
|
14835
|
+
state.queueItemsByTarget[runtimeKeyFor(owner.workspaceId, owner.conversationId)] = (Array.isArray(items) ? items : []).map(function(item) {
|
|
14836
|
+
return {
|
|
14837
|
+
id: String(item && item.id || ''),
|
|
14838
|
+
text: String(item && item.text || ''),
|
|
14839
|
+
queueMode: String(item && item.queueMode || 'followUp'),
|
|
14840
|
+
requestedMode: String(item && item.requestedMode || 'build'),
|
|
14841
|
+
goalObjective: String(item && item.goalObjective || ''),
|
|
14842
|
+
runId: String(item && item.runId || ''),
|
|
14843
|
+
createdAt: String(item && item.createdAt || ''),
|
|
14844
|
+
};
|
|
14845
|
+
});
|
|
14846
|
+
}
|
|
14847
|
+
|
|
14848
|
+
function queueItemIdForText(text, target) {
|
|
14849
|
+
var items = queueItemsForTarget(target);
|
|
14850
|
+
var normalized = String(text || '');
|
|
14851
|
+
for (var i = 0; i < items.length; i++) {
|
|
14852
|
+
if (String(items[i].text || '') === normalized) return items[i].id;
|
|
14853
|
+
}
|
|
14854
|
+
return '';
|
|
14855
|
+
}
|
|
14856
|
+
|
|
14857
|
+
function syncQueueItemsFromSnapshot(snapshot, target) {
|
|
14858
|
+
if (snapshot && Array.isArray(snapshot.queueItems)) setQueueItemsForTarget(snapshot.queueItems, target);
|
|
14859
|
+
}
|
|
14860
|
+
|
|
14861
|
+
window.queueAction = function(action, input) {
|
|
14862
|
+
if (!api.queueAction) return Promise.resolve({ ok: false, error: 'queueAction unavailable' });
|
|
14863
|
+
return api.queueAction(action, input);
|
|
14864
|
+
};
|
|
13172
14865
|
function setBackendQueueForTarget(queue, target) {
|
|
13173
14866
|
var owner = normalizeQueuedConversationTarget(target || currentConversationTarget());
|
|
13174
14867
|
var normalized = normalizeBackendQueue(queue);
|
|
@@ -13248,19 +14941,22 @@ window.renderQueuePanel = function() {
|
|
|
13248
14941
|
if (queueIndex === state._editingQueueIndex) continue;
|
|
13249
14942
|
var request = state.nextQueueRequests[queueIndex];
|
|
13250
14943
|
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
var
|
|
13255
|
-
|
|
13256
|
-
|
|
13257
|
-
|
|
13258
|
-
|
|
13259
|
-
|
|
13260
|
-
|
|
14944
|
+
// dev-0.5.5: a backend-managed item is editable/deletable when the kernel
|
|
14945
|
+
// exposed its stable queueItem id (mobile-originated queue entries now
|
|
14946
|
+
// carry ids through queueItems). Items without an id stay locked.
|
|
14947
|
+
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[queueIndex] || '', currentConversationTarget()) : '';
|
|
14948
|
+
var operable = !backendManaged || !!queueItemId;
|
|
14949
|
+
var dragAttributes = operable
|
|
14950
|
+
? ' draggable="true" ondragstart="window.startQueueDrag(event,' + queueIndex + ')" ondragover="window.overQueueDrag(event,' + queueIndex + ')" ondrop="window.dropQueueDrag(event,' + queueIndex + ')" ondragend="window.endQueueDrag(event)"'
|
|
14951
|
+
: ' draggable="false"';
|
|
14952
|
+
var dragHandle = operable
|
|
14953
|
+
? '<button class="queue-drag" title="' + escAttr(t('queue.title')) + '">' + iconSvg('grip-vertical', t('queue.title'), 'tiny') + '</button>'
|
|
14954
|
+
: '<span class="queue-drag" aria-hidden="true">' + iconSvg('grip-vertical', t('queue.title'), 'tiny') + '</span>';
|
|
14955
|
+
var inputAttributes = ' readonly aria-readonly="true"';
|
|
14956
|
+
var actions = operable ?
|
|
13261
14957
|
'<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
14958
|
'<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>';
|
|
14959
|
+
'<button class="stack-icon-btn danger" onclick="window.deleteQueueItem(' + queueIndex + ')" title="' + escAttr(t('common.delete')) + '">' + iconSvg('trash', t('common.delete'), 'tiny') + '</button>' : '';
|
|
13264
14960
|
html += '<div class="queue-item"' + dragAttributes + '>' +
|
|
13265
14961
|
dragHandle +
|
|
13266
14962
|
'<input class="queue-edit" value="' + escAttr(state.nextQueue[queueIndex] || '') + '"' + inputAttributes + '>' +
|
|
@@ -13293,9 +14989,11 @@ window.editQueueItem = function(idx, value) {
|
|
|
13293
14989
|
|
|
13294
14990
|
window.focusQueueItem = function(idx) {
|
|
13295
14991
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
13296
|
-
if (
|
|
14992
|
+
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) return;
|
|
14993
|
+
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
14994
|
+
if (backendManaged && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget())) return;
|
|
13297
14995
|
state._editingQueueIndex = idx;
|
|
13298
|
-
state.pendingInputEdit = { kind: 'queue', index: idx, request: request, target: currentConversationTarget() };
|
|
14996
|
+
state.pendingInputEdit = { kind: 'queue', index: idx, request: request, target: currentConversationTarget(), originalText: String(state.nextQueue[idx] || '') };
|
|
13299
14997
|
els.prompt.value = String(state.nextQueue[idx] || '');
|
|
13300
14998
|
state.promptAttachments = (request.images || []).map(function(image, imageIndex) {
|
|
13301
14999
|
return { id: 'queue-edit-' + imageIndex + '-' + Date.now(), name: image.name || 'queued image', type: image.type || 'image/png', dataUrl: image.dataUrl };
|
|
@@ -13309,13 +15007,23 @@ window.focusQueueItem = function(idx) {
|
|
|
13309
15007
|
window.deleteQueueItem = function(idx) {
|
|
13310
15008
|
if (!state.nextQueue) return;
|
|
13311
15009
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
13312
|
-
if (
|
|
15010
|
+
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) return;
|
|
15011
|
+
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
15012
|
+
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget()) : '';
|
|
15013
|
+
if (backendManaged && !queueItemId) return;
|
|
13313
15014
|
if (!state.queueHiddenItems) state.queueHiddenItems = {};
|
|
13314
15015
|
if (state.nextQueue[idx]) state.queueHiddenItems[queueHiddenItemKey(state.nextQueue[idx], currentConversationTarget())] = true;
|
|
13315
15016
|
state.nextQueue.splice(idx, 1);
|
|
13316
15017
|
if (state.nextQueueRequests) state.nextQueueRequests.splice(idx, 1);
|
|
13317
15018
|
refreshNextPromptForTarget(currentConversationTarget());
|
|
13318
15019
|
window.renderInputStack();
|
|
15020
|
+
if (backendManaged) {
|
|
15021
|
+
window.queueAction('queue_delete', { id: queueItemId }).then(function(result) {
|
|
15022
|
+
if (!result || result.ok === false) {
|
|
15023
|
+
showUiNotice(result && result.error ? result.error : (currentLang() === 'zh' ? '删除队列项失败' : 'Failed to delete queue item'), 'error', 'queue-delete-' + queueItemId);
|
|
15024
|
+
}
|
|
15025
|
+
});
|
|
15026
|
+
}
|
|
13319
15027
|
};
|
|
13320
15028
|
|
|
13321
15029
|
function restoreQueueItemAfterGuideFailure(idx, text, request, target, reuseClientMessageId) {
|
|
@@ -13332,7 +15040,10 @@ function restoreQueueItemAfterGuideFailure(idx, text, request, target, reuseClie
|
|
|
13332
15040
|
window.guideQueueItem = function(idx) {
|
|
13333
15041
|
if (!state.nextQueue || !state.nextQueue[idx]) return;
|
|
13334
15042
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
13335
|
-
if (
|
|
15043
|
+
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) return;
|
|
15044
|
+
var backendManaged = queuedRequestIsBackendManaged(request);
|
|
15045
|
+
var queueItemId = backendManaged ? queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget()) : '';
|
|
15046
|
+
if (backendManaged && !queueItemId) return;
|
|
13336
15047
|
var target = normalizeQueuedConversationTarget(request.target) || currentConversationTarget();
|
|
13337
15048
|
var deliverDuringRun = isCurrentConversationRunning();
|
|
13338
15049
|
var next = state.nextQueue.splice(idx, 1)[0];
|
|
@@ -13341,6 +15052,20 @@ window.guideQueueItem = function(idx) {
|
|
|
13341
15052
|
if (next) state.queueHiddenItems[queueHiddenItemKey(next, target)] = true;
|
|
13342
15053
|
refreshNextPromptForTarget(currentConversationTarget());
|
|
13343
15054
|
window.renderInputStack();
|
|
15055
|
+
if (backendManaged) {
|
|
15056
|
+
// The kernel owns this entry; guide it there so the mobile side stays
|
|
15057
|
+
// in sync. On failure restore the local row.
|
|
15058
|
+
return window.queueAction('queue_guide', { id: queueItemId }).then(function(outcome) {
|
|
15059
|
+
if (!outcome || outcome.ok === false) {
|
|
15060
|
+
restoreQueueItemAfterGuideFailure(idx, next, nextRequest, target, true);
|
|
15061
|
+
showUiNotice(outcome && outcome.error ? outcome.error : (currentLang() === 'zh' ? 'Guide 投递失败' : 'Guide delivery failed'), 'error', 'queue-guide-' + queueItemId);
|
|
15062
|
+
}
|
|
15063
|
+
return outcome || { ok: false };
|
|
15064
|
+
}, function(error) {
|
|
15065
|
+
restoreQueueItemAfterGuideFailure(idx, next, nextRequest, target, true);
|
|
15066
|
+
return { ok: false, error: error && error.message ? error.message : String(error) };
|
|
15067
|
+
});
|
|
15068
|
+
}
|
|
13344
15069
|
if (next) {
|
|
13345
15070
|
var delivery = window.sendMessage('guide', next, {
|
|
13346
15071
|
fromQueue: true,
|
|
@@ -13365,7 +15090,11 @@ window.guideQueueItem = function(idx) {
|
|
|
13365
15090
|
|
|
13366
15091
|
window.startQueueDrag = function(event, idx) {
|
|
13367
15092
|
var request = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
13368
|
-
if (
|
|
15093
|
+
if (!queuedRequestMatchesTarget(request, currentConversationTarget())) {
|
|
15094
|
+
state.queueDragIndex = -1;
|
|
15095
|
+
return;
|
|
15096
|
+
}
|
|
15097
|
+
if (queuedRequestIsBackendManaged(request) && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget())) {
|
|
13369
15098
|
state.queueDragIndex = -1;
|
|
13370
15099
|
return;
|
|
13371
15100
|
}
|
|
@@ -13386,9 +15115,12 @@ window.dropQueueDrag = function(event, idx) {
|
|
|
13386
15115
|
if (!state.nextQueue || from < 0 || from === idx || from >= state.nextQueue.length) return;
|
|
13387
15116
|
var sourceRequest = state.nextQueueRequests && state.nextQueueRequests[from];
|
|
13388
15117
|
var destinationRequest = state.nextQueueRequests && state.nextQueueRequests[idx];
|
|
13389
|
-
if (
|
|
13390
|
-
|| !queuedRequestMatchesTarget(sourceRequest, currentConversationTarget())
|
|
15118
|
+
if (!queuedRequestMatchesTarget(sourceRequest, currentConversationTarget())
|
|
13391
15119
|
|| !queuedRequestMatchesTarget(destinationRequest, currentConversationTarget())) return;
|
|
15120
|
+
var sourceBackend = queuedRequestIsBackendManaged(sourceRequest);
|
|
15121
|
+
var destinationBackend = queuedRequestIsBackendManaged(destinationRequest);
|
|
15122
|
+
if ((sourceBackend && !queueItemIdForText(state.nextQueue[from] || '', currentConversationTarget()))
|
|
15123
|
+
|| (destinationBackend && !queueItemIdForText(state.nextQueue[idx] || '', currentConversationTarget()))) return;
|
|
13392
15124
|
var item = state.nextQueue.splice(from, 1)[0];
|
|
13393
15125
|
var requestItem = state.nextQueueRequests ? state.nextQueueRequests.splice(from, 1)[0] : null;
|
|
13394
15126
|
state.nextQueue.splice(idx, 0, item);
|
|
@@ -13396,6 +15128,17 @@ window.dropQueueDrag = function(event, idx) {
|
|
|
13396
15128
|
state.queueDragIndex = -1;
|
|
13397
15129
|
refreshNextPromptForTarget(currentConversationTarget());
|
|
13398
15130
|
window.renderInputStack();
|
|
15131
|
+
if (sourceBackend || destinationBackend) {
|
|
15132
|
+
// The kernel owns at least one affected entry: forward the complete
|
|
15133
|
+
// ordered id list so mobile and desktop stay in sync.
|
|
15134
|
+
var visibleIndexes = queueIndexesForTarget(currentConversationTarget());
|
|
15135
|
+
var orderedIds = visibleIndexes.map(function(vi) {
|
|
15136
|
+
return queueItemIdForText(state.nextQueue[vi] || '', currentConversationTarget());
|
|
15137
|
+
}).filter(function(id) { return !!id; });
|
|
15138
|
+
if (orderedIds.length) {
|
|
15139
|
+
window.queueAction('queue_reorder', { orderedIds: orderedIds });
|
|
15140
|
+
}
|
|
15141
|
+
}
|
|
13399
15142
|
};
|
|
13400
15143
|
|
|
13401
15144
|
window.endQueueDrag = function(event) {
|
|
@@ -13445,7 +15188,24 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13445
15188
|
els.prompt.dispatchEvent(new Event('input', { bubbles: true }));
|
|
13446
15189
|
if (pendingEdit.kind === 'queue') {
|
|
13447
15190
|
var editIndex = Number(pendingEdit.index);
|
|
15191
|
+
var editRequest = pendingEdit.request || (state.nextQueueRequests && state.nextQueueRequests[editIndex]);
|
|
15192
|
+
var editBackendManaged = queuedRequestIsBackendManaged(editRequest);
|
|
15193
|
+
var editQueueItemId = editBackendManaged ? queueItemIdForText(String(state.nextQueue[editIndex] || pendingEdit.originalText || ''), pendingEdit.target) : '';
|
|
15194
|
+
if (editBackendManaged && !editQueueItemId) {
|
|
15195
|
+
showUiNotice(currentLang() === 'zh' ? '无法编辑该队列项:缺少后端 id' : 'Cannot edit queue item: backend id missing', 'error', 'queue-edit-' + editIndex);
|
|
15196
|
+
return { ok: false, error: 'missing backend queue id' };
|
|
15197
|
+
}
|
|
13448
15198
|
if (effectiveInputMode === 'next') {
|
|
15199
|
+
if (editBackendManaged) {
|
|
15200
|
+
// The kernel owns this entry: forward the edit and let the next
|
|
15201
|
+
// snapshot reconcile the local row.
|
|
15202
|
+
window.queueAction('queue_update', { id: editQueueItemId, text: editedValue, requestedMode: String(editRequest && editRequest.requestedMode || 'build'), goalObjective: String(editRequest && editRequest.goalObjective || ''), createdAt: String(editRequest && editRequest.createdAt || '') });
|
|
15203
|
+
state.nextQueue[editIndex] = editedValue;
|
|
15204
|
+
if (state.nextQueueRequests[editIndex]) state.nextQueueRequests[editIndex].text = editedValue;
|
|
15205
|
+
refreshNextPromptForTarget(pendingEdit.target);
|
|
15206
|
+
window.renderInputStack();
|
|
15207
|
+
return { ok: true, queued: true };
|
|
15208
|
+
}
|
|
13449
15209
|
state.nextQueue[editIndex] = editedValue;
|
|
13450
15210
|
if (state.nextQueueRequests[editIndex]) {
|
|
13451
15211
|
state.nextQueueRequests[editIndex].text = editedValue;
|
|
@@ -13808,7 +15568,11 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13808
15568
|
}));
|
|
13809
15569
|
var lockedUi = conversationWorkUiState(lockedConversationId, lockedTarget.workspaceId);
|
|
13810
15570
|
if (stillActive && r && Array.isArray(r.diffs) && r.diffs.length) {
|
|
13811
|
-
|
|
15571
|
+
var reviewRecord = {
|
|
15572
|
+
diffs: r.diffs,
|
|
15573
|
+
runId: String(r.runId || localRunId || '')
|
|
15574
|
+
};
|
|
15575
|
+
upsertWorkReviewRecord(lockedUi, reviewRecord.diffs, reviewRecord.runId, [localRunId]);
|
|
13812
15576
|
}
|
|
13813
15577
|
if (stillActive && !responseOwnedByRun) {
|
|
13814
15578
|
responseMsg = lockedUi.activeWorkflowMsg;
|
|
@@ -13837,9 +15601,13 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13837
15601
|
hydrateConversationBranchState(r);
|
|
13838
15602
|
renderChatMessages(r.chatMessages);
|
|
13839
15603
|
}
|
|
13840
|
-
|
|
15604
|
+
// Re-render every retained completed-run review after a transcript rebuild.
|
|
15605
|
+
// addWorkReview dedupes by run identity and normalized file totals.
|
|
15606
|
+
if (stillActive) {
|
|
13841
15607
|
var completedReviewUi = conversationWorkUiState(lockedConversationId);
|
|
13842
|
-
|
|
15608
|
+
(completedReviewUi.workReviews || []).forEach(function(item) {
|
|
15609
|
+
addWorkReview(item.diffs || item, item.runId || '', [localRunId]);
|
|
15610
|
+
});
|
|
13843
15611
|
}
|
|
13844
15612
|
if (r && r.conversations) {
|
|
13845
15613
|
applyBackendConversations(r.conversations || [], stillActive ? lockedConversationId : activeConversationId(), lockedTarget.workspaceId);
|
|
@@ -13860,6 +15628,7 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13860
15628
|
if (r && r.queued) {
|
|
13861
15629
|
var responseQueue = setBackendQueueForTarget(r.queued, lockedTarget);
|
|
13862
15630
|
window.syncNextQueueFromBackend(responseQueue, lockedTarget);
|
|
15631
|
+
syncQueueItemsFromSnapshot(r, lockedTarget);
|
|
13863
15632
|
if (stillActive) state.backendQueue = responseQueue;
|
|
13864
15633
|
}
|
|
13865
15634
|
if (r && r.options) {
|
|
@@ -13905,6 +15674,7 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13905
15674
|
if (s && s.queued) {
|
|
13906
15675
|
var refreshedQueue = setBackendQueueForTarget(s.queued, lockedTarget);
|
|
13907
15676
|
window.syncNextQueueFromBackend(refreshedQueue, lockedTarget);
|
|
15677
|
+
syncQueueItemsFromSnapshot(s, lockedTarget);
|
|
13908
15678
|
if (stillActiveAfterRefresh) state.backendQueue = refreshedQueue;
|
|
13909
15679
|
}
|
|
13910
15680
|
if (s && s.conversations) {
|
|
@@ -14349,6 +16119,7 @@ window.switchRightTab = function(tab) {
|
|
|
14349
16119
|
var rightTabs = els['right-tabs'] || document.getElementById('right-tabs');
|
|
14350
16120
|
var rightContent = els['right-content'] || document.getElementById('right-content');
|
|
14351
16121
|
if (!rightTabs || !rightContent) return;
|
|
16122
|
+
wireLiquidRailInteractions(rightTabs, '.tab-btn[data-tab]');
|
|
14352
16123
|
var btns = rightTabs.querySelectorAll('.tab-btn[data-tab]');
|
|
14353
16124
|
for (var i = 0; i < btns.length; i++) {
|
|
14354
16125
|
var selected = btns[i].getAttribute('data-tab') === tab;
|
|
@@ -15257,7 +17028,7 @@ if (api.onTerminalTakeover) {
|
|
|
15257
17028
|
if (api.terminalTakeoverState) window.refreshTerminalTakeoverPanes();
|
|
15258
17029
|
|
|
15259
17030
|
// === Scroll to Bottom ===
|
|
15260
|
-
var _chatScrollNearBottomPx =
|
|
17031
|
+
var _chatScrollNearBottomPx = 24;
|
|
15261
17032
|
var _chatShouldAutoScroll = true;
|
|
15262
17033
|
function shouldAutoScroll(el) { return el.scrollHeight - el.scrollTop - el.clientHeight <= _chatScrollNearBottomPx; }
|
|
15263
17034
|
function autoScrollIfAtBottom() { var el = els['chat-area']; if (!el) return; if (_chatShouldAutoScroll) el.scrollTop = el.scrollHeight; }
|
|
@@ -15547,6 +17318,7 @@ window.openSubWin = function(title, html) {
|
|
|
15547
17318
|
els['sub-win-title'].textContent = title;
|
|
15548
17319
|
els['sub-win-body'].innerHTML = html;
|
|
15549
17320
|
els['sub-win'].classList.toggle('memory-lab-window', !!(state.activeSubWindowView && state.activeSubWindowView.name === 'memoryLab'));
|
|
17321
|
+
els['sub-win'].classList.toggle('settings-window', !!(state.activeSubWindowView && state.activeSubWindowView.name === 'settings'));
|
|
15550
17322
|
els['sub-win-overlay'].classList.add('open');
|
|
15551
17323
|
updateApplicationInertState();
|
|
15552
17324
|
requestAnimationFrame(focusSubWindowInitial);
|
|
@@ -15577,6 +17349,7 @@ window.closeSubWin = function() {
|
|
|
15577
17349
|
els['sub-win-title'].textContent = previous.title;
|
|
15578
17350
|
els['sub-win-body'].innerHTML = previous.html;
|
|
15579
17351
|
els['sub-win'].classList.toggle('memory-lab-window', !!(previous.view && previous.view.name === 'memoryLab'));
|
|
17352
|
+
els['sub-win'].classList.toggle('settings-window', !!(previous.view && previous.view.name === 'settings'));
|
|
15580
17353
|
requestAnimationFrame(function() {
|
|
15581
17354
|
els['sub-win-body'].scrollTop = previous.scrollTop || 0;
|
|
15582
17355
|
state.restoringSubWindow = false;
|
|
@@ -15587,7 +17360,7 @@ window.closeSubWin = function() {
|
|
|
15587
17360
|
els['sub-win-overlay'].classList.remove('open');
|
|
15588
17361
|
state.activeSubWindowView = null;
|
|
15589
17362
|
var win = els['sub-win'];
|
|
15590
|
-
if (win) { win.classList.remove('memory-lab-window'); win.style.left = ''; win.style.top = ''; win.style.margin = ''; }
|
|
17363
|
+
if (win) { win.classList.remove('memory-lab-window', 'settings-window'); win.style.left = ''; win.style.top = ''; win.style.margin = ''; }
|
|
15591
17364
|
var restoreFocus = subWindowOriginFocus;
|
|
15592
17365
|
subWindowOriginFocus = null;
|
|
15593
17366
|
updateApplicationInertState();
|
|
@@ -15660,7 +17433,10 @@ window.openSettings = function(tab) {
|
|
|
15660
17433
|
if (refreshingSettings) state.restoringSubWindow = true;
|
|
15661
17434
|
window.openSubWin(t('settings.title'), html);
|
|
15662
17435
|
state.restoringSubWindow = false;
|
|
15663
|
-
requestAnimationFrame(function() {
|
|
17436
|
+
requestAnimationFrame(function() {
|
|
17437
|
+
window.settingsTab(activeTab);
|
|
17438
|
+
wireLiquidRailInteractions(document.querySelector('.settings-tabs:not(.plugin-tabs)'), '.stab-btn[data-stab]');
|
|
17439
|
+
});
|
|
15664
17440
|
};
|
|
15665
17441
|
|
|
15666
17442
|
function renderGeneralSettings() {
|
|
@@ -15740,7 +17516,7 @@ function renderGeneralSettings() {
|
|
|
15740
17516
|
'</div>' +
|
|
15741
17517
|
'<div class="setting-row">' +
|
|
15742
17518
|
'<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)">' +
|
|
17519
|
+
'<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
17520
|
'<div class="setting-desc" id="glass-desc" aria-live="polite">' + esc(glassDescription) + '</div></div>' +
|
|
15745
17521
|
'</div>' +
|
|
15746
17522
|
'<div class="setting-row">' +
|
|
@@ -16240,6 +18016,7 @@ function glassPresentationForOpacity(value) {
|
|
|
16240
18016
|
var alpha = opacityPercent / 100;
|
|
16241
18017
|
var transparencyPercent = 100 - opacityPercent;
|
|
16242
18018
|
var glassWidth = 20 * transparencyPercent / 100;
|
|
18019
|
+
var opticalStrength = Math.min(2, transparencyPercent / 15);
|
|
16243
18020
|
var rounded = function(number) { return Math.round(number * 10000) / 10000; };
|
|
16244
18021
|
return {
|
|
16245
18022
|
opacityPercent: rounded(opacityPercent),
|
|
@@ -16248,6 +18025,12 @@ function glassPresentationForOpacity(value) {
|
|
|
16248
18025
|
blur1: rounded(0.4 * glassWidth),
|
|
16249
18026
|
blur2: rounded(0.8 * glassWidth),
|
|
16250
18027
|
blur3: rounded(glassWidth),
|
|
18028
|
+
carrierBlur: rounded(glassWidth * 2.2),
|
|
18029
|
+
carrierAlpha: rounded(Math.max(0.08, 0.44 * alpha)),
|
|
18030
|
+
floatRefractionHeight: rounded(12 * opticalStrength),
|
|
18031
|
+
floatRefractionAmount: rounded(24 * opticalStrength),
|
|
18032
|
+
floatChromaticAberration: rounded(opticalStrength),
|
|
18033
|
+
floatOpticalOpacity: rounded(Math.min(1, opticalStrength)),
|
|
16251
18034
|
alpha1: rounded(0.75 * alpha),
|
|
16252
18035
|
alpha2: rounded(0.80 * alpha),
|
|
16253
18036
|
alpha3: rounded(0.85 * alpha),
|
|
@@ -16280,11 +18063,21 @@ function applyGlassPresentation(presentation) {
|
|
|
16280
18063
|
root.style.setProperty('--glass-blur-2', presentation.blur2 + 'px');
|
|
16281
18064
|
root.style.setProperty('--glass-blur-3', presentation.blur3 + 'px');
|
|
16282
18065
|
}
|
|
18066
|
+
root.style.setProperty('--carrier-glass-blur', presentation.carrierBlur + 'px');
|
|
18067
|
+
root.style.setProperty('--carrier-glass-alpha', String(presentation.carrierAlpha));
|
|
18068
|
+
root.style.setProperty('--liquid-float-refraction-amount', presentation.floatRefractionAmount + 'px');
|
|
18069
|
+
root.style.setProperty('--liquid-float-chromatic-aberration', String(presentation.floatChromaticAberration));
|
|
18070
|
+
root.style.setProperty('--liquid-float-optical-opacity', String(presentation.floatOpticalOpacity));
|
|
16283
18071
|
var description = glassDescriptionText(presentation);
|
|
16284
18072
|
var desc = document.getElementById('glass-desc');
|
|
16285
18073
|
if (desc) desc.textContent = description;
|
|
16286
18074
|
var slider = document.getElementById('settings-glass-opacity');
|
|
16287
|
-
if (slider)
|
|
18075
|
+
if (slider) {
|
|
18076
|
+
slider.setAttribute('aria-valuetext', description);
|
|
18077
|
+
slider.style.setProperty('--liquid-range-progress', presentation.opacityPercent + '%');
|
|
18078
|
+
}
|
|
18079
|
+
var valueOutput = document.getElementById('settings-glass-value');
|
|
18080
|
+
if (valueOutput) valueOutput.textContent = glassNumberText(presentation.opacityPercent) + '%';
|
|
16288
18081
|
return presentation;
|
|
16289
18082
|
}
|
|
16290
18083
|
|
|
@@ -19431,14 +21224,25 @@ function pendingConversationActivation(target) {
|
|
|
19431
21224
|
}
|
|
19432
21225
|
|
|
19433
21226
|
function applyBackendConversations(items, activeId, workspaceId) {
|
|
19434
|
-
if (!state.currentWorkspace
|
|
21227
|
+
if (!state.currentWorkspace) return;
|
|
19435
21228
|
var key = currentWorkspaceKey(workspaceId);
|
|
19436
21229
|
var isForegroundWorkspace = key === currentWorkspaceKey();
|
|
19437
21230
|
var existingConversations = state.workspaceConversations[key] || [];
|
|
19438
|
-
var convs = [];
|
|
19439
|
-
var requestedActiveId = String(activeId || '');
|
|
19440
21231
|
var holdId = String(state.foregroundConversationHoldId || '');
|
|
19441
21232
|
var useHold = !!(isForegroundWorkspace && holdId && Date.now() < Number(state.foregroundConversationHoldUntil || 0));
|
|
21233
|
+
if ((!items || !items.length) && !useHold) {
|
|
21234
|
+
// 后端权威列表为空:工作区对话确实已清空。保持为空,并移除本地
|
|
21235
|
+
// 自动兜底的 default 对话,跨重启/切换工作区后也不会复活。
|
|
21236
|
+
state.workspaceConversations[key] = [];
|
|
21237
|
+
state.workspaceActiveConversation[key] = -1;
|
|
21238
|
+
if (isForegroundWorkspace) {
|
|
21239
|
+
state.conversations = [];
|
|
21240
|
+
state.activeConversation = -1;
|
|
21241
|
+
}
|
|
21242
|
+
return;
|
|
21243
|
+
}
|
|
21244
|
+
var convs = [];
|
|
21245
|
+
var requestedActiveId = String(activeId || '');
|
|
19442
21246
|
var seenIds = {};
|
|
19443
21247
|
for (var i = 0; i < items.length; i++) {
|
|
19444
21248
|
var item = items[i] || {};
|
|
@@ -19564,16 +21368,244 @@ function appendDomNodesInBatches(container, nodes, batchKey, batchSize) {
|
|
|
19564
21368
|
appendBatch();
|
|
19565
21369
|
}
|
|
19566
21370
|
|
|
21371
|
+
var conversationGlassFlight = { float: null, source: null, covered: null, timer: 0, generation: 0 };
|
|
21372
|
+
var conversationNativeDrag = {
|
|
21373
|
+
row: null,
|
|
21374
|
+
float: null,
|
|
21375
|
+
arrived: false,
|
|
21376
|
+
grabX: 0,
|
|
21377
|
+
grabY: 0,
|
|
21378
|
+
clientX: 0,
|
|
21379
|
+
clientY: 0
|
|
21380
|
+
};
|
|
21381
|
+
|
|
21382
|
+
function positionConversationGlassAroundRow(float, row) {
|
|
21383
|
+
if (!float || !row) return;
|
|
21384
|
+
var rect = row.getBoundingClientRect();
|
|
21385
|
+
var edge = liquidInteractionEdgePx();
|
|
21386
|
+
keepLiquidFloatExpansionInsideViewport(float, rect);
|
|
21387
|
+
float._liquidTargetRect = { left: rect.left - edge, top: rect.top - edge, width: rect.width + edge * 2, height: rect.height + edge * 2 };
|
|
21388
|
+
float.style.left = (rect.left - edge) + 'px';
|
|
21389
|
+
float.style.top = (rect.top - edge) + 'px';
|
|
21390
|
+
float.style.width = (rect.width + edge * 2) + 'px';
|
|
21391
|
+
float.style.height = (rect.height + edge * 2) + 'px';
|
|
21392
|
+
if (float._renderKyantGlass) float._renderKyantGlass({
|
|
21393
|
+
left: rect.left - edge, top: rect.top - edge,
|
|
21394
|
+
width: rect.width + edge * 2, height: rect.height + edge * 2
|
|
21395
|
+
}, true);
|
|
21396
|
+
}
|
|
21397
|
+
|
|
21398
|
+
function followConversationDragPointer() {
|
|
21399
|
+
var drag = conversationNativeDrag;
|
|
21400
|
+
if (!drag.arrived || !drag.float) return;
|
|
21401
|
+
var list = els['conversation-list'];
|
|
21402
|
+
if (!list || !drag.row) return;
|
|
21403
|
+
var conversations = currentWorkspaceConversations();
|
|
21404
|
+
var draggedId = String(drag.row.getAttribute('data-conversation-id') || '');
|
|
21405
|
+
var draggedConversation = conversations.find(function(item) { return String(item && item.id || '') === draggedId; });
|
|
21406
|
+
var trackRows = Array.from(list.querySelectorAll('.conv-item')).filter(function(row) {
|
|
21407
|
+
var id = String(row.getAttribute('data-conversation-id') || '');
|
|
21408
|
+
var conversation = conversations.find(function(item) { return String(item && item.id || '') === id; });
|
|
21409
|
+
return conversation && draggedConversation && !!conversation.pinned === !!draggedConversation.pinned;
|
|
21410
|
+
});
|
|
21411
|
+
if (!trackRows.length) trackRows = [drag.row];
|
|
21412
|
+
var firstRect = trackRows[0].getBoundingClientRect();
|
|
21413
|
+
var lastRect = trackRows[trackRows.length - 1].getBoundingClientRect();
|
|
21414
|
+
var rowRect = drag.row.getBoundingClientRect();
|
|
21415
|
+
var projectedTop = drag.clientY - drag.grabY;
|
|
21416
|
+
var minTop = firstRect.top - rowRect.height;
|
|
21417
|
+
var maxTop = Math.max(minTop, lastRect.bottom);
|
|
21418
|
+
var trackTop = Math.max(minTop, Math.min(maxTop, projectedTop));
|
|
21419
|
+
var edge = liquidInteractionEdgePx();
|
|
21420
|
+
drag.float.style.left = (rowRect.left - edge) + 'px';
|
|
21421
|
+
drag.float.style.top = (trackTop - edge) + 'px';
|
|
21422
|
+
if (drag.float._renderKyantGlass) drag.float._renderKyantGlass();
|
|
21423
|
+
}
|
|
21424
|
+
|
|
21425
|
+
function wrapConversationDragContent(row, float) {
|
|
21426
|
+
if (!row || !float || !row.isConnected) return;
|
|
21427
|
+
var content = document.createElement('div');
|
|
21428
|
+
content.className = 'conversation-drag-content';
|
|
21429
|
+
Array.prototype.slice.call(row.childNodes).forEach(function(node) { content.appendChild(node.cloneNode(true)); });
|
|
21430
|
+
float.appendChild(content);
|
|
21431
|
+
row.classList.add('dragging', 'conversation-glass-dragging');
|
|
21432
|
+
float.classList.add('conversation-drag-following');
|
|
21433
|
+
conversationNativeDrag.arrived = true;
|
|
21434
|
+
conversationNativeDrag.float = float;
|
|
21435
|
+
followConversationDragPointer();
|
|
21436
|
+
}
|
|
21437
|
+
|
|
21438
|
+
window.updateConversationNativeDrag = function(clientX, clientY) {
|
|
21439
|
+
conversationNativeDrag.clientX = clientX;
|
|
21440
|
+
conversationNativeDrag.clientY = clientY;
|
|
21441
|
+
var drag = conversationNativeDrag;
|
|
21442
|
+
var list = els['conversation-list'];
|
|
21443
|
+
if (drag.row && list) {
|
|
21444
|
+
var conversations = currentWorkspaceConversations();
|
|
21445
|
+
var draggedId = String(drag.row.getAttribute('data-conversation-id') || '');
|
|
21446
|
+
var draggedConversation = conversations.find(function(item) { return String(item && item.id || '') === draggedId; });
|
|
21447
|
+
var peers = Array.from(list.querySelectorAll('.conv-item')).filter(function(row) {
|
|
21448
|
+
if (row === drag.row) return false;
|
|
21449
|
+
var id = String(row.getAttribute('data-conversation-id') || '');
|
|
21450
|
+
var conversation = conversations.find(function(item) { return String(item && item.id || '') === id; });
|
|
21451
|
+
return conversation && draggedConversation && !!conversation.pinned === !!draggedConversation.pinned;
|
|
21452
|
+
});
|
|
21453
|
+
if (peers.length) {
|
|
21454
|
+
var firstPeerRect = peers[0].getBoundingClientRect();
|
|
21455
|
+
var lastPeerRect = peers[peers.length - 1].getBoundingClientRect();
|
|
21456
|
+
if (clientY <= firstPeerRect.top) {
|
|
21457
|
+
list.insertBefore(drag.row, peers[0]);
|
|
21458
|
+
} else if (clientY >= lastPeerRect.bottom) {
|
|
21459
|
+
list.insertBefore(drag.row, peers[peers.length - 1].nextSibling);
|
|
21460
|
+
}
|
|
21461
|
+
}
|
|
21462
|
+
}
|
|
21463
|
+
followConversationDragPointer();
|
|
21464
|
+
};
|
|
21465
|
+
|
|
21466
|
+
// Native conversation drags can leave the list while the mouse button remains
|
|
21467
|
+
// held. Track at document capture scope so the carried glass never freezes at
|
|
21468
|
+
// the list boundary; drop/reorder ownership remains scoped to the list itself.
|
|
21469
|
+
document.addEventListener('dragover', function(event) {
|
|
21470
|
+
if (conversationNativeDrag.row) window.updateConversationNativeDrag(event.clientX, event.clientY);
|
|
21471
|
+
}, true);
|
|
21472
|
+
|
|
21473
|
+
window.cancelConversationGlassFlight = function() {
|
|
21474
|
+
conversationGlassFlight.generation += 1;
|
|
21475
|
+
if (conversationGlassFlight.timer) clearTimeout(conversationGlassFlight.timer);
|
|
21476
|
+
conversationGlassFlight.timer = 0;
|
|
21477
|
+
if (conversationGlassFlight.source) conversationGlassFlight.source.classList.remove('liquid-selection-source');
|
|
21478
|
+
conversationGlassFlight.source = null;
|
|
21479
|
+
if (conversationGlassFlight.covered) conversationGlassFlight.covered.classList.remove('liquid-selection-covered');
|
|
21480
|
+
conversationGlassFlight.covered = null;
|
|
21481
|
+
stopLiquidMotionTracking(conversationGlassFlight.float);
|
|
21482
|
+
if (conversationGlassFlight.float && conversationGlassFlight.float._releaseKyantGlass) {
|
|
21483
|
+
conversationGlassFlight.float._releaseKyantGlass();
|
|
21484
|
+
}
|
|
21485
|
+
if (conversationGlassFlight.float && conversationGlassFlight.float.parentNode) {
|
|
21486
|
+
conversationGlassFlight.float.parentNode.removeChild(conversationGlassFlight.float);
|
|
21487
|
+
}
|
|
21488
|
+
conversationGlassFlight.float = null;
|
|
21489
|
+
if (conversationNativeDrag.row) conversationNativeDrag.row.classList.remove('dragging', 'conversation-glass-dragging');
|
|
21490
|
+
conversationNativeDrag.row = null;
|
|
21491
|
+
conversationNativeDrag.float = null;
|
|
21492
|
+
conversationNativeDrag.arrived = false;
|
|
21493
|
+
window.setLiquidSidebarGestureLock('conversation-flight', false);
|
|
21494
|
+
};
|
|
21495
|
+
|
|
21496
|
+
window.animateConversationGlassTo = function(target, options) {
|
|
21497
|
+
options = options || {};
|
|
21498
|
+
var list = els['conversation-list'];
|
|
21499
|
+
if (!list || !target) { if (options.onArrive) options.onArrive(); return; }
|
|
21500
|
+
var source = list.querySelector('.conv-item.active') || target;
|
|
21501
|
+
var float = conversationGlassFlight.float;
|
|
21502
|
+
var redirecting = !!(float && float.isConnected);
|
|
21503
|
+
if (!redirecting) {
|
|
21504
|
+
float = document.createElement('div');
|
|
21505
|
+
float.className = 'liquid-selection-float conversation-selection-float liquid-selection-primed';
|
|
21506
|
+
mountLiquidViewportFloat(float);
|
|
21507
|
+
captureLiquidBackdropFrame().then(function(frame) {
|
|
21508
|
+
if (float && float.isConnected) attachKyantLiquidRenderer(float, frame);
|
|
21509
|
+
});
|
|
21510
|
+
conversationGlassFlight.float = float;
|
|
21511
|
+
conversationGlassFlight.source = source;
|
|
21512
|
+
source.classList.add('liquid-selection-source');
|
|
21513
|
+
}
|
|
21514
|
+
function place(row) {
|
|
21515
|
+
if (conversationGlassFlight.covered && conversationGlassFlight.covered !== row) {
|
|
21516
|
+
conversationGlassFlight.covered.classList.remove('liquid-selection-covered');
|
|
21517
|
+
}
|
|
21518
|
+
conversationGlassFlight.covered = row;
|
|
21519
|
+
row.classList.add('liquid-selection-covered');
|
|
21520
|
+
positionConversationGlassAroundRow(float, row);
|
|
21521
|
+
}
|
|
21522
|
+
var generation = ++conversationGlassFlight.generation;
|
|
21523
|
+
if (!redirecting) {
|
|
21524
|
+
place(source);
|
|
21525
|
+
showLiquidViewportFloat(float);
|
|
21526
|
+
place(source);
|
|
21527
|
+
// Lock the true source geometry before enabling lift and movement together.
|
|
21528
|
+
float.getBoundingClientRect();
|
|
21529
|
+
requestAnimationFrame(function() {
|
|
21530
|
+
if (conversationGlassFlight.generation !== generation || !float.isConnected) return;
|
|
21531
|
+
float.classList.remove('liquid-selection-primed');
|
|
21532
|
+
float.classList.add('visible');
|
|
21533
|
+
place(target);
|
|
21534
|
+
});
|
|
21535
|
+
window.setLiquidSidebarGestureLock('conversation-flight', true);
|
|
21536
|
+
} else {
|
|
21537
|
+
// Updating top/left while the transition is running makes CSS continue
|
|
21538
|
+
// from the currently rendered position; no second glass block is born.
|
|
21539
|
+
float.classList.remove('liquid-selection-landing');
|
|
21540
|
+
place(target);
|
|
21541
|
+
}
|
|
21542
|
+
if (conversationGlassFlight.timer) clearTimeout(conversationGlassFlight.timer);
|
|
21543
|
+
waitForLiquidSelectionArrival(float, function() {
|
|
21544
|
+
if (conversationGlassFlight.generation !== generation) return;
|
|
21545
|
+
conversationGlassFlight.timer = 0;
|
|
21546
|
+
if (options.dragging && target.isConnected) {
|
|
21547
|
+
wrapConversationDragContent(target, float);
|
|
21548
|
+
window.setLiquidSidebarGestureLock('conversation-flight', false);
|
|
21549
|
+
if (options.onArrive) options.onArrive();
|
|
21550
|
+
return;
|
|
21551
|
+
}
|
|
21552
|
+
landLiquidSelectionFloat(float, function() {
|
|
21553
|
+
if (conversationGlassFlight.generation !== generation) return;
|
|
21554
|
+
if (float._releaseKyantGlass) float._releaseKyantGlass();
|
|
21555
|
+
if (float.parentNode) float.parentNode.removeChild(float);
|
|
21556
|
+
conversationGlassFlight.float = null;
|
|
21557
|
+
if (conversationGlassFlight.source) conversationGlassFlight.source.classList.remove('liquid-selection-source');
|
|
21558
|
+
conversationGlassFlight.source = null;
|
|
21559
|
+
if (conversationGlassFlight.covered) conversationGlassFlight.covered.classList.remove('liquid-selection-covered');
|
|
21560
|
+
conversationGlassFlight.covered = null;
|
|
21561
|
+
window.setLiquidSidebarGestureLock('conversation-flight', false);
|
|
21562
|
+
if (options.onArrive) options.onArrive();
|
|
21563
|
+
});
|
|
21564
|
+
});
|
|
21565
|
+
};
|
|
21566
|
+
|
|
21567
|
+
window.activateConversationWithGlass = function(idx, row) {
|
|
21568
|
+
var conversations = currentWorkspaceConversations();
|
|
21569
|
+
if (!row || !conversations[idx]) {
|
|
21570
|
+
window.switchConversation(idx);
|
|
21571
|
+
return;
|
|
21572
|
+
}
|
|
21573
|
+
var targetId = String(conversations[idx].id || '');
|
|
21574
|
+
window.animateConversationGlassTo(row, { onArrive: function() {
|
|
21575
|
+
var current = currentWorkspaceConversations();
|
|
21576
|
+
var targetIndex = current.findIndex(function(item) { return String(item.id || '') === targetId; });
|
|
21577
|
+
if (targetIndex >= 0) window.switchConversation(targetIndex);
|
|
21578
|
+
} });
|
|
21579
|
+
};
|
|
21580
|
+
|
|
19567
21581
|
function renderConversations() {
|
|
19568
21582
|
var list = els['conversation-list'];
|
|
19569
21583
|
if (!list) return;
|
|
19570
|
-
list.
|
|
21584
|
+
if (list.dataset.conversationDropWired !== 'true') {
|
|
21585
|
+
list.dataset.conversationDropWired = 'true';
|
|
21586
|
+
list.addEventListener('dragover', function(event) {
|
|
21587
|
+
window.updateConversationNativeDrag(event.clientX, event.clientY);
|
|
21588
|
+
if (conversationNativeDrag.row) event.preventDefault();
|
|
21589
|
+
});
|
|
21590
|
+
list.addEventListener('drop', function(event) {
|
|
21591
|
+
if (!list.querySelector('.conv-item.dragging')) return;
|
|
21592
|
+
event.preventDefault();
|
|
21593
|
+
list.dataset.conversationDropCommitted = 'true';
|
|
21594
|
+
window.commitConversationDomOrder();
|
|
21595
|
+
});
|
|
21596
|
+
}
|
|
19571
21597
|
if (!state.currentWorkspace) {
|
|
19572
21598
|
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
21599
|
updateWorkspaceGate();
|
|
19574
21600
|
return;
|
|
19575
21601
|
}
|
|
19576
21602
|
var conversations = currentWorkspaceConversations();
|
|
21603
|
+
// 工作区对话被清空后保持为空:只显示空态提示,不自动创建对话。
|
|
21604
|
+
if (!conversations.length) {
|
|
21605
|
+
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>';
|
|
21606
|
+
updateWorkspaceGate();
|
|
21607
|
+
return;
|
|
21608
|
+
}
|
|
19577
21609
|
var summaryCounts = {};
|
|
19578
21610
|
for (var ci = 0; ci < conversations.length; ci++) {
|
|
19579
21611
|
var summaryKey = String(conversations[ci] && conversations[ci].summary || '');
|
|
@@ -19582,7 +21614,17 @@ function renderConversations() {
|
|
|
19582
21614
|
var activeBeforeRender = (conversations.find(function(c) { return c && c.active; }) || {}).id;
|
|
19583
21615
|
if (activeBeforeRender) setActiveWorkspaceConversationById(activeBeforeRender);
|
|
19584
21616
|
else setActiveWorkspaceConversation(state.workspaceActiveConversation[currentWorkspaceKey()] || 0);
|
|
19585
|
-
|
|
21617
|
+
// 增量更新:复用已存在的行节点,就地更新 class 与内容。行节点不被替换,
|
|
21618
|
+
// 运行中对话的 marquee 跑马灯 CSS 动画就不会因重建 DOM 而从头开始。
|
|
21619
|
+
Array.prototype.slice.call(list.children).forEach(function(child) {
|
|
21620
|
+
if (!child || !child.classList || !child.classList.contains('conv-item')) child.parentNode.removeChild(child);
|
|
21621
|
+
});
|
|
21622
|
+
var existingRows = {};
|
|
21623
|
+
Array.prototype.slice.call(list.querySelectorAll('.conv-item')).forEach(function(row) {
|
|
21624
|
+
existingRows[String(row.getAttribute('data-conversation-id') || '')] = row;
|
|
21625
|
+
});
|
|
21626
|
+
var keepRows = {};
|
|
21627
|
+
var orderedRows = [];
|
|
19586
21628
|
for (var i = 0; i < conversations.length; i++) {
|
|
19587
21629
|
var conv = conversations[i];
|
|
19588
21630
|
var runtimeKey = currentRuntimeKey(conv.id);
|
|
@@ -19591,53 +21633,114 @@ function renderConversations() {
|
|
|
19591
21633
|
if (summaryCounts[String(conv.summary || '')] > 1) {
|
|
19592
21634
|
displaySummary += ' · ' + String(conv.id || '').slice(-8);
|
|
19593
21635
|
}
|
|
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');
|
|
21636
|
+
var running = !!(runtimeState && ['running', 'stopping', 'force_restarting'].indexOf(String(runtimeState.status || '')) >= 0);
|
|
19602
21637
|
var runtimeBadge = runtimeState && runtimeState.status && runtimeState.status !== 'idle'
|
|
19603
21638
|
? '<span class="conv-runtime-badge ' + escAttr(String(runtimeState.status)) + '">' + esc(String(runtimeState.status)) + '</span>' : '';
|
|
19604
21639
|
var branchCommIcon = conv.branchCommunication
|
|
19605
21640
|
? '<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
|
-
|
|
21641
|
+
var innerKey = [String(displaySummary), String(conv.messageCount || ''), String(runtimeState && runtimeState.status || ''), conv.branchCommunication ? '1' : '0'].join('|');
|
|
21642
|
+
var nextInner = '<span class="conv-summary" title="' + escAttr(String(conv.id || '')) + '">' + esc(displaySummary) + (conv.messageCount ? ' (' + esc(String(conv.messageCount)) + ')' : '') + '</span>' + branchCommIcon + runtimeBadge +
|
|
19607
21643
|
'<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
|
-
|
|
21644
|
+
var div = existingRows[String(conv.id || '')];
|
|
21645
|
+
if (div) {
|
|
21646
|
+
keepRows[String(conv.id || '')] = true;
|
|
21647
|
+
} else {
|
|
21648
|
+
div = document.createElement('div');
|
|
21649
|
+
div.setAttribute('role', 'listitem');
|
|
21650
|
+
div.setAttribute('draggable', 'true');
|
|
21651
|
+
div.setAttribute('data-conversation-id', String(conv.id || ''));
|
|
21652
|
+
div.addEventListener('keydown', window.handleConversationKey);
|
|
21653
|
+
div.addEventListener('contextmenu', function(event) {
|
|
21654
|
+
event.preventDefault();
|
|
21655
|
+
event.stopPropagation();
|
|
21656
|
+
window.openConversationActionMenu(event.currentTarget, this.getAttribute('data-conversation-id') || '', { x: event.clientX, y: event.clientY });
|
|
21657
|
+
});
|
|
21658
|
+
div.addEventListener('dragstart', function(event) {
|
|
21659
|
+
window.cancelConversationGlassFlight();
|
|
21660
|
+
var rect = this.getBoundingClientRect();
|
|
21661
|
+
conversationNativeDrag.row = this;
|
|
21662
|
+
conversationNativeDrag.arrived = false;
|
|
21663
|
+
conversationNativeDrag.grabX = Math.max(0, Math.min(rect.width, event.clientX - rect.left));
|
|
21664
|
+
conversationNativeDrag.grabY = Math.max(0, Math.min(rect.height, event.clientY - rect.top));
|
|
21665
|
+
conversationNativeDrag.clientX = event.clientX;
|
|
21666
|
+
conversationNativeDrag.clientY = event.clientY;
|
|
21667
|
+
if (event.dataTransfer) {
|
|
21668
|
+
var transparentDragImage = document.createElement('canvas');
|
|
21669
|
+
transparentDragImage.width = transparentDragImage.height = 1;
|
|
21670
|
+
document.body.appendChild(transparentDragImage);
|
|
21671
|
+
event.dataTransfer.setDragImage(transparentDragImage, 0, 0);
|
|
21672
|
+
setTimeout(function() { transparentDragImage.remove(); }, 0);
|
|
21673
|
+
}
|
|
21674
|
+
list.classList.add('conversation-reordering');
|
|
21675
|
+
list.dataset.conversationDropCommitted = 'false';
|
|
21676
|
+
window.setLiquidSidebarGestureLock('conversation-reorder', true);
|
|
21677
|
+
window.animateConversationGlassTo(this, { dragging: true });
|
|
21678
|
+
if (event.dataTransfer) { event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.setData('text/plain', this.getAttribute('data-conversation-id') || ''); }
|
|
21679
|
+
});
|
|
21680
|
+
div.addEventListener('drag', function(event) {
|
|
21681
|
+
if (conversationNativeDrag.row === this && (event.clientX || event.clientY)) {
|
|
21682
|
+
window.updateConversationNativeDrag(event.clientX, event.clientY);
|
|
21683
|
+
}
|
|
21684
|
+
});
|
|
21685
|
+
div.addEventListener('dragend', function() {
|
|
21686
|
+
var releasedRow = this;
|
|
21687
|
+
list.classList.remove('conversation-reordering');
|
|
21688
|
+
Array.from(list.querySelectorAll('.conv-item')).forEach(function(row) { row.classList.remove('drag-over'); });
|
|
21689
|
+
window.animateConversationGlassTo(releasedRow, { onArrive: function() {
|
|
21690
|
+
releasedRow.classList.remove('dragging', 'conversation-glass-dragging');
|
|
21691
|
+
conversationNativeDrag.row = null;
|
|
21692
|
+
conversationNativeDrag.float = null;
|
|
21693
|
+
conversationNativeDrag.arrived = false;
|
|
21694
|
+
window.setLiquidSidebarGestureLock('conversation-reorder', false);
|
|
21695
|
+
renderConversations();
|
|
21696
|
+
} });
|
|
21697
|
+
});
|
|
21698
|
+
div.addEventListener('dragover', function(event) {
|
|
21699
|
+
window.updateConversationNativeDrag(event.clientX, event.clientY);
|
|
21700
|
+
var dragging = list.querySelector('.conv-item.dragging');
|
|
21701
|
+
if (!dragging || dragging === this) return;
|
|
21702
|
+
var convsNow = currentWorkspaceConversations();
|
|
21703
|
+
var draggingConv = convsNow.find(function(item) { return String(item.id) === String(dragging.getAttribute('data-conversation-id')); });
|
|
21704
|
+
var targetConv = convsNow.find(function(item) { return String(item.id) === String(this.getAttribute('data-conversation-id')); }, this);
|
|
21705
|
+
if (!draggingConv || !targetConv || !!draggingConv.pinned !== !!targetConv.pinned) return;
|
|
21706
|
+
event.preventDefault();
|
|
21707
|
+
var targetRect = this.getBoundingClientRect();
|
|
21708
|
+
var after = event.clientY >= targetRect.top + targetRect.height / 2;
|
|
21709
|
+
var anchor = after ? this.nextSibling : this;
|
|
21710
|
+
if (anchor !== dragging) list.insertBefore(dragging, anchor);
|
|
21711
|
+
Array.from(list.querySelectorAll('.conv-item')).forEach(function(row) { row.classList.remove('drag-over'); });
|
|
21712
|
+
});
|
|
21713
|
+
div.addEventListener('dragleave', function() { this.classList.remove('drag-over'); });
|
|
21714
|
+
div.addEventListener('drop', function(event) {
|
|
21715
|
+
event.preventDefault();
|
|
21716
|
+
event.stopPropagation();
|
|
21717
|
+
this.classList.remove('drag-over');
|
|
21718
|
+
list.dataset.conversationDropCommitted = 'true';
|
|
21719
|
+
window.commitConversationDomOrder();
|
|
21720
|
+
});
|
|
21721
|
+
existingRows[String(conv.id || '')] = div;
|
|
21722
|
+
}
|
|
21723
|
+
div.className = 'conv-item' + (conv.active ? ' active' : '');
|
|
21724
|
+
if (running) div.classList.add('marquee-border');
|
|
21725
|
+
div.setAttribute('tabindex', conv.active ? '0' : '-1');
|
|
21726
|
+
div.setAttribute('aria-current', conv.active ? 'true' : 'false');
|
|
21727
|
+
if (div.getAttribute('data-inner-key') !== innerKey) {
|
|
21728
|
+
div.setAttribute('data-inner-key', innerKey);
|
|
21729
|
+
div.innerHTML = nextInner;
|
|
21730
|
+
}
|
|
21731
|
+
div.onclick = function(idx) { return function(event) { window.activateConversationWithGlass(idx, event.currentTarget); }; }(i);
|
|
21732
|
+
orderedRows.push(div);
|
|
19639
21733
|
}
|
|
19640
|
-
|
|
21734
|
+
Object.keys(existingRows).forEach(function(id) {
|
|
21735
|
+
if (keepRows[id]) return;
|
|
21736
|
+
var stale = existingRows[id];
|
|
21737
|
+
if (stale && stale.parentNode === list) stale.parentNode.removeChild(stale);
|
|
21738
|
+
});
|
|
21739
|
+
orderedRows.forEach(function(row, idx) {
|
|
21740
|
+
var current = list.children[idx];
|
|
21741
|
+
if (current === row) return;
|
|
21742
|
+
list.insertBefore(row, current || null);
|
|
21743
|
+
});
|
|
19641
21744
|
updateWorkspaceGate();
|
|
19642
21745
|
}
|
|
19643
21746
|
|
|
@@ -19925,16 +22028,28 @@ window.archiveConv = function(conversationId) {
|
|
|
19925
22028
|
var currentConvs = currentWorkspaceConversations();
|
|
19926
22029
|
var currentIdx = currentConvs.findIndex(function(item) { return String(item && item.id || 'default') === targetId; });
|
|
19927
22030
|
if (currentIdx >= 0) currentConvs.splice(currentIdx, 1);
|
|
19928
|
-
if (!currentConvs.length)
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
|
|
19932
|
-
|
|
19933
|
-
|
|
19934
|
-
|
|
19935
|
-
|
|
19936
|
-
|
|
19937
|
-
|
|
22031
|
+
if (!currentConvs.length) {
|
|
22032
|
+
// 工作区所有对话被清空后保持为空:不再自动创建 default 对话。
|
|
22033
|
+
for (var emptyI = 0; emptyI < currentConvs.length; emptyI++) currentConvs[emptyI].active = false;
|
|
22034
|
+
state.workspaceActiveConversation[workspaceKey] = -1;
|
|
22035
|
+
state.activeConversation = -1;
|
|
22036
|
+
state.conversations = currentConvs;
|
|
22037
|
+
if (priorActiveId === targetId) {
|
|
22038
|
+
state.activeBackendConversationId = '';
|
|
22039
|
+
if (els['chat-area']) els['chat-area'].innerHTML = '';
|
|
22040
|
+
if (window.renderFlowTakeover) window.renderFlowTakeover(false, '', { target: targetRuntime });
|
|
22041
|
+
}
|
|
22042
|
+
} else {
|
|
22043
|
+
var nextActiveId = priorActiveId && priorActiveId !== targetId ? priorActiveId : String(currentConvs[Math.min(Math.max(currentIdx, 0), currentConvs.length - 1)].id || 'default');
|
|
22044
|
+
var nextActiveIndex = Math.max(0, currentConvs.findIndex(function(item) { return String(item && item.id || '') === nextActiveId; }));
|
|
22045
|
+
for (var i = 0; i < currentConvs.length; i++) currentConvs[i].active = i === nextActiveIndex;
|
|
22046
|
+
state.workspaceActiveConversation[workspaceKey] = nextActiveIndex;
|
|
22047
|
+
state.activeConversation = nextActiveIndex;
|
|
22048
|
+
state.conversations = currentConvs;
|
|
22049
|
+
if (priorActiveId === targetId) {
|
|
22050
|
+
state.activeBackendConversationId = nextActiveId;
|
|
22051
|
+
if (els['chat-area']) els['chat-area'].innerHTML = '';
|
|
22052
|
+
}
|
|
19938
22053
|
}
|
|
19939
22054
|
if (state.flowTakeovers) {
|
|
19940
22055
|
var archivedFlowKey = runtimeKeyFor(targetRuntime.workspaceId, targetRuntime.conversationId);
|
|
@@ -20138,6 +22253,7 @@ window.renderMemoryLab = function() {
|
|
|
20138
22253
|
html += '</div>';
|
|
20139
22254
|
panel.innerHTML = html;
|
|
20140
22255
|
if (state.memoryLabSearchQuery) window.updateMemoryLabSearch(state.memoryLabSearchQuery);
|
|
22256
|
+
wireLiquidRailInteractions(panel.querySelector('.memory-lab-view-menu'), 'button[data-memory-view]');
|
|
20141
22257
|
if (state.memoryLabView === 'overview') requestAnimationFrame(function() { window.initMemoryLabOverview(); });
|
|
20142
22258
|
state.memoryLabNavDirection = 'fade';
|
|
20143
22259
|
};
|
|
@@ -20146,8 +22262,8 @@ window.memoryLabTopbar = function(lab) {
|
|
|
20146
22262
|
var view = state.memoryLabView || 'overview';
|
|
20147
22263
|
return '<div class="memory-lab-panel"><div class="memory-lab-topbar">' +
|
|
20148
22264
|
'<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>' +
|
|
22265
|
+
'<button data-memory-view="overview" class="' + (view === 'overview' ? 'active' : '') + '" onclick="window.switchMemoryLabView(\'overview\')">' + esc(t('memoryLab.overview')) + '</button>' +
|
|
22266
|
+
'<button data-memory-view="detail" class="' + (view === 'detail' ? 'active' : '') + '" onclick="window.switchMemoryLabView(\'detail\')">' + esc(t('memoryLab.detail')) + '</button>' +
|
|
20151
22267
|
'</div>' +
|
|
20152
22268
|
'<div class="memory-lab-search">' +
|
|
20153
22269
|
'<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 +23001,7 @@ window.showPluginList = function(tab, options) {
|
|
|
20885
23001
|
state.activeSubWindowView = { name: 'plugins', tab: requested };
|
|
20886
23002
|
}
|
|
20887
23003
|
window.syncPluginTabs(!pluginOpen || !!(options && options.focusTab));
|
|
23004
|
+
wireLiquidRailInteractions(document.querySelector('.plugin-tabs[role="tablist"]'), '.stab-btn[data-plugin-tab]');
|
|
20888
23005
|
var panel = document.getElementById('plugin-panel');
|
|
20889
23006
|
if (panel) panel.replaceChildren();
|
|
20890
23007
|
if (requested === 'mcp') window.renderMcpManager();
|
|
@@ -21798,6 +23915,30 @@ window.renderLeftWsList = function() {
|
|
|
21798
23915
|
var holder = document.createElement('div');
|
|
21799
23916
|
holder.innerHTML = html;
|
|
21800
23917
|
appendDomNodesInBatches(container, Array.from(holder.children), 'workspace-list', 24);
|
|
23918
|
+
wireLiquidRailInteractions(container, '.left-ws-item');
|
|
23919
|
+
};
|
|
23920
|
+
|
|
23921
|
+
window.commitConversationDomOrder = function() {
|
|
23922
|
+
var list = els['conversation-list'];
|
|
23923
|
+
var convs = currentWorkspaceConversations();
|
|
23924
|
+
if (!list || !convs.length) return;
|
|
23925
|
+
window.cancelConversationGlassFlight();
|
|
23926
|
+
var byId = {};
|
|
23927
|
+
convs.forEach(function(item) { byId[String(item.id)] = item; });
|
|
23928
|
+
var ids = Array.from(list.querySelectorAll('.conv-item')).map(function(row) {
|
|
23929
|
+
return String(row.getAttribute('data-conversation-id') || '');
|
|
23930
|
+
}).filter(function(id) { return !!byId[id]; });
|
|
23931
|
+
if (ids.length !== convs.length) return renderConversations();
|
|
23932
|
+
var activeId = activeConversationId();
|
|
23933
|
+
convs.splice.apply(convs, [0, convs.length].concat(ids.map(function(id) { return byId[id]; })));
|
|
23934
|
+
setActiveWorkspaceConversationById(activeId);
|
|
23935
|
+
renderConversations();
|
|
23936
|
+
Promise.resolve(api.reorderConversations && api.reorderConversations(ids)).then(function(saved) {
|
|
23937
|
+
if (!saved) throw new Error(t('conversation.reorderFailed'));
|
|
23938
|
+
}).catch(function() {
|
|
23939
|
+
showUiNotice(t('conversation.reorderFailed'), 'error', 'reorder-conversations');
|
|
23940
|
+
if (api.getState) api.getState(currentConversationTarget(activeId)).then(function(s) { if (s) applyWorkspaceStateFromBackend(s); });
|
|
23941
|
+
});
|
|
21801
23942
|
};
|
|
21802
23943
|
|
|
21803
23944
|
window.handleWorkspaceKey = function(event) {
|
|
@@ -21916,10 +24057,10 @@ window.showNewWorkspaceDialog = function() {
|
|
|
21916
24057
|
state.activeSubWindowView = { name: 'workspaceNew' };
|
|
21917
24058
|
var html = '<div style="padding:10px;">' +
|
|
21918
24059
|
'<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
|
|
24060
|
+
'<div class="workspace-type-pager" role="radiogroup" aria-label="' + escAttr(t('workspace.type')) + '">' +
|
|
24061
|
+
'<label class="workspace-type-option active"><input type="radio" name="ws-type" value="internal" checked onchange="window.toggleWsType()">' + esc(t('workspace.internal')) + '</label>' +
|
|
24062
|
+
'<label class="workspace-type-option"><input type="radio" name="ws-type" value="external" onchange="window.toggleWsType()">' + esc(t('workspace.external')) + '</label>' +
|
|
24063
|
+
'<label class="workspace-type-option"><input type="radio" name="ws-type" value="ssh" onchange="window.toggleWsType()">' + esc(t('workspace.ssh')) + '</label>' +
|
|
21923
24064
|
'</div>' +
|
|
21924
24065
|
'<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
24066
|
'<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 +24085,7 @@ window.showNewWorkspaceDialog = function() {
|
|
|
21944
24085
|
'<button class="sec-btn" onclick="window.closeSubWin()">' + esc(t('common.cancel')) + '</button>' +
|
|
21945
24086
|
'</div></div>';
|
|
21946
24087
|
window.openSubWin(t('workspace.new'), html);
|
|
24088
|
+
wireLiquidRailInteractions(document.querySelector('.workspace-type-pager'), '.workspace-type-option');
|
|
21947
24089
|
setTimeout(function() {
|
|
21948
24090
|
var inp = document.getElementById('new-ws-input');
|
|
21949
24091
|
if (inp) inp.focus();
|
|
@@ -21955,6 +24097,11 @@ window.toggleWsType = function() {
|
|
|
21955
24097
|
for (var i = 0; i < radios.length; i++) {
|
|
21956
24098
|
if (radios[i].checked) { selected = radios[i].value; break; }
|
|
21957
24099
|
}
|
|
24100
|
+
var options = document.querySelectorAll('.workspace-type-option');
|
|
24101
|
+
for (var oi = 0; oi < options.length; oi++) {
|
|
24102
|
+
var radio = options[oi].querySelector('input[name="ws-type"]');
|
|
24103
|
+
options[oi].classList.toggle('active', !!(radio && radio.checked));
|
|
24104
|
+
}
|
|
21958
24105
|
var extPath = document.getElementById('ws-ext-path');
|
|
21959
24106
|
var sshFields = document.getElementById('ws-ssh-fields');
|
|
21960
24107
|
var nameLabel = document.getElementById('ws-name-label-dlg');
|
|
@@ -23058,6 +25205,7 @@ function schedulePostStartupUiRendering() {
|
|
|
23058
25205
|
this.setAttribute('aria-valuenow', String(Math.round(size)));
|
|
23059
25206
|
});
|
|
23060
25207
|
handles[i].addEventListener('mousedown', function(e) {
|
|
25208
|
+
if (window.liquidSidebarGesturesLocked && window.liquidSidebarGesturesLocked()) return;
|
|
23061
25209
|
e.preventDefault();
|
|
23062
25210
|
var handle = this;
|
|
23063
25211
|
var target = this.getAttribute('data-target');
|