@mochabug/adapt-web 1.0.0-rc43 → 1.0.0-rc47

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.
@@ -86,7 +86,7 @@ export class AdaptCapWidget {
86
86
  --mb-cap-spinner-bg: #e5e7eb;
87
87
 
88
88
  /* Typography */
89
- --mb-cap-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
89
+ --mb-cap-font: inherit;
90
90
  --mb-cap-font-size: 15px;
91
91
  --mb-cap-font-weight: 500;
92
92
 
package/dist/esm/index.js CHANGED
@@ -6,8 +6,8 @@ if (typeof window !== "undefined" &&
6
6
  }
7
7
  import { configure, createAdaptClient, resetConfig, } from "@mochabug/adapt-core";
8
8
  import { createConnectClient } from "@mochabug/adapt-core/connect";
9
- import { getIframeSrc } from "./iframe-url.js";
10
9
  import { AdaptCapWidget } from "./AdaptCapWidget.js";
10
+ import { getIframeSrc } from "./iframe-url.js";
11
11
  // Re-export standalone Cap widget for independent use
12
12
  export { AdaptCapWidget, } from "./AdaptCapWidget.js";
13
13
  // Re-export challenge functions for direct use
@@ -36,9 +36,14 @@ const DEFAULT_STYLES = `
36
36
  --mb-adapt-dialog-z: 1000;
37
37
  --mb-adapt-dialog-width: 80%;
38
38
  --mb-adapt-dialog-height: 80%;
39
- --mb-adapt-drag-handle-width: 6px;
40
- --mb-adapt-drag-handle-color: #d1d5db;
41
- --mb-adapt-drag-handle-hover: #6b7280;
39
+ --mb-adapt-drag-handle-width: 8px;
40
+ --mb-adapt-drag-handle-color: #cbd5e1;
41
+ --mb-adapt-drag-handle-hover: #94a3b8;
42
+ --mb-adapt-toolbar-height: 32px;
43
+ --mb-adapt-toolbar-bg: rgba(249, 250, 251, 0.85);
44
+ --mb-adapt-toolbar-border: #e5e7eb;
45
+ --mb-adapt-toolbar-color: #6b7280;
46
+ --mb-adapt-toolbar-btn-hover: #e5e7eb;
42
47
  --mb-adapt-banner-height: 40px;
43
48
  --mb-adapt-banner-bg: #f9fafb;
44
49
  --mb-adapt-banner-border: #e5e7eb;
@@ -60,6 +65,7 @@ const DEFAULT_STYLES = `
60
65
  --mb-adapt-cap-checkbox-background: #f8fafc;
61
66
  --mb-adapt-cap-spinner-color: #6366f1;
62
67
  --mb-adapt-cap-spinner-bg: #e2e8f0;
68
+ --mb-adapt-cap-font: inherit;
63
69
  --mb-adapt-cap-spinner-thickness: 3px;
64
70
  --mb-adapt-cap-shadow: 0 4px 20px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.04);
65
71
  --mb-adapt-cap-shadow-hover: 0 8px 30px rgba(0, 0, 0, 0.12), 0 4px 12px rgba(0, 0, 0, 0.06);
@@ -70,8 +76,12 @@ const DEFAULT_STYLES = `
70
76
  --mb-adapt-dialog-bg: #1f2937;
71
77
  --mb-adapt-banner-bg: #111827;
72
78
  --mb-adapt-banner-border: #374151;
73
- --mb-adapt-drag-handle-color: #4b5563;
74
- --mb-adapt-drag-handle-hover: #6b7280;
79
+ --mb-adapt-drag-handle-color: #475569;
80
+ --mb-adapt-drag-handle-hover: #64748b;
81
+ --mb-adapt-toolbar-bg: rgba(17, 24, 39, 0.85);
82
+ --mb-adapt-toolbar-border: #374151;
83
+ --mb-adapt-toolbar-color: #9ca3af;
84
+ --mb-adapt-toolbar-btn-hover: #374151;
75
85
  --mb-adapt-button-color: #e5e7eb;
76
86
  --mb-adapt-button-hover-bg: #374151;
77
87
 
@@ -121,12 +131,6 @@ const DEFAULT_STYLES = `
121
131
  transition: opacity 0.1s ease-out;
