@designfever/web-review-kit 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,11 @@
1
1
  import {
2
2
  REVIEW_WORKFLOW_STATUS_OPTIONS,
3
+ clamp,
3
4
  createWebReviewKit,
4
5
  getNumberedReviewItems,
5
- normalizeReviewItemStatus
6
- } from "./chunk-6L2KJ7XL.js";
6
+ normalizeReviewItemStatus,
7
+ runWithAutoScrollBehavior
8
+ } from "./chunk-IN36JHEU.js";
7
9
 
8
10
  // src/react-shell.tsx
9
11
  import React2 from "react";
@@ -68,10 +70,10 @@ function ensureReviewShellStyle() {
68
70
  --df-review-font-size-lg: 14px;
69
71
  --df-review-font-size-xl: 15px;
70
72
  --df-review-font-size-2xl: 18px;
71
- --df-review-font-weight-medium: 650;
72
- --df-review-font-weight-bold: 700;
73
- --df-review-font-weight-strong: 800;
74
- --df-review-font-weight-heavy: 900;
73
+ --df-review-font-weight-medium: 400;
74
+ --df-review-font-weight-bold: 500;
75
+ --df-review-font-weight-strong: 500;
76
+ --df-review-font-weight-heavy: 600;
75
77
  --df-review-line-height-tight: 1.25;
76
78
  --df-review-line-height-base: 1.42;
77
79
  --df-review-line-height-relaxed: 1.55;
@@ -88,7 +90,7 @@ function ensureReviewShellStyle() {
88
90
  --df-review-space-8: 32px;
89
91
  --df-review-control-height-sm: 32px;
90
92
  --df-review-control-height-md: 34px;
91
- --df-review-control-height-lg: 38px;
93
+ --df-review-control-height-lg: 34px;
92
94
  --df-review-radius-xs: 3px;
93
95
  --df-review-radius-sm: 6px;
94
96
  --df-review-radius-md: 8px;
@@ -261,8 +263,9 @@ function ensureReviewShellStyle() {
261
263
 
262
264
  .df-review-shell {
263
265
  --df-review-frame-gutter-x: var(--df-review-space-4);
266
+ position: relative;
264
267
  display: grid;
265
- grid-template-columns: minmax(0, 1fr) 0 32px;
268
+ grid-template-columns: minmax(0, 1fr) 0 48px;
266
269
  grid-template-rows: auto auto minmax(0, 1fr);
267
270
  width: 100%;
268
271
  height: 100%;
@@ -271,7 +274,7 @@ function ensureReviewShellStyle() {
271
274
  }
272
275
 
273
276
  .df-review-shell.is-list-visible {
274
- grid-template-columns: minmax(0, 1fr) clamp(320px, 28vw, 420px) 32px;
277
+ grid-template-columns: minmax(0, 1fr) clamp(320px, 28vw, 420px) 48px;
275
278
  }
276
279
 
277
280
  .df-review-topbar {
@@ -290,23 +293,9 @@ function ensureReviewShellStyle() {
290
293
  box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.025);
291
294
  }
292
295
 
293
- .df-review-presence-row {
294
- grid-column: 1;
295
- grid-row: 2;
296
- position: relative;
297
- z-index: 590;
298
- display: flex;
299
- align-items: center;
300
- justify-content: flex-start;
301
- min-width: 0;
302
- padding: var(--df-review-space-2) var(--df-review-frame-gutter-x);
303
- border-bottom: 1px solid var(--df-review-line-soft);
304
- background: var(--df-review-panel);
305
- }
306
-
307
296
  .df-review-address {
308
297
  display: grid;
309
- grid-template-columns: auto minmax(160px, 1fr) auto auto;
298
+ grid-template-columns: auto minmax(160px, 1fr) auto;
310
299
  align-items: stretch;
311
300
  gap: var(--df-review-space-2);
312
301
  width: 100%;
@@ -314,8 +303,16 @@ function ensureReviewShellStyle() {
314
303
  margin: 0 auto;
315
304
  }
316
305
 
306
+ .df-review-address-actions {
307
+ display: flex;
308
+ align-items: stretch;
309
+ gap: 4px;
310
+ min-width: 0;
311
+ }
312
+
317
313
  .df-review-address input {
318
314
  width: 100%;
315
+ height: var(--df-review-control-height-md);
319
316
  min-height: var(--df-review-control-height-md);
320
317
  border: 1px solid var(--df-review-line);
321
318
  border-radius: var(--df-review-radius-sm);
@@ -333,7 +330,6 @@ function ensureReviewShellStyle() {
333
330
  }
334
331
 
335
332
  .df-review-address button,
336
- .df-review-side-toggle,
337
333
  .df-review-presets button,
338
334
  .df-review-overlay-button,
339
335
  .df-review-mode-button,
@@ -342,6 +338,7 @@ function ensureReviewShellStyle() {
342
338
  .df-review-settings-actions button,
343
339
  .df-review-prompt-block-header button,
344
340
  .df-review-item-actions button {
341
+ height: var(--df-review-control-height-md);
345
342
  min-height: var(--df-review-control-height-md);
346
343
  border: 1px solid var(--df-review-line);
347
344
  border-radius: var(--df-review-radius-sm);
@@ -349,12 +346,11 @@ function ensureReviewShellStyle() {
349
346
  box-shadow: var(--df-review-shadow-control);
350
347
  color: var(--df-review-text);
351
348
  font-size: var(--df-review-font-size-sm);
352
- font-weight: 700;
349
+ font-weight: 500;
353
350
  transition: border-color 140ms ease, background 140ms ease, color 140ms ease, box-shadow 140ms ease, opacity 140ms ease;
354
351
  }
355
352
 
356
353
  .df-review-address button:hover,
357
- .df-review-side-toggle:hover,
358
354
  .df-review-presets button:hover,
359
355
  .df-review-overlay-button:hover,
360
356
  .df-review-mode-button:hover,
@@ -392,6 +388,24 @@ function ensureReviewShellStyle() {
392
388
  color: var(--df-review-accent);
393
389
  }
394
390
 
391
+ .df-review-address-refresh {
392
+ display: inline-grid;
393
+ place-items: center;
394
+ width: var(--df-review-control-height-md);
395
+ min-width: var(--df-review-control-height-md);
396
+ padding: 0;
397
+ }
398
+
399
+ .df-review-address-refresh svg {
400
+ width: 16px;
401
+ height: 16px;
402
+ fill: none;
403
+ stroke: currentColor;
404
+ stroke-linecap: round;
405
+ stroke-linejoin: round;
406
+ stroke-width: 1.9;
407
+ }
408
+
395
409
  .df-review-sitemap-button svg,
396
410
  .df-review-sitemap-header button svg {
397
411
  width: 18px;
@@ -455,12 +469,13 @@ function ensureReviewShellStyle() {
455
469
 
456
470
  .df-review-sitemap-header strong {
457
471
  font-size: var(--df-review-font-size-lg);
472
+ font-weight: 600;
458
473
  }
459
474
 
460
475
  .df-review-sitemap-header span {
461
476
  color: var(--df-review-muted);
462
477
  font-size: var(--df-review-font-size-sm);
463
- font-weight: 700;
478
+ font-weight: 500;
464
479
  }
465
480
 
466
481
  .df-review-sitemap-header button {
@@ -474,7 +489,7 @@ function ensureReviewShellStyle() {
474
489
  background: var(--df-review-control);
475
490
  color: var(--df-review-text);
476
491
  font-size: var(--df-review-font-size-md);
477
- font-weight: 800;
492
+ font-weight: 500;
478
493
  }
479
494
 
480
495
  .df-review-sitemap-header button:hover {
@@ -502,7 +517,7 @@ function ensureReviewShellStyle() {
502
517
  background: var(--df-review-control);
503
518
  box-shadow: var(--df-review-shadow-control);
504
519
  font-size: var(--df-review-font-size-xs);
505
- font-weight: 850;
520
+ font-weight: 600;
506
521
  }
507
522
 
508
523
  .df-review-sitemap-list {
@@ -538,7 +553,7 @@ function ensureReviewShellStyle() {
538
553
  0 1px 0 var(--df-review-line);
539
554
  color: var(--df-review-muted);
540
555
  font-size: var(--df-review-font-size-xs);
541
- font-weight: 720;
556
+ font-weight: 500;
542
557
  letter-spacing: 0.03em;
543
558
  text-transform: uppercase;
544
559
  }
@@ -636,7 +651,7 @@ function ensureReviewShellStyle() {
636
651
  overflow-wrap: anywhere;
637
652
  color: var(--df-review-text);
638
653
  font-size: var(--df-review-font-size-md);
639
- font-weight: 650;
654
+ font-weight: 400;
640
655
  line-height: 1.35;
641
656
  }
642
657
 
@@ -648,7 +663,7 @@ function ensureReviewShellStyle() {
648
663
  flex: 0 0 auto;
649
664
  color: var(--df-review-muted);
650
665
  font-family: var(--df-review-font-mono);
651
- font-weight: 500;
666
+ font-weight: 400;
652
667
  white-space: pre;
653
668
  }
654
669
 
@@ -663,14 +678,14 @@ function ensureReviewShellStyle() {
663
678
  color: var(--df-review-muted);
664
679
  font-size: var(--df-review-font-size-sm);
665
680
  font-variant-numeric: tabular-nums;
666
- font-weight: 650;
681
+ font-weight: 400;
667
682
  line-height: 1;
668
683
  text-align: right;
669
684
  }
670
685
 
671
686
  .df-review-sitemap-cell.is-total {
672
687
  color: var(--df-review-accent);
673
- font-weight: 760;
688
+ font-weight: 500;
674
689
  }
675
690
 
676
691
  .df-review-sitemap-cell.is-total strong {
@@ -703,7 +718,7 @@ function ensureReviewShellStyle() {
703
718
  background: var(--df-review-chip-bg);
704
719
  color: var(--df-review-text);
705
720
  font-size: var(--df-review-font-size-xs);
706
- font-weight: 900;
721
+ font-weight: 600;
707
722
  line-height: 1.1;
708
723
  white-space: nowrap;
709
724
  }
@@ -775,12 +790,13 @@ function ensureReviewShellStyle() {
775
790
  .df-review-settings-header strong {
776
791
  color: var(--df-review-text);
777
792
  font-size: var(--df-review-font-size-lg);
793
+ font-weight: 600;
778
794
  }
779
795
 
780
796
  .df-review-settings-header span {
781
797
  color: var(--df-review-muted);
782
798
  font-size: var(--df-review-font-size-xs);
783
- font-weight: 800;
799
+ font-weight: 500;
784
800
  }
785
801
 
786
802
  .df-review-settings-header button {
@@ -790,7 +806,7 @@ function ensureReviewShellStyle() {
790
806
  min-width: 34px;
791
807
  padding: 0;
792
808
  font-size: var(--df-review-font-size-md);
793
- font-weight: 800;
809
+ font-weight: 500;
794
810
  }
795
811
 
796
812
  .df-review-settings-body {
@@ -816,7 +832,7 @@ function ensureReviewShellStyle() {
816
832
  .df-review-settings-label-row label {
817
833
  color: var(--df-review-muted);
818
834
  font-size: var(--df-review-font-size-sm);
819
- font-weight: 800;
835
+ font-weight: 500;
820
836
  }
821
837
 
822
838
  .df-review-settings-theme-options {
@@ -828,17 +844,32 @@ function ensureReviewShellStyle() {
828
844
  }
829
845
 
830
846
  .df-review-settings-theme-option {
847
+ display: inline-flex;
848
+ align-items: center;
849
+ justify-content: center;
850
+ gap: 7px;
831
851
  min-height: 30px;
832
852
  border: 1px solid var(--df-review-line);
833
853
  border-radius: var(--df-review-radius-sm);
834
- padding: 0 10px;
854
+ padding: 0 11px;
835
855
  color: var(--df-review-muted);
836
856
  background: var(--df-review-control);
837
857
  box-shadow: var(--df-review-shadow-control);
838
858
  font-size: var(--df-review-font-size-sm);
839
- font-weight: 800;
859
+ font-weight: 500;
840
860
  }
841
861
 
862
+ .df-review-settings-theme-option svg {
863
+ width: 15px;
864
+ height: 15px;
865
+ flex: 0 0 auto;
866
+ stroke-width: 2.2;
867
+ }
868
+
869
+ .df-review-settings-theme-option span {
870
+ color: inherit;
871
+ }
872
+
842
873
  .df-review-settings-theme-option:hover {
843
874
  border-color: var(--df-review-accent);
844
875
  background: var(--df-review-control-hover);
@@ -983,7 +1014,7 @@ function ensureReviewShellStyle() {
983
1014
  background: var(--df-review-chip-bg);
984
1015
  color: var(--df-review-muted);
985
1016
  font-size: var(--df-review-font-size-xs);
986
- font-weight: 700;
1017
+ font-weight: 500;
987
1018
  line-height: 1.55;
988
1019
  }
989
1020
 
@@ -999,7 +1030,7 @@ function ensureReviewShellStyle() {
999
1030
  margin: 0;
1000
1031
  color: var(--df-review-accent);
1001
1032
  font-size: var(--df-review-font-size-sm);
1002
- font-weight: 800;
1033
+ font-weight: 500;
1003
1034
  }
1004
1035
 
1005
1036
  .df-review-settings-actions {
@@ -1065,7 +1096,7 @@ function ensureReviewShellStyle() {
1065
1096
  margin: 0;
1066
1097
  color: var(--df-review-danger);
1067
1098
  font-size: var(--df-review-font-size-sm);
1068
- font-weight: 800;
1099
+ font-weight: 500;
1069
1100
  }
1070
1101
 
1071
1102
  .df-review-edit-actions {
@@ -1118,7 +1149,7 @@ function ensureReviewShellStyle() {
1118
1149
  box-shadow: var(--df-review-shadow-modal);
1119
1150
  color: var(--df-review-text);
1120
1151
  font-size: var(--df-review-font-size-sm);
1121
- font-weight: 800;
1152
+ font-weight: 500;
1122
1153
  line-height: 1.25;
1123
1154
  pointer-events: none;
1124
1155
  }
@@ -1142,13 +1173,14 @@ function ensureReviewShellStyle() {
1142
1173
  .df-review-prompt-header strong {
1143
1174
  color: var(--df-review-text);
1144
1175
  font-size: var(--df-review-font-size-lg);
1176
+ font-weight: 600;
1145
1177
  }
1146
1178
 
1147
1179
  .df-review-prompt-header span {
1148
1180
  overflow: hidden;
1149
1181
  color: var(--df-review-muted);
1150
1182
  font-size: var(--df-review-font-size-xs);
1151
- font-weight: 800;
1183
+ font-weight: 500;
1152
1184
  text-overflow: ellipsis;
1153
1185
  white-space: nowrap;
1154
1186
  }
@@ -1160,7 +1192,7 @@ function ensureReviewShellStyle() {
1160
1192
  min-width: 34px;
1161
1193
  padding: 0;
1162
1194
  font-size: var(--df-review-font-size-md);
1163
- font-weight: 800;
1195
+ font-weight: 500;
1164
1196
  }
1165
1197
 
1166
1198
  .df-review-prompt-body {
@@ -1194,13 +1226,13 @@ function ensureReviewShellStyle() {
1194
1226
  .df-review-prompt-block-header strong {
1195
1227
  color: var(--df-review-text);
1196
1228
  font-size: var(--df-review-font-size-sm);
1197
- font-weight: 900;
1229
+ font-weight: 600;
1198
1230
  }
1199
1231
 
1200
1232
  .df-review-prompt-block-header span {
1201
1233
  color: var(--df-review-muted);
1202
1234
  font-size: var(--df-review-font-size-xs);
1203
- font-weight: 800;
1235
+ font-weight: 500;
1204
1236
  }
1205
1237
 
1206
1238
  .df-review-prompt-block-header button {
@@ -1239,7 +1271,7 @@ function ensureReviewShellStyle() {
1239
1271
  color: var(--df-review-text);
1240
1272
  font-family: var(--df-review-font-mono);
1241
1273
  font-size: var(--df-review-font-size-xs);
1242
- font-weight: 600;
1274
+ font-weight: 400;
1243
1275
  line-height: 1.5;
1244
1276
  white-space: pre;
1245
1277
  }
@@ -1258,13 +1290,13 @@ function ensureReviewShellStyle() {
1258
1290
  .df-review-prompt-section-header strong {
1259
1291
  color: var(--df-review-text);
1260
1292
  font-size: var(--df-review-font-size-md);
1261
- font-weight: 900;
1293
+ font-weight: 600;
1262
1294
  }
1263
1295
 
1264
1296
  .df-review-prompt-section-header span {
1265
1297
  color: var(--df-review-muted);
1266
1298
  font-size: var(--df-review-font-size-xs);
1267
- font-weight: 800;
1299
+ font-weight: 500;
1268
1300
  }
1269
1301
 
1270
1302
  .df-review-prompt-about {
@@ -1293,14 +1325,14 @@ function ensureReviewShellStyle() {
1293
1325
  .df-review-prompt-about-grid strong {
1294
1326
  color: var(--df-review-text);
1295
1327
  font-size: var(--df-review-font-size-sm);
1296
- font-weight: 900;
1328
+ font-weight: 600;
1297
1329
  }
1298
1330
 
1299
1331
  .df-review-prompt-about-grid p {
1300
1332
  margin: 0;
1301
1333
  color: var(--df-review-muted);
1302
1334
  font-size: var(--df-review-font-size-sm);
1303
- font-weight: 700;
1335
+ font-weight: 500;
1304
1336
  line-height: 1.55;
1305
1337
  }
1306
1338
 
@@ -1325,46 +1357,86 @@ function ensureReviewShellStyle() {
1325
1357
  }
1326
1358
 
1327
1359
  .df-review-presets,
1328
- .df-review-mode,
1329
- .df-review-overlays {
1360
+ .df-review-mode {
1330
1361
  display: flex;
1331
1362
  align-items: center;
1332
1363
  gap: var(--df-review-space-1-5);
1333
- min-height: calc(var(--df-review-control-height-lg) + 6px);
1334
- padding: 3px;
1364
+ height: var(--df-review-control-height-md);
1365
+ min-height: var(--df-review-control-height-md);
1366
+ padding: 0;
1335
1367
  border: 1px solid var(--df-review-line-soft);
1336
1368
  border-radius: var(--df-review-radius-md);
1337
1369
  background: var(--df-review-card);
1338
1370
  }
1339
1371
 
1372
+ .df-review-overlays {
1373
+ display: flex;
1374
+ align-items: center;
1375
+ gap: 4px;
1376
+ height: var(--df-review-control-height-md);
1377
+ min-height: var(--df-review-control-height-md);
1378
+ padding: 0;
1379
+ border: 0;
1380
+ border-radius: 0;
1381
+ background: transparent;
1382
+ box-shadow: none;
1383
+ }
1384
+
1385
+ .df-review-mode {
1386
+ gap: 3px;
1387
+ padding: 3px;
1388
+ }
1389
+
1340
1390
  .df-review-tool-divider,
1341
1391
  .df-review-mode-divider {
1342
1392
  display: inline-flex;
1343
1393
  align-items: center;
1344
1394
  color: var(--df-review-line);
1345
1395
  font-size: var(--df-review-font-size-2xl);
1346
- font-weight: 700;
1396
+ font-weight: 500;
1347
1397
  line-height: 1;
1348
1398
  user-select: none;
1349
1399
  }
1350
1400
 
1351
- .df-review-active-size {
1401
+ .df-review-active-size {
1352
1402
  flex: 0 0 auto;
1353
1403
  display: inline-flex;
1354
1404
  align-items: center;
1355
- min-height: var(--df-review-control-height-lg);
1405
+ min-height: var(--df-review-control-height-md);
1356
1406
  color: var(--df-review-muted);
1357
1407
  font-size: var(--df-review-font-size-sm);
1358
1408
  font-variant-numeric: tabular-nums;
1359
- font-weight: 800;
1409
+ font-weight: 500;
1360
1410
  line-height: 1;
1361
1411
  }
1362
1412
 
1413
+ .df-review-preset-select {
1414
+ -webkit-appearance: none;
1415
+ appearance: none;
1416
+ display: none;
1417
+ height: var(--df-review-control-height-md);
1418
+ min-height: var(--df-review-control-height-md);
1419
+ min-width: 154px;
1420
+ border: 1px solid var(--df-review-line-soft);
1421
+ border-radius: var(--df-review-radius-md);
1422
+ padding: 0 42px 0 14px;
1423
+ color: var(--df-review-text);
1424
+ background-color: var(--df-review-control);
1425
+ background-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m6 9 6 6 6-6' stroke='%23d7deea' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
1426
+ background-position: right 14px center;
1427
+ background-repeat: no-repeat;
1428
+ background-size: 18px 18px;
1429
+ box-shadow: var(--df-review-shadow-control);
1430
+ font-size: var(--df-review-font-size-sm);
1431
+ font-weight: 500;
1432
+ }
1433
+
1363
1434
  .df-review-presets button {
1364
1435
  display: inline-flex;
1365
1436
  align-items: center;
1366
1437
  gap: 7px;
1367
- min-height: var(--df-review-control-height-lg);
1438
+ height: var(--df-review-control-height-md);
1439
+ min-height: var(--df-review-control-height-md);
1368
1440
  padding: 0 11px 0 9px;
1369
1441
  border-color: transparent;
1370
1442
  background: transparent;
@@ -1389,7 +1461,7 @@ function ensureReviewShellStyle() {
1389
1461
  background: var(--df-review-line-soft);
1390
1462
  color: var(--df-review-muted);
1391
1463
  font-size: var(--df-review-font-size-2xs);
1392
- font-weight: 800;
1464
+ font-weight: 500;
1393
1465
  line-height: 16px;
1394
1466
  text-align: center;
1395
1467
  }
@@ -1415,6 +1487,7 @@ function ensureReviewShellStyle() {
1415
1487
  .df-review-preset-copy strong {
1416
1488
  color: var(--df-review-text);
1417
1489
  font-size: var(--df-review-font-size-sm);
1490
+ font-weight: 500;
1418
1491
  }
1419
1492
 
1420
1493
  .df-review-overlay-button,
@@ -1422,8 +1495,10 @@ function ensureReviewShellStyle() {
1422
1495
  position: relative;
1423
1496
  display: inline-grid;
1424
1497
  place-items: center;
1425
- width: 38px;
1426
- min-width: 38px;
1498
+ width: var(--df-review-control-height-md);
1499
+ min-width: var(--df-review-control-height-md);
1500
+ height: var(--df-review-control-height-md);
1501
+ min-height: var(--df-review-control-height-md);
1427
1502
  padding: 0;
1428
1503
  border-color: transparent;
1429
1504
  background: transparent;
@@ -1431,6 +1506,101 @@ function ensureReviewShellStyle() {
1431
1506
  color: var(--df-review-muted);
1432
1507
  }
1433
1508
 
1509
+ .df-review-overlay-button {
1510
+ border: 1px solid var(--df-review-line-soft);
1511
+ border-radius: var(--df-review-radius-sm);
1512
+ background: var(--df-review-control);
1513
+ box-shadow: var(--df-review-shadow-control);
1514
+ }
1515
+
1516
+ .df-review-overlay-button:hover,
1517
+ .df-review-overlay-button:focus-visible {
1518
+ border-color: var(--df-review-accent);
1519
+ background: var(--df-review-control-hover);
1520
+ color: var(--df-review-text);
1521
+ outline: 0;
1522
+ }
1523
+
1524
+ .df-review-overlay-button.is-active {
1525
+ border-color: var(--df-review-accent);
1526
+ background: var(--df-review-accent-soft);
1527
+ box-shadow: inset 0 0 0 1px var(--df-review-accent-hover);
1528
+ color: var(--df-review-accent);
1529
+ }
1530
+
1531
+ .df-review-mode-button {
1532
+ width: calc(var(--df-review-control-height-md) - 6px);
1533
+ min-width: calc(var(--df-review-control-height-md) - 6px);
1534
+ height: calc(var(--df-review-control-height-md) - 6px);
1535
+ min-height: calc(var(--df-review-control-height-md) - 6px);
1536
+ }
1537
+
1538
+ .df-review-overlays-menu {
1539
+ position: relative;
1540
+ display: none;
1541
+ flex: 0 0 auto;
1542
+ }
1543
+
1544
+ .df-review-overlays-menu summary {
1545
+ display: grid;
1546
+ place-items: center;
1547
+ width: var(--df-review-control-height-md);
1548
+ min-width: var(--df-review-control-height-md);
1549
+ height: var(--df-review-control-height-md);
1550
+ border: 1px solid var(--df-review-line-soft);
1551
+ border-radius: var(--df-review-radius-md);
1552
+ padding: 0;
1553
+ color: var(--df-review-muted);
1554
+ background: var(--df-review-card);
1555
+ box-shadow: var(--df-review-shadow-control);
1556
+ cursor: pointer;
1557
+ }
1558
+
1559
+ .df-review-overlays-menu summary::-webkit-details-marker {
1560
+ display: none;
1561
+ }
1562
+
1563
+ .df-review-overlays-menu summary::marker {
1564
+ content: '';
1565
+ }
1566
+
1567
+ .df-review-overlays-menu summary:hover,
1568
+ .df-review-overlays-menu[open] summary {
1569
+ border-color: var(--df-review-accent);
1570
+ background: var(--df-review-control-hover);
1571
+ color: var(--df-review-accent);
1572
+ }
1573
+
1574
+ .df-review-overlays-menu summary svg {
1575
+ width: 20px;
1576
+ height: 20px;
1577
+ fill: none;
1578
+ stroke: currentColor;
1579
+ stroke-linecap: round;
1580
+ stroke-linejoin: round;
1581
+ stroke-width: 1.9;
1582
+ }
1583
+
1584
+ .df-review-overlays-popover {
1585
+ position: absolute;
1586
+ top: calc(100% + 6px);
1587
+ right: 0;
1588
+ z-index: 700;
1589
+ display: none;
1590
+ align-items: center;
1591
+ gap: var(--df-review-space-1-5);
1592
+ min-height: var(--df-review-control-height-md);
1593
+ padding: 0;
1594
+ border: 1px solid var(--df-review-line-soft);
1595
+ border-radius: var(--df-review-radius-md);
1596
+ background: var(--df-review-card);
1597
+ box-shadow: var(--df-review-shadow-modal);
1598
+ }
1599
+
1600
+ .df-review-overlays-menu[open] .df-review-overlays-popover {
1601
+ display: flex;
1602
+ }
1603
+
1434
1604
  .df-review-overlay-button svg,
1435
1605
  .df-review-mode-button svg {
1436
1606
  width: 18px;
@@ -1466,33 +1636,45 @@ function ensureReviewShellStyle() {
1466
1636
 
1467
1637
  @container (max-width: 1040px) {
1468
1638
  .df-review-tools {
1469
- flex-direction: column;
1470
- align-items: stretch;
1471
- justify-content: flex-start;
1639
+ flex-direction: row;
1640
+ align-items: center;
1641
+ justify-content: space-between;
1472
1642
  }
1473
1643
 
1474
1644
  .df-review-tool-controls {
1475
- width: 100%;
1645
+ flex: 1 1 auto;
1646
+ width: auto;
1647
+ min-width: 0;
1648
+ flex-wrap: nowrap;
1476
1649
  }
1477
1650
 
1478
1651
  .df-review-presets {
1479
- flex-wrap: wrap;
1652
+ display: none;
1480
1653
  }
1481
1654
 
1482
- .df-review-overlays {
1483
- width: 100%;
1484
- justify-content: flex-start;
1655
+ .df-review-preset-select {
1656
+ display: block;
1657
+ flex: 0 1 150px;
1658
+ min-width: 128px;
1659
+ width: clamp(128px, 20vw, 150px);
1485
1660
  }
1486
- }
1487
1661
 
1488
- .df-review-side-rail {
1662
+ .df-review-tool-divider,
1663
+ .df-review-active-size {
1664
+ display: none;
1665
+ }
1666
+
1667
+ }
1668
+
1669
+ .df-review-side-rail {
1489
1670
  grid-column: 3;
1490
1671
  grid-row: 1 / span 3;
1491
1672
  position: relative;
1492
- z-index: 600;
1673
+ z-index: 640;
1493
1674
  display: flex;
1675
+ flex-direction: column;
1494
1676
  align-items: stretch;
1495
- justify-content: center;
1677
+ justify-content: flex-start;
1496
1678
  min-width: 0;
1497
1679
  min-height: 0;
1498
1680
  border-left: 1px solid var(--df-review-line);
@@ -1500,49 +1682,186 @@ function ensureReviewShellStyle() {
1500
1682
  }
1501
1683
 
1502
1684
  .df-review-side-toggle {
1685
+ position: relative;
1686
+ flex: 0 0 48px;
1503
1687
  display: grid;
1504
- grid-template-rows: 28px auto;
1505
- align-items: start;
1506
- justify-items: center;
1507
- gap: 8px;
1688
+ place-items: center;
1508
1689
  width: 100%;
1509
- min-height: 100%;
1690
+ min-height: 48px;
1510
1691
  border: 0;
1511
1692
  border-radius: 0;
1512
- padding: 10px 0;
1693
+ padding: 0;
1513
1694
  background: transparent;
1514
1695
  color: var(--df-review-muted);
1696
+ transition: color 140ms ease, background 140ms ease;
1515
1697
  }
1516
1698
 
1517
1699
  .df-review-side-toggle:hover {
1518
- background: var(--df-review-accent-soft);
1519
1700
  color: var(--df-review-text);
1520
1701
  }
1521
1702
 
1703
+ .df-review-side-toggle.is-active {
1704
+ color: var(--df-review-accent);
1705
+ }
1706
+
1707
+ .df-review-side-toggle.is-active::after {
1708
+ position: absolute;
1709
+ top: 9px;
1710
+ right: 0;
1711
+ bottom: 9px;
1712
+ width: 3px;
1713
+ border-radius: 3px 0 0 3px;
1714
+ background: var(--df-review-accent);
1715
+ content: '';
1716
+ }
1717
+
1718
+ .df-review-side-toggle:focus-visible {
1719
+ outline: 0;
1720
+ color: var(--df-review-text);
1721
+ }
1722
+
1723
+ .df-review-side-toggle:focus-visible span {
1724
+ border-radius: 6px;
1725
+ outline: 2px solid var(--df-review-focus-ring);
1726
+ outline-offset: 3px;
1727
+ }
1728
+
1522
1729
  .df-review-side-toggle span {
1523
1730
  display: grid;
1524
1731
  place-items: center;
1525
- width: 20px;
1526
- height: 24px;
1732
+ width: 30px;
1733
+ height: 30px;
1527
1734
  line-height: 1;
1528
1735
  }
1529
1736
 
1530
1737
  .df-review-side-toggle svg {
1531
- width: 18px;
1532
- height: 18px;
1738
+ width: 30px;
1739
+ height: 30px;
1533
1740
  fill: none;
1534
1741
  stroke: currentColor;
1535
1742
  stroke-linecap: round;
1536
- stroke-width: 2;
1743
+ stroke-width: 1.55;
1537
1744
  }
1538
1745
 
1539
- .df-review-side-toggle strong {
1540
- writing-mode: vertical-rl;
1541
- text-orientation: mixed;
1542
- color: inherit;
1543
- font-size: var(--df-review-font-size-2xs);
1544
- font-weight: 900;
1545
- letter-spacing: 0.08em;
1746
+ .df-review-side-actions {
1747
+ display: flex;
1748
+ flex-direction: column;
1749
+ align-items: stretch;
1750
+ width: 100%;
1751
+ margin-top: auto;
1752
+ }
1753
+
1754
+ .df-review-presence-overlay {
1755
+ position: relative;
1756
+ margin-top: auto;
1757
+ }
1758
+
1759
+ .df-review-side-actions .df-review-presence-overlay {
1760
+ margin-top: 0;
1761
+ }
1762
+
1763
+ .df-review-presence-button {
1764
+ position: relative;
1765
+ display: grid;
1766
+ place-items: center;
1767
+ width: 100%;
1768
+ min-height: 48px;
1769
+ border: 0;
1770
+ border-radius: 0;
1771
+ padding: 0;
1772
+ color: var(--df-review-muted);
1773
+ background: transparent;
1774
+ }
1775
+
1776
+ .df-review-presence-button:hover,
1777
+ .df-review-presence-button[aria-expanded="true"] {
1778
+ color: var(--df-review-text);
1779
+ }
1780
+
1781
+ .df-review-presence-button:focus-visible {
1782
+ outline: 0;
1783
+ }
1784
+
1785
+ .df-review-presence-button:focus-visible svg {
1786
+ border-radius: 6px;
1787
+ outline: 2px solid var(--df-review-focus-ring);
1788
+ outline-offset: 3px;
1789
+ }
1790
+
1791
+ .df-review-presence-button svg {
1792
+ width: 30px;
1793
+ height: 30px;
1794
+ fill: none;
1795
+ stroke: currentColor;
1796
+ stroke-linecap: round;
1797
+ stroke-linejoin: round;
1798
+ stroke-width: 1.55;
1799
+ }
1800
+
1801
+ .df-review-presence-badge {
1802
+ position: absolute;
1803
+ top: 6px;
1804
+ right: 6px;
1805
+ display: grid;
1806
+ place-items: center;
1807
+ min-width: 17px;
1808
+ height: 17px;
1809
+ border: 1px solid var(--df-review-side-rail);
1810
+ border-radius: var(--df-review-radius-pill);
1811
+ padding: 0 4px;
1812
+ color: #17202c;
1813
+ background: var(--df-review-accent);
1814
+ font-size: var(--df-review-font-size-3xs);
1815
+ font-weight: 600;
1816
+ line-height: 1;
1817
+ pointer-events: none;
1818
+ }
1819
+
1820
+ .df-review-presence-list {
1821
+ position: absolute;
1822
+ right: calc(100% + 8px);
1823
+ bottom: 8px;
1824
+ display: flex;
1825
+ flex-direction: column;
1826
+ align-items: flex-end;
1827
+ gap: 5px;
1828
+ width: max-content;
1829
+ min-width: 0;
1830
+ max-width: 220px;
1831
+ }
1832
+
1833
+ .df-review-presence-chip {
1834
+ --df-review-presence-color: var(--df-review-accent);
1835
+ display: inline-flex;
1836
+ align-items: center;
1837
+ width: fit-content;
1838
+ max-width: 180px;
1839
+ min-width: 0;
1840
+ min-height: 22px;
1841
+ justify-content: flex-end;
1842
+ border: 1px solid var(--df-review-line-soft);
1843
+ border-radius: var(--df-review-radius-pill);
1844
+ padding: 0 9px;
1845
+ color: var(--df-review-text);
1846
+ background: var(--df-review-control);
1847
+ font-size: var(--df-review-font-size-xs);
1848
+ font-weight: 500;
1849
+ line-height: 1.1;
1850
+ white-space: nowrap;
1851
+ }
1852
+
1853
+ .df-review-presence-chip span {
1854
+ min-width: 0;
1855
+ overflow: hidden;
1856
+ text-overflow: ellipsis;
1857
+ text-align: right;
1858
+ white-space: nowrap;
1859
+ }
1860
+
1861
+ .df-review-presence-chip.is-self {
1862
+ color: var(--df-review-accent);
1863
+ border-color: var(--df-review-accent-soft);
1864
+ background: var(--df-review-accent-soft);
1546
1865
  }
1547
1866
 
1548
1867
  .df-review-qa-panel {
@@ -1551,7 +1870,7 @@ function ensureReviewShellStyle() {
1551
1870
  position: relative;
1552
1871
  z-index: 600;
1553
1872
  display: grid;
1554
- grid-template-rows: minmax(0, 1fr);
1873
+ grid-template-rows: minmax(0, 1fr) auto;
1555
1874
  min-width: 0;
1556
1875
  min-height: 0;
1557
1876
  overflow: hidden;
@@ -1560,14 +1879,56 @@ function ensureReviewShellStyle() {
1560
1879
  linear-gradient(180deg, var(--df-review-panel), var(--df-review-bg));
1561
1880
  }
1562
1881
 
1563
- .df-review-shell:not(.is-list-visible) .df-review-qa-panel {
1564
- visibility: hidden;
1565
- border-left: 0;
1566
- }
1882
+ .df-review-qa-draft-host {
1883
+ position: relative;
1884
+ z-index: 2;
1885
+ display: none;
1886
+ justify-self: end;
1887
+ width: calc(100% - var(--df-review-space-4));
1888
+ margin: 0 var(--df-review-space-2) var(--df-review-space-2) auto;
1889
+ min-width: 0;
1890
+ }
1567
1891
 
1568
- .df-review-panel-body {
1569
- display: grid;
1570
- min-width: 0;
1892
+ .df-review-qa-draft-host[data-has-draft-composer="true"] {
1893
+ display: block;
1894
+ }
1895
+
1896
+ .df-review-qa-panel[data-has-draft-composer="true"] .df-review-panel-body {
1897
+ opacity: 0.36;
1898
+ pointer-events: none;
1899
+ }
1900
+
1901
+ .df-review-shell:not(.is-list-visible) .df-review-qa-panel,
1902
+ .df-review-qa-panel[aria-hidden="true"] {
1903
+ visibility: hidden;
1904
+ border-left: 0;
1905
+ pointer-events: none;
1906
+ }
1907
+
1908
+ .df-review-source-tree-panel {
1909
+ grid-column: 2;
1910
+ grid-row: 1 / span 3;
1911
+ position: relative;
1912
+ z-index: 600;
1913
+ display: grid;
1914
+ min-width: 0;
1915
+ min-height: 0;
1916
+ overflow: hidden;
1917
+ border-left: 1px solid var(--df-review-line-soft);
1918
+ background:
1919
+ linear-gradient(180deg, var(--df-review-panel), var(--df-review-bg));
1920
+ }
1921
+
1922
+ .df-review-shell:not(.is-list-visible) .df-review-source-tree-panel,
1923
+ .df-review-source-tree-panel[aria-hidden="true"] {
1924
+ visibility: hidden;
1925
+ border-left: 0;
1926
+ pointer-events: none;
1927
+ }
1928
+
1929
+ .df-review-panel-body {
1930
+ display: grid;
1931
+ min-width: 0;
1571
1932
  min-height: 0;
1572
1933
  overflow: hidden;
1573
1934
  }
@@ -1581,14 +1942,16 @@ function ensureReviewShellStyle() {
1581
1942
 
1582
1943
  .df-review-list-header {
1583
1944
  display: grid;
1584
- grid-template-rows: auto auto;
1945
+ grid-template-rows:
1946
+ var(--df-review-control-height-md)
1947
+ var(--df-review-control-height-md);
1585
1948
  gap: var(--df-review-space-2);
1586
1949
  padding: var(--df-review-space-3) var(--df-review-frame-gutter-x);
1587
1950
  border-bottom: 1px solid var(--df-review-line-soft);
1588
1951
  background: var(--df-review-card);
1589
1952
  color: var(--df-review-muted);
1590
1953
  font-size: var(--df-review-font-size-sm);
1591
- font-weight: 800;
1954
+ font-weight: 500;
1592
1955
  }
1593
1956
 
1594
1957
  .df-review-list-toolbar {
@@ -1596,104 +1959,41 @@ function ensureReviewShellStyle() {
1596
1959
  grid-template-columns: minmax(0, 1fr) auto;
1597
1960
  align-items: center;
1598
1961
  gap: 8px;
1962
+ min-height: var(--df-review-control-height-md);
1599
1963
  min-width: 0;
1600
1964
  }
1601
1965
 
1602
1966
  .df-review-list-title {
1603
- display: flex;
1967
+ display: grid;
1968
+ grid-template-columns: minmax(0, 1fr) auto;
1604
1969
  align-items: center;
1605
1970
  gap: 8px;
1971
+ min-height: var(--df-review-control-height-md);
1606
1972
  min-width: 0;
1607
1973
  }
1608
1974
 
1609
- .df-review-list-title span {
1610
- min-width: 0;
1611
- overflow: hidden;
1612
- text-overflow: ellipsis;
1613
- white-space: nowrap;
1614
- }
1615
-
1616
- .df-review-list-title strong {
1617
- flex: 0 0 auto;
1618
- color: var(--df-review-muted);
1619
- font-size: var(--df-review-font-size-xs);
1620
- font-variant-numeric: tabular-nums;
1621
- }
1622
-
1623
- .df-review-presence-overlay {
1975
+ .df-review-list-meta {
1624
1976
  display: flex;
1625
1977
  align-items: center;
1626
- flex-wrap: wrap;
1627
- gap: 5px;
1628
- max-width: 100%;
1629
- pointer-events: none;
1630
- }
1631
-
1632
- .df-review-presence-row .df-review-presence-overlay {
1633
- width: 100%;
1634
- max-width: 1440px;
1635
- margin: 0 auto;
1636
- }
1637
-
1638
- .df-review-presence-chip {
1639
- --df-review-presence-color: var(--df-review-accent);
1640
- display: inline-flex;
1641
- align-items: center;
1978
+ gap: 8px;
1642
1979
  min-width: 0;
1643
- max-width: min(220px, calc(100% - 20px));
1644
- min-height: 24px;
1645
- border: 1px solid rgba(15, 23, 42, 0.12);
1646
- border-radius: var(--df-review-radius-pill);
1647
- padding: 0 8px;
1648
- color: #17202c;
1649
- background: rgba(255, 255, 255, 0.92);
1650
- box-shadow: 0 8px 22px rgba(15, 23, 42, 0.14);
1651
- font-size: var(--df-review-font-size-2xs);
1652
- font-weight: 900;
1653
- line-height: 1.1;
1654
- overflow: hidden;
1655
- text-overflow: ellipsis;
1656
- white-space: nowrap;
1657
- backdrop-filter: blur(8px);
1658
- }
1659
-
1660
- .df-review-presence-row .df-review-presence-chip {
1661
- max-width: none;
1662
- }
1663
-
1664
- .df-review-presence-chip.is-self {
1665
- border-color: var(--df-review-presence-color);
1666
- box-shadow:
1667
- inset 0 0 0 1px var(--df-review-presence-color),
1668
- 0 8px 22px rgba(15, 23, 42, 0.14);
1669
1980
  }
1670
1981
 
1671
- .df-review-presence-more {
1672
- display: inline-flex;
1673
- align-items: center;
1674
- justify-content: center;
1675
- min-width: 28px;
1676
- min-height: 24px;
1677
- border: 1px solid rgba(15, 23, 42, 0.12);
1678
- border-radius: var(--df-review-radius-pill);
1679
- padding: 0 8px;
1680
- color: #17202c;
1681
- background: rgba(255, 255, 255, 0.92);
1682
- box-shadow: 0 8px 22px rgba(15, 23, 42, 0.14);
1683
- cursor: pointer;
1684
- font: inherit;
1685
- font-size: var(--df-review-font-size-2xs);
1686
- font-weight: 900;
1687
- line-height: 1;
1688
- pointer-events: auto;
1689
- backdrop-filter: blur(8px);
1690
- }
1982
+ .df-review-list-meta span {
1983
+ min-width: 0;
1984
+ overflow: hidden;
1985
+ color: #fff;
1986
+ text-overflow: ellipsis;
1987
+ white-space: nowrap;
1988
+ }
1691
1989
 
1692
- .df-review-presence-more:hover {
1693
- border-color: rgba(0, 102, 255, 0.42);
1694
- color: #005be8;
1695
- background: rgba(255, 255, 255, 0.98);
1696
- }
1990
+ .df-review-list-meta strong {
1991
+ flex: 0 0 auto;
1992
+ color: #fff;
1993
+ font-size: var(--df-review-font-size-xs);
1994
+ font-variant-numeric: tabular-nums;
1995
+ font-weight: 500;
1996
+ }
1697
1997
 
1698
1998
  .df-review-list-controls {
1699
1999
  display: flex;
@@ -1703,7 +2003,8 @@ function ensureReviewShellStyle() {
1703
2003
 
1704
2004
  .df-review-source-select,
1705
2005
  .df-review-status-filter-select {
1706
- min-height: 30px;
2006
+ height: var(--df-review-control-height-md);
2007
+ min-height: var(--df-review-control-height-md);
1707
2008
  border: 1px solid var(--df-review-line-soft);
1708
2009
  border-radius: var(--df-review-radius-sm);
1709
2010
  padding: 0 24px 0 8px;
@@ -1711,7 +2012,7 @@ function ensureReviewShellStyle() {
1711
2012
  background: var(--df-review-control);
1712
2013
  box-shadow: var(--df-review-shadow-control);
1713
2014
  font-size: var(--df-review-font-size-xs);
1714
- font-weight: 800;
2015
+ font-weight: 500;
1715
2016
  }
1716
2017
 
1717
2018
  .df-review-source-select {
@@ -1722,17 +2023,13 @@ function ensureReviewShellStyle() {
1722
2023
  width: 124px;
1723
2024
  }
1724
2025
 
1725
- .df-review-list-title .df-review-status-filter-select {
1726
- margin-left: auto;
1727
- }
1728
-
1729
2026
  .df-review-source-refresh {
1730
2027
  position: relative;
1731
2028
  display: inline-grid;
1732
2029
  place-items: center;
1733
- width: 30px;
1734
- min-width: 30px;
1735
- height: 30px;
2030
+ width: var(--df-review-control-height-md);
2031
+ min-width: var(--df-review-control-height-md);
2032
+ height: var(--df-review-control-height-md);
1736
2033
  border: 1px solid var(--df-review-line-soft);
1737
2034
  border-radius: var(--df-review-radius-sm);
1738
2035
  padding: 0;
@@ -1742,20 +2039,22 @@ function ensureReviewShellStyle() {
1742
2039
  }
1743
2040
 
1744
2041
  .df-review-source-refresh svg {
1745
- width: 14px;
1746
- height: 14px;
2042
+ width: 16px;
2043
+ height: 16px;
1747
2044
  }
1748
2045
 
1749
2046
  .df-review-filter-tabs {
1750
2047
  display: flex;
1751
2048
  align-items: center;
2049
+ justify-self: end;
1752
2050
  gap: 2px;
1753
- height: 30px;
1754
- padding: 2px;
1755
- border: 1px solid var(--df-review-line-soft);
1756
- border-radius: var(--df-review-radius-md);
1757
- background: var(--df-review-card);
1758
- box-shadow: var(--df-review-shadow-control);
2051
+ height: 26px;
2052
+ min-height: 26px;
2053
+ padding: 0;
2054
+ border: 0;
2055
+ border-radius: 0;
2056
+ background: transparent;
2057
+ box-shadow: none;
1759
2058
  }
1760
2059
 
1761
2060
  .df-review-filter-tab {
@@ -1766,9 +2065,10 @@ function ensureReviewShellStyle() {
1766
2065
  min-width: 26px;
1767
2066
  height: 26px;
1768
2067
  border: 0;
1769
- border-radius: 5px;
2068
+ border-radius: 0;
1770
2069
  background: transparent;
1771
2070
  color: var(--df-review-subtle);
2071
+ opacity: 0.3;
1772
2072
  }
1773
2073
 
1774
2074
  .df-review-filter-icon {
@@ -1789,29 +2089,29 @@ function ensureReviewShellStyle() {
1789
2089
 
1790
2090
  .df-review-filter-tab:hover,
1791
2091
  .df-review-filter-tab.is-active {
1792
- background: var(--df-review-accent-soft);
1793
2092
  color: var(--df-review-text);
2093
+ opacity: 1;
1794
2094
  }
1795
2095
 
1796
2096
  .df-review-filter-tab.is-active {
1797
- box-shadow: inset 0 0 0 1px rgba(124, 199, 255, 0.42);
2097
+ box-shadow: none;
1798
2098
  color: var(--df-review-accent);
1799
2099
  }
1800
2100
 
1801
2101
  .df-review-filter-icon svg {
1802
- width: 15px;
1803
- height: 15px;
2102
+ width: 14px;
2103
+ height: 14px;
1804
2104
  fill: none;
1805
2105
  stroke: currentColor;
1806
2106
  stroke-linecap: round;
1807
2107
  stroke-linejoin: round;
1808
- stroke-width: 2;
2108
+ stroke-width: 1.9;
1809
2109
  }
1810
2110
 
1811
2111
  .df-review-filter-count {
1812
2112
  color: currentColor;
1813
2113
  font-size: var(--df-review-font-size-3xs);
1814
- font-weight: 900;
2114
+ font-weight: 600;
1815
2115
  font-variant-numeric: tabular-nums;
1816
2116
  line-height: 1;
1817
2117
  }
@@ -1892,6 +2192,7 @@ function ensureReviewShellStyle() {
1892
2192
  .df-review-item-main strong {
1893
2193
  overflow-wrap: anywhere;
1894
2194
  font-size: var(--df-review-font-size-md);
2195
+ font-weight: 500;
1895
2196
  line-height: 1.35;
1896
2197
  }
1897
2198
 
@@ -1939,15 +2240,27 @@ function ensureReviewShellStyle() {
1939
2240
  border-radius: var(--df-review-radius-pill);
1940
2241
  padding: 0 7px;
1941
2242
  font-size: var(--df-review-font-size-2xs);
1942
- font-weight: 900;
2243
+ font-weight: 600;
1943
2244
  line-height: 1;
1944
2245
  text-transform: uppercase;
1945
2246
  }
1946
2247
 
1947
2248
  .df-review-item-id {
2249
+ appearance: none;
1948
2250
  border-color: var(--df-review-line);
1949
2251
  background: rgba(255, 255, 255, 0.03);
1950
2252
  color: var(--df-review-text);
2253
+ cursor: copy;
2254
+ font-family: inherit;
2255
+ }
2256
+
2257
+ .df-review-item-id:hover,
2258
+ .df-review-item-id:focus-visible,
2259
+ .df-review-item-id.is-copied {
2260
+ border-color: var(--df-review-accent);
2261
+ background: var(--df-review-accent-soft);
2262
+ color: var(--df-review-accent);
2263
+ outline: none;
1951
2264
  }
1952
2265
 
1953
2266
  .df-review-item-scope {
@@ -2006,7 +2319,7 @@ function ensureReviewShellStyle() {
2006
2319
  border-radius: var(--df-review-radius-sm);
2007
2320
  padding: 0 11px;
2008
2321
  font-size: var(--df-review-font-size-xs);
2009
- font-weight: 750;
2322
+ font-weight: 500;
2010
2323
  text-transform: none;
2011
2324
  }
2012
2325
 
@@ -2019,7 +2332,7 @@ function ensureReviewShellStyle() {
2019
2332
  cursor: pointer;
2020
2333
  outline: 0;
2021
2334
  font-size: var(--df-review-font-size-xs);
2022
- font-weight: 750;
2335
+ font-weight: 500;
2023
2336
  text-transform: none;
2024
2337
  }
2025
2338
 
@@ -2244,7 +2557,7 @@ function ensureReviewShellStyle() {
2244
2557
 
2245
2558
  .df-review-item-submit-button span {
2246
2559
  font-size: var(--df-review-font-size-xs);
2247
- font-weight: 750;
2560
+ font-weight: 500;
2248
2561
  line-height: 1;
2249
2562
  text-transform: none;
2250
2563
  white-space: nowrap;
@@ -2416,6 +2729,9 @@ function ensureReviewShellStyle() {
2416
2729
  --df-review-source-popover-text: #edf3fb;
2417
2730
  --df-review-source-popover-muted: rgba(237, 243, 251, 0.68);
2418
2731
  --df-review-source-popover-subtle: rgba(237, 243, 251, 0.5);
2732
+ --df-review-source-popover-data: #f3b75f;
2733
+ --df-review-source-popover-data-muted: rgba(243, 183, 95, 0.78);
2734
+ --df-review-source-popover-data-subtle: rgba(243, 183, 95, 0.62);
2419
2735
  --df-review-source-popover-hover: rgba(124, 199, 255, 0.14);
2420
2736
  position: fixed;
2421
2737
  z-index: 890;
@@ -2427,7 +2743,7 @@ function ensureReviewShellStyle() {
2427
2743
  overflow: hidden;
2428
2744
  border: 1px solid var(--df-review-source-popover-line);
2429
2745
  border-radius: var(--df-review-radius-md);
2430
- padding: 8px 6px 6px;
2746
+ padding: 6px;
2431
2747
  color: var(--df-review-source-popover-text);
2432
2748
  color-scheme: dark;
2433
2749
  background: rgba(19, 24, 33, 0.96);
@@ -2442,85 +2758,572 @@ function ensureReviewShellStyle() {
2442
2758
  z-index: 1;
2443
2759
  }
2444
2760
 
2445
- .df-review-source-popover-close button {
2446
- display: grid;
2761
+ .df-review-source-popover-close button {
2762
+ display: grid;
2763
+ place-items: center;
2764
+ width: 24px;
2765
+ height: 24px;
2766
+ border: 1px solid transparent;
2767
+ border-radius: var(--df-review-radius-sm);
2768
+ padding: 0;
2769
+ color: var(--df-review-source-popover-subtle);
2770
+ background: transparent;
2771
+ font-size: 16px;
2772
+ font-weight: 500;
2773
+ line-height: 1;
2774
+ }
2775
+
2776
+ .df-review-source-popover-close button:hover {
2777
+ border-color: var(--df-review-source-popover-line);
2778
+ color: var(--df-review-source-popover-text);
2779
+ background: var(--df-review-source-popover-hover);
2780
+ }
2781
+
2782
+ .df-review-source-candidate-list {
2783
+ display: grid;
2784
+ gap: 0;
2785
+ max-height: min(220px, calc(100vh - 96px));
2786
+ min-height: 0;
2787
+ overflow-x: hidden;
2788
+ overflow-y: auto;
2789
+ padding-right: 2px;
2790
+ scrollbar-gutter: stable;
2791
+ }
2792
+
2793
+ .df-review-source-candidate {
2794
+ display: grid;
2795
+ width: 100%;
2796
+ border: 0;
2797
+ border-top: 1px solid transparent;
2798
+ border-radius: var(--df-review-radius-sm);
2799
+ padding: 9px 30px 9px 8px;
2800
+ color: var(--df-review-source-popover-text);
2801
+ background: transparent;
2802
+ text-align: left;
2803
+ }
2804
+
2805
+ .df-review-source-candidate + .df-review-source-candidate {
2806
+ border-top-color: var(--df-review-source-popover-line);
2807
+ }
2808
+
2809
+ .df-review-source-candidate:hover {
2810
+ background: var(--df-review-source-popover-hover);
2811
+ }
2812
+
2813
+ .df-review-source-candidate-main {
2814
+ display: grid;
2815
+ gap: 1px;
2816
+ min-width: 0;
2817
+ }
2818
+
2819
+ .df-review-source-candidate-main strong,
2820
+ .df-review-source-candidate-main small {
2821
+ overflow: hidden;
2822
+ text-overflow: ellipsis;
2823
+ white-space: nowrap;
2824
+ }
2825
+
2826
+ .df-review-source-candidate-main strong {
2827
+ font-size: var(--df-review-font-size-xs);
2828
+ font-weight: 600;
2829
+ line-height: 1.2;
2830
+ }
2831
+
2832
+ .df-review-source-candidate-main span {
2833
+ overflow-wrap: anywhere;
2834
+ color: var(--df-review-source-popover-muted);
2835
+ font-family: var(--df-review-font-mono);
2836
+ font-size: var(--df-review-font-size-2xs);
2837
+ line-height: 1.24;
2838
+ white-space: normal;
2839
+ }
2840
+
2841
+ .df-review-source-candidate-main small {
2842
+ color: var(--df-review-source-popover-subtle);
2843
+ font-family: var(--df-review-font-mono);
2844
+ font-size: var(--df-review-font-size-2xs);
2845
+ line-height: 1.2;
2846
+ }
2847
+
2848
+ .df-review-source-candidate.is-data .df-review-source-candidate-main strong {
2849
+ color: var(--df-review-source-popover-data);
2850
+ }
2851
+
2852
+ .df-review-source-candidate.is-data .df-review-source-candidate-main span {
2853
+ color: var(--df-review-source-popover-data-muted);
2854
+ }
2855
+
2856
+ .df-review-source-candidate.is-data .df-review-source-candidate-main small {
2857
+ color: var(--df-review-source-popover-data-subtle);
2858
+ }
2859
+
2860
+ .df-review-section-outline {
2861
+ position: relative;
2862
+ z-index: 1;
2863
+ display: grid;
2864
+ grid-template-rows: auto minmax(0, 1fr);
2865
+ width: 100%;
2866
+ height: 100%;
2867
+ min-width: 0;
2868
+ min-height: 0;
2869
+ border: 0;
2870
+ border-radius: 0;
2871
+ color: var(--df-review-text);
2872
+ background: transparent;
2873
+ box-shadow: none;
2874
+ overflow: hidden;
2875
+ }
2876
+
2877
+ .df-review-section-outline-head {
2878
+ display: grid;
2879
+ grid-template-rows:
2880
+ var(--df-review-control-height-md)
2881
+ var(--df-review-control-height-md);
2882
+ align-items: center;
2883
+ gap: var(--df-review-space-2);
2884
+ min-width: 0;
2885
+ padding: var(--df-review-space-3) var(--df-review-frame-gutter-x);
2886
+ border-bottom: 1px solid var(--df-review-line-soft);
2887
+ background: var(--df-review-card);
2888
+ color: var(--df-review-muted);
2889
+ font-size: var(--df-review-font-size-sm);
2890
+ font-weight: 500;
2891
+ }
2892
+
2893
+ .df-review-section-outline-summary {
2894
+ display: grid;
2895
+ grid-template-columns: minmax(0, 1fr) auto;
2896
+ align-items: center;
2897
+ gap: 8px;
2898
+ min-height: var(--df-review-control-height-md);
2899
+ min-width: 0;
2900
+ }
2901
+
2902
+ .df-review-section-outline-summary span {
2903
+ display: inline-flex;
2904
+ align-items: baseline;
2905
+ gap: 6px;
2906
+ min-width: 0;
2907
+ }
2908
+
2909
+ .df-review-section-outline-head strong {
2910
+ overflow: hidden;
2911
+ color: var(--df-review-text);
2912
+ font-size: inherit;
2913
+ font-weight: 600;
2914
+ line-height: 1;
2915
+ text-overflow: ellipsis;
2916
+ white-space: nowrap;
2917
+ }
2918
+
2919
+ .df-review-section-outline-head small {
2920
+ flex: 0 0 auto;
2921
+ overflow: hidden;
2922
+ color: var(--df-review-muted);
2923
+ font-size: var(--df-review-font-size-xs);
2924
+ font-weight: 500;
2925
+ text-overflow: ellipsis;
2926
+ white-space: nowrap;
2927
+ }
2928
+
2929
+ .df-review-section-outline-meta-controls {
2930
+ display: inline-flex;
2931
+ align-items: center;
2932
+ justify-content: flex-end;
2933
+ gap: 2px;
2934
+ height: 26px;
2935
+ min-height: 26px;
2936
+ min-width: 0;
2937
+ border: 0;
2938
+ border-radius: 0;
2939
+ padding: 0;
2940
+ background: transparent;
2941
+ box-shadow: none;
2942
+ }
2943
+
2944
+ .df-review-section-outline-meta-toggle {
2945
+ display: inline-grid;
2946
+ place-items: center;
2947
+ width: 26px;
2948
+ min-width: 26px;
2949
+ height: 26px;
2950
+ border: 0;
2951
+ border-radius: 0;
2952
+ padding: 0;
2953
+ color: var(--df-review-subtle);
2954
+ background: transparent;
2955
+ box-shadow: none;
2956
+ cursor: pointer;
2957
+ opacity: 0.3;
2958
+ }
2959
+
2960
+ .df-review-section-outline-meta-toggle:hover,
2961
+ .df-review-section-outline-meta-toggle:focus-visible,
2962
+ .df-review-section-outline-meta-toggle.is-active {
2963
+ color: var(--df-review-text);
2964
+ opacity: 1;
2965
+ outline: 0;
2966
+ }
2967
+
2968
+ .df-review-section-outline-meta-toggle.is-active {
2969
+ box-shadow: none;
2970
+ color: var(--df-review-accent);
2971
+ }
2972
+
2973
+ .df-review-section-outline-meta-toggle svg {
2974
+ width: 14px;
2975
+ height: 14px;
2976
+ fill: none;
2977
+ stroke: currentColor;
2978
+ stroke-linecap: round;
2979
+ stroke-linejoin: round;
2980
+ stroke-width: 2;
2981
+ }
2982
+
2983
+ .df-review-section-outline-filter {
2984
+ display: grid;
2985
+ grid-template-columns: 18px minmax(0, 1fr) auto;
2986
+ align-items: center;
2987
+ align-self: stretch;
2988
+ gap: 8px;
2989
+ min-width: 0;
2990
+ height: var(--df-review-control-height-md);
2991
+ min-height: var(--df-review-control-height-md);
2992
+ border: 1px solid var(--df-review-line-soft);
2993
+ border-radius: var(--df-review-radius-sm);
2994
+ padding: 0 7px 0 11px;
2995
+ color: var(--df-review-muted);
2996
+ background: var(--df-review-control);
2997
+ box-shadow: var(--df-review-shadow-control);
2998
+ }
2999
+
3000
+ .df-review-section-outline-filter:focus-within {
3001
+ border-color: var(--df-review-accent);
3002
+ box-shadow: inset 0 0 0 1px var(--df-review-accent-hover);
3003
+ }
3004
+
3005
+ .df-review-section-outline-filter svg {
3006
+ width: 18px;
3007
+ height: 18px;
3008
+ fill: none;
3009
+ stroke: currentColor;
3010
+ stroke-linecap: round;
3011
+ stroke-linejoin: round;
3012
+ stroke-width: 1.9;
3013
+ }
3014
+
3015
+ .df-review-section-outline-filter input {
3016
+ width: 100%;
3017
+ min-width: 0;
3018
+ height: 100%;
3019
+ border: 0;
3020
+ padding: 0;
3021
+ color: var(--df-review-text);
3022
+ background: transparent;
3023
+ font-size: var(--df-review-font-size-xs);
3024
+ font-weight: 500;
3025
+ outline: 0;
3026
+ -webkit-appearance: none;
3027
+ appearance: none;
3028
+ }
3029
+
3030
+ .df-review-section-outline-filter input::placeholder {
3031
+ color: var(--df-review-subtle);
3032
+ }
3033
+
3034
+ .df-review-section-outline-filter input::-webkit-search-cancel-button,
3035
+ .df-review-section-outline-filter input::-webkit-search-decoration {
3036
+ display: none;
3037
+ -webkit-appearance: none;
3038
+ appearance: none;
3039
+ }
3040
+
3041
+ .df-review-section-outline-filter-clear {
3042
+ display: inline-grid;
3043
+ place-items: center;
3044
+ width: var(--df-review-control-height-md);
3045
+ height: var(--df-review-control-height-md);
3046
+ border: 0;
3047
+ border-radius: var(--df-review-radius-sm);
3048
+ padding: 0;
3049
+ color: var(--df-review-muted);
3050
+ background: transparent;
3051
+ cursor: pointer;
3052
+ }
3053
+
3054
+ .df-review-section-outline-filter-clear:hover,
3055
+ .df-review-section-outline-filter-clear:focus-visible {
3056
+ color: var(--df-review-text);
3057
+ background: var(--df-review-accent-soft);
3058
+ outline: 0;
3059
+ }
3060
+
3061
+ .df-review-section-outline-list {
3062
+ display: grid;
3063
+ align-content: start;
3064
+ gap: 2px;
3065
+ min-height: 0;
3066
+ overflow-y: auto;
3067
+ padding: 8px 1px 8px 15px;
3068
+ scrollbar-gutter: stable;
3069
+ }
3070
+
3071
+ .df-review-section-outline-item {
3072
+ display: grid;
3073
+ }
3074
+
3075
+ .df-review-section-outline-entry-body {
3076
+ display: grid;
3077
+ border-radius: var(--df-review-radius-sm);
3078
+ }
3079
+
3080
+ .df-review-section-outline-entry-body:hover {
3081
+ background: var(--df-review-accent-soft);
3082
+ }
3083
+
3084
+ .df-review-section-outline-item.is-depth-1:not(:last-child) {
3085
+ margin-bottom: 7px;
3086
+ padding-bottom: 7px;
3087
+ border-bottom: 1px solid var(--df-review-line-soft);
3088
+ }
3089
+
3090
+ .df-review-section-outline-row {
3091
+ display: grid;
3092
+ grid-template-columns: 18px minmax(0, 1fr) auto;
3093
+ align-items: center;
3094
+ gap: 5px;
3095
+ border-radius: var(--df-review-radius-sm);
3096
+ padding: 7px 6px;
3097
+ }
3098
+
3099
+ .df-review-section-outline-toggle {
3100
+ display: grid;
3101
+ place-items: center;
3102
+ width: 18px;
3103
+ height: 18px;
3104
+ border: 0;
3105
+ border-radius: var(--df-review-radius-sm);
3106
+ padding: 0;
3107
+ color: var(--df-review-muted);
3108
+ background: transparent;
3109
+ }
3110
+
3111
+ .df-review-section-outline-toggle:hover {
3112
+ color: var(--df-review-text);
3113
+ background: var(--df-review-accent-soft);
3114
+ }
3115
+
3116
+ .df-review-section-outline-toggle svg {
3117
+ width: 13px;
3118
+ height: 13px;
3119
+ fill: none;
3120
+ stroke: currentColor;
3121
+ stroke-linecap: round;
3122
+ stroke-linejoin: round;
3123
+ stroke-width: 2.4;
3124
+ transition: transform 120ms ease;
3125
+ }
3126
+
3127
+ .df-review-section-outline-toggle.is-collapsed svg {
3128
+ transform: rotate(-90deg);
3129
+ }
3130
+
3131
+ .df-review-section-outline-toggle.is-placeholder {
3132
+ pointer-events: none;
3133
+ }
3134
+
3135
+ .df-review-section-outline-name {
3136
+ display: grid;
3137
+ gap: 2px;
3138
+ min-width: 0;
3139
+ overflow: visible;
3140
+ border: 0;
3141
+ padding: 0;
3142
+ color: var(--df-review-text);
3143
+ background: transparent;
3144
+ font-size: var(--df-review-font-size-sm);
3145
+ font-weight: 500;
3146
+ text-align: left;
3147
+ white-space: normal;
3148
+ cursor: pointer;
3149
+ }
3150
+
3151
+ .df-review-section-outline-name span,
3152
+ .df-review-section-outline-name small {
3153
+ overflow: visible;
3154
+ overflow-wrap: anywhere;
3155
+ text-overflow: clip;
3156
+ white-space: normal;
3157
+ }
3158
+
3159
+ .df-review-section-outline-name small {
3160
+ color: var(--df-review-muted);
3161
+ font-family: var(--df-review-font-mono);
3162
+ font-size: var(--df-review-font-size-xs);
3163
+ font-weight: 500;
3164
+ }
3165
+
3166
+ .df-review-section-outline-name:hover {
3167
+ color: var(--df-review-accent);
3168
+ }
3169
+
3170
+ .df-review-section-outline-meta {
3171
+ display: grid;
3172
+ gap: 3px;
3173
+ min-width: 0;
3174
+ padding: 0 6px 7px;
3175
+ }
3176
+
3177
+ .df-review-section-outline-meta-row {
3178
+ display: grid;
3179
+ grid-template-columns: 52px minmax(0, 1fr);
3180
+ align-items: start;
3181
+ gap: 6px;
3182
+ min-width: 0;
3183
+ color: var(--df-review-muted);
3184
+ font-family: var(--df-review-font-mono);
3185
+ font-size: var(--df-review-font-size-2xs);
3186
+ line-height: 1.35;
3187
+ }
3188
+
3189
+ .df-review-section-outline-meta-row b {
3190
+ color: var(--df-review-subtle);
3191
+ font-weight: 600;
3192
+ }
3193
+
3194
+ .df-review-section-outline-meta-row code {
3195
+ min-width: 0;
3196
+ overflow-wrap: anywhere;
3197
+ color: var(--df-review-muted);
3198
+ font-family: inherit;
3199
+ }
3200
+
3201
+ .df-review-section-outline-meta-row.is-text code {
3202
+ color: var(--df-review-text);
3203
+ }
3204
+
3205
+ .df-review-section-outline-meta-row.is-media code {
3206
+ color: var(--df-review-accent);
3207
+ }
3208
+
3209
+ .df-review-section-outline-media-link {
3210
+ display: block;
3211
+ min-width: 0;
3212
+ color: var(--df-review-accent);
3213
+ text-decoration: none;
3214
+ }
3215
+
3216
+ .df-review-section-outline-media-link:hover,
3217
+ .df-review-section-outline-media-link:focus-visible {
3218
+ color: var(--df-review-text);
3219
+ outline: 0;
3220
+ }
3221
+
3222
+ .df-review-section-outline-meta-row.is-class code {
3223
+ color: var(--df-review-source-popover-data-subtle);
3224
+ }
3225
+
3226
+ .df-review-section-outline-meta-row.is-class {
3227
+ align-items: center;
3228
+ }
3229
+
3230
+ .df-review-section-outline-class-tags {
3231
+ display: flex;
3232
+ align-items: center;
3233
+ flex-wrap: wrap;
3234
+ gap: 3px;
3235
+ min-width: 0;
3236
+ }
3237
+
3238
+ .df-review-section-outline-class-tags code {
3239
+ display: inline-flex;
3240
+ align-items: center;
3241
+ max-width: 100%;
3242
+ min-height: 20px;
3243
+ border: 1px solid var(--df-review-line-soft);
3244
+ border-radius: var(--df-review-radius-pill);
3245
+ padding: 0 6px;
3246
+ background: var(--df-review-control);
3247
+ line-height: 1;
3248
+ }
3249
+
3250
+ .df-review-section-outline-links {
3251
+ display: inline-flex;
3252
+ align-items: center;
3253
+ justify-content: flex-end;
3254
+ gap: 2px;
3255
+ }
3256
+
3257
+ .df-review-section-outline-divider {
3258
+ color: var(--df-review-subtle);
3259
+ font-size: var(--df-review-font-size-xs);
3260
+ line-height: 1;
3261
+ user-select: none;
3262
+ }
3263
+
3264
+ .df-review-section-outline-link {
3265
+ display: inline-grid;
2447
3266
  place-items: center;
2448
- width: 24px;
2449
- height: 24px;
3267
+ width: 26px;
3268
+ height: 26px;
2450
3269
  border: 1px solid transparent;
2451
3270
  border-radius: var(--df-review-radius-sm);
2452
3271
  padding: 0;
2453
- color: var(--df-review-source-popover-subtle);
3272
+ color: var(--df-review-muted);
2454
3273
  background: transparent;
2455
- font-size: 16px;
2456
- font-weight: 800;
2457
- line-height: 1;
2458
- }
2459
-
2460
- .df-review-source-popover-close button:hover {
2461
- border-color: var(--df-review-source-popover-line);
2462
- color: var(--df-review-source-popover-text);
2463
- background: var(--df-review-source-popover-hover);
3274
+ cursor: pointer;
3275
+ transition: border-color 140ms ease, background 140ms ease,
3276
+ color 140ms ease, opacity 140ms ease;
2464
3277
  }
2465
3278
 
2466
- .df-review-source-candidate-list {
2467
- display: grid;
2468
- gap: 0;
2469
- max-height: min(220px, calc(100vh - 96px));
2470
- min-height: 0;
2471
- overflow-x: hidden;
2472
- overflow-y: auto;
2473
- padding-right: 2px;
2474
- scrollbar-gutter: stable;
3279
+ .df-review-section-outline-link.is-dom-select {
3280
+ width: 26px;
3281
+ min-width: 26px;
3282
+ padding: 0;
2475
3283
  }
2476
3284
 
2477
- .df-review-source-candidate {
2478
- display: grid;
2479
- width: 100%;
2480
- min-height: 54px;
2481
- border: 0;
2482
- border-radius: var(--df-review-radius-sm);
2483
- padding: 6px 30px 6px 8px;
2484
- color: var(--df-review-source-popover-text);
2485
- background: transparent;
2486
- text-align: left;
3285
+ .df-review-section-outline-link.is-dom-select svg {
3286
+ width: 16px;
3287
+ height: 16px;
2487
3288
  }
2488
3289
 
2489
- .df-review-source-candidate:hover {
2490
- background: var(--df-review-source-popover-hover);
3290
+ .df-review-section-outline-link:hover {
3291
+ border-color: rgba(124, 199, 255, 0.34);
3292
+ color: var(--df-review-accent);
3293
+ background: var(--df-review-accent-soft);
2491
3294
  }
2492
3295
 
2493
- .df-review-source-candidate-main {
2494
- display: grid;
2495
- gap: 2px;
2496
- min-width: 0;
3296
+ .df-review-section-outline-link svg {
3297
+ width: 14px;
3298
+ height: 14px;
3299
+ fill: none;
3300
+ stroke: currentColor;
3301
+ stroke-linecap: round;
3302
+ stroke-linejoin: round;
3303
+ stroke-width: 2;
2497
3304
  }
2498
3305
 
2499
- .df-review-source-candidate-main strong,
2500
- .df-review-source-candidate-main small {
2501
- overflow: hidden;
2502
- text-overflow: ellipsis;
2503
- white-space: nowrap;
3306
+ .df-review-section-outline-link:disabled {
3307
+ color: var(--df-review-subtle);
3308
+ cursor: not-allowed;
3309
+ opacity: 0.42;
2504
3310
  }
2505
3311
 
2506
- .df-review-source-candidate-main strong {
2507
- font-size: var(--df-review-font-size-xs);
2508
- font-weight: 900;
3312
+ .df-review-section-outline-link:disabled:hover {
3313
+ border-color: transparent;
3314
+ color: var(--df-review-subtle);
3315
+ background: transparent;
2509
3316
  }
2510
3317
 
2511
- .df-review-source-candidate-main span {
2512
- overflow-wrap: anywhere;
2513
- color: var(--df-review-source-popover-muted);
2514
- font-family: var(--df-review-font-mono);
2515
- font-size: var(--df-review-font-size-2xs);
2516
- line-height: 1.25;
2517
- white-space: normal;
3318
+ .df-review-section-outline-children {
3319
+ display: grid;
2518
3320
  }
2519
3321
 
2520
- .df-review-source-candidate-main small {
2521
- color: var(--df-review-source-popover-subtle);
2522
- font-family: var(--df-review-font-mono);
2523
- font-size: var(--df-review-font-size-2xs);
3322
+ .df-review-section-outline-empty {
3323
+ padding: 14px 12px 16px;
3324
+ color: var(--df-review-muted);
3325
+ font-size: var(--df-review-font-size-xs);
3326
+ font-weight: 500;
2524
3327
  }
2525
3328
 
2526
3329
  .df-review-device-frame {
@@ -2617,13 +3420,13 @@ function ensureReviewShellStyle() {
2617
3420
  .df-review-ruler-frame-label strong {
2618
3421
  color: var(--df-review-color-ruler-label-text);
2619
3422
  font-size: var(--df-review-font-size-xs);
2620
- font-weight: 900;
3423
+ font-weight: 600;
2621
3424
  }
2622
3425
 
2623
3426
  .df-review-ruler-frame-label span {
2624
3427
  color: var(--df-review-color-ruler-label-text);
2625
3428
  font-size: var(--df-review-font-size-xs);
2626
- font-weight: 900;
3429
+ font-weight: 600;
2627
3430
  opacity: 0.78;
2628
3431
  }
2629
3432
 
@@ -2636,7 +3439,7 @@ function ensureReviewShellStyle() {
2636
3439
  background: var(--df-review-color-ruler-coord-bg);
2637
3440
  color: var(--df-review-color-ruler-coord-text);
2638
3441
  font-size: var(--df-review-font-size-xs);
2639
- font-weight: 900;
3442
+ font-weight: 600;
2640
3443
  line-height: 1;
2641
3444
  white-space: nowrap;
2642
3445
  pointer-events: none;
@@ -2714,7 +3517,7 @@ function ensureReviewShellStyle() {
2714
3517
  color: var(--df-review-color-ruler-popover-text);
2715
3518
  font-family: var(--df-review-font-mono);
2716
3519
  font-size: var(--df-review-font-size-lg);
2717
- font-weight: 900;
3520
+ font-weight: 600;
2718
3521
  line-height: 1;
2719
3522
  white-space: nowrap;
2720
3523
  letter-spacing: -0.02em;
@@ -2727,12 +3530,12 @@ function ensureReviewShellStyle() {
2727
3530
  @media (max-width: 860px) {
2728
3531
  .df-review-shell,
2729
3532
  .df-review-shell.is-list-visible {
2730
- grid-template-columns: minmax(0, 1fr) 0 32px;
3533
+ grid-template-columns: minmax(0, 1fr) 0 48px;
2731
3534
  grid-template-rows: auto auto minmax(0, 1fr);
2732
3535
  }
2733
3536
 
2734
3537
  .df-review-shell.is-list-visible {
2735
- grid-template-columns: minmax(0, 1fr) minmax(260px, 70vw) 32px;
3538
+ grid-template-columns: minmax(0, 1fr) minmax(260px, 70vw) 48px;
2736
3539
  }
2737
3540
 
2738
3541
  .df-review-qa-panel {
@@ -2741,7 +3544,7 @@ function ensureReviewShellStyle() {
2741
3544
  }
2742
3545
 
2743
3546
  .df-review-tools {
2744
- flex-wrap: wrap;
3547
+ flex-wrap: nowrap;
2745
3548
  }
2746
3549
 
2747
3550
  .df-review-tool-controls {
@@ -2771,11 +3574,19 @@ function ensureReviewShellStyle() {
2771
3574
  max-height: calc(100vh - 270px);
2772
3575
  }
2773
3576
 
2774
- .df-review-panel-body {
2775
- min-height: 0;
2776
- }
2777
- }
2778
- `;
3577
+ .df-review-panel-body {
3578
+ min-height: 0;
3579
+ }
3580
+ }
3581
+
3582
+ @media (hover: none) and (pointer: coarse) {
3583
+ .df-review-edit-textarea textarea,
3584
+ .df-review-preset-select,
3585
+ .df-review-prompt-block textarea {
3586
+ font-size: 16px;
3587
+ }
3588
+ }
3589
+ `;
2779
3590
  document.head.append(style);
2780
3591
  }
2781
3592
  }
@@ -2904,31 +3715,51 @@ var __iconNode = [
2904
3715
  ];
2905
3716
  var Bot = createLucideIcon("bot", __iconNode);
2906
3717
 
3718
+ // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/chevron-down.mjs
3719
+ var __iconNode2 = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
3720
+ var ChevronDown = createLucideIcon("chevron-down", __iconNode2);
3721
+
2907
3722
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/circle-question-mark.mjs
2908
- var __iconNode2 = [
3723
+ var __iconNode3 = [
2909
3724
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
2910
3725
  ["path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3", key: "1u773s" }],
2911
3726
  ["path", { d: "M12 17h.01", key: "p32p05" }]
2912
3727
  ];
2913
- var CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode2);
3728
+ var CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode3);
3729
+
3730
+ // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/code-xml.mjs
3731
+ var __iconNode4 = [
3732
+ ["path", { d: "m18 16 4-4-4-4", key: "1inbqp" }],
3733
+ ["path", { d: "m6 8-4 4 4 4", key: "15zrgr" }],
3734
+ ["path", { d: "m14.5 4-5 16", key: "e7oirm" }]
3735
+ ];
3736
+ var CodeXml = createLucideIcon("code-xml", __iconNode4);
2914
3737
 
2915
3738
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/copy.mjs
2916
- var __iconNode3 = [
3739
+ var __iconNode5 = [
2917
3740
  ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
2918
3741
  ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
2919
3742
  ];
2920
- var Copy = createLucideIcon("copy", __iconNode3);
3743
+ var Copy = createLucideIcon("copy", __iconNode5);
3744
+
3745
+ // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/database.mjs
3746
+ var __iconNode6 = [
3747
+ ["ellipse", { cx: "12", cy: "5", rx: "9", ry: "3", key: "msslwz" }],
3748
+ ["path", { d: "M3 5V19A9 3 0 0 0 21 19V5", key: "1wlel7" }],
3749
+ ["path", { d: "M3 12A9 3 0 0 0 21 12", key: "mv7ke4" }]
3750
+ ];
3751
+ var Database = createLucideIcon("database", __iconNode6);
2921
3752
 
2922
3753
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/external-link.mjs
2923
- var __iconNode4 = [
3754
+ var __iconNode7 = [
2924
3755
  ["path", { d: "M15 3h6v6", key: "1q9fwt" }],
2925
3756
  ["path", { d: "M10 14 21 3", key: "gplh6r" }],
2926
3757
  ["path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6", key: "a6xqqp" }]
2927
3758
  ];
2928
- var ExternalLink = createLucideIcon("external-link", __iconNode4);
3759
+ var ExternalLink = createLucideIcon("external-link", __iconNode7);
2929
3760
 
2930
3761
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/eye-off.mjs
2931
- var __iconNode5 = [
3762
+ var __iconNode8 = [
2932
3763
  [
2933
3764
  "path",
2934
3765
  {
@@ -2946,10 +3777,10 @@ var __iconNode5 = [
2946
3777
  ],
2947
3778
  ["path", { d: "m2 2 20 20", key: "1ooewy" }]
2948
3779
  ];
2949
- var EyeOff = createLucideIcon("eye-off", __iconNode5);
3780
+ var EyeOff = createLucideIcon("eye-off", __iconNode8);
2950
3781
 
2951
3782
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/eye.mjs
2952
- var __iconNode6 = [
3783
+ var __iconNode9 = [
2953
3784
  [
2954
3785
  "path",
2955
3786
  {
@@ -2959,54 +3790,59 @@ var __iconNode6 = [
2959
3790
  ],
2960
3791
  ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
2961
3792
  ];
2962
- var Eye = createLucideIcon("eye", __iconNode6);
3793
+ var Eye = createLucideIcon("eye", __iconNode9);
2963
3794
 
2964
- // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/grip-vertical.mjs
2965
- var __iconNode7 = [
2966
- ["circle", { cx: "9", cy: "12", r: "1", key: "1vctgf" }],
2967
- ["circle", { cx: "9", cy: "5", r: "1", key: "hp0tcf" }],
2968
- ["circle", { cx: "9", cy: "19", r: "1", key: "fkjjf6" }],
2969
- ["circle", { cx: "15", cy: "12", r: "1", key: "1tmaij" }],
2970
- ["circle", { cx: "15", cy: "5", r: "1", key: "19l28e" }],
2971
- ["circle", { cx: "15", cy: "19", r: "1", key: "f4zoj3" }]
3795
+ // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/file-text.mjs
3796
+ var __iconNode10 = [
3797
+ [
3798
+ "path",
3799
+ {
3800
+ d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",
3801
+ key: "1oefj6"
3802
+ }
3803
+ ],
3804
+ ["path", { d: "M14 2v5a1 1 0 0 0 1 1h5", key: "wfsgrz" }],
3805
+ ["path", { d: "M10 9H8", key: "b1mrlr" }],
3806
+ ["path", { d: "M16 13H8", key: "t4e002" }],
3807
+ ["path", { d: "M16 17H8", key: "z1uh3a" }]
2972
3808
  ];
2973
- var GripVertical = createLucideIcon("grip-vertical", __iconNode7);
3809
+ var FileText = createLucideIcon("file-text", __iconNode10);
2974
3810
 
2975
3811
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/image.mjs
2976
- var __iconNode8 = [
3812
+ var __iconNode11 = [
2977
3813
  ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
2978
3814
  ["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
2979
3815
  ["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
2980
3816
  ];
2981
- var Image = createLucideIcon("image", __iconNode8);
3817
+ var Image = createLucideIcon("image", __iconNode11);
2982
3818
 
2983
3819
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/layout-grid.mjs
2984
- var __iconNode9 = [
3820
+ var __iconNode12 = [
2985
3821
  ["rect", { width: "7", height: "7", x: "3", y: "3", rx: "1", key: "1g98yp" }],
2986
3822
  ["rect", { width: "7", height: "7", x: "14", y: "3", rx: "1", key: "6d4xhi" }],
2987
3823
  ["rect", { width: "7", height: "7", x: "14", y: "14", rx: "1", key: "nxv5o0" }],
2988
3824
  ["rect", { width: "7", height: "7", x: "3", y: "14", rx: "1", key: "1bb6yr" }]
2989
3825
  ];
2990
- var LayoutGrid = createLucideIcon("layout-grid", __iconNode9);
3826
+ var LayoutGrid = createLucideIcon("layout-grid", __iconNode12);
2991
3827
 
2992
3828
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/link-2.mjs
2993
- var __iconNode10 = [
3829
+ var __iconNode13 = [
2994
3830
  ["path", { d: "M9 17H7A5 5 0 0 1 7 7h2", key: "8i5ue5" }],
2995
3831
  ["path", { d: "M15 7h2a5 5 0 1 1 0 10h-2", key: "1b9ql8" }],
2996
3832
  ["line", { x1: "8", x2: "16", y1: "12", y2: "12", key: "1jonct" }]
2997
3833
  ];
2998
- var Link2 = createLucideIcon("link-2", __iconNode10);
3834
+ var Link2 = createLucideIcon("link-2", __iconNode13);
2999
3835
 
3000
3836
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/list-filter.mjs
3001
- var __iconNode11 = [
3837
+ var __iconNode14 = [
3002
3838
  ["path", { d: "M2 5h20", key: "1fs1ex" }],
3003
3839
  ["path", { d: "M6 12h12", key: "8npq4p" }],
3004
3840
  ["path", { d: "M9 19h6", key: "456am0" }]
3005
3841
  ];
3006
- var ListFilter = createLucideIcon("list-filter", __iconNode11);
3842
+ var ListFilter = createLucideIcon("list-filter", __iconNode14);
3007
3843
 
3008
3844
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/map.mjs
3009
- var __iconNode12 = [
3845
+ var __iconNode15 = [
3010
3846
  [
3011
3847
  "path",
3012
3848
  {
@@ -3017,27 +3853,39 @@ var __iconNode12 = [
3017
3853
  ["path", { d: "M15 5.764v15", key: "1pn4in" }],
3018
3854
  ["path", { d: "M9 3.236v15", key: "1uimfh" }]
3019
3855
  ];
3020
- var Map2 = createLucideIcon("map", __iconNode12);
3856
+ var Map2 = createLucideIcon("map", __iconNode15);
3021
3857
 
3022
3858
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/maximize-2.mjs
3023
- var __iconNode13 = [
3859
+ var __iconNode16 = [
3024
3860
  ["path", { d: "M15 3h6v6", key: "1q9fwt" }],
3025
3861
  ["path", { d: "m21 3-7 7", key: "1l2asr" }],
3026
3862
  ["path", { d: "m3 21 7-7", key: "tjx5ai" }],
3027
3863
  ["path", { d: "M9 21H3v-6", key: "wtvkvv" }]
3028
3864
  ];
3029
- var Maximize2 = createLucideIcon("maximize-2", __iconNode13);
3865
+ var Maximize2 = createLucideIcon("maximize-2", __iconNode16);
3030
3866
 
3031
3867
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/monitor.mjs
3032
- var __iconNode14 = [
3868
+ var __iconNode17 = [
3033
3869
  ["rect", { width: "20", height: "14", x: "2", y: "3", rx: "2", key: "48i651" }],
3034
3870
  ["line", { x1: "8", x2: "16", y1: "21", y2: "21", key: "1svkeh" }],
3035
3871
  ["line", { x1: "12", x2: "12", y1: "17", y2: "21", key: "vw1qmm" }]
3036
3872
  ];
3037
- var Monitor = createLucideIcon("monitor", __iconNode14);
3873
+ var Monitor = createLucideIcon("monitor", __iconNode17);
3874
+
3875
+ // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/moon.mjs
3876
+ var __iconNode18 = [
3877
+ [
3878
+ "path",
3879
+ {
3880
+ d: "M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",
3881
+ key: "kfwtm"
3882
+ }
3883
+ ]
3884
+ ];
3885
+ var Moon = createLucideIcon("moon", __iconNode18);
3038
3886
 
3039
3887
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/pencil.mjs
3040
- var __iconNode15 = [
3888
+ var __iconNode19 = [
3041
3889
  [
3042
3890
  "path",
3043
3891
  {
@@ -3047,25 +3895,25 @@ var __iconNode15 = [
3047
3895
  ],
3048
3896
  ["path", { d: "m15 5 4 4", key: "1mk7zo" }]
3049
3897
  ];
3050
- var Pencil = createLucideIcon("pencil", __iconNode15);
3898
+ var Pencil = createLucideIcon("pencil", __iconNode19);
3051
3899
 
3052
3900
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/rectangle-horizontal.mjs
3053
- var __iconNode16 = [
3901
+ var __iconNode20 = [
3054
3902
  ["rect", { width: "20", height: "12", x: "2", y: "6", rx: "2", key: "9lu3g6" }]
3055
3903
  ];
3056
- var RectangleHorizontal = createLucideIcon("rectangle-horizontal", __iconNode16);
3904
+ var RectangleHorizontal = createLucideIcon("rectangle-horizontal", __iconNode20);
3057
3905
 
3058
3906
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/refresh-cw.mjs
3059
- var __iconNode17 = [
3907
+ var __iconNode21 = [
3060
3908
  ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
3061
3909
  ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
3062
3910
  ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
3063
3911
  ["path", { d: "M8 16H3v5", key: "1cv678" }]
3064
3912
  ];
3065
- var RefreshCw = createLucideIcon("refresh-cw", __iconNode17);
3913
+ var RefreshCw = createLucideIcon("refresh-cw", __iconNode21);
3066
3914
 
3067
3915
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/ruler.mjs
3068
- var __iconNode18 = [
3916
+ var __iconNode22 = [
3069
3917
  [
3070
3918
  "path",
3071
3919
  {
@@ -3078,19 +3926,26 @@ var __iconNode18 = [
3078
3926
  ["path", { d: "m8.5 6.5 2-2", key: "vc6u1g" }],
3079
3927
  ["path", { d: "m17.5 15.5 2-2", key: "wo5hmg" }]
3080
3928
  ];
3081
- var Ruler = createLucideIcon("ruler", __iconNode18);
3929
+ var Ruler = createLucideIcon("ruler", __iconNode22);
3082
3930
 
3083
3931
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/scan.mjs
3084
- var __iconNode19 = [
3932
+ var __iconNode23 = [
3085
3933
  ["path", { d: "M3 7V5a2 2 0 0 1 2-2h2", key: "aa7l1z" }],
3086
3934
  ["path", { d: "M17 3h2a2 2 0 0 1 2 2v2", key: "4qcy5o" }],
3087
3935
  ["path", { d: "M21 17v2a2 2 0 0 1-2 2h-2", key: "6vwrx8" }],
3088
3936
  ["path", { d: "M7 21H5a2 2 0 0 1-2-2v-2", key: "ioqczr" }]
3089
3937
  ];
3090
- var Scan = createLucideIcon("scan", __iconNode19);
3938
+ var Scan = createLucideIcon("scan", __iconNode23);
3939
+
3940
+ // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/search.mjs
3941
+ var __iconNode24 = [
3942
+ ["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
3943
+ ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
3944
+ ];
3945
+ var Search = createLucideIcon("search", __iconNode24);
3091
3946
 
3092
3947
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/settings.mjs
3093
- var __iconNode20 = [
3948
+ var __iconNode25 = [
3094
3949
  [
3095
3950
  "path",
3096
3951
  {
@@ -3100,17 +3955,34 @@ var __iconNode20 = [
3100
3955
  ],
3101
3956
  ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
3102
3957
  ];
3103
- var Settings = createLucideIcon("settings", __iconNode20);
3958
+ var Settings = createLucideIcon("settings", __iconNode25);
3104
3959
 
3105
3960
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/smartphone.mjs
3106
- var __iconNode21 = [
3961
+ var __iconNode26 = [
3107
3962
  ["rect", { width: "14", height: "20", x: "5", y: "2", rx: "2", ry: "2", key: "1yt0o3" }],
3108
3963
  ["path", { d: "M12 18h.01", key: "mhygvu" }]
3109
3964
  ];
3110
- var Smartphone = createLucideIcon("smartphone", __iconNode21);
3965
+ var Smartphone = createLucideIcon("smartphone", __iconNode26);
3966
+
3967
+ // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/square-dashed.mjs
3968
+ var __iconNode27 = [
3969
+ ["path", { d: "M5 3a2 2 0 0 0-2 2", key: "y57alp" }],
3970
+ ["path", { d: "M19 3a2 2 0 0 1 2 2", key: "18rm91" }],
3971
+ ["path", { d: "M21 19a2 2 0 0 1-2 2", key: "1j7049" }],
3972
+ ["path", { d: "M5 21a2 2 0 0 1-2-2", key: "sbafld" }],
3973
+ ["path", { d: "M9 3h1", key: "1yesri" }],
3974
+ ["path", { d: "M9 21h1", key: "15o7lz" }],
3975
+ ["path", { d: "M14 3h1", key: "1ec4yj" }],
3976
+ ["path", { d: "M14 21h1", key: "v9vybs" }],
3977
+ ["path", { d: "M3 9v1", key: "1r0deq" }],
3978
+ ["path", { d: "M21 9v1", key: "mxsmne" }],
3979
+ ["path", { d: "M3 14v1", key: "vnatye" }],
3980
+ ["path", { d: "M21 14v1", key: "169vum" }]
3981
+ ];
3982
+ var SquareDashed = createLucideIcon("square-dashed", __iconNode27);
3111
3983
 
3112
3984
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/square-mouse-pointer.mjs
3113
- var __iconNode22 = [
3985
+ var __iconNode28 = [
3114
3986
  [
3115
3987
  "path",
3116
3988
  {
@@ -3120,10 +3992,10 @@ var __iconNode22 = [
3120
3992
  ],
3121
3993
  ["path", { d: "M21 11V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6", key: "14rsvq" }]
3122
3994
  ];
3123
- var SquareMousePointer = createLucideIcon("square-mouse-pointer", __iconNode22);
3995
+ var SquareMousePointer = createLucideIcon("square-mouse-pointer", __iconNode28);
3124
3996
 
3125
3997
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/sticky-note.mjs
3126
- var __iconNode23 = [
3998
+ var __iconNode29 = [
3127
3999
  [
3128
4000
  "path",
3129
4001
  {
@@ -3133,22 +4005,52 @@ var __iconNode23 = [
3133
4005
  ],
3134
4006
  ["path", { d: "M15 3v5a1 1 0 0 0 1 1h5", key: "6s6qgf" }]
3135
4007
  ];
3136
- var StickyNote = createLucideIcon("sticky-note", __iconNode23);
4008
+ var StickyNote = createLucideIcon("sticky-note", __iconNode29);
4009
+
4010
+ // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/sun.mjs
4011
+ var __iconNode30 = [
4012
+ ["circle", { cx: "12", cy: "12", r: "4", key: "4exip2" }],
4013
+ ["path", { d: "M12 2v2", key: "tus03m" }],
4014
+ ["path", { d: "M12 20v2", key: "1lh1kg" }],
4015
+ ["path", { d: "m4.93 4.93 1.41 1.41", key: "149t6j" }],
4016
+ ["path", { d: "m17.66 17.66 1.41 1.41", key: "ptbguv" }],
4017
+ ["path", { d: "M2 12h2", key: "1t8f8n" }],
4018
+ ["path", { d: "M20 12h2", key: "1q8mjw" }],
4019
+ ["path", { d: "m6.34 17.66-1.41 1.41", key: "1m8zz5" }],
4020
+ ["path", { d: "m19.07 4.93-1.41 1.41", key: "1shlcs" }]
4021
+ ];
4022
+ var Sun = createLucideIcon("sun", __iconNode30);
4023
+
4024
+ // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/type.mjs
4025
+ var __iconNode31 = [
4026
+ ["path", { d: "M12 4v16", key: "1654pz" }],
4027
+ ["path", { d: "M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2", key: "e0r10z" }],
4028
+ ["path", { d: "M9 20h6", key: "s66wpe" }]
4029
+ ];
4030
+ var Type = createLucideIcon("type", __iconNode31);
3137
4031
 
3138
4032
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/upload.mjs
3139
- var __iconNode24 = [
4033
+ var __iconNode32 = [
3140
4034
  ["path", { d: "M12 3v12", key: "1x0j5s" }],
3141
4035
  ["path", { d: "m17 8-5-5-5 5", key: "7q97r8" }],
3142
4036
  ["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }]
3143
4037
  ];
3144
- var Upload = createLucideIcon("upload", __iconNode24);
4038
+ var Upload = createLucideIcon("upload", __iconNode32);
4039
+
4040
+ // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/workflow.mjs
4041
+ var __iconNode33 = [
4042
+ ["rect", { width: "8", height: "8", x: "3", y: "3", rx: "2", key: "by2w9f" }],
4043
+ ["path", { d: "M7 11v4a2 2 0 0 0 2 2h4", key: "xkn7yn" }],
4044
+ ["rect", { width: "8", height: "8", x: "13", y: "13", rx: "2", key: "1cgmvn" }]
4045
+ ];
4046
+ var Workflow = createLucideIcon("workflow", __iconNode33);
3145
4047
 
3146
4048
  // node_modules/.pnpm/lucide-react@1.20.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/x.mjs
3147
- var __iconNode25 = [
4049
+ var __iconNode34 = [
3148
4050
  ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
3149
4051
  ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
3150
4052
  ];
3151
- var X = createLucideIcon("x", __iconNode25);
4053
+ var X = createLucideIcon("x", __iconNode34);
3152
4054
 
3153
4055
  // src/react-shell/constants.ts
3154
4056
  var REVIEW_QA_FILTERS = [
@@ -3162,6 +4064,11 @@ var FIGMA_OVERLAY_UNAVAILABLE_MESSAGE = "\uD53C\uADF8\uB9C8 \uC624\uBC84\uB808\u
3162
4064
  var FIGMA_TOKEN_STORAGE_KEY = "figma-token";
3163
4065
  var REVIEW_USER_ID_STORAGE_KEY = "user-id";
3164
4066
  var REVIEW_THEME_STORAGE_KEY = "df-review-theme";
4067
+ var REVIEW_SIDE_PANEL_STORAGE_KEY = "df-review-side-panel";
4068
+ var REVIEW_SIDE_PANEL_VISIBLE_STORAGE_KEY = "df-review-side-panel-visible";
4069
+ var REVIEW_SOURCE_TREE_FILTER_STORAGE_KEY = "df-review-source-tree-filter";
4070
+ var REVIEW_SOURCE_TREE_META_STORAGE_KEY = "df-review-source-tree-meta-visibility";
4071
+ var REVIEW_QA_STATUS_FILTER_STORAGE_KEY = "df-review-qa-status-filter";
3165
4072
  var DEFAULT_REVIEW_THEME = "dark";
3166
4073
  var FIGMA_TOKEN_GUIDE_ID = "df-review-figma-token-guide";
3167
4074
  var DEFAULT_INITIAL_REVIEW_PROMPT = "You are fixing QA issues collected with df-web-review-kit. Use the copied QA prompt as the source of truth for page, viewport, selector, DOM metadata, coordinates, and user comment. Make the smallest code or CSS change that fixes the issue, preserve unrelated behavior, then verify the target viewport again.";
@@ -3587,7 +4494,35 @@ var PromptModal = ({
3587
4494
  };
3588
4495
 
3589
4496
  // src/react-shell/settings.ts
4497
+ var DEFAULT_SOURCE_TREE_META_VISIBILITY = {
4498
+ box: true,
4499
+ font: true,
4500
+ media: true,
4501
+ className: false
4502
+ };
4503
+ var REVIEW_QA_STATUS_FILTER_VALUES = /* @__PURE__ */ new Set([
4504
+ "all",
4505
+ "todo",
4506
+ "doing",
4507
+ "review",
4508
+ "hold",
4509
+ "done"
4510
+ ]);
3590
4511
  var normalizeReviewTheme = (value) => value === "light" || value === "system" || value === "dark" ? value : DEFAULT_REVIEW_THEME;
4512
+ var normalizeStoredReviewSidePanel = (value) => value === "source" ? "source" : "qa";
4513
+ var normalizeStoredReviewQaStatusFilter = (value) => value && REVIEW_QA_STATUS_FILTER_VALUES.has(value) ? value : "all";
4514
+ var normalizeStoredSourceTreeMetaVisibility = (value) => {
4515
+ if (!value || typeof value !== "object") {
4516
+ return DEFAULT_SOURCE_TREE_META_VISIBILITY;
4517
+ }
4518
+ const metaVisibility = value;
4519
+ return {
4520
+ box: typeof metaVisibility.box === "boolean" ? metaVisibility.box : DEFAULT_SOURCE_TREE_META_VISIBILITY.box,
4521
+ font: typeof metaVisibility.font === "boolean" ? metaVisibility.font : DEFAULT_SOURCE_TREE_META_VISIBILITY.font,
4522
+ media: typeof metaVisibility.media === "boolean" ? metaVisibility.media : DEFAULT_SOURCE_TREE_META_VISIBILITY.media,
4523
+ className: typeof metaVisibility.className === "boolean" ? metaVisibility.className : DEFAULT_SOURCE_TREE_META_VISIBILITY.className
4524
+ };
4525
+ };
3591
4526
  var getStoredFigmaToken = () => {
3592
4527
  if (typeof window === "undefined") return "";
3593
4528
  try {
@@ -3602,50 +4537,162 @@ var writeStoredFigmaToken = (token) => {
3602
4537
  if (token) {
3603
4538
  window.localStorage.setItem(FIGMA_TOKEN_STORAGE_KEY, token);
3604
4539
  } else {
3605
- window.localStorage.removeItem(FIGMA_TOKEN_STORAGE_KEY);
4540
+ window.localStorage.removeItem(FIGMA_TOKEN_STORAGE_KEY);
4541
+ }
4542
+ } catch {
4543
+ return;
4544
+ }
4545
+ };
4546
+ var getStoredReviewUserId = () => {
4547
+ if (typeof window === "undefined") return "";
4548
+ try {
4549
+ return window.localStorage.getItem(REVIEW_USER_ID_STORAGE_KEY) ?? "";
4550
+ } catch {
4551
+ return "";
4552
+ }
4553
+ };
4554
+ var writeStoredReviewUserId = (userId) => {
4555
+ if (typeof window === "undefined") return;
4556
+ try {
4557
+ if (userId) {
4558
+ window.localStorage.setItem(REVIEW_USER_ID_STORAGE_KEY, userId);
4559
+ } else {
4560
+ window.localStorage.removeItem(REVIEW_USER_ID_STORAGE_KEY);
4561
+ }
4562
+ } catch {
4563
+ return;
4564
+ }
4565
+ };
4566
+ var getStoredReviewTheme = () => {
4567
+ if (typeof window === "undefined") return DEFAULT_REVIEW_THEME;
4568
+ try {
4569
+ return normalizeReviewTheme(
4570
+ window.localStorage.getItem(REVIEW_THEME_STORAGE_KEY)
4571
+ );
4572
+ } catch {
4573
+ return DEFAULT_REVIEW_THEME;
4574
+ }
4575
+ };
4576
+ var writeStoredReviewTheme = (theme) => {
4577
+ if (typeof window === "undefined") return;
4578
+ try {
4579
+ if (theme === DEFAULT_REVIEW_THEME) {
4580
+ window.localStorage.removeItem(REVIEW_THEME_STORAGE_KEY);
4581
+ } else {
4582
+ window.localStorage.setItem(REVIEW_THEME_STORAGE_KEY, theme);
4583
+ }
4584
+ } catch {
4585
+ return;
4586
+ }
4587
+ };
4588
+ var getStoredReviewSidePanel = () => {
4589
+ if (typeof window === "undefined") return "qa";
4590
+ try {
4591
+ return normalizeStoredReviewSidePanel(
4592
+ window.localStorage.getItem(REVIEW_SIDE_PANEL_STORAGE_KEY)
4593
+ );
4594
+ } catch {
4595
+ return "qa";
4596
+ }
4597
+ };
4598
+ var writeStoredReviewSidePanel = (sidePanel) => {
4599
+ if (typeof window === "undefined") return;
4600
+ try {
4601
+ window.localStorage.setItem(
4602
+ REVIEW_SIDE_PANEL_STORAGE_KEY,
4603
+ normalizeStoredReviewSidePanel(sidePanel)
4604
+ );
4605
+ } catch {
4606
+ return;
4607
+ }
4608
+ };
4609
+ var getStoredReviewSidePanelVisible = () => {
4610
+ if (typeof window === "undefined") return true;
4611
+ try {
4612
+ const value = window.localStorage.getItem(
4613
+ REVIEW_SIDE_PANEL_VISIBLE_STORAGE_KEY
4614
+ );
4615
+ return value === null ? true : value === "true";
4616
+ } catch {
4617
+ return true;
4618
+ }
4619
+ };
4620
+ var writeStoredReviewSidePanelVisible = (isVisible) => {
4621
+ if (typeof window === "undefined") return;
4622
+ try {
4623
+ window.localStorage.setItem(
4624
+ REVIEW_SIDE_PANEL_VISIBLE_STORAGE_KEY,
4625
+ isVisible ? "true" : "false"
4626
+ );
4627
+ } catch {
4628
+ return;
4629
+ }
4630
+ };
4631
+ var getStoredReviewQaStatusFilter = () => {
4632
+ if (typeof window === "undefined") return "all";
4633
+ try {
4634
+ return normalizeStoredReviewQaStatusFilter(
4635
+ window.localStorage.getItem(REVIEW_QA_STATUS_FILTER_STORAGE_KEY)
4636
+ );
4637
+ } catch {
4638
+ return "all";
4639
+ }
4640
+ };
4641
+ var writeStoredReviewQaStatusFilter = (filter) => {
4642
+ if (typeof window === "undefined") return;
4643
+ try {
4644
+ const normalizedFilter = normalizeStoredReviewQaStatusFilter(filter);
4645
+ if (normalizedFilter === "all") {
4646
+ window.localStorage.removeItem(REVIEW_QA_STATUS_FILTER_STORAGE_KEY);
4647
+ } else {
4648
+ window.localStorage.setItem(
4649
+ REVIEW_QA_STATUS_FILTER_STORAGE_KEY,
4650
+ normalizedFilter
4651
+ );
3606
4652
  }
3607
4653
  } catch {
3608
4654
  return;
3609
4655
  }
3610
4656
  };
3611
- var getStoredReviewUserId = () => {
4657
+ var getStoredSourceTreeFilter = () => {
3612
4658
  if (typeof window === "undefined") return "";
3613
4659
  try {
3614
- return window.localStorage.getItem(REVIEW_USER_ID_STORAGE_KEY) ?? "";
4660
+ return window.localStorage.getItem(REVIEW_SOURCE_TREE_FILTER_STORAGE_KEY) ?? "";
3615
4661
  } catch {
3616
4662
  return "";
3617
4663
  }
3618
4664
  };
3619
- var writeStoredReviewUserId = (userId) => {
4665
+ var writeStoredSourceTreeFilter = (filter) => {
3620
4666
  if (typeof window === "undefined") return;
3621
4667
  try {
3622
- if (userId) {
3623
- window.localStorage.setItem(REVIEW_USER_ID_STORAGE_KEY, userId);
4668
+ if (filter) {
4669
+ window.localStorage.setItem(REVIEW_SOURCE_TREE_FILTER_STORAGE_KEY, filter);
3624
4670
  } else {
3625
- window.localStorage.removeItem(REVIEW_USER_ID_STORAGE_KEY);
4671
+ window.localStorage.removeItem(REVIEW_SOURCE_TREE_FILTER_STORAGE_KEY);
3626
4672
  }
3627
4673
  } catch {
3628
4674
  return;
3629
4675
  }
3630
4676
  };
3631
- var getStoredReviewTheme = () => {
3632
- if (typeof window === "undefined") return DEFAULT_REVIEW_THEME;
4677
+ var getStoredSourceTreeMetaVisibility = () => {
4678
+ if (typeof window === "undefined") return DEFAULT_SOURCE_TREE_META_VISIBILITY;
3633
4679
  try {
3634
- return normalizeReviewTheme(
3635
- window.localStorage.getItem(REVIEW_THEME_STORAGE_KEY)
4680
+ const value = window.localStorage.getItem(
4681
+ REVIEW_SOURCE_TREE_META_STORAGE_KEY
3636
4682
  );
4683
+ if (!value) return DEFAULT_SOURCE_TREE_META_VISIBILITY;
4684
+ return normalizeStoredSourceTreeMetaVisibility(JSON.parse(value));
3637
4685
  } catch {
3638
- return DEFAULT_REVIEW_THEME;
4686
+ return DEFAULT_SOURCE_TREE_META_VISIBILITY;
3639
4687
  }
3640
4688
  };
3641
- var writeStoredReviewTheme = (theme) => {
4689
+ var writeStoredSourceTreeMetaVisibility = (metaVisibility) => {
3642
4690
  if (typeof window === "undefined") return;
3643
4691
  try {
3644
- if (theme === DEFAULT_REVIEW_THEME) {
3645
- window.localStorage.removeItem(REVIEW_THEME_STORAGE_KEY);
3646
- } else {
3647
- window.localStorage.setItem(REVIEW_THEME_STORAGE_KEY, theme);
3648
- }
4692
+ window.localStorage.setItem(
4693
+ REVIEW_SOURCE_TREE_META_STORAGE_KEY,
4694
+ JSON.stringify(normalizeStoredSourceTreeMetaVisibility(metaVisibility))
4695
+ );
3649
4696
  } catch {
3650
4697
  return;
3651
4698
  }
@@ -3657,6 +4704,11 @@ var getSystemReviewTheme = () => {
3657
4704
 
3658
4705
  // src/react-shell/review/settings.modal.tsx
3659
4706
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
4707
+ var getReviewThemeIcon = (theme) => {
4708
+ if (theme === "light") return Sun;
4709
+ if (theme === "system") return Monitor;
4710
+ return Moon;
4711
+ };
3660
4712
  var ReviewSettingsModal = ({
3661
4713
  figmaTokenDraft,
3662
4714
  reviewUserIdDraft,
@@ -3716,20 +4768,28 @@ var ReviewSettingsModal = ({
3716
4768
  /* @__PURE__ */ jsxs2("div", { className: "df-review-settings-body", children: [
3717
4769
  /* @__PURE__ */ jsxs2("div", { className: "df-review-settings-row", children: [
3718
4770
  /* @__PURE__ */ jsx2("span", { children: "Theme" }),
3719
- /* @__PURE__ */ jsx2("div", { className: "df-review-settings-theme-options", children: REVIEW_THEME_OPTIONS.map((option) => /* @__PURE__ */ jsx2(
3720
- "button",
3721
- {
3722
- "aria-pressed": reviewThemeDraft === option.value,
3723
- className: `df-review-settings-theme-option${reviewThemeDraft === option.value ? " is-active" : ""}`,
3724
- type: "button",
3725
- onClick: () => {
3726
- onReviewThemeDraftChange(normalizeReviewTheme(option.value));
3727
- onClearStatus();
4771
+ /* @__PURE__ */ jsx2("div", { className: "df-review-settings-theme-options", children: REVIEW_THEME_OPTIONS.map((option) => {
4772
+ const ThemeIcon = getReviewThemeIcon(option.value);
4773
+ return /* @__PURE__ */ jsxs2(
4774
+ "button",
4775
+ {
4776
+ "aria-pressed": reviewThemeDraft === option.value,
4777
+ className: `df-review-settings-theme-option${reviewThemeDraft === option.value ? " is-active" : ""}`,
4778
+ type: "button",
4779
+ onClick: () => {
4780
+ onReviewThemeDraftChange(
4781
+ normalizeReviewTheme(option.value)
4782
+ );
4783
+ onClearStatus();
4784
+ },
4785
+ children: [
4786
+ /* @__PURE__ */ jsx2(ThemeIcon, { "aria-hidden": "true" }),
4787
+ /* @__PURE__ */ jsx2("span", { children: option.label })
4788
+ ]
3728
4789
  },
3729
- children: option.label
3730
- },
3731
- option.value
3732
- )) })
4790
+ option.value
4791
+ );
4792
+ }) })
3733
4793
  ] }),
3734
4794
  /* @__PURE__ */ jsxs2("div", { className: "df-review-settings-field", children: [
3735
4795
  /* @__PURE__ */ jsxs2("div", { className: "df-review-settings-label-row", children: [
@@ -4704,6 +5764,7 @@ var QaItemCard = ({
4704
5764
  onChangeItemStatus,
4705
5765
  onClearSelectedItem,
4706
5766
  onRemoveItem,
5767
+ onCopyItemLabel,
4707
5768
  onCopyItemLink,
4708
5769
  onCopyItemPrompt,
4709
5770
  onEditItem,
@@ -4719,8 +5780,10 @@ var QaItemCard = ({
4719
5780
  const itemComment = item.comment.trim() || getItemTitle(item);
4720
5781
  const itemAuthor = item.createdBy?.trim();
4721
5782
  const promptCopyKey = `qa:${item.id}`;
5783
+ const labelCopyKey = `label:${item.id}`;
4722
5784
  const linkCopyKey = `link:${item.id}`;
4723
5785
  const isPromptCopied = copiedPromptKey === promptCopyKey;
5786
+ const isLabelCopied = copiedPromptKey === labelCopyKey;
4724
5787
  const isLinkCopied = copiedPromptKey === linkCopyKey;
4725
5788
  const statusOptions = activeAdapterEntry.statusOptions;
4726
5789
  const isActive = item.id === selectedItemId;
@@ -4742,7 +5805,20 @@ var QaItemCard = ({
4742
5805
  /* @__PURE__ */ jsxs6("div", { className: "df-review-item-header", children: [
4743
5806
  /* @__PURE__ */ jsxs6("div", { className: "df-review-item-main", children: [
4744
5807
  /* @__PURE__ */ jsxs6("span", { className: "df-review-item-badges", children: [
4745
- /* @__PURE__ */ jsx8("span", { className: "df-review-item-id", children: numberedItem.displayLabel }),
5808
+ /* @__PURE__ */ jsx8(
5809
+ "button",
5810
+ {
5811
+ "aria-label": isLabelCopied ? "Copied QA number" : "Copy QA number",
5812
+ className: `df-review-item-id${isLabelCopied ? " is-copied" : ""}`,
5813
+ title: isLabelCopied ? "Copied QA number" : "Copy QA number",
5814
+ type: "button",
5815
+ onClick: (event) => {
5816
+ event.stopPropagation();
5817
+ onCopyItemLabel(numberedItem);
5818
+ },
5819
+ children: numberedItem.displayLabel
5820
+ }
5821
+ ),
4746
5822
  /* @__PURE__ */ jsxs6(
4747
5823
  "span",
4748
5824
  {
@@ -4883,7 +5959,36 @@ var QaPanelHeader = ({
4883
5959
  }) => {
4884
5960
  const statusFilterOptions = getStatusFilterOptions(statusOptions);
4885
5961
  const hasActiveFilter = qaFilter !== "all" || qaStatusFilter !== "all";
5962
+ const displayLabel = getQaSourceDisplayLabel(label);
4886
5963
  return /* @__PURE__ */ jsxs7("div", { className: "df-review-list-header", children: [
5964
+ /* @__PURE__ */ jsxs7("div", { className: "df-review-list-title", children: [
5965
+ /* @__PURE__ */ jsxs7("span", { className: "df-review-list-meta", children: [
5966
+ /* @__PURE__ */ jsx9("span", { children: isAllQaVisible ? `${displayLabel} QA \xB7 All pages` : `${displayLabel} QA` }),
5967
+ /* @__PURE__ */ jsx9(
5968
+ "strong",
5969
+ {
5970
+ title: `${activeRemainingItemCount} remaining of ${activeItemCount}`,
5971
+ children: !hasActiveFilter ? `${activeRemainingItemCount}/${activeItemCount}` : `${filteredItemCount}/${activeItemCount}`
5972
+ }
5973
+ )
5974
+ ] }),
5975
+ /* @__PURE__ */ jsx9("div", { className: "df-review-filter-tabs", "aria-label": "QA filters", children: REVIEW_QA_FILTERS.map((filter) => {
5976
+ const count = qaFilterCounts.get(filter.key) ?? 0;
5977
+ const isActive = qaFilter === filter.key;
5978
+ return /* @__PURE__ */ jsx9(
5979
+ "button",
5980
+ {
5981
+ "aria-label": `${filter.label} QA (${count})`,
5982
+ "aria-pressed": isActive,
5983
+ className: `df-review-filter-tab${isActive ? " is-active" : ""}`,
5984
+ type: "button",
5985
+ onClick: () => onQaFilterChange(filter.key),
5986
+ children: /* @__PURE__ */ jsx9("span", { className: "df-review-filter-icon", children: filter.scope ? /* @__PURE__ */ jsx9(ReviewScopeIcon, { scope: filter.scope }) : /* @__PURE__ */ jsx9(ListFilter, { "aria-hidden": "true" }) })
5987
+ },
5988
+ filter.key
5989
+ );
5990
+ }) })
5991
+ ] }),
4887
5992
  /* @__PURE__ */ jsxs7("div", { className: "df-review-list-toolbar", children: [
4888
5993
  /* @__PURE__ */ jsxs7("div", { className: "df-review-list-controls", children: [
4889
5994
  showSourceSelect && /* @__PURE__ */ jsx9(
@@ -4907,26 +6012,6 @@ var QaPanelHeader = ({
4907
6012
  }
4908
6013
  )
4909
6014
  ] }),
4910
- /* @__PURE__ */ jsx9("div", { className: "df-review-filter-tabs", "aria-label": "QA filters", children: REVIEW_QA_FILTERS.map((filter) => {
4911
- const count = qaFilterCounts.get(filter.key) ?? 0;
4912
- const isActive = qaFilter === filter.key;
4913
- return /* @__PURE__ */ jsx9(
4914
- "button",
4915
- {
4916
- "aria-label": `${filter.label} QA (${count})`,
4917
- "aria-pressed": isActive,
4918
- className: `df-review-filter-tab${isActive ? " is-active" : ""}`,
4919
- type: "button",
4920
- onClick: () => onQaFilterChange(filter.key),
4921
- children: /* @__PURE__ */ jsx9("span", { className: "df-review-filter-icon", children: filter.scope ? /* @__PURE__ */ jsx9(ReviewScopeIcon, { scope: filter.scope }) : /* @__PURE__ */ jsx9(ListFilter, { "aria-hidden": "true" }) })
4922
- },
4923
- filter.key
4924
- );
4925
- }) })
4926
- ] }),
4927
- /* @__PURE__ */ jsxs7("div", { className: "df-review-list-title", children: [
4928
- /* @__PURE__ */ jsx9("span", { children: isAllQaVisible ? `${label} QA \xB7 All pages` : `${label} QA` }),
4929
- /* @__PURE__ */ jsx9("strong", { title: `${activeRemainingItemCount} remaining of ${activeItemCount}`, children: !hasActiveFilter ? `${activeRemainingItemCount}/${activeItemCount}` : `${filteredItemCount}/${activeItemCount}` }),
4930
6015
  /* @__PURE__ */ jsxs7(
4931
6016
  "select",
4932
6017
  {
@@ -4945,6 +6030,9 @@ var QaPanelHeader = ({
4945
6030
  ] })
4946
6031
  ] });
4947
6032
  };
6033
+ function getQaSourceDisplayLabel(label) {
6034
+ return label === "local" ? "Local" : label;
6035
+ }
4948
6036
  function getStatusFilterOptions(statusOptions) {
4949
6037
  const seen = /* @__PURE__ */ new Set();
4950
6038
  return statusOptions.flatMap((statusOption) => {
@@ -4984,6 +6072,7 @@ var ReviewQaPanel = ({
4984
6072
  onChangeItemStatus,
4985
6073
  onClearSelectedItem,
4986
6074
  onChangeReviewSource,
6075
+ onCopyItemLabel,
4987
6076
  onCopyItemLink,
4988
6077
  onCopyItemPrompt,
4989
6078
  onEditItem,
@@ -4996,119 +6085,143 @@ var ReviewQaPanel = ({
4996
6085
  onToggleItemOverlayVisibility
4997
6086
  }) => {
4998
6087
  const emptyMessage = isAllQaVisible ? `No ${activeAdapterEntry.label} QA.` : isRemoteSource ? `No ${activeAdapterEntry.label} QA on this page.` : "No QA on this page.";
4999
- return /* @__PURE__ */ jsx10("aside", { className: "df-review-qa-panel", "aria-hidden": !isListVisible, children: /* @__PURE__ */ jsx10("div", { className: "df-review-panel-body", children: /* @__PURE__ */ jsxs8("section", { className: "df-review-item-list", children: [
5000
- /* @__PURE__ */ jsx10(
5001
- QaPanelHeader,
5002
- {
5003
- activeItemCount: activeItems.length,
5004
- activeRemainingItemCount,
5005
- filteredItemCount: filteredNumberedActiveItems.length,
5006
- isAllQaVisible,
5007
- label: activeAdapterEntry.label,
5008
- qaFilter,
5009
- qaFilterCounts,
5010
- qaStatusFilter,
5011
- qaStatusFilterCounts,
5012
- showSourceSelect,
5013
- source,
5014
- sourceEntries,
5015
- statusOptions: activeAdapterEntry.statusOptions,
5016
- onChangeReviewSource,
5017
- onQaFilterChange,
5018
- onQaStatusFilterChange,
5019
- onRefreshReviewData
5020
- }
5021
- ),
5022
- /* @__PURE__ */ jsxs8(
5023
- "div",
5024
- {
5025
- className: "df-review-list-scroll",
5026
- onClick: (event) => {
5027
- if (event.target === event.currentTarget) {
5028
- onClearSelectedItem();
5029
- }
5030
- },
5031
- children: [
5032
- activeItems.length === 0 && /* @__PURE__ */ jsx10("p", { className: "df-review-empty", children: emptyMessage }),
5033
- activeItems.length > 0 && filteredNumberedActiveItems.length === 0 && /* @__PURE__ */ jsx10("p", { className: "df-review-empty", children: "No QA in this filter." }),
5034
- filteredNumberedActiveItems.map((numberedItem) => {
5035
- const { item } = numberedItem;
5036
- return /* @__PURE__ */ jsx10(
5037
- QaItemCard,
5038
- {
5039
- activeAdapterEntry,
5040
- currentPresetScope,
5041
- getItemPresetScope,
5042
- isOverlayVisible: !hiddenOverlayItemIds.has(item.id),
5043
- isRemoteSource,
5044
- numberedItem,
5045
- remoteAdapterEntry,
5046
- copiedPromptKey,
5047
- selectedItemId,
5048
- onChangeItemStatus,
5049
- onClearSelectedItem,
5050
- onCopyItemLink,
5051
- onCopyItemPrompt,
5052
- onEditItem,
5053
- onRemoveItem,
5054
- onRestoreReviewItem,
5055
- onSubmitItem,
5056
- onToggleItemOverlayVisibility
5057
- },
5058
- item.id
5059
- );
5060
- })
5061
- ]
5062
- }
5063
- )
5064
- ] }) }) });
6088
+ return /* @__PURE__ */ jsxs8("aside", { className: "df-review-qa-panel", "aria-hidden": !isListVisible, children: [
6089
+ /* @__PURE__ */ jsx10("div", { className: "df-review-panel-body", children: /* @__PURE__ */ jsxs8("section", { className: "df-review-item-list", children: [
6090
+ /* @__PURE__ */ jsx10(
6091
+ QaPanelHeader,
6092
+ {
6093
+ activeItemCount: activeItems.length,
6094
+ activeRemainingItemCount,
6095
+ filteredItemCount: filteredNumberedActiveItems.length,
6096
+ isAllQaVisible,
6097
+ label: activeAdapterEntry.label,
6098
+ qaFilter,
6099
+ qaFilterCounts,
6100
+ qaStatusFilter,
6101
+ qaStatusFilterCounts,
6102
+ showSourceSelect,
6103
+ source,
6104
+ sourceEntries,
6105
+ statusOptions: activeAdapterEntry.statusOptions,
6106
+ onChangeReviewSource,
6107
+ onQaFilterChange,
6108
+ onQaStatusFilterChange,
6109
+ onRefreshReviewData
6110
+ }
6111
+ ),
6112
+ /* @__PURE__ */ jsxs8(
6113
+ "div",
6114
+ {
6115
+ className: "df-review-list-scroll",
6116
+ onClick: (event) => {
6117
+ if (event.target === event.currentTarget) {
6118
+ onClearSelectedItem();
6119
+ }
6120
+ },
6121
+ children: [
6122
+ activeItems.length === 0 && /* @__PURE__ */ jsx10("p", { className: "df-review-empty", children: emptyMessage }),
6123
+ activeItems.length > 0 && filteredNumberedActiveItems.length === 0 && /* @__PURE__ */ jsx10("p", { className: "df-review-empty", children: "No QA in this filter." }),
6124
+ filteredNumberedActiveItems.map((numberedItem) => {
6125
+ const { item } = numberedItem;
6126
+ return /* @__PURE__ */ jsx10(
6127
+ QaItemCard,
6128
+ {
6129
+ activeAdapterEntry,
6130
+ currentPresetScope,
6131
+ getItemPresetScope,
6132
+ isOverlayVisible: !hiddenOverlayItemIds.has(item.id),
6133
+ isRemoteSource,
6134
+ numberedItem,
6135
+ remoteAdapterEntry,
6136
+ copiedPromptKey,
6137
+ selectedItemId,
6138
+ onChangeItemStatus,
6139
+ onClearSelectedItem,
6140
+ onCopyItemLabel,
6141
+ onCopyItemLink,
6142
+ onCopyItemPrompt,
6143
+ onEditItem,
6144
+ onRemoveItem,
6145
+ onRestoreReviewItem,
6146
+ onSubmitItem,
6147
+ onToggleItemOverlayVisibility
6148
+ },
6149
+ item.id
6150
+ );
6151
+ })
6152
+ ]
6153
+ }
6154
+ )
6155
+ ] }) }),
6156
+ /* @__PURE__ */ jsx10("div", { className: "df-review-qa-draft-host" })
6157
+ ] });
5065
6158
  };
5066
6159
 
5067
6160
  // src/react-shell/presence/overlay.tsx
5068
6161
  import { useState as useState3 } from "react";
5069
6162
  import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
5070
- var COLLAPSED_USER_COUNT = 1;
5071
6163
  var getPresenceName = (user) => user.displayName || user.userId;
6164
+ var PresenceUserIcon = () => /* @__PURE__ */ jsxs9("svg", { "aria-hidden": "true", viewBox: "0 0 30 30", children: [
6165
+ /* @__PURE__ */ jsx11(
6166
+ "circle",
6167
+ {
6168
+ cx: "15",
6169
+ cy: "15",
6170
+ r: "12.5",
6171
+ fill: "none",
6172
+ stroke: "currentColor",
6173
+ strokeWidth: "2.6"
6174
+ }
6175
+ ),
6176
+ /* @__PURE__ */ jsx11("circle", { cx: "15", cy: "10.5", r: "3.4", fill: "currentColor", stroke: "none" }),
6177
+ /* @__PURE__ */ jsx11(
6178
+ "path",
6179
+ {
6180
+ d: "M7.8 22.1c.9-4.1 3.4-6.1 7.2-6.1s6.3 2 7.2 6.1c-1.7 1.5-4.1 2.4-7.2 2.4s-5.5-.9-7.2-2.4z",
6181
+ fill: "currentColor",
6182
+ stroke: "none"
6183
+ }
6184
+ )
6185
+ ] });
5072
6186
  var PresenceOverlay = ({
5073
6187
  presenceSessionId,
5074
6188
  users
5075
6189
  }) => {
5076
6190
  const [isExpanded, setIsExpanded] = useState3(false);
5077
6191
  if (users.length === 0) return null;
5078
- const visibleUsers = isExpanded ? users : users.slice(0, COLLAPSED_USER_COUNT);
5079
- const hiddenUserCount = users.length - visibleUsers.length;
5080
6192
  return /* @__PURE__ */ jsxs9(
5081
6193
  "div",
5082
6194
  {
5083
6195
  "aria-label": `Review presence, ${users.length} online`,
5084
6196
  className: `df-review-presence-overlay${isExpanded ? " is-expanded" : ""}`,
5085
6197
  children: [
5086
- visibleUsers.map((user) => /* @__PURE__ */ jsx11(
6198
+ /* @__PURE__ */ jsxs9(
6199
+ "button",
6200
+ {
6201
+ "aria-label": `Show online reviewers, ${users.length} online`,
6202
+ "aria-expanded": isExpanded,
6203
+ className: "df-review-presence-button",
6204
+ type: "button",
6205
+ onClick: () => setIsExpanded((current) => !current),
6206
+ children: [
6207
+ /* @__PURE__ */ jsx11(PresenceUserIcon, {}),
6208
+ /* @__PURE__ */ jsx11("span", { className: "df-review-presence-badge", children: users.length })
6209
+ ]
6210
+ }
6211
+ ),
6212
+ isExpanded && /* @__PURE__ */ jsx11("div", { className: "df-review-presence-list", role: "list", children: users.map((user) => /* @__PURE__ */ jsx11(
5087
6213
  "span",
5088
6214
  {
5089
6215
  className: `df-review-presence-chip${user.sessionId === presenceSessionId ? " is-self" : ""}`,
6216
+ role: "listitem",
5090
6217
  style: {
5091
6218
  "--df-review-presence-color": user.color
5092
6219
  },
5093
6220
  title: getPresenceName(user),
5094
- children: getPresenceName(user)
6221
+ children: /* @__PURE__ */ jsx11("span", { children: getPresenceName(user) })
5095
6222
  },
5096
6223
  user.sessionId
5097
- )),
5098
- hiddenUserCount > 0 && /* @__PURE__ */ jsxs9(
5099
- "button",
5100
- {
5101
- "aria-label": `Show ${hiddenUserCount} more online reviewers`,
5102
- "aria-expanded": isExpanded,
5103
- className: "df-review-presence-more",
5104
- type: "button",
5105
- onClick: () => setIsExpanded(true),
5106
- children: [
5107
- "+",
5108
- hiddenUserCount
5109
- ]
5110
- }
5111
- )
6224
+ )) })
5112
6225
  ]
5113
6226
  }
5114
6227
  );
@@ -5125,28 +6238,281 @@ var SOURCE_SELECTOR = [
5125
6238
  "[data-section-index]",
5126
6239
  "[data-section-id]"
5127
6240
  ].join(", ");
5128
- var getSourceCandidates = (target) => {
6241
+ var SECTION_OUTLINE_ROOT_SELECTOR = [
6242
+ "[data-wrk-source-component]",
6243
+ "header[data-wrk-source-file]",
6244
+ "footer[data-wrk-source-file]",
6245
+ '[role="banner"][data-wrk-source-file]',
6246
+ '[role="contentinfo"][data-wrk-source-file]'
6247
+ ].join(", ");
6248
+ var matchesIgnore = (file, patterns) => {
6249
+ const normalized = file.replace(/\\/g, "/");
6250
+ return patterns.some(
6251
+ (pattern) => typeof pattern === "string" ? normalized.includes(pattern) : pattern.test(normalized)
6252
+ );
6253
+ };
6254
+ var getSourceCandidates = (target, options) => {
5129
6255
  const startElement = getEventElement(target);
5130
6256
  if (!startElement) return [];
5131
6257
  const candidates = [];
5132
6258
  const seen = /* @__PURE__ */ new Set();
6259
+ const add = (element2, source, depth2, kind) => {
6260
+ if (!source?.file) return;
6261
+ const key = getSourceFileCompareKey(source.file);
6262
+ if (!addSourceFileCompareKey(seen, key)) return;
6263
+ candidates.push(createSourceCandidate(element2, source, depth2, kind));
6264
+ };
5133
6265
  let element = startElement;
5134
6266
  let depth = 0;
5135
6267
  while (element && element.nodeType === 1) {
5136
- const source = getSourceHintFromElement(element);
5137
- if (source?.file) {
5138
- const key = getSourceCandidateKey(source);
5139
- if (!seen.has(key)) {
5140
- seen.add(key);
5141
- candidates.push(createSourceCandidate(element, source, depth));
5142
- }
5143
- }
6268
+ add(element, getSourceHintFromElement(element), depth, "source");
6269
+ add(element, getDataHintFromElement(element), 0, "data");
5144
6270
  if (element === element.ownerDocument.documentElement) break;
5145
6271
  element = element.parentElement;
5146
6272
  depth += 1;
5147
6273
  }
5148
- return candidates.slice(0, 8);
6274
+ const ignore = options?.ignore;
6275
+ const visible = candidates.filter(
6276
+ (candidate) => !isCoreOutlineNode(candidate.label, candidate.source.file) && !(ignore?.length && matchesIgnore(candidate.source.file ?? "", ignore))
6277
+ );
6278
+ return visible.slice(0, 8);
6279
+ };
6280
+ var getSectionOutline = (root, options) => {
6281
+ const maxDepth = options?.maxDepth ?? 9;
6282
+ return getSectionOutlineRoots(root, options).map((element, index) => {
6283
+ const source = getSourceHintFromElement(element);
6284
+ const label = getOutlineLabel(element, source, "section");
6285
+ const seen = /* @__PURE__ */ new Set();
6286
+ if (source?.file) {
6287
+ addSourceFileCompareKey(seen, getOutlineSourceKey(source));
6288
+ }
6289
+ return createSectionOutlineEntry({
6290
+ id: `${label}-${index}`,
6291
+ label,
6292
+ depth: 1,
6293
+ filePath: getDisplaySourcePath(source?.file) ?? label,
6294
+ element,
6295
+ source,
6296
+ data: getDataHintFromElement(element),
6297
+ children: getSectionOutlineChildren(
6298
+ element,
6299
+ 2,
6300
+ maxDepth,
6301
+ seen,
6302
+ options
6303
+ )
6304
+ });
6305
+ });
5149
6306
  };
6307
+ function getSectionOutlineRoots(root, options) {
6308
+ return Array.from(root.querySelectorAll(SECTION_OUTLINE_ROOT_SELECTOR)).filter(
6309
+ (element) => {
6310
+ const source = getSourceHintFromElement(element);
6311
+ const label = getOutlineLabel(element, source, "");
6312
+ return !isSkippedOutlineNode(label, source?.file, options);
6313
+ }
6314
+ );
6315
+ }
6316
+ function getSectionOutlineChildren(parent, depth, maxDepth, seen, options) {
6317
+ if (depth > maxDepth) return [];
6318
+ const entries = [];
6319
+ for (const child of Array.from(parent.children)) {
6320
+ const source = getSourceHintFromElement(child);
6321
+ const label = getOutlineLabel(child, source, child.tagName.toLowerCase());
6322
+ const sourceKey = source?.file ? getOutlineSourceKey(source) : "";
6323
+ const isNewSource = Boolean(sourceKey) && !hasEquivalentSourceFileKey(seen, sourceKey);
6324
+ if (shouldStopOutlineBranch(label, source?.file, options)) continue;
6325
+ if (isHiddenOutlineNode(label, source?.file, options)) {
6326
+ entries.push(
6327
+ ...getSectionOutlineChildren(child, depth, maxDepth, seen, options)
6328
+ );
6329
+ continue;
6330
+ }
6331
+ if (source?.file && isNewSource) {
6332
+ const childSeen = new Set(seen);
6333
+ addSourceFileCompareKey(childSeen, sourceKey);
6334
+ entries.push(createSectionOutlineEntry({
6335
+ id: `${sourceKey}-${getElementOutlinePath(child)}-${entries.length}`,
6336
+ label,
6337
+ depth,
6338
+ filePath: getDisplaySourcePath(source.file) ?? source.file,
6339
+ element: child,
6340
+ source,
6341
+ data: getDataHintFromElement(child),
6342
+ children: getSectionOutlineChildren(
6343
+ child,
6344
+ depth + 1,
6345
+ maxDepth,
6346
+ childSeen,
6347
+ options
6348
+ )
6349
+ }));
6350
+ continue;
6351
+ }
6352
+ entries.push(
6353
+ ...getSectionOutlineChildren(child, depth, maxDepth, seen, options)
6354
+ );
6355
+ }
6356
+ return entries;
6357
+ }
6358
+ function getElementOutlinePath(element) {
6359
+ const indices = [];
6360
+ let current = element;
6361
+ while (current?.parentElement) {
6362
+ indices.unshift(Array.from(current.parentElement.children).indexOf(current));
6363
+ current = current.parentElement;
6364
+ }
6365
+ return indices.join("-");
6366
+ }
6367
+ function createSectionOutlineEntry(entry) {
6368
+ return {
6369
+ ...entry,
6370
+ metadata: getSectionOutlineMetadata(entry.element, entry.label, entry.source)
6371
+ };
6372
+ }
6373
+ var truncateOutlineValue = (value, maxLength) => value.length > maxLength ? `${value.slice(0, maxLength - 1)}\u2026` : value;
6374
+ var normalizeOutlineValue = (value) => value?.replace(/\s+/g, " ").trim() ?? "";
6375
+ function getSectionOutlineMetadata(element, label, source) {
6376
+ const textElement = getPlacerTextElement(element, label, source?.file);
6377
+ return {
6378
+ rect: getSectionOutlineRect(element),
6379
+ textValue: textElement ? truncateOutlineValue(
6380
+ normalizeOutlineValue(textElement.textContent),
6381
+ 180
6382
+ ) : void 0,
6383
+ fontLabel: textElement ? getFontLabel(textElement) : void 0,
6384
+ mediaItems: getPlacerMediaItems(element, label, source?.file),
6385
+ classNames: getElementClassNames(element)
6386
+ };
6387
+ }
6388
+ function getSectionOutlineRect(element) {
6389
+ const rect = element.getBoundingClientRect();
6390
+ return {
6391
+ top: Math.round(rect.top),
6392
+ left: Math.round(rect.left),
6393
+ width: Math.round(rect.width),
6394
+ height: Math.round(rect.height)
6395
+ };
6396
+ }
6397
+ function getElementClassNames(element) {
6398
+ const classNames = Array.from(element.classList).filter(Boolean);
6399
+ return classNames.length > 0 ? classNames : void 0;
6400
+ }
6401
+ function getPlacerTextElement(element, label, file) {
6402
+ if (!isPlacerTextOutlineNode(label, file) && !element.hasAttribute("data-font")) {
6403
+ return null;
6404
+ }
6405
+ return element.hasAttribute("data-font") ? element : element.querySelector("[data-font]");
6406
+ }
6407
+ function isPlacerTextOutlineNode(label, file) {
6408
+ return `${label} ${file ?? ""}`.toLowerCase().includes("placertext");
6409
+ }
6410
+ function getFontLabel(element) {
6411
+ const dataFont = element.getAttribute("data-font");
6412
+ if (dataFont) {
6413
+ return dataFont.replace(/\bs\b/g, "sb").replace(/\bsemibold\b/g, "sb").replace(/\bregular\b/g, "r");
6414
+ }
6415
+ const style = window.getComputedStyle(element);
6416
+ return `${Math.round(parseFloat(style.fontSize))}px ${style.fontWeight}`;
6417
+ }
6418
+ function getPlacerMediaItems(element, label, file) {
6419
+ if (!isPlacerMediaOutlineNode(label, file)) return void 0;
6420
+ const mediaItems = [];
6421
+ const seen = /* @__PURE__ */ new Set();
6422
+ const addMediaItem = (target, type, url) => {
6423
+ const normalizedUrl = normalizeOutlineValue(url);
6424
+ if (!normalizedUrl) return;
6425
+ const variant = getPlacerMediaVariant(target, element);
6426
+ const key = `${variant}:${type}:${normalizedUrl}`;
6427
+ if (seen.has(key)) return;
6428
+ seen.add(key);
6429
+ mediaItems.push({ type, url: normalizedUrl, variant });
6430
+ };
6431
+ if (element instanceof HTMLVideoElement) {
6432
+ addMediaItem(element, "video", getVideoElementUrl(element));
6433
+ addMediaItem(element, "image", element.getAttribute("poster"));
6434
+ }
6435
+ if (element instanceof HTMLImageElement) {
6436
+ addMediaItem(element, "image", getMediaElementUrl(element));
6437
+ }
6438
+ if (element instanceof HTMLSourceElement) {
6439
+ addMediaItem(element, "video", getSourceElementUrl(element));
6440
+ }
6441
+ Array.from(element.querySelectorAll("video")).forEach((video) => {
6442
+ addMediaItem(video, "video", getVideoElementUrl(video));
6443
+ addMediaItem(video, "image", video.getAttribute("poster"));
6444
+ });
6445
+ Array.from(element.querySelectorAll("source")).forEach((source) => {
6446
+ addMediaItem(source, "video", getSourceElementUrl(source));
6447
+ });
6448
+ Array.from(element.querySelectorAll("img")).forEach((img) => {
6449
+ addMediaItem(img, "image", getMediaElementUrl(img));
6450
+ });
6451
+ return mediaItems.length > 0 ? mediaItems : void 0;
6452
+ }
6453
+ function isPlacerMediaOutlineNode(label, file) {
6454
+ return `${label} ${file ?? ""}`.toLowerCase().includes("placermedia");
6455
+ }
6456
+ function getPlacerMediaVariant(target, root) {
6457
+ let current = target;
6458
+ while (current) {
6459
+ if (current.classList.contains("d-block-pc")) return "desktop";
6460
+ if (current.classList.contains("d-block-m")) return "mobile";
6461
+ if (current === root) break;
6462
+ current = current.parentElement;
6463
+ }
6464
+ return "media";
6465
+ }
6466
+ function getVideoElementUrl(video) {
6467
+ return video.currentSrc || video.getAttribute("src") || video.querySelector("source")?.getAttribute("src") || video.getAttribute("poster") || video.src;
6468
+ }
6469
+ function getSourceElementUrl(source) {
6470
+ return source.getAttribute("src") || source.src;
6471
+ }
6472
+ function getMediaElementUrl(img) {
6473
+ return img.currentSrc || img.getAttribute("src") || img.src;
6474
+ }
6475
+ function getOutlineSourceKey(source) {
6476
+ return getSourceFileCompareKey(source.file);
6477
+ }
6478
+ function getSourceFileCompareKey(file) {
6479
+ return file?.trim().replace(/\\/g, "/") ?? "";
6480
+ }
6481
+ function addSourceFileCompareKey(seen, key) {
6482
+ if (!key || hasEquivalentSourceFileKey(seen, key)) return false;
6483
+ seen.add(key);
6484
+ return true;
6485
+ }
6486
+ function hasEquivalentSourceFileKey(seen, key) {
6487
+ for (const seenKey of seen) {
6488
+ if (isEquivalentSourceFileKey(seenKey, key)) return true;
6489
+ }
6490
+ return false;
6491
+ }
6492
+ function isEquivalentSourceFileKey(a, b) {
6493
+ return a === b || a.endsWith(`/${b}`) || b.endsWith(`/${a}`);
6494
+ }
6495
+ function getOutlineLabel(element, source, fallback) {
6496
+ return source?.component?.trim() || getComponentNameFromSourceFile(source?.file) || element.id.trim() || fallback;
6497
+ }
6498
+ function isHiddenOutlineNode(label, file, options) {
6499
+ const ignore = options?.ignore;
6500
+ const isIgnoredSource = file && ignore?.length ? matchesIgnore(file, ignore) : false;
6501
+ return isCoreOutlineNode(label, file) || isIgnoredSource;
6502
+ }
6503
+ function shouldStopOutlineBranch(label, file, options) {
6504
+ return !options?.includePlacer && isPlacerOutlineNode(label, file);
6505
+ }
6506
+ function isSkippedOutlineNode(label, file, options) {
6507
+ return shouldStopOutlineBranch(label, file, options) || isHiddenOutlineNode(label, file, options);
6508
+ }
6509
+ function isCoreOutlineNode(label, file) {
6510
+ const text = `${label} ${file ?? ""}`.toLowerCase();
6511
+ return text.includes("core.section") || text.includes("core.content") || text.includes("core.column") || ["coresection", "corecontent", "corecolumn"].includes(label.toLowerCase());
6512
+ }
6513
+ function isPlacerOutlineNode(label, file) {
6514
+ return `${label} ${file ?? ""}`.toLowerCase().includes("placer");
6515
+ }
5150
6516
  var getSourceOpenUrl = (source, options) => {
5151
6517
  const normalizedOptions = normalizeSourceOpenOptions(options);
5152
6518
  const file = source?.file?.trim();
@@ -5185,6 +6551,18 @@ var openSourceInEditor = (source, options) => {
5185
6551
  window.open(url, "_blank", "noreferrer");
5186
6552
  return true;
5187
6553
  };
6554
+ function getDataHintFromElement(element) {
6555
+ const file = getSourceAttribute(element, "data-wrk-data-file");
6556
+ if (!file) return void 0;
6557
+ return {
6558
+ component: void 0,
6559
+ file,
6560
+ line: getSourceAttribute(element, "data-wrk-data-line"),
6561
+ column: void 0,
6562
+ sectionId: void 0,
6563
+ sectionIndex: void 0
6564
+ };
6565
+ }
5188
6566
  function getSourceHintFromElement(element) {
5189
6567
  const source = {
5190
6568
  component: getSourceAttribute(
@@ -5200,7 +6578,7 @@ function getSourceHintFromElement(element) {
5200
6578
  };
5201
6579
  return Object.values(source).some(Boolean) ? source : void 0;
5202
6580
  }
5203
- function createSourceCandidate(element, source, depth) {
6581
+ function createSourceCandidate(element, source, depth, kind) {
5204
6582
  const confidence = getSourceConfidence(source, depth);
5205
6583
  const fileName = source.file?.split(/[\\/]/).pop() ?? source.file ?? "source";
5206
6584
  const component = source.component?.trim();
@@ -5210,7 +6588,7 @@ function createSourceCandidate(element, source, depth) {
5210
6588
  const column = getSourcePosition(source.column);
5211
6589
  const position = line ? `:${line}${column ? `:${column}` : ""}` : "";
5212
6590
  return {
5213
- id: getSourceCandidateKey(source),
6591
+ id: `${kind}:${getSourceCandidateKey(source)}`,
5214
6592
  depth,
5215
6593
  element,
5216
6594
  filePath: getDisplaySourcePath(source.file) ?? fileName,
@@ -5219,6 +6597,7 @@ function createSourceCandidate(element, source, depth) {
5219
6597
  positionLabel: line ? `${line}:${column ?? 1}` : "",
5220
6598
  confidence,
5221
6599
  confidenceLabel: confidence >= 0.82 ? "high" : confidence >= 0.58 ? "medium" : "low",
6600
+ kind,
5222
6601
  usesPosition: confidence >= 0.72 && Boolean(line),
5223
6602
  source
5224
6603
  };
@@ -5242,7 +6621,7 @@ function getDisplaySourcePath(file) {
5242
6621
  const normalizedFile = file?.trim().replace(/\\/g, "/");
5243
6622
  if (!normalizedFile) return void 0;
5244
6623
  const sourceRootMatch = normalizedFile.match(
5245
- /(?:^|\/)((?:dev\/)?src\/.+|app\/.+|pages\/.+|components\/.+)$/
6624
+ /(?:^|\/)((?:dev\/)?src\/.+|app\/.+|pages?\/.+|components\/.+)$/
5246
6625
  );
5247
6626
  return sourceRootMatch?.[1] ?? normalizedFile;
5248
6627
  }
@@ -5710,7 +7089,7 @@ var getStoredOverlayState = (targetDocument, overlay) => {
5710
7089
  };
5711
7090
  var getTargetOverlayState = (targetDocument) => ({
5712
7091
  grid: Boolean(
5713
- targetDocument?.body.classList.contains("is-help") || targetDocument?.querySelector(".helper.onShow") || getStoredOverlayState(targetDocument, "grid")
7092
+ targetDocument?.body?.classList.contains("is-help") || targetDocument?.querySelector(".helper.onShow") || getStoredOverlayState(targetDocument, "grid")
5714
7093
  ),
5715
7094
  figma: Boolean(
5716
7095
  targetDocument?.querySelector(
@@ -5733,6 +7112,7 @@ var ViewportPresetIcon = ({
5733
7112
  }) => {
5734
7113
  return /* @__PURE__ */ jsx16(ReviewScopeIcon2, { scope: getViewportPresetKind(preset) });
5735
7114
  };
7115
+ var getPresetSelectValue = (preset) => `${preset.label}:${preset.width}x${preset.height}`;
5736
7116
  var ReviewTopbar = ({
5737
7117
  draftTarget,
5738
7118
  copyLabel,
@@ -5749,10 +7129,15 @@ var ReviewTopbar = ({
5749
7129
  onCopyCurrentUrl,
5750
7130
  onSizeChange,
5751
7131
  onToggleRuler,
5752
- onToggleTargetOverlay,
5753
- onOpenInitialPrompt,
5754
- onOpenSettings
7132
+ onToggleTargetOverlay
5755
7133
  }) => {
7134
+ const selectedPresetValue = getPresetSelectValue(size);
7135
+ const handlePresetSelectChange = (event) => {
7136
+ const nextPreset = viewportPresets.find(
7137
+ (preset) => getPresetSelectValue(preset) === event.currentTarget.value
7138
+ );
7139
+ if (nextPreset) onSizeChange(nextPreset);
7140
+ };
5756
7141
  return /* @__PURE__ */ jsxs14("header", { className: "df-review-topbar", children: [
5757
7142
  /* @__PURE__ */ jsxs14(
5758
7143
  "form",
@@ -5781,8 +7166,19 @@ var ReviewTopbar = ({
5781
7166
  onChange: (event) => onDraftTargetChange(event.target.value)
5782
7167
  }
5783
7168
  ),
5784
- /* @__PURE__ */ jsx16("button", { type: "submit", children: "Load" }),
5785
- /* @__PURE__ */ jsx16("button", { type: "button", onClick: onCopyCurrentUrl, children: copyLabel })
7169
+ /* @__PURE__ */ jsxs14("div", { className: "df-review-address-actions", children: [
7170
+ /* @__PURE__ */ jsx16(
7171
+ "button",
7172
+ {
7173
+ "aria-label": "Refresh target",
7174
+ className: "df-review-address-refresh",
7175
+ title: "Refresh target",
7176
+ type: "submit",
7177
+ children: /* @__PURE__ */ jsx16(RefreshCw, { "aria-hidden": "true" })
7178
+ }
7179
+ ),
7180
+ /* @__PURE__ */ jsx16("button", { type: "button", onClick: onCopyCurrentUrl, children: copyLabel })
7181
+ ] })
5786
7182
  ]
5787
7183
  }
5788
7184
  ),
@@ -5802,6 +7198,27 @@ var ReviewTopbar = ({
5802
7198
  },
5803
7199
  preset.label
5804
7200
  )) }),
7201
+ /* @__PURE__ */ jsx16(
7202
+ "select",
7203
+ {
7204
+ "aria-label": "Viewport preset",
7205
+ className: "df-review-preset-select",
7206
+ value: selectedPresetValue,
7207
+ onChange: handlePresetSelectChange,
7208
+ children: viewportPresets.map((preset) => {
7209
+ const scope = getViewportPresetKind(preset);
7210
+ const count = presetScopeCounts.get(scope) ?? 0;
7211
+ return /* @__PURE__ */ jsx16(
7212
+ "option",
7213
+ {
7214
+ value: getPresetSelectValue(preset),
7215
+ children: `${preset.label} (${count})`
7216
+ },
7217
+ getPresetSelectValue(preset)
7218
+ );
7219
+ })
7220
+ }
7221
+ ),
5805
7222
  /* @__PURE__ */ jsx16("span", { className: "df-review-tool-divider", "aria-hidden": "true", children: "|" }),
5806
7223
  /* @__PURE__ */ jsxs14("span", { className: "df-review-active-size", children: [
5807
7224
  size.width,
@@ -5841,27 +7258,6 @@ var ReviewTopbar = ({
5841
7258
  onClick: () => onToggleTargetOverlay("figma"),
5842
7259
  children: /* @__PURE__ */ jsx16(Image, { "aria-hidden": "true" })
5843
7260
  }
5844
- ),
5845
- /* @__PURE__ */ jsx16("span", { className: "df-review-tool-divider", "aria-hidden": "true", children: "|" }),
5846
- /* @__PURE__ */ jsx16(
5847
- "button",
5848
- {
5849
- "aria-label": "Open initial prompt",
5850
- className: "df-review-overlay-button is-prompt",
5851
- type: "button",
5852
- onClick: onOpenInitialPrompt,
5853
- children: /* @__PURE__ */ jsx16(CircleQuestionMark, { "aria-hidden": "true" })
5854
- }
5855
- ),
5856
- /* @__PURE__ */ jsx16(
5857
- "button",
5858
- {
5859
- "aria-label": "Open settings",
5860
- className: "df-review-overlay-button is-settings",
5861
- type: "button",
5862
- onClick: onOpenSettings,
5863
- children: /* @__PURE__ */ jsx16(Settings, { "aria-hidden": "true" })
5864
- }
5865
7261
  )
5866
7262
  ] })
5867
7263
  ] })
@@ -5877,7 +7273,7 @@ import {
5877
7273
  import {
5878
7274
  useCallback
5879
7275
  } from "react";
5880
- function runWithAutoScrollBehavior(targetDocument, callback) {
7276
+ function runWithAutoScrollBehavior2(targetDocument, callback) {
5881
7277
  const elements = [
5882
7278
  targetDocument?.documentElement,
5883
7279
  targetDocument?.body
@@ -5978,7 +7374,7 @@ var useReviewItemRestore = ({
5978
7374
  isCurrentRestore
5979
7375
  );
5980
7376
  if (!isCurrentRestore()) return false;
5981
- runWithAutoScrollBehavior(targetDocument, () => {
7377
+ runWithAutoScrollBehavior2(targetDocument, () => {
5982
7378
  setDocumentScrollInstantly(
5983
7379
  targetWindow,
5984
7380
  targetDocument,
@@ -6166,7 +7562,8 @@ var getReviewKitTarget = ({
6166
7562
  width: Math.max(0, right - left),
6167
7563
  height: Math.max(0, bottom - top)
6168
7564
  };
6169
- }
7565
+ },
7566
+ getComposerHost: () => document.querySelector(".df-review-qa-draft-host")
6170
7567
  };
6171
7568
  };
6172
7569
 
@@ -7323,7 +8720,7 @@ var useReviewSettings = ({
7323
8720
  const nextToken = token.trim();
7324
8721
  const nextUserId = userId.trim();
7325
8722
  const nextTheme = normalizeReviewTheme(theme);
7326
- const shouldReload = nextToken !== getStoredFigmaToken() || nextUserId !== getStoredReviewUserId();
8723
+ const shouldReload = nextToken !== getStoredFigmaToken();
7327
8724
  writeStoredFigmaToken(nextToken);
7328
8725
  writeStoredReviewUserId(nextUserId);
7329
8726
  writeStoredReviewTheme(nextTheme);
@@ -7414,7 +8811,7 @@ var useReviewShellData = ({
7414
8811
  () => /* @__PURE__ */ new Set()
7415
8812
  );
7416
8813
  const [qaFilter, setQaFilter] = useState8("all");
7417
- const [qaStatusFilter, setQaStatusFilter] = useState8("all");
8814
+ const [qaStatusFilter, setQaStatusFilterState] = useState8(getStoredReviewQaStatusFilter);
7418
8815
  const [sitemapItems, setSitemapItems] = useState8(() => ({
7419
8816
  local: [],
7420
8817
  remote: []
@@ -7464,8 +8861,18 @@ var useReviewShellData = ({
7464
8861
  [qaStatusFilter, scopeFilteredNumberedActiveItems]
7465
8862
  );
7466
8863
  const hiddenOverlayItemIdList = useMemo5(
7467
- () => Array.from(hiddenOverlayItemIds),
7468
- [hiddenOverlayItemIds]
8864
+ () => {
8865
+ const nextHiddenItemIds = new Set(hiddenOverlayItemIds);
8866
+ if (qaStatusFilter !== "all") {
8867
+ activeItems.forEach((item) => {
8868
+ if (normalizeReviewItemStatus(item.status) !== qaStatusFilter) {
8869
+ nextHiddenItemIds.add(item.id);
8870
+ }
8871
+ });
8872
+ }
8873
+ return Array.from(nextHiddenItemIds);
8874
+ },
8875
+ [activeItems, hiddenOverlayItemIds, qaStatusFilter]
7469
8876
  );
7470
8877
  const qaFilterCounts = useMemo5(() => {
7471
8878
  const counts = /* @__PURE__ */ new Map();
@@ -7523,6 +8930,10 @@ var useReviewShellData = ({
7523
8930
  return counts;
7524
8931
  }, [activeItems, getItemPresetScope]);
7525
8932
  const currentPresetScope = getViewportPresetKind(size);
8933
+ const setQaStatusFilter = useCallback10((filter) => {
8934
+ setQaStatusFilterState(filter);
8935
+ writeStoredReviewQaStatusFilter(filter);
8936
+ }, []);
7526
8937
  const pageQaCounts = useMemo5(() => {
7527
8938
  const counts = /* @__PURE__ */ new Map();
7528
8939
  const addItems = (sourceKey, sourceItems) => {
@@ -7897,7 +9308,9 @@ var useReviewShellState = ({
7897
9308
  figma: false
7898
9309
  });
7899
9310
  const [selectedItemId, setSelectedItemId] = useState9(getInitialItemId());
7900
- const [isListVisible, setIsListVisible] = useState9(true);
9311
+ const [isListVisible, setIsListVisible] = useState9(
9312
+ getStoredReviewSidePanelVisible
9313
+ );
7901
9314
  const [isSitemapOpen, setIsSitemapOpen] = useState9(false);
7902
9315
  const [isInitialPromptOpen, setIsInitialPromptOpen] = useState9(false);
7903
9316
  const [copyLabel, setCopyLabel] = useState9("Copy URL");
@@ -7961,34 +9374,40 @@ var useReviewShellState = ({
7961
9374
  };
7962
9375
 
7963
9376
  // src/react-shell/review/shell.actions.ts
9377
+ var writeClipboardTextFallback = (value) => {
9378
+ const selection = document.getSelection();
9379
+ const activeElement = document.activeElement instanceof HTMLElement ? document.activeElement : null;
9380
+ const ranges = selection ? Array.from(
9381
+ { length: selection.rangeCount },
9382
+ (_, index) => selection.getRangeAt(index)
9383
+ ) : [];
9384
+ const textarea = document.createElement("textarea");
9385
+ textarea.value = value;
9386
+ textarea.setAttribute("readonly", "");
9387
+ textarea.style.position = "fixed";
9388
+ textarea.style.left = "-9999px";
9389
+ textarea.style.top = "0";
9390
+ document.body.appendChild(textarea);
9391
+ textarea.focus();
9392
+ textarea.select();
9393
+ const isCopied = document.execCommand("copy");
9394
+ textarea.remove();
9395
+ selection?.removeAllRanges();
9396
+ ranges.forEach((range) => selection?.addRange(range));
9397
+ activeElement?.focus();
9398
+ if (!isCopied) {
9399
+ throw new Error("Failed to copy to clipboard");
9400
+ }
9401
+ };
7964
9402
  var writeClipboardText = async (value) => {
7965
9403
  try {
7966
- await navigator.clipboard.writeText(value);
9404
+ writeClipboardTextFallback(value);
7967
9405
  return;
7968
- } catch {
7969
- const selection = document.getSelection();
7970
- const activeElement = document.activeElement instanceof HTMLElement ? document.activeElement : null;
7971
- const ranges = selection ? Array.from(
7972
- { length: selection.rangeCount },
7973
- (_, index) => selection.getRangeAt(index)
7974
- ) : [];
7975
- const textarea = document.createElement("textarea");
7976
- textarea.value = value;
7977
- textarea.setAttribute("readonly", "");
7978
- textarea.style.position = "fixed";
7979
- textarea.style.left = "-9999px";
7980
- textarea.style.top = "0";
7981
- document.body.appendChild(textarea);
7982
- textarea.focus();
7983
- textarea.select();
7984
- const isCopied = document.execCommand("copy");
7985
- textarea.remove();
7986
- selection?.removeAllRanges();
7987
- ranges.forEach((range) => selection?.addRange(range));
7988
- activeElement?.focus();
7989
- if (!isCopied) {
7990
- throw new Error("Failed to copy to clipboard");
9406
+ } catch (error) {
9407
+ if (!navigator.clipboard?.writeText) {
9408
+ throw error;
7991
9409
  }
9410
+ await navigator.clipboard.writeText(value);
7992
9411
  }
7993
9412
  };
7994
9413
  var listReviewItems = async ({
@@ -8203,6 +9622,124 @@ var getReviewModeWriteMode = (mode) => {
8203
9622
  var SOURCE_PANEL_MAX_WIDTH = 440;
8204
9623
  var SOURCE_PANEL_MIN_WIDTH = 240;
8205
9624
  var SOURCE_PANEL_MAX_HEIGHT = 260;
9625
+ var SOURCE_TREE_PANEL_CLOSE_DELAY_MS = 180;
9626
+ var waitForFrame = (targetWindow) => new Promise((resolve) => {
9627
+ (targetWindow ?? window).requestAnimationFrame(() => resolve());
9628
+ });
9629
+ var waitForMs = (ms) => new Promise((resolve) => {
9630
+ window.setTimeout(resolve, ms);
9631
+ });
9632
+ var getScrollElement = (targetDocument) => targetDocument.scrollingElement;
9633
+ var scrollElementInTarget = (element, block) => {
9634
+ const targetWindow = element.ownerDocument.defaultView;
9635
+ if (!targetWindow) return;
9636
+ const targetDocument = element.ownerDocument;
9637
+ const scrollElement = getScrollElement(targetDocument);
9638
+ const rect = element.getBoundingClientRect();
9639
+ const currentLeft = scrollElement?.scrollLeft ?? targetWindow.scrollX;
9640
+ const currentTop = scrollElement?.scrollTop ?? targetWindow.scrollY;
9641
+ const clientWidth = scrollElement?.clientWidth ?? targetWindow.innerWidth;
9642
+ const clientHeight = scrollElement?.clientHeight ?? targetWindow.innerHeight;
9643
+ const scrollWidth = scrollElement?.scrollWidth ?? targetDocument.documentElement.scrollWidth;
9644
+ const scrollHeight = scrollElement?.scrollHeight ?? targetDocument.documentElement.scrollHeight;
9645
+ const nextLeft = clamp(
9646
+ currentLeft + rect.left + rect.width / 2 - clientWidth / 2,
9647
+ 0,
9648
+ Math.max(0, scrollWidth - clientWidth)
9649
+ );
9650
+ const nextTop = block === "center" ? clamp(
9651
+ currentTop + rect.top + rect.height / 2 - clientHeight / 2,
9652
+ 0,
9653
+ Math.max(0, scrollHeight - clientHeight)
9654
+ ) : clamp(
9655
+ currentTop + rect.top,
9656
+ 0,
9657
+ Math.max(0, scrollHeight - clientHeight)
9658
+ );
9659
+ runWithAutoScrollBehavior(targetDocument, () => {
9660
+ if (scrollElement) {
9661
+ scrollElement.scrollLeft = Math.round(nextLeft);
9662
+ scrollElement.scrollTop = Math.round(nextTop);
9663
+ return;
9664
+ }
9665
+ targetWindow.scrollTo(Math.round(nextLeft), Math.round(nextTop));
9666
+ });
9667
+ };
9668
+ var centerFrameScrollOnElement = (frameScroll, frame, element) => {
9669
+ if (!frameScroll || !frame) return;
9670
+ const frameScrollRect = frameScroll.getBoundingClientRect();
9671
+ const frameRect = frame.getBoundingClientRect();
9672
+ const elementRect = element.getBoundingClientRect();
9673
+ const elementHostCenterX = frameRect.left + elementRect.left + elementRect.width / 2;
9674
+ const elementHostCenterY = frameRect.top + elementRect.top + elementRect.height / 2;
9675
+ const visibleCenterX = frameScrollRect.left + frameScrollRect.width / 2;
9676
+ const visibleCenterY = frameScrollRect.top + frameScrollRect.height / 2;
9677
+ const nextLeft = clamp(
9678
+ frameScroll.scrollLeft + elementHostCenterX - visibleCenterX,
9679
+ 0,
9680
+ Math.max(0, frameScroll.scrollWidth - frameScroll.clientWidth)
9681
+ );
9682
+ const nextTop = clamp(
9683
+ frameScroll.scrollTop + elementHostCenterY - visibleCenterY,
9684
+ 0,
9685
+ Math.max(0, frameScroll.scrollHeight - frameScroll.clientHeight)
9686
+ );
9687
+ const previousScrollBehavior = frameScroll.style.scrollBehavior;
9688
+ frameScroll.style.scrollBehavior = "auto";
9689
+ frameScroll.scrollLeft = Math.round(nextLeft);
9690
+ frameScroll.scrollTop = Math.round(nextTop);
9691
+ frameScroll.style.scrollBehavior = previousScrollBehavior;
9692
+ };
9693
+ var getSectionOutlineFilterTerms = (value) => value.trim().toLowerCase().split(/\s+/).filter(Boolean);
9694
+ var getSectionOutlineEntryCount = (entries) => entries.reduce(
9695
+ (count, entry) => count + 1 + getSectionOutlineEntryCount(entry.children),
9696
+ 0
9697
+ );
9698
+ var getDefaultCollapsedSectionOutlineIds = (entries) => {
9699
+ const collapsedIds = /* @__PURE__ */ new Set();
9700
+ const visit = (entry) => {
9701
+ if (entry.children.length > 0) {
9702
+ collapsedIds.add(entry.id);
9703
+ }
9704
+ entry.children.forEach(visit);
9705
+ };
9706
+ entries.forEach(visit);
9707
+ return collapsedIds;
9708
+ };
9709
+ var getLiveSectionOutlineRect = (entry) => {
9710
+ if (!entry.element.isConnected) return entry.metadata.rect;
9711
+ const rect = entry.element.getBoundingClientRect();
9712
+ return {
9713
+ top: Math.round(rect.top),
9714
+ left: Math.round(rect.left),
9715
+ width: Math.round(rect.width),
9716
+ height: Math.round(rect.height)
9717
+ };
9718
+ };
9719
+ var matchesSectionOutlineFilter = (entry, terms) => {
9720
+ if (terms.length === 0) return true;
9721
+ const text = [
9722
+ entry.label,
9723
+ entry.filePath,
9724
+ entry.source?.file,
9725
+ entry.data?.file,
9726
+ entry.metadata.textValue,
9727
+ entry.metadata.fontLabel,
9728
+ entry.metadata.mediaItems?.map((mediaItem) => `${mediaItem.variant} ${mediaItem.type} ${mediaItem.url}`).join(" "),
9729
+ entry.metadata.classNames?.join(" ")
9730
+ ].filter(Boolean).join(" ").toLowerCase();
9731
+ return terms.every((term) => text.includes(term));
9732
+ };
9733
+ var filterSectionOutlineEntries = (entries, terms) => {
9734
+ if (terms.length === 0) return entries;
9735
+ return entries.flatMap((entry) => {
9736
+ const children = filterSectionOutlineEntries(entry.children, terms);
9737
+ if (!matchesSectionOutlineFilter(entry, terms) && children.length === 0) {
9738
+ return [];
9739
+ }
9740
+ return [{ ...entry, children }];
9741
+ });
9742
+ };
8206
9743
  var ReviewShell = ({
8207
9744
  projectId,
8208
9745
  pages,
@@ -8275,6 +9812,16 @@ var ReviewShell = ({
8275
9812
  const sourceShortcutCleanupRef = useRef5(null);
8276
9813
  const sourceInspectorInteractionRef = useRef5(false);
8277
9814
  const [sourceInspectorState, setSourceInspectorState] = useState10(null);
9815
+ const [sectionOutline, setSectionOutline] = useState10(null);
9816
+ const [sectionOutlineFilter, setSectionOutlineFilter] = useState10(
9817
+ () => getStoredSourceTreeFilter()
9818
+ );
9819
+ const [sectionOutlineMetaVisibility, setSectionOutlineMetaVisibility] = useState10(() => getStoredSourceTreeMetaVisibility());
9820
+ const isSectionOutlineBoxMetaVisible = sectionOutlineMetaVisibility.box;
9821
+ const isSectionOutlineFontMetaVisible = sectionOutlineMetaVisibility.font;
9822
+ const isSectionOutlineMediaMetaVisible = sectionOutlineMetaVisibility.media;
9823
+ const isSectionOutlineClassMetaVisible = sectionOutlineMetaVisibility.className;
9824
+ const [collapsedSectionOutlineIds, setCollapsedSectionOutlineIds] = useState10(() => /* @__PURE__ */ new Set());
8278
9825
  const [isAllQaVisible, setIsAllQaVisible] = useState10(false);
8279
9826
  const sourceOpenOptions = useMemo7(
8280
9827
  () => ({
@@ -8283,7 +9830,69 @@ var ReviewShell = ({
8283
9830
  }),
8284
9831
  [sourceInspector, sourceRoot]
8285
9832
  );
9833
+ const sourceCandidateOptions = useMemo7(
9834
+ () => ({
9835
+ ignore: sourceInspector?.ignore,
9836
+ includePlacer: sourceInspector?.includePlacer
9837
+ }),
9838
+ [sourceInspector]
9839
+ );
9840
+ const sectionOutlineOptions = useMemo7(
9841
+ () => ({
9842
+ includePlacer: sourceInspector?.includePlacer,
9843
+ ignore: sourceInspector?.ignore,
9844
+ maxDepth: sourceInspector?.maxDepth
9845
+ }),
9846
+ [sourceInspector]
9847
+ );
8286
9848
  const isSourceInspectorEnabled = sourceInspector?.enabled !== false;
9849
+ const [sidePanel, setSidePanel] = useState10(
9850
+ () => isSourceInspectorEnabled ? getStoredReviewSidePanel() : "qa"
9851
+ );
9852
+ const isSourceTreeHoverOutlineEnabled = sourceInspector?.hoverOutline !== false;
9853
+ const isQaPanelVisible = isListVisible && sidePanel === "qa";
9854
+ const isSourceTreePanelVisible = isSourceInspectorEnabled && isListVisible && sidePanel === "source";
9855
+ useEffect10(() => {
9856
+ if (isSourceInspectorEnabled || sidePanel !== "source") return;
9857
+ setSidePanel("qa");
9858
+ }, [isSourceInspectorEnabled, sidePanel]);
9859
+ useEffect10(() => {
9860
+ writeStoredReviewSidePanel(sidePanel);
9861
+ }, [sidePanel]);
9862
+ useEffect10(() => {
9863
+ writeStoredReviewSidePanelVisible(isListVisible);
9864
+ }, [isListVisible]);
9865
+ const updateSectionOutlineFilter = useCallback11((nextFilter) => {
9866
+ setSectionOutlineFilter(nextFilter);
9867
+ writeStoredSourceTreeFilter(nextFilter);
9868
+ }, []);
9869
+ const updateSectionOutlineMetaVisibility = useCallback11(
9870
+ (key) => {
9871
+ setSectionOutlineMetaVisibility((current) => {
9872
+ const next = { ...current, [key]: !current[key] };
9873
+ writeStoredSourceTreeMetaVisibility(next);
9874
+ return next;
9875
+ });
9876
+ },
9877
+ []
9878
+ );
9879
+ const sectionOutlineFilterTerms = useMemo7(
9880
+ () => getSectionOutlineFilterTerms(sectionOutlineFilter),
9881
+ [sectionOutlineFilter]
9882
+ );
9883
+ const filteredSectionOutline = useMemo7(
9884
+ () => sectionOutline ? filterSectionOutlineEntries(sectionOutline, sectionOutlineFilterTerms) : [],
9885
+ [sectionOutline, sectionOutlineFilterTerms]
9886
+ );
9887
+ const sectionOutlineTotalCount = useMemo7(
9888
+ () => getSectionOutlineEntryCount(sectionOutline ?? []),
9889
+ [sectionOutline]
9890
+ );
9891
+ const filteredSectionOutlineCount = useMemo7(
9892
+ () => getSectionOutlineEntryCount(filteredSectionOutline),
9893
+ [filteredSectionOutline]
9894
+ );
9895
+ const isSectionOutlineFiltering = sectionOutlineFilterTerms.length > 0;
8287
9896
  const {
8288
9897
  activeItems,
8289
9898
  activeRemainingItemCount,
@@ -8534,6 +10143,7 @@ var ReviewShell = ({
8534
10143
  parsedInput.height
8535
10144
  ) : sizeRef.current;
8536
10145
  const nextAdapter = sourceEntries.find((entry) => entry.label === nextSource) ?? activeAdapterEntry;
10146
+ const isCurrentTarget = targetRef.current === normalizedTarget && source === nextSource && sizeRef.current.width === nextSize.width && sizeRef.current.height === nextSize.height;
8537
10147
  if (parsedInput.itemId) {
8538
10148
  const item = await nextAdapter.adapter.get(parsedInput.itemId);
8539
10149
  if (item) {
@@ -8552,6 +10162,7 @@ var ReviewShell = ({
8552
10162
  setSize(nextSize);
8553
10163
  setTarget(normalizedTarget);
8554
10164
  updateShellUrl(normalizedTarget, nextSize, nextSource);
10165
+ if (isCurrentTarget) reloadTargetFrame();
8555
10166
  };
8556
10167
  const selectPage = (href) => {
8557
10168
  const normalizedTarget = normalizeTarget(href, reviewPathPrefix);
@@ -8572,6 +10183,10 @@ var ReviewShell = ({
8572
10183
  const writeMode = getReviewModeWriteMode(nextMode);
8573
10184
  if (writeMode && !activeAdapterEntry.writeModes.includes(writeMode)) return;
8574
10185
  closeRuler();
10186
+ if (writeMode && mode !== nextMode) {
10187
+ setSidePanel("qa");
10188
+ setIsListVisible(true);
10189
+ }
8575
10190
  setControllerReviewMode(nextMode);
8576
10191
  };
8577
10192
  useReviewShellHotkeys({
@@ -8619,6 +10234,11 @@ var ReviewShell = ({
8619
10234
  sourceInspectorInteractionRef.current = false;
8620
10235
  setSourceInspectorState(null);
8621
10236
  }, []);
10237
+ useEffect10(() => {
10238
+ clearSourceInspector();
10239
+ setCollapsedSectionOutlineIds(/* @__PURE__ */ new Set());
10240
+ setSectionOutline(null);
10241
+ }, [clearSourceInspector, targetSrc]);
8622
10242
  const getSourceInspectorRect = useCallback11(
8623
10243
  (element) => {
8624
10244
  const frame = iframeRef.current;
@@ -8672,13 +10292,15 @@ var ReviewShell = ({
8672
10292
  );
8673
10293
  const showSourceInspectorForTarget = useCallback11(
8674
10294
  (target2, isPinned = false) => {
8675
- const candidates = getSourceCandidates(target2).map((candidate) => ({
8676
- ...candidate,
8677
- openUrl: getSourceOpenUrl(candidate.source, {
8678
- ...sourceOpenOptions,
8679
- omitPosition: !candidate.usesPosition
10295
+ const candidates = getSourceCandidates(target2, sourceCandidateOptions).map(
10296
+ (candidate) => ({
10297
+ ...candidate,
10298
+ openUrl: getSourceOpenUrl(candidate.source, {
10299
+ ...sourceOpenOptions,
10300
+ omitPosition: !candidate.usesPosition
10301
+ })
8680
10302
  })
8681
- }));
10303
+ );
8682
10304
  const firstCandidate = candidates[0];
8683
10305
  const rect = firstCandidate ? getSourceInspectorRect(firstCandidate.element) : null;
8684
10306
  if (!firstCandidate || !rect) {
@@ -8700,12 +10322,16 @@ var ReviewShell = ({
8700
10322
  [
8701
10323
  getSourceInspectorPanelPosition,
8702
10324
  getSourceInspectorRect,
10325
+ sourceCandidateOptions,
8703
10326
  sourceOpenOptions
8704
10327
  ]
8705
10328
  );
8706
10329
  const showSourceOutlineForTarget = useCallback11(
8707
10330
  (target2) => {
8708
- const firstCandidate = getSourceCandidates(target2)[0];
10331
+ const firstCandidate = getSourceCandidates(
10332
+ target2,
10333
+ sourceCandidateOptions
10334
+ )[0];
8709
10335
  const rect = firstCandidate ? getSourceInspectorRect(firstCandidate.element) : null;
8710
10336
  if (!firstCandidate || !rect) {
8711
10337
  setSourceInspectorState(null);
@@ -8722,8 +10348,35 @@ var ReviewShell = ({
8722
10348
  });
8723
10349
  return firstCandidate;
8724
10350
  },
8725
- [getSourceInspectorRect]
10351
+ [getSourceInspectorRect, sourceCandidateOptions]
10352
+ );
10353
+ const showSourceOutlineForElement = useCallback11(
10354
+ (element) => {
10355
+ if (!isSourceTreeHoverOutlineEnabled) return;
10356
+ const rect = getSourceInspectorRect(element);
10357
+ if (!rect) {
10358
+ setSourceInspectorState(
10359
+ (current) => current?.isPinned ? current : null
10360
+ );
10361
+ return;
10362
+ }
10363
+ setSourceInspectorState(
10364
+ (current) => current?.isPinned ? current : {
10365
+ candidates: [],
10366
+ isPinned: false,
10367
+ panelLeft: 0,
10368
+ panelMaxWidth: SOURCE_PANEL_MAX_WIDTH,
10369
+ panelRight: null,
10370
+ panelTop: 0,
10371
+ rect
10372
+ }
10373
+ );
10374
+ },
10375
+ [getSourceInspectorRect, isSourceTreeHoverOutlineEnabled]
8726
10376
  );
10377
+ const clearSourceOutlineHover = useCallback11(() => {
10378
+ setSourceInspectorState((current) => current?.isPinned ? current : null);
10379
+ }, []);
8727
10380
  const openSourceCandidate = useCallback11(
8728
10381
  (candidate) => {
8729
10382
  const didOpen = openSourceInEditor(candidate.source, {
@@ -8735,6 +10388,162 @@ var ReviewShell = ({
8735
10388
  },
8736
10389
  [clearSourceInspector, showToast, sourceOpenOptions]
8737
10390
  );
10391
+ const getCurrentSectionOutline = useCallback11(
10392
+ () => {
10393
+ let frameDocument = null;
10394
+ try {
10395
+ frameDocument = iframeRef.current?.contentDocument ?? null;
10396
+ } catch {
10397
+ frameDocument = null;
10398
+ }
10399
+ if (!frameDocument || frameDocument.readyState !== "complete") {
10400
+ return null;
10401
+ }
10402
+ return getSectionOutline(frameDocument, sectionOutlineOptions);
10403
+ },
10404
+ [iframeRef, sectionOutlineOptions]
10405
+ );
10406
+ const setSectionOutlineWithDefaultCollapse = useCallback11(
10407
+ (nextSectionOutline) => {
10408
+ setSectionOutline(nextSectionOutline);
10409
+ setCollapsedSectionOutlineIds(
10410
+ getDefaultCollapsedSectionOutlineIds(nextSectionOutline)
10411
+ );
10412
+ },
10413
+ []
10414
+ );
10415
+ useEffect10(() => {
10416
+ if (sidePanel !== "source" || !isListVisible) return void 0;
10417
+ const refreshSectionOutline = () => {
10418
+ const nextSectionOutline = getCurrentSectionOutline();
10419
+ if (!nextSectionOutline) return;
10420
+ setSectionOutlineWithDefaultCollapse(nextSectionOutline);
10421
+ };
10422
+ const animationFrame = window.requestAnimationFrame(refreshSectionOutline);
10423
+ const firstTimeout = window.setTimeout(refreshSectionOutline, 120);
10424
+ const secondTimeout = window.setTimeout(refreshSectionOutline, 500);
10425
+ return () => {
10426
+ window.cancelAnimationFrame(animationFrame);
10427
+ window.clearTimeout(firstTimeout);
10428
+ window.clearTimeout(secondTimeout);
10429
+ };
10430
+ }, [
10431
+ getCurrentSectionOutline,
10432
+ isListVisible,
10433
+ setSectionOutlineWithDefaultCollapse,
10434
+ sidePanel,
10435
+ targetSrc
10436
+ ]);
10437
+ const toggleQaPanel = useCallback11(() => {
10438
+ setSidePanel("qa");
10439
+ setIsListVisible((current) => sidePanel === "qa" ? !current : true);
10440
+ }, [setIsListVisible, sidePanel]);
10441
+ const toggleSourceTreePanel = useCallback11(() => {
10442
+ if (!isSourceInspectorEnabled) return;
10443
+ if (sidePanel === "source" && isListVisible) {
10444
+ setIsListVisible(false);
10445
+ return;
10446
+ }
10447
+ setSidePanel("source");
10448
+ const nextSectionOutline = getCurrentSectionOutline();
10449
+ if (nextSectionOutline) {
10450
+ setSectionOutlineWithDefaultCollapse(nextSectionOutline);
10451
+ }
10452
+ setIsListVisible(true);
10453
+ }, [
10454
+ getCurrentSectionOutline,
10455
+ isListVisible,
10456
+ isSourceInspectorEnabled,
10457
+ setSectionOutlineWithDefaultCollapse,
10458
+ setIsListVisible,
10459
+ sidePanel
10460
+ ]);
10461
+ const toggleSectionOutlineEntry = useCallback11((entryId) => {
10462
+ setCollapsedSectionOutlineIds((current) => {
10463
+ const next = new Set(current);
10464
+ if (next.has(entryId)) {
10465
+ next.delete(entryId);
10466
+ } else {
10467
+ next.add(entryId);
10468
+ }
10469
+ return next;
10470
+ });
10471
+ }, []);
10472
+ const scrollToSection = useCallback11((entry) => {
10473
+ scrollElementInTarget(entry.element, "start");
10474
+ centerFrameScrollOnElement(
10475
+ frameScrollRef.current,
10476
+ iframeRef.current,
10477
+ entry.element
10478
+ );
10479
+ }, [frameScrollRef, iframeRef]);
10480
+ const openSectionSource = useCallback11(
10481
+ (entry) => {
10482
+ const didOpen = openSourceInEditor(entry.source, {
10483
+ ...sourceOpenOptions,
10484
+ omitPosition: true
10485
+ });
10486
+ showToast(didOpen ? "Source opened" : "Source root required");
10487
+ },
10488
+ [showToast, sourceOpenOptions]
10489
+ );
10490
+ const openSectionData = useCallback11(
10491
+ (entry) => {
10492
+ const didOpen = openSourceInEditor(entry.data, sourceOpenOptions);
10493
+ showToast(didOpen ? "Data opened" : "Data hint not found");
10494
+ },
10495
+ [showToast, sourceOpenOptions]
10496
+ );
10497
+ const startSectionDomReview = useCallback11(
10498
+ (entry) => {
10499
+ if (!canWriteDom) {
10500
+ showToast("DOM QA unavailable");
10501
+ return;
10502
+ }
10503
+ clearSourceInspector();
10504
+ setSidePanel("qa");
10505
+ setIsListVisible(true);
10506
+ let targetWindow = null;
10507
+ try {
10508
+ targetWindow = entry.element.ownerDocument.defaultView ?? iframeRef.current?.contentWindow ?? null;
10509
+ } catch {
10510
+ targetWindow = null;
10511
+ }
10512
+ void waitForMs(SOURCE_TREE_PANEL_CLOSE_DELAY_MS).then(async () => {
10513
+ initReviewKit();
10514
+ await waitForFrame(targetWindow);
10515
+ const controller = controllerRef.current;
10516
+ if (!controller) {
10517
+ showToast("DOM QA unavailable");
10518
+ return;
10519
+ }
10520
+ scrollElementInTarget(entry.element, "center");
10521
+ await waitForFrame(targetWindow);
10522
+ centerFrameScrollOnElement(
10523
+ frameScrollRef.current,
10524
+ iframeRef.current,
10525
+ entry.element
10526
+ );
10527
+ await waitForFrame(targetWindow);
10528
+ await controller.startElementReview(entry.element);
10529
+ await waitForFrame(targetWindow);
10530
+ setMode(controller.getMode());
10531
+ }).catch(() => {
10532
+ setMode(controllerRef.current?.getMode() ?? "idle");
10533
+ });
10534
+ },
10535
+ [
10536
+ canWriteDom,
10537
+ clearSourceInspector,
10538
+ controllerRef,
10539
+ frameScrollRef,
10540
+ iframeRef,
10541
+ initReviewKit,
10542
+ setIsListVisible,
10543
+ setMode,
10544
+ showToast
10545
+ ]
10546
+ );
8738
10547
  const cleanupSourceOpenShortcut = useCallback11(() => {
8739
10548
  sourceShortcutCleanupRef.current?.();
8740
10549
  sourceShortcutCleanupRef.current = null;
@@ -8782,7 +10591,7 @@ var ReviewShell = ({
8782
10591
  background: rgba(15, 23, 42, 0.86) !important;
8783
10592
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.24) !important;
8784
10593
  content: "Source select" !important;
8785
- font: 700 12px/1 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
10594
+ font: 500 12px/1 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
8786
10595
  pointer-events: none !important;
8787
10596
  }
8788
10597
 
@@ -8799,7 +10608,7 @@ var ReviewShell = ({
8799
10608
  color: #ffffff !important;
8800
10609
  background: rgba(15, 23, 42, 0.9) !important;
8801
10610
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.28) !important;
8802
- font: 800 11px/1.35 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace !important;
10611
+ font: 500 11px/1.35 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace !important;
8803
10612
  overflow-wrap: anywhere !important;
8804
10613
  pointer-events: none !important;
8805
10614
  white-space: normal !important;
@@ -8900,8 +10709,9 @@ var ReviewShell = ({
8900
10709
  }
8901
10710
  };
8902
10711
  const handleMouseMove = (event) => {
10712
+ if (isSourcePanelPinned) return;
8903
10713
  lastSourceTarget = event.target;
8904
- const candidates = getSourceCandidates(event.target);
10714
+ const candidates = getSourceCandidates(event.target, sourceCandidateOptions);
8905
10715
  const sourceElement = candidates[0]?.element ?? null;
8906
10716
  if (event.altKey && !isSourceSelecting) {
8907
10717
  setSourceSelecting(true);
@@ -8935,6 +10745,7 @@ var ReviewShell = ({
8935
10745
  return;
8936
10746
  }
8937
10747
  if (!isOptionKeyEvent(event)) return;
10748
+ if (isSourcePanelPinned) return;
8938
10749
  cancelReviewMode();
8939
10750
  setSourceSelecting(true);
8940
10751
  };
@@ -8985,6 +10796,7 @@ var ReviewShell = ({
8985
10796
  iframeRef,
8986
10797
  isSourceInspectorEnabled,
8987
10798
  showToast,
10799
+ sourceCandidateOptions,
8988
10800
  showSourceOutlineForTarget,
8989
10801
  showSourceInspectorForTarget
8990
10802
  ]);
@@ -8999,12 +10811,22 @@ var ReviewShell = ({
8999
10811
  mode === "element"
9000
10812
  );
9001
10813
  bindSourceOpenShortcut();
10814
+ if (sidePanel === "source" && isListVisible) {
10815
+ const nextSectionOutline = getCurrentSectionOutline();
10816
+ if (nextSectionOutline) {
10817
+ setSectionOutlineWithDefaultCollapse(nextSectionOutline);
10818
+ }
10819
+ }
9002
10820
  }, [
9003
10821
  bindSourceOpenShortcut,
10822
+ getCurrentSectionOutline,
9004
10823
  iframeRef,
9005
10824
  initReviewKit,
10825
+ isListVisible,
9006
10826
  mode,
9007
- refreshTargetFigmaConfig
10827
+ refreshTargetFigmaConfig,
10828
+ setSectionOutlineWithDefaultCollapse,
10829
+ sidePanel
9008
10830
  ]);
9009
10831
  useEffect10(() => {
9010
10832
  const frame = window.requestAnimationFrame(bindSourceOpenShortcut);
@@ -9060,6 +10882,11 @@ var ReviewShell = ({
9060
10882
  `qa:${numberedItem.item.id}`,
9061
10883
  "QA prompt copied"
9062
10884
  );
10885
+ const copyItemLabel = (numberedItem) => copyPrompt(
10886
+ numberedItem.displayLabel,
10887
+ `label:${numberedItem.item.id}`,
10888
+ "QA number copied"
10889
+ );
9063
10890
  const copyItemLink = (numberedItem) => {
9064
10891
  const { item } = numberedItem;
9065
10892
  return copyPrompt(
@@ -9085,6 +10912,214 @@ var ReviewShell = ({
9085
10912
  onRefreshReviewData: refreshReviewData2,
9086
10913
  onToast: showToast
9087
10914
  });
10915
+ const renderSectionOutlineMeta = (entry) => {
10916
+ const { metadata } = entry;
10917
+ const rows = [];
10918
+ const metaPaddingLeft = Math.max(0, entry.depth - 1) * 12 + 29;
10919
+ const rect = getLiveSectionOutlineRect(entry);
10920
+ if (isSectionOutlineBoxMetaVisible) {
10921
+ rows.push(
10922
+ /* @__PURE__ */ jsxs15("span", { className: "df-review-section-outline-meta-row", children: [
10923
+ /* @__PURE__ */ jsx17("b", { children: "box" }),
10924
+ /* @__PURE__ */ jsxs15("code", { children: [
10925
+ "top ",
10926
+ rect.top,
10927
+ " / left ",
10928
+ rect.left,
10929
+ " / width ",
10930
+ rect.width,
10931
+ " / height",
10932
+ " ",
10933
+ rect.height
10934
+ ] })
10935
+ ] }, "box")
10936
+ );
10937
+ }
10938
+ if (metadata.textValue) {
10939
+ rows.push(
10940
+ /* @__PURE__ */ jsxs15(
10941
+ "span",
10942
+ {
10943
+ className: "df-review-section-outline-meta-row is-text",
10944
+ children: [
10945
+ /* @__PURE__ */ jsx17("b", { children: "text" }),
10946
+ /* @__PURE__ */ jsx17("code", { children: metadata.textValue })
10947
+ ]
10948
+ },
10949
+ "text"
10950
+ )
10951
+ );
10952
+ }
10953
+ if (isSectionOutlineFontMetaVisible && metadata.fontLabel) {
10954
+ rows.push(
10955
+ /* @__PURE__ */ jsxs15("span", { className: "df-review-section-outline-meta-row", children: [
10956
+ /* @__PURE__ */ jsx17("b", { children: "font" }),
10957
+ /* @__PURE__ */ jsx17("code", { children: metadata.fontLabel })
10958
+ ] }, "font")
10959
+ );
10960
+ }
10961
+ if (isSectionOutlineMediaMetaVisible && metadata.mediaItems?.length) {
10962
+ metadata.mediaItems.forEach((mediaItem) => {
10963
+ const mediaKey = `${mediaItem.variant}:${mediaItem.type}:${mediaItem.url}`;
10964
+ const mediaLabel = mediaItem.variant === "media" ? mediaItem.type : mediaItem.variant;
10965
+ rows.push(
10966
+ /* @__PURE__ */ jsxs15(
10967
+ "span",
10968
+ {
10969
+ className: "df-review-section-outline-meta-row is-media",
10970
+ children: [
10971
+ /* @__PURE__ */ jsx17("b", { children: mediaLabel }),
10972
+ /* @__PURE__ */ jsx17(
10973
+ "a",
10974
+ {
10975
+ className: "df-review-section-outline-media-link",
10976
+ href: mediaItem.url,
10977
+ rel: "noopener noreferrer",
10978
+ target: "_blank",
10979
+ title: `${mediaLabel} ${mediaItem.type}`,
10980
+ children: /* @__PURE__ */ jsx17("code", { children: mediaItem.url })
10981
+ }
10982
+ )
10983
+ ]
10984
+ },
10985
+ mediaKey
10986
+ )
10987
+ );
10988
+ });
10989
+ }
10990
+ if (isSectionOutlineClassMetaVisible && metadata.classNames?.length) {
10991
+ rows.push(
10992
+ /* @__PURE__ */ jsxs15("span", { className: "df-review-section-outline-meta-row is-class", children: [
10993
+ /* @__PURE__ */ jsx17("b", { children: "class" }),
10994
+ /* @__PURE__ */ jsx17("span", { className: "df-review-section-outline-class-tags", children: metadata.classNames.map((className) => /* @__PURE__ */ jsx17("code", { children: className }, className)) })
10995
+ ] }, "class")
10996
+ );
10997
+ }
10998
+ if (rows.length === 0) return null;
10999
+ return /* @__PURE__ */ jsx17(
11000
+ "div",
11001
+ {
11002
+ className: "df-review-section-outline-meta",
11003
+ style: { paddingLeft: `${metaPaddingLeft}px` },
11004
+ children: rows
11005
+ }
11006
+ );
11007
+ };
11008
+ const renderSectionOutlineEntry = (entry) => {
11009
+ const hasChildren = entry.children.length > 0;
11010
+ const isCollapsed = !isSectionOutlineFiltering && collapsedSectionOutlineIds.has(entry.id);
11011
+ return /* @__PURE__ */ jsxs15(
11012
+ "div",
11013
+ {
11014
+ className: `df-review-section-outline-item is-depth-${entry.depth}`,
11015
+ children: [
11016
+ /* @__PURE__ */ jsxs15(
11017
+ "div",
11018
+ {
11019
+ className: "df-review-section-outline-entry-body",
11020
+ onMouseEnter: () => showSourceOutlineForElement(entry.element),
11021
+ onMouseLeave: clearSourceOutlineHover,
11022
+ onMouseOver: () => showSourceOutlineForElement(entry.element),
11023
+ onMouseOut: (event) => {
11024
+ if (event.relatedTarget instanceof Node && event.currentTarget.contains(event.relatedTarget)) {
11025
+ return;
11026
+ }
11027
+ clearSourceOutlineHover();
11028
+ },
11029
+ onPointerEnter: () => showSourceOutlineForElement(entry.element),
11030
+ onPointerLeave: clearSourceOutlineHover,
11031
+ children: [
11032
+ /* @__PURE__ */ jsxs15(
11033
+ "div",
11034
+ {
11035
+ className: "df-review-section-outline-row",
11036
+ style: { paddingLeft: `${Math.max(0, entry.depth - 1) * 12 + 6}px` },
11037
+ children: [
11038
+ hasChildren ? /* @__PURE__ */ jsx17(
11039
+ "button",
11040
+ {
11041
+ "aria-label": isCollapsed ? `Expand ${entry.label}` : `Collapse ${entry.label}`,
11042
+ "aria-expanded": !isCollapsed,
11043
+ className: `df-review-section-outline-toggle${isCollapsed ? " is-collapsed" : ""}`,
11044
+ type: "button",
11045
+ onClick: () => toggleSectionOutlineEntry(entry.id),
11046
+ children: /* @__PURE__ */ jsx17(ChevronDown, { "aria-hidden": "true" })
11047
+ }
11048
+ ) : /* @__PURE__ */ jsx17(
11049
+ "span",
11050
+ {
11051
+ "aria-hidden": "true",
11052
+ className: "df-review-section-outline-toggle is-placeholder"
11053
+ }
11054
+ ),
11055
+ /* @__PURE__ */ jsx17(
11056
+ "button",
11057
+ {
11058
+ className: "df-review-section-outline-name",
11059
+ type: "button",
11060
+ onClick: () => scrollToSection(entry),
11061
+ children: /* @__PURE__ */ jsx17("span", { children: entry.label })
11062
+ }
11063
+ ),
11064
+ /* @__PURE__ */ jsxs15("span", { className: "df-review-section-outline-links", children: [
11065
+ /* @__PURE__ */ jsx17(
11066
+ "button",
11067
+ {
11068
+ "aria-label": `Open ${entry.label} data`,
11069
+ className: "df-review-section-outline-link",
11070
+ title: "Open data",
11071
+ type: "button",
11072
+ disabled: !entry.data?.file,
11073
+ onClick: () => openSectionData(entry),
11074
+ children: /* @__PURE__ */ jsx17(Database, { "aria-hidden": "true" })
11075
+ }
11076
+ ),
11077
+ /* @__PURE__ */ jsx17(
11078
+ "button",
11079
+ {
11080
+ "aria-label": `Open ${entry.label} source`,
11081
+ className: "df-review-section-outline-link",
11082
+ title: "Open source",
11083
+ type: "button",
11084
+ disabled: !entry.source?.file,
11085
+ onClick: () => openSectionSource(entry),
11086
+ children: /* @__PURE__ */ jsx17(CodeXml, { "aria-hidden": "true" })
11087
+ }
11088
+ ),
11089
+ /* @__PURE__ */ jsx17(
11090
+ "span",
11091
+ {
11092
+ "aria-hidden": "true",
11093
+ className: "df-review-section-outline-divider",
11094
+ children: "|"
11095
+ }
11096
+ ),
11097
+ /* @__PURE__ */ jsx17(
11098
+ "button",
11099
+ {
11100
+ "aria-label": `Start DOM QA for ${entry.label}`,
11101
+ className: "df-review-section-outline-link is-dom-select",
11102
+ title: "DOM select",
11103
+ type: "button",
11104
+ disabled: !canWriteDom,
11105
+ onClick: () => startSectionDomReview(entry),
11106
+ children: /* @__PURE__ */ jsx17(SquareMousePointer, { "aria-hidden": "true" })
11107
+ }
11108
+ )
11109
+ ] })
11110
+ ]
11111
+ }
11112
+ ),
11113
+ renderSectionOutlineMeta(entry)
11114
+ ]
11115
+ }
11116
+ ),
11117
+ hasChildren && !isCollapsed && /* @__PURE__ */ jsx17("div", { className: "df-review-section-outline-children", children: entry.children.map(renderSectionOutlineEntry) })
11118
+ ]
11119
+ },
11120
+ entry.id
11121
+ );
11122
+ };
9088
11123
  return /* @__PURE__ */ jsxs15(
9089
11124
  "div",
9090
11125
  {
@@ -9108,20 +11143,9 @@ var ReviewShell = ({
9108
11143
  onCopyCurrentUrl: () => void copyCurrentUrl(),
9109
11144
  onSizeChange: setSize,
9110
11145
  onToggleRuler: toggleRuler,
9111
- onToggleTargetOverlay: toggleTargetOverlay,
9112
- onOpenInitialPrompt: () => {
9113
- setIsInitialPromptOpen(true);
9114
- },
9115
- onOpenSettings: openFigmaSettings
11146
+ onToggleTargetOverlay: toggleTargetOverlay
9116
11147
  }
9117
11148
  ),
9118
- currentPagePresenceUsers.length > 0 && /* @__PURE__ */ jsx17("div", { className: "df-review-presence-row", children: /* @__PURE__ */ jsx17(
9119
- PresenceOverlay,
9120
- {
9121
- presenceSessionId,
9122
- users: currentPagePresenceUsers
9123
- }
9124
- ) }),
9125
11149
  isSitemapOpen && /* @__PURE__ */ jsx17(
9126
11150
  SitemapModal,
9127
11151
  {
@@ -9174,19 +11198,66 @@ var ReviewShell = ({
9174
11198
  }
9175
11199
  ),
9176
11200
  toastMessage && /* @__PURE__ */ jsx17("div", { className: "df-review-copy-toast", role: "status", children: toastMessage }),
9177
- /* @__PURE__ */ jsx17("div", { className: "df-review-side-rail", children: /* @__PURE__ */ jsxs15(
9178
- "button",
9179
- {
9180
- "aria-label": isListVisible ? "Hide QA list" : "Show QA list",
9181
- className: "df-review-side-toggle",
9182
- type: "button",
9183
- onClick: () => setIsListVisible((current) => !current),
9184
- children: [
9185
- /* @__PURE__ */ jsx17("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx17(GripVertical, {}) }),
9186
- /* @__PURE__ */ jsx17("strong", { children: "QA" })
9187
- ]
9188
- }
9189
- ) }),
11201
+ /* @__PURE__ */ jsxs15("div", { className: "df-review-side-rail", children: [
11202
+ /* @__PURE__ */ jsx17(
11203
+ "button",
11204
+ {
11205
+ "aria-label": isQaPanelVisible ? "Hide QA list" : "Show QA list",
11206
+ "aria-pressed": isQaPanelVisible,
11207
+ className: `df-review-side-toggle${isQaPanelVisible ? " is-active" : ""}`,
11208
+ type: "button",
11209
+ onClick: toggleQaPanel,
11210
+ title: "QA",
11211
+ children: /* @__PURE__ */ jsx17("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx17(FileText, {}) })
11212
+ }
11213
+ ),
11214
+ isSourceInspectorEnabled && /* @__PURE__ */ jsx17(
11215
+ "button",
11216
+ {
11217
+ "aria-controls": "df-review-section-outline",
11218
+ "aria-label": isSourceTreePanelVisible ? "Hide source tree" : "Show source tree",
11219
+ "aria-pressed": isSourceTreePanelVisible,
11220
+ className: `df-review-side-toggle${isSourceTreePanelVisible ? " is-active" : ""}`,
11221
+ type: "button",
11222
+ onClick: toggleSourceTreePanel,
11223
+ title: "Source Tree",
11224
+ children: /* @__PURE__ */ jsx17("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx17(Workflow, {}) })
11225
+ }
11226
+ ),
11227
+ /* @__PURE__ */ jsxs15("div", { className: "df-review-side-actions", children: [
11228
+ /* @__PURE__ */ jsx17(
11229
+ "button",
11230
+ {
11231
+ "aria-label": "Open initial prompt",
11232
+ className: "df-review-side-toggle",
11233
+ type: "button",
11234
+ onClick: () => {
11235
+ setIsInitialPromptOpen(true);
11236
+ },
11237
+ title: "Help",
11238
+ children: /* @__PURE__ */ jsx17("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx17(CircleQuestionMark, {}) })
11239
+ }
11240
+ ),
11241
+ /* @__PURE__ */ jsx17(
11242
+ "button",
11243
+ {
11244
+ "aria-label": "Open settings",
11245
+ className: "df-review-side-toggle",
11246
+ type: "button",
11247
+ onClick: openFigmaSettings,
11248
+ title: "Settings",
11249
+ children: /* @__PURE__ */ jsx17("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx17(Settings, {}) })
11250
+ }
11251
+ ),
11252
+ currentPagePresenceUsers.length > 0 && /* @__PURE__ */ jsx17(
11253
+ PresenceOverlay,
11254
+ {
11255
+ presenceSessionId,
11256
+ users: currentPagePresenceUsers
11257
+ }
11258
+ )
11259
+ ] })
11260
+ ] }),
9190
11261
  /* @__PURE__ */ jsx17(
9191
11262
  ReviewQaPanel,
9192
11263
  {
@@ -9197,7 +11268,7 @@ var ReviewShell = ({
9197
11268
  filteredNumberedActiveItems,
9198
11269
  getItemPresetScope,
9199
11270
  hiddenOverlayItemIds,
9200
- isListVisible,
11271
+ isListVisible: isQaPanelVisible,
9201
11272
  isAllQaVisible,
9202
11273
  isRemoteSource,
9203
11274
  copiedPromptKey,
@@ -9213,6 +11284,7 @@ var ReviewShell = ({
9213
11284
  onChangeItemStatus: changeItemStatus,
9214
11285
  onClearSelectedItem: clearSelectedReviewItem,
9215
11286
  onChangeReviewSource: changeReviewSource,
11287
+ onCopyItemLabel: (numberedItem) => void copyItemLabel(numberedItem),
9216
11288
  onCopyItemLink: (numberedItem) => void copyItemLink(numberedItem),
9217
11289
  onCopyItemPrompt: (numberedItem) => void copyItemPrompt(numberedItem),
9218
11290
  onEditItem: setEditingItem,
@@ -9225,6 +11297,101 @@ var ReviewShell = ({
9225
11297
  onToggleItemOverlayVisibility: toggleItemOverlayVisibility
9226
11298
  }
9227
11299
  ),
11300
+ isSourceInspectorEnabled && /* @__PURE__ */ jsx17(
11301
+ "aside",
11302
+ {
11303
+ className: "df-review-source-tree-panel",
11304
+ "aria-hidden": !isSourceTreePanelVisible,
11305
+ children: /* @__PURE__ */ jsxs15("div", { id: "df-review-section-outline", className: "df-review-section-outline", children: [
11306
+ /* @__PURE__ */ jsxs15("div", { className: "df-review-section-outline-head", children: [
11307
+ /* @__PURE__ */ jsxs15("div", { className: "df-review-section-outline-summary", children: [
11308
+ /* @__PURE__ */ jsxs15("span", { children: [
11309
+ /* @__PURE__ */ jsx17("strong", { children: "Component" }),
11310
+ /* @__PURE__ */ jsx17("small", { children: isSectionOutlineFiltering ? `${filteredSectionOutlineCount} / ${sectionOutlineTotalCount} results` : `${sectionOutline?.length ?? 0} ${sectionOutline?.length === 1 ? "root" : "roots"}` })
11311
+ ] }),
11312
+ /* @__PURE__ */ jsxs15("div", { className: "df-review-section-outline-meta-controls", children: [
11313
+ /* @__PURE__ */ jsx17(
11314
+ "button",
11315
+ {
11316
+ "aria-label": "Toggle source tree box metadata",
11317
+ "aria-pressed": isSectionOutlineBoxMetaVisible,
11318
+ className: `df-review-section-outline-meta-toggle${isSectionOutlineBoxMetaVisible ? " is-active" : ""}`,
11319
+ title: "top / left / width / height",
11320
+ type: "button",
11321
+ onClick: () => updateSectionOutlineMetaVisibility("box"),
11322
+ children: /* @__PURE__ */ jsx17(SquareDashed, { "aria-hidden": "true" })
11323
+ }
11324
+ ),
11325
+ /* @__PURE__ */ jsx17(
11326
+ "button",
11327
+ {
11328
+ "aria-label": "Toggle source tree font metadata",
11329
+ "aria-pressed": isSectionOutlineFontMetaVisible,
11330
+ className: `df-review-section-outline-meta-toggle${isSectionOutlineFontMetaVisible ? " is-active" : ""}`,
11331
+ title: "font size / weight",
11332
+ type: "button",
11333
+ onClick: () => updateSectionOutlineMetaVisibility("font"),
11334
+ children: /* @__PURE__ */ jsx17(Type, { "aria-hidden": "true" })
11335
+ }
11336
+ ),
11337
+ /* @__PURE__ */ jsx17(
11338
+ "button",
11339
+ {
11340
+ "aria-label": "Toggle source tree media metadata",
11341
+ "aria-pressed": isSectionOutlineMediaMetaVisible,
11342
+ className: `df-review-section-outline-meta-toggle${isSectionOutlineMediaMetaVisible ? " is-active" : ""}`,
11343
+ title: "media urls",
11344
+ type: "button",
11345
+ onClick: () => updateSectionOutlineMetaVisibility("media"),
11346
+ children: /* @__PURE__ */ jsx17(Image, { "aria-hidden": "true" })
11347
+ }
11348
+ ),
11349
+ /* @__PURE__ */ jsx17(
11350
+ "button",
11351
+ {
11352
+ "aria-label": "Toggle source tree class metadata",
11353
+ "aria-pressed": isSectionOutlineClassMetaVisible,
11354
+ className: `df-review-section-outline-meta-toggle${isSectionOutlineClassMetaVisible ? " is-active" : ""}`,
11355
+ title: "class names",
11356
+ type: "button",
11357
+ onClick: () => updateSectionOutlineMetaVisibility("className"),
11358
+ children: /* @__PURE__ */ jsx17(CodeXml, { "aria-hidden": "true" })
11359
+ }
11360
+ )
11361
+ ] })
11362
+ ] }),
11363
+ /* @__PURE__ */ jsxs15("div", { className: "df-review-section-outline-filter", children: [
11364
+ /* @__PURE__ */ jsx17(Search, { "aria-hidden": "true" }),
11365
+ /* @__PURE__ */ jsx17(
11366
+ "input",
11367
+ {
11368
+ "aria-label": "Filter source tree",
11369
+ type: "text",
11370
+ value: sectionOutlineFilter,
11371
+ placeholder: "Filter",
11372
+ autoComplete: "off",
11373
+ enterKeyHint: "search",
11374
+ spellCheck: false,
11375
+ onChange: (event) => updateSectionOutlineFilter(event.currentTarget.value)
11376
+ }
11377
+ ),
11378
+ sectionOutlineFilter && /* @__PURE__ */ jsx17(
11379
+ "button",
11380
+ {
11381
+ "aria-label": "Clear source tree filter",
11382
+ className: "df-review-section-outline-filter-clear",
11383
+ type: "button",
11384
+ onMouseDown: (event) => event.preventDefault(),
11385
+ onClick: () => updateSectionOutlineFilter(""),
11386
+ children: /* @__PURE__ */ jsx17(X, { "aria-hidden": "true" })
11387
+ }
11388
+ )
11389
+ ] })
11390
+ ] }),
11391
+ filteredSectionOutline.length > 0 ? /* @__PURE__ */ jsx17("div", { className: "df-review-section-outline-list", children: filteredSectionOutline.map(renderSectionOutlineEntry) }) : /* @__PURE__ */ jsx17("div", { className: "df-review-section-outline-empty", children: isSectionOutlineFiltering ? "No source matches" : "No sections found" })
11392
+ ] })
11393
+ }
11394
+ ),
9228
11395
  /* @__PURE__ */ jsx17(
9229
11396
  ReviewTargetFrame,
9230
11397
  {
@@ -9296,7 +11463,7 @@ var ReviewShell = ({
9296
11463
  /* @__PURE__ */ jsx17("div", { className: "df-review-source-candidate-list", children: sourceInspectorState.candidates.map((candidate) => /* @__PURE__ */ jsx17(
9297
11464
  "button",
9298
11465
  {
9299
- className: "df-review-source-candidate",
11466
+ className: `df-review-source-candidate is-${candidate.kind}`,
9300
11467
  type: "button",
9301
11468
  onClick: (event) => {
9302
11469
  event.preventDefault();
@@ -9306,7 +11473,7 @@ var ReviewShell = ({
9306
11473
  children: /* @__PURE__ */ jsxs15("span", { className: "df-review-source-candidate-main", children: [
9307
11474
  /* @__PURE__ */ jsx17("strong", { children: candidate.label }),
9308
11475
  /* @__PURE__ */ jsx17("span", { children: candidate.filePath }),
9309
- /* @__PURE__ */ jsx17("small", { children: candidate.positionLabel || (candidate.usesPosition ? "" : "file only") })
11476
+ /* @__PURE__ */ jsx17("small", { children: candidate.positionLabel || "-:-" })
9310
11477
  ] })
9311
11478
  },
9312
11479
  candidate.id
@@ -9529,12 +11696,15 @@ lucide-react/dist/esm/context.mjs:
9529
11696
  lucide-react/dist/esm/Icon.mjs:
9530
11697
  lucide-react/dist/esm/createLucideIcon.mjs:
9531
11698
  lucide-react/dist/esm/icons/bot.mjs:
11699
+ lucide-react/dist/esm/icons/chevron-down.mjs:
9532
11700
  lucide-react/dist/esm/icons/circle-question-mark.mjs:
11701
+ lucide-react/dist/esm/icons/code-xml.mjs:
9533
11702
  lucide-react/dist/esm/icons/copy.mjs:
11703
+ lucide-react/dist/esm/icons/database.mjs:
9534
11704
  lucide-react/dist/esm/icons/external-link.mjs:
9535
11705
  lucide-react/dist/esm/icons/eye-off.mjs:
9536
11706
  lucide-react/dist/esm/icons/eye.mjs:
9537
- lucide-react/dist/esm/icons/grip-vertical.mjs:
11707
+ lucide-react/dist/esm/icons/file-text.mjs:
9538
11708
  lucide-react/dist/esm/icons/image.mjs:
9539
11709
  lucide-react/dist/esm/icons/layout-grid.mjs:
9540
11710
  lucide-react/dist/esm/icons/link-2.mjs:
@@ -9542,16 +11712,22 @@ lucide-react/dist/esm/icons/list-filter.mjs:
9542
11712
  lucide-react/dist/esm/icons/map.mjs:
9543
11713
  lucide-react/dist/esm/icons/maximize-2.mjs:
9544
11714
  lucide-react/dist/esm/icons/monitor.mjs:
11715
+ lucide-react/dist/esm/icons/moon.mjs:
9545
11716
  lucide-react/dist/esm/icons/pencil.mjs:
9546
11717
  lucide-react/dist/esm/icons/rectangle-horizontal.mjs:
9547
11718
  lucide-react/dist/esm/icons/refresh-cw.mjs:
9548
11719
  lucide-react/dist/esm/icons/ruler.mjs:
9549
11720
  lucide-react/dist/esm/icons/scan.mjs:
11721
+ lucide-react/dist/esm/icons/search.mjs:
9550
11722
  lucide-react/dist/esm/icons/settings.mjs:
9551
11723
  lucide-react/dist/esm/icons/smartphone.mjs:
11724
+ lucide-react/dist/esm/icons/square-dashed.mjs:
9552
11725
  lucide-react/dist/esm/icons/square-mouse-pointer.mjs:
9553
11726
  lucide-react/dist/esm/icons/sticky-note.mjs:
11727
+ lucide-react/dist/esm/icons/sun.mjs:
11728
+ lucide-react/dist/esm/icons/type.mjs:
9554
11729
  lucide-react/dist/esm/icons/upload.mjs:
11730
+ lucide-react/dist/esm/icons/workflow.mjs:
9555
11731
  lucide-react/dist/esm/icons/x.mjs:
9556
11732
  lucide-react/dist/esm/lucide-react.mjs:
9557
11733
  (**