122
132
  }
123
133
 
124
- /* When inside main-container (dialog mode), allow iframe to size itself */
125
- .mb-adapt__main-container .mb-adapt__iframe {
126
- height: auto;
127
- min-height: 300px;
128
- }
129
-
130
134
  .mb-adapt__iframe--visible {
131
135
  opacity: 1;
132
136
  }
@@ -143,11 +147,32 @@ const DEFAULT_STYLES = `
143
147
  background-color: var(--mb-adapt-drag-handle-color);
144
148
  cursor: ew-resize;
145
149
  z-index: 10;
146
- transition: background-color 0.2s, left 0.15s ease-out;
150
+ transition: background-color 0.2s, left 0.15s ease-out, box-shadow 0.2s;
151
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04);
152
+ }
153
+
154
+ .mb-adapt__drag-handle::after {
155
+ content: '';
156
+ position: absolute;
157
+ top: 50%;
158
+ left: 50%;
159
+ transform: translate(-50%, -50%);
160
+ width: 4px;
161
+ height: 24px;
162
+ background-image: radial-gradient(circle, currentColor 1px, transparent 1px);
163
+ background-size: 4px 6px;
164
+ background-position: center;
165
+ opacity: 0.5;
166
+ color: var(--mb-adapt-drag-handle-hover);
147
167
  }
148
168
 
149
169
  .mb-adapt__drag-handle:hover {
150
170
  background-color: var(--mb-adapt-drag-handle-hover);
171
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08);
172
+ }
173
+
174
+ .mb-adapt__drag-handle:hover::after {
175
+ opacity: 0.8;
151
176
  }
152
177
 
153
178
  .mb-adapt__drag-overlay {
@@ -228,43 +253,42 @@ const DEFAULT_STYLES = `
228
253
  position: absolute;
229
254
  top: 50%;
230
255
  transform: translateY(-50%);
231
- background: linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(248,250,252,0.98) 100%);
232
- border: none;
233
- color: #64748b;
256
+ background: linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(241,245,249,0.98) 100%);
257
+ border: 1px solid rgba(0, 0, 0, 0.08);
258
+ color: #475569;
234
259
  cursor: pointer;
235
- width: 14px;
236
- height: 48px;
260
+ width: 22px;
261
+ height: 64px;
237
262
  display: none;
238
263
  align-items: center;
239
264
  justify-content: center;
240
265
  z-index: 100;
241
266
  transition: all 0.2s ease-out;
242
- font-size: 14px;
267
+ font-size: 16px;
268
+ font-weight: 600;
243
269
  line-height: 1;
244
270
  padding: 0;
245
271
  box-shadow:
246
- 0 2px 8px rgba(0, 0, 0, 0.12),
247
- 0 1px 3px rgba(0, 0, 0, 0.08),
248
- inset 0 1px 0 rgba(255, 255, 255, 0.8);
249
- border-radius: 4px;
250
- backdrop-filter: blur(8px);
251
- -webkit-backdrop-filter: blur(8px);
272
+ 0 2px 12px rgba(0, 0, 0, 0.14),
273
+ 0 1px 4px rgba(0, 0, 0, 0.10);
274
+ border-radius: 6px;
275
+ backdrop-filter: blur(12px);
276
+ -webkit-backdrop-filter: blur(12px);
252
277
  }
253
278
 
254
279
  .mb-adapt__expand-button:hover {
255
- background: linear-gradient(180deg, rgba(255,255,255,1) 0%, rgba(241,245,249,1) 100%);
256
- color: #475569;
257
- transform: translateY(-50%) scale(1.02);
280
+ background: linear-gradient(180deg, rgba(255,255,255,1) 0%, rgba(226,232,240,1) 100%);
281
+ color: #1e293b;
282
+ transform: translateY(-50%) scale(1.04);
258
283
  box-shadow:
259
- 0 4px 12px rgba(0, 0, 0, 0.15),
260
- 0 2px 4px rgba(0, 0, 0, 0.1),
261
- inset 0 1px 0 rgba(255, 255, 255, 1);
284
+ 0 4px 16px rgba(0, 0, 0, 0.18),
285
+ 0 2px 6px rgba(0, 0, 0, 0.12);
262
286
  }
263
287
 
264
288
  .mb-adapt__expand-button:active {
265
- transform: translateY(-50%) scale(0.98);
289
+ transform: translateY(-50%) scale(0.97);
266
290
  box-shadow:
267
- 0 1px 4px rgba(0, 0, 0, 0.1),
291
+ 0 1px 4px rgba(0, 0, 0, 0.10),
268
292
  0 1px 2px rgba(0, 0, 0, 0.06);
269
293
  }
270
294
 
@@ -276,83 +300,79 @@ const DEFAULT_STYLES = `
276
300
  left: 0;
277
301
  border-top-left-radius: 0;
278
302
  border-bottom-left-radius: 0;
303
+ border-left: none;
279
304
  box-shadow:
280
- 2px 2px 8px rgba(0, 0, 0, 0.12),
281
- 1px 1px 3px rgba(0, 0, 0, 0.08),
282
- inset 0 1px 0 rgba(255, 255, 255, 0.8);
305
+ 2px 2px 12px rgba(0, 0, 0, 0.14),
306
+ 1px 1px 4px rgba(0, 0, 0, 0.10);
283
307
  }
284
308
 
285
309
  .mb-adapt__expand-button--left:hover {
286
310
  box-shadow:
287
- 3px 4px 12px rgba(0, 0, 0, 0.15),
288
- 2px 2px 4px rgba(0, 0, 0, 0.1),
289
- inset 0 1px 0 rgba(255, 255, 255, 1);
311
+ 3px 4px 16px rgba(0, 0, 0, 0.18),
312
+ 2px 2px 6px rgba(0, 0, 0, 0.12);
290
313
  }
291
314
 
292
315
  .mb-adapt__expand-button--right {
293
316
  right: 0;
294
317
  border-top-right-radius: 0;
295
318
  border-bottom-right-radius: 0;
319
+ border-right: none;
296
320
  box-shadow:
297
- -2px 2px 8px rgba(0, 0, 0, 0.12),
298
- -1px 1px 3px rgba(0, 0, 0, 0.08),
299
- inset 0 1px 0 rgba(255, 255, 255, 0.8);
321
+ -2px 2px 12px rgba(0, 0, 0, 0.14),
322
+ -1px 1px 4px rgba(0, 0, 0, 0.10);
300
323
  }
301
324
 
302
325
  .mb-adapt__expand-button--right:hover {
303
326
  box-shadow:
304
- -3px 4px 12px rgba(0, 0, 0, 0.15),
305
- -2px 2px 4px rgba(0, 0, 0, 0.1),
306
- inset 0 1px 0 rgba(255, 255, 255, 1);
327
+ -3px 4px 16px rgba(0, 0, 0, 0.18),
328
+ -2px 2px 6px rgba(0, 0, 0, 0.12);
307
329
  }
308
330
 
309
331
  .mb-adapt__expand-button--dialog {
310
332
  left: 50%;
311
333
  top: 0;
312
334
  transform: translateX(-50%);
313
- width: 56px;
314
- height: 20px;
335
+ width: 64px;
336
+ height: 24px;
315
337
  font-size: 16px;
316
- line-height: 20px;
338
+ line-height: 24px;
317
339
  vertical-align: middle;
318
340
  text-align: center;
319
- border-radius: 6px;
341
+ border-radius: 8px;
320
342
  border-top-left-radius: 0;
321
343
  border-top-right-radius: 0;
344
+ border-top: none;
322
345
  box-shadow:
323
- 0 3px 8px rgba(0, 0, 0, 0.12),
324
- 0 1px 3px rgba(0, 0, 0, 0.08),
325
- inset 0 1px 0 rgba(255, 255, 255, 0.8);
346
+ 0 3px 12px rgba(0, 0, 0, 0.14),
347
+ 0 1px 4px rgba(0, 0, 0, 0.10);
326
348
  }
327
349
 
328
350
  .mb-adapt__expand-button--dialog:hover {
329
- transform: translateX(-50%) scale(1.02);
351
+ transform: translateX(-50%) scale(1.04);
330
352
  box-shadow:
331
- 0 5px 12px rgba(0, 0, 0, 0.15),
332
- 0 2px 4px rgba(0, 0, 0, 0.1),
333
- inset 0 1px 0 rgba(255, 255, 255, 1);
353
+ 0 5px 16px rgba(0, 0, 0, 0.18),
354
+ 0 2px 6px rgba(0, 0, 0, 0.12);
334
355
  }
335
356
 
336
357
  .mb-adapt__expand-button--dialog:active {
337
- transform: translateX(-50%) scale(0.98);
358
+ transform: translateX(-50%) scale(0.97);
338
359
  }
339
360
 
340
361
  .mb-adapt--dark .mb-adapt__expand-button {
341
- background: linear-gradient(180deg, rgba(31,41,55,0.98) 0%, rgba(17,24,39,0.98) 100%);
342
- color: #9ca3af;
362
+ background: linear-gradient(180deg, rgba(51,65,85,0.98) 0%, rgba(30,41,59,0.98) 100%);
363
+ border-color: rgba(255, 255, 255, 0.1);
364
+ color: #cbd5e1;
343
365
  box-shadow:
344
- 0 2px 8px rgba(0, 0, 0, 0.3),
345
- 0 1px 3px rgba(0, 0, 0, 0.2),
346
- inset 0 1px 0 rgba(255, 255, 255, 0.1);
366
+ 0 2px 12px rgba(0, 0, 0, 0.35),
367
+ 0 1px 4px rgba(0, 0, 0, 0.25);
347
368
  }
348
369
 
349
370
  .mb-adapt--dark .mb-adapt__expand-button:hover {
350
- background: linear-gradient(180deg, rgba(55,65,81,1) 0%, rgba(31,41,55,1) 100%);
351
- color: #d1d5db;
371
+ background: linear-gradient(180deg, rgba(71,85,105,1) 0%, rgba(51,65,85,1) 100%);
372
+ color: #f1f5f9;
352
373
  box-shadow:
353
- 0 4px 12px rgba(0, 0, 0, 0.4),
354
- 0 2px 4px rgba(0, 0, 0, 0.3),
355
- inset 0 1px 0 rgba(255, 255, 255, 0.15);
374
+ 0 4px 16px rgba(0, 0, 0, 0.45),
375
+ 0 2px 6px rgba(0, 0, 0, 0.35);
356
376
  }
357
377
 
358
378
  .mb-adapt__drag-handle--disabled {
@@ -360,14 +380,90 @@ const DEFAULT_STYLES = `
360
380
  opacity: 0.3;
361
381
  }
362
382
 
383
+ .mb-adapt__frame--has-toolbar {
384
+ display: flex;
385
+ flex-direction: column;
386
+ }
387
+
388
+ .mb-adapt__frame--has-toolbar.mb-adapt__frame--hidden {
389
+ display: none;
390
+ }
391
+
392
+ .mb-adapt__frame--has-toolbar .mb-adapt__iframe {
393
+ flex: 1;
394
+ height: auto;
395
+ }
396
+
397
+ .mb-adapt__toolbar {
398
+ display: flex;
399
+ align-items: center;
400
+ justify-content: flex-end;
401
+ height: var(--mb-adapt-toolbar-height);
402
+ background: var(--mb-adapt-toolbar-bg);
403
+ border-bottom: 1px solid var(--mb-adapt-toolbar-border);
404
+ padding: 0 4px;
405
+ flex-shrink: 0;
406
+ backdrop-filter: blur(12px);
407
+ -webkit-backdrop-filter: blur(12px);
408
+ gap: 2px;
409
+ }
410
+
411
+ .mb-adapt__toolbar-btn {
412
+ display: flex;
413
+ align-items: center;
414
+ justify-content: center;
415
+ width: 26px;
416
+ height: 26px;
417
+ border: none;
418
+ border-radius: 6px;
419
+ background: transparent;
420
+ color: var(--mb-adapt-toolbar-color);
421
+ cursor: pointer;
422
+ padding: 0;
423
+ transition: background-color 0.15s, color 0.15s;
424
+ }
425
+
426
+ .mb-adapt__toolbar-btn:hover {
427
+ background: var(--mb-adapt-toolbar-btn-hover);
428
+ color: var(--mb-adapt-button-color);
429
+ }
430
+
431
+ .mb-adapt__toolbar-btn svg {
432
+ width: 14px;
433
+ height: 14px;
434
+ stroke: currentColor;
435
+ stroke-width: 2;
436
+ fill: none;
437
+ stroke-linecap: round;
438
+ stroke-linejoin: round;
439
+ }
440
+
441
+ .mb-adapt__toolbar-btn--exit:hover {
442
+ background: #fecaca;
443
+ color: #dc2626;
444
+ }
445
+
446
+ .mb-adapt--dark .mb-adapt__toolbar-btn--exit:hover {
447
+ background: rgba(220, 38, 38, 0.2);
448
+ color: #fca5a5;
449
+ }
450
+
363
451
  .mb-adapt__main-container {
364
452
  position: relative;
453
+ display: flex;
454
+ flex-direction: column;
365
455
  width: 100%;
366
- flex: 0 0 auto;
456
+ flex: 1;
457
+ min-height: 0;
367
458
  overflow: visible;
368
459
  background: transparent;
369
460
  }
370
461
 
462
+ .mb-adapt__main-container .mb-adapt__iframe {
463
+ flex: 1;
464
+ min-height: 0;
465
+ }
466
+
371
467
  /* Responsive dialog - full screen on small viewports */
372
468
  @media (max-width: 600px) {
373
469
  .mb-adapt__dialog {
@@ -395,7 +491,7 @@ const DEFAULT_STYLES = `
395
491
  --cap-checkbox-border-radius: var(--mb-adapt-cap-checkbox-radius);
396
492
  --cap-checkbox-background: var(--mb-adapt-cap-checkbox-background);
397
493
  --cap-checkbox-margin: 0;
398
- --cap-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
494
+ --cap-font: var(--mb-adapt-cap-font);
399
495
  --cap-spinner-color: var(--mb-adapt-cap-spinner-color);
400
496
  --cap-spinner-background-color: var(--mb-adapt-cap-spinner-bg);
401
497
  --cap-spinner-thickness: var(--mb-adapt-cap-spinner-thickness);
@@ -453,6 +549,7 @@ export class AdaptWebClient {
453
549
  this.dragHandleElement = null;
454
550
  this.mainExpandButton = null;
455
551
  this.forkExpandButton = null;
552
+ this.forkToolbarElement = null;
456
553
  // Dialog elements
457
554
  this.mainContainer = null;
458
555
  this.dialogBackdrop = null;
@@ -665,6 +762,7 @@ export class AdaptWebClient {
665
762
  this.dragHandleElement = null;
666
763
  this.mainExpandButton = null;
667
764
  this.forkExpandButton = null;
765
+ this.forkToolbarElement = null;
668
766
  this.mainContainer = null;
669
767
  this.dialogBackdrop = null;
670
768
  this.dialogElement = null;
@@ -720,14 +818,10 @@ export class AdaptWebClient {
720
818
  this.dialogBackdrop.removeEventListener("click", this.backdropClickHandler);
721
819
  this.backdropClickHandler = null;
722
820
  }
723
- // Add new handler if enabled
821
+ // Add new handler if enabled (acts as minimize)
724
822
  if (enabled) {
725
823
  this.backdropClickHandler = () => {
726
- if (this.currentFork) {
727
- this.forkQueue.unshift(this.currentFork);
728
- }
729
- this.currentFork = null;
730
- this.updateDialogVisibility();
824
+ this.handleForkMinimize();
731
825
  };
732
826
  this.dialogBackdrop.addEventListener("click", this.backdropClickHandler);
733
827
  }
@@ -1021,6 +1115,7 @@ export class AdaptWebClient {
1021
1115
  this.dragHandleElement = null;
1022
1116
  this.mainExpandButton = null;
1023
1117
  this.forkExpandButton = null;
1118
+ this.forkToolbarElement = null;
1024
1119
  this.mainContainer = null;
1025
1120
  this.dialogBackdrop = null;
1026
1121
  this.dialogElement = null;
@@ -1070,23 +1165,19 @@ export class AdaptWebClient {
1070
1165
  if (!fork) {
1071
1166
  return;
1072
1167
  }
1073
- // Remove from queue
1074
- this.forkQueue = this.forkQueue.filter((item) => item.fork !== fork);
1075
- // If current fork is done, clear tracking but keep iframe mounted
1076
- // Then check if there's another fork waiting
1168
+ // Remove from queue (but not the current fork — it's tracked separately).
1169
+ // Also discard any previously-completed (stale) forks from the queue.
1170
+ this.forkQueue = this.forkQueue.filter((item) => item.fork !== fork && !item.completed);
1077
1171
  if (this.currentFork?.fork === fork) {
1078
- this.currentFork = null;
1079
- // Check if there's another fork in queue to display
1080
1172
  if (this.forkQueue.length > 0) {
1173
+ // Another fork is waiting — advance to it
1174
+ this.currentFork = null;
1081
1175
  this.activateNextFork();
1082
1176
  }
1083
1177
  else {
1084
- // No more forks - update visibility (hide fork frame in side-by-side,
1085
- // or keep dialog open until user closes it)
1086
- if (this.options.forkDisplayMode === "side-by-side") {
1087
- this.updateSideBySideVisibility();
1088
- }
1089
- // For dialog mode: keep dialog open until user clicks close or backdrop
1178
+ // No active forks remain keep this fork visible (graceful completion)
1179
+ // but mark it so it gets evicted when a new fork arrives.
1180
+ this.currentFork.completed = true;
1090
1181
  }
1091
1182
  }
1092
1183
  }
@@ -1148,9 +1239,10 @@ export class AdaptWebClient {
1148
1239
  this.mainToken = token;
1149
1240
  this.updateMainIframe();
1150
1241
  }
1151
- // Add to queue and activate if no current fork
1242
+ // Add to queue and activate if no current fork or current fork is completed.
1152
1243
  this.forkQueue.push(forkItem);
1153
- if (!this.currentFork) {
1244
+ if (!this.currentFork || this.currentFork.completed) {
1245
+ this.currentFork = null;
1154
1246
  this.activateNextFork();
1155
1247
  }
1156
1248
  }
@@ -1191,11 +1283,16 @@ export class AdaptWebClient {
1191
1283
  updateForkIframe() {
1192
1284
  if (!this.currentFork || !this.forkIframe)
1193
1285
  return;
1194
- // Update iframe src if changed
1195
1286
  const newSrc = getIframeSrc(this.currentFork.url, this.currentFork.token);
1196
- if (this.forkIframe.src !== newSrc) {
1197
- this.forkIframe.src = newSrc;
1198
- }
1287
+ // Replace iframe to force a fresh load — browsers don't reload when only
1288
+ // the hash fragment changes (same plugin URL, different fork token).
1289
+ const parent = this.forkIframe.parentElement;
1290
+ if (parent) {
1291
+ const fresh = this.createHiddenIframe();
1292
+ parent.replaceChild(fresh, this.forkIframe);
1293
+ this.forkIframe = fresh;
1294
+ }
1295
+ this.forkIframe.src = newSrc;
1199
1296
  // Show the iframe with fade-in
1200
1297
  this.showIframe(this.forkIframe);
1201
1298
  }
@@ -1228,6 +1325,10 @@ export class AdaptWebClient {
1228
1325
  // Reset border radius in case coming from dialog mode
1229
1326
  this.forkIframe.style.borderRadius = "";
1230
1327
  this.mainFrameElement.appendChild(this.mainIframe);
1328
+ // Fork toolbar with minimize + exit
1329
+ this.forkToolbarElement = this.createForkToolbar();
1330
+ this.forkFrameElement.classList.add("mb-adapt__frame--has-toolbar");
1331
+ this.forkFrameElement.appendChild(this.forkToolbarElement);
1231
1332
  this.forkFrameElement.appendChild(this.forkIframe);
1232
1333
  // Drag handle - initially hidden until fork arrives
1233
1334
  this.dragHandleElement = document.createElement("div");
@@ -1374,6 +1475,75 @@ export class AdaptWebClient {
1374
1475
  this.forkFrameElement.style.width = `${forkWidth}%`;
1375
1476
  this.dragHandleElement.style.left = `${this.splitPercentage}%`;
1376
1477
  }
1478
+ createForkToolbar() {
1479
+ const toolbar = document.createElement("div");
1480
+ toolbar.className = this.options.classNames?.toolbar || "mb-adapt__toolbar";
1481
+ // Minimize button (horizontal line icon)
1482
+ const minimizeBtn = document.createElement("button");
1483
+ minimizeBtn.className =
1484
+ this.options.classNames?.toolbarButton || "mb-adapt__toolbar-btn";
1485
+ minimizeBtn.setAttribute("aria-label", "Minimize fork");
1486
+ minimizeBtn.setAttribute("title", "Minimize");
1487
+ minimizeBtn.innerHTML = `<svg viewBox="0 0 16 16"><line x1="3" y1="8" x2="13" y2="8"/></svg>`;
1488
+ minimizeBtn.addEventListener("click", () => {
1489
+ this.handleForkMinimize();
1490
+ });
1491
+ // Exit button (X icon) — stops the fork via the API
1492
+ const exitBtn = document.createElement("button");
1493
+ exitBtn.className = `${this.options.classNames?.toolbarButton || "mb-adapt__toolbar-btn"} mb-adapt__toolbar-btn--exit`;
1494
+ exitBtn.setAttribute("aria-label", "Close fork");
1495
+ exitBtn.setAttribute("title", "Close");
1496
+ exitBtn.innerHTML = `<svg viewBox="0 0 16 16"><line x1="4" y1="4" x2="12" y2="12"/><line x1="12" y1="4" x2="4" y2="12"/></svg>`;
1497
+ exitBtn.addEventListener("click", () => {
1498
+ this.handleForkExit();
1499
+ });
1500
+ toolbar.appendChild(minimizeBtn);
1501
+ toolbar.appendChild(exitBtn);
1502
+ return toolbar;
1503
+ }
1504
+ handleForkMinimize() {
1505
+ if (!this.currentFork)
1506
+ return;
1507
+ if (this.options.forkDisplayMode === "side-by-side") {
1508
+ this.splitPercentage = 100;
1509
+ this.updateSideBySideVisibility();
1510
+ }
1511
+ else {
1512
+ // Dialog mode: hide dialog, put fork back in queue so expand button shows
1513
+ this.forkQueue.unshift(this.currentFork);
1514
+ this.currentFork = null;
1515
+ this.updateDialogVisibility();
1516
+ }
1517
+ }
1518
+ handleForkExit() {
1519
+ if (!this.currentFork)
1520
+ return;
1521
+ const fork = this.currentFork.fork;
1522
+ // Stop the fork via the API (best-effort, just like Run.tsx/ClientWindow.tsx)
1523
+ if (this.sessionToken) {
1524
+ this.client.stop(this.sessionToken, fork).catch(() => { });
1525
+ }
1526
+ // Remove from queue immediately (user-dismissable, same as ClientWindow.tsx)
1527
+ this.forkQueue = this.forkQueue.filter((item) => item.fork !== fork);
1528
+ this.currentFork = null;
1529
+ if (this.forkQueue.length > 0) {
1530
+ this.activateNextFork();
1531
+ }
1532
+ else {
1533
+ // Clear the fork iframe
1534
+ if (this.forkIframe) {
1535
+ this.forkIframe.src = "about:blank";
1536
+ this.forkIframe.classList.remove("mb-adapt__iframe--visible");
1537
+ this.forkIframe.classList.add("mb-adapt__iframe--hidden");
1538
+ }
1539
+ if (this.options.forkDisplayMode === "side-by-side") {
1540
+ this.updateSideBySideVisibility();
1541
+ }
1542
+ else {
1543
+ this.updateDialogVisibility();
1544
+ }
1545
+ }
1546
+ }
1377
1547
  createDialogStructure() {
1378
1548
  if (this.mainContainer)
1379
1549
  return;
@@ -1401,14 +1571,10 @@ export class AdaptWebClient {
1401
1571
  this.dialogBackdrop = document.createElement("div");
1402
1572
  this.dialogBackdrop.className =
1403
1573
  this.options.classNames?.dialogBackdrop || "mb-adapt__dialog-backdrop";
1404
- // Set up backdrop click handler if enabled
1574
+ // Set up backdrop click handler if enabled (acts as minimize)
1405
1575
  if (this.options.dialogBackdropClose) {
1406
1576
  this.backdropClickHandler = () => {
1407
- if (this.currentFork) {
1408
- this.forkQueue.unshift(this.currentFork);
1409
- }
1410
- this.currentFork = null;
1411
- this.updateDialogVisibility();
1577
+ this.handleForkMinimize();
1412
1578
  };
1413
1579
  this.dialogBackdrop.addEventListener("click", this.backdropClickHandler);
1414
1580
  }
@@ -1416,28 +1582,13 @@ export class AdaptWebClient {
1416
1582
  this.dialogElement = document.createElement("div");
1417
1583
  this.dialogElement.className =
1418
1584
  this.options.classNames?.dialog || "mb-adapt__dialog";
1419
- const banner = document.createElement("div");
1420
- banner.className =
1421
- this.options.classNames?.dialogBanner || "mb-adapt__dialog-banner";
1422
- const closeBtn = document.createElement("button");
1423
- closeBtn.className =
1424
- this.options.classNames?.dialogClose || "mb-adapt__dialog-close";
1425
- closeBtn.innerHTML = "×";
1426
- closeBtn.setAttribute("aria-label", "Close dialog");
1427
- closeBtn.setAttribute("title", "Close dialog");
1428
- closeBtn.addEventListener("click", () => {
1429
- // Put current fork back in queue before closing
1430
- if (this.currentFork) {
1431
- this.forkQueue.unshift(this.currentFork);
1432
- }
1433
- this.currentFork = null;
1434
- this.updateDialogVisibility();
1435
- });
1436
- banner.appendChild(closeBtn);
1437
- this.dialogElement.appendChild(banner);
1585
+ // Same toolbar as side-by-side (minimize + exit)
1586
+ this.forkToolbarElement = this.createForkToolbar();
1587
+ this.dialogElement.appendChild(this.forkToolbarElement);
1438
1588
  const iframeContainer = document.createElement("div");
1439
1589
  iframeContainer.style.flex = "1";
1440
- iframeContainer.style.overflow = "hidden";
1590
+ iframeContainer.style.overflow = "auto";
1591
+ iframeContainer.style.minHeight = "0";
1441
1592
  // Reuse existing fork iframe or create new one
1442
1593
  if (!this.forkIframe) {
1443
1594
  this.forkIframe = this.createHiddenIframe();
@@ -1508,7 +1659,6 @@ export class AdaptWebClient {
1508
1659
  }
1509
1660
  showIframe(iframe) {
1510
1661
  iframe.classList.remove("mb-adapt__iframe--hidden");
1511
- // Trigger reflow to ensure transition works
1512
1662
  void iframe.offsetHeight;
1513
1663
  iframe.classList.add("mb-adapt__iframe--visible");
1514
1664
  }