@in-the-loop-labs/pair-review 3.5.2 → 3.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.
- package/package.json +15 -20
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
- package/plugin-code-critic/skills/analyze/scripts/git-diff-lines +0 -0
- package/public/css/pr.css +603 -6
- package/public/js/components/ChatPanel.js +163 -3
- package/public/js/components/KeyboardShortcuts.js +10 -26
- package/public/js/components/TourBar.js +248 -0
- package/public/js/local.js +6 -0
- package/public/js/modules/cancel-background-job.js +183 -0
- package/public/js/modules/hunk-summary-renderer.js +116 -0
- package/public/js/modules/storage-cleanup.js +16 -0
- package/public/js/modules/tour-renderer.js +725 -0
- package/public/js/pr.js +1276 -2
- package/public/js/utils/modal-detection.js +77 -0
- package/public/local.html +17 -0
- package/public/pr.html +17 -0
- package/src/ai/abort-signal-wiring.js +130 -0
- package/src/ai/background-queue.js +290 -0
- package/src/ai/claude-cli.js +1 -1
- package/src/ai/claude-provider.js +50 -7
- package/src/ai/codex-provider.js +28 -5
- package/src/ai/copilot-provider.js +22 -3
- package/src/ai/cursor-agent-provider.js +22 -6
- package/src/ai/executable-provider.js +4 -19
- package/src/ai/gemini-provider.js +22 -5
- package/src/ai/hunk-hashing.js +161 -0
- package/src/ai/index.js +2 -0
- package/src/ai/opencode-provider.js +21 -5
- package/src/ai/pi-provider.js +21 -5
- package/src/ai/prompts/hunk-summary.js +199 -0
- package/src/ai/prompts/tour.js +232 -0
- package/src/ai/provider.js +21 -1
- package/src/ai/summary-generator.js +469 -0
- package/src/ai/tour-generator.js +568 -0
- package/src/config.js +114 -0
- package/src/database.js +282 -1
- package/src/local-review.js +189 -169
- package/src/routes/config.js +16 -1
- package/src/routes/context-files.js +2 -29
- package/src/routes/local.js +311 -4
- package/src/routes/middleware/validate-review-id.js +53 -0
- package/src/routes/pr.js +259 -4
- package/src/routes/reviews.js +145 -29
- package/src/utils/diff-hunks.js +65 -0
- package/src/utils/json-extractor.js +5 -2
package/public/css/pr.css
CHANGED
|
@@ -1445,7 +1445,8 @@
|
|
|
1445
1445
|
|
|
1446
1446
|
/* File header comment button and file header chat button */
|
|
1447
1447
|
.file-header-comment-btn,
|
|
1448
|
-
.file-header-chat-btn
|
|
1448
|
+
.file-header-chat-btn,
|
|
1449
|
+
.file-header-summary-toggle {
|
|
1449
1450
|
display: flex;
|
|
1450
1451
|
align-items: center;
|
|
1451
1452
|
justify-content: center;
|
|
@@ -1459,38 +1460,66 @@
|
|
|
1459
1460
|
color: var(--color-text-secondary, #656d76);
|
|
1460
1461
|
transition: background-color 0.15s, color 0.15s, border-color 0.15s;
|
|
1461
1462
|
flex-shrink: 0;
|
|
1463
|
+
position: relative;
|
|
1462
1464
|
}
|
|
1463
1465
|
|
|
1464
1466
|
.file-header-comment-btn:hover,
|
|
1465
|
-
.file-header-chat-btn:hover
|
|
1467
|
+
.file-header-chat-btn:hover,
|
|
1468
|
+
.file-header-summary-toggle:not([disabled]):hover {
|
|
1466
1469
|
background-color: var(--color-accent-bg, rgba(9, 105, 218, 0.1));
|
|
1467
1470
|
border-color: var(--color-accent-primary, #0969da);
|
|
1468
1471
|
color: var(--color-accent-primary, #0969da);
|
|
1469
1472
|
}
|
|
1470
1473
|
|
|
1471
1474
|
.file-header-comment-btn:active,
|
|
1472
|
-
.file-header-chat-btn:active
|
|
1475
|
+
.file-header-chat-btn:active,
|
|
1476
|
+
.file-header-summary-toggle:not([disabled]):active {
|
|
1473
1477
|
background-color: var(--color-accent-bg, rgba(9, 105, 218, 0.15));
|
|
1474
1478
|
}
|
|
1475
1479
|
|
|
1476
1480
|
.file-header-comment-btn svg,
|
|
1477
|
-
.file-header-chat-btn svg
|
|
1481
|
+
.file-header-chat-btn svg,
|
|
1482
|
+
.file-header-summary-toggle svg {
|
|
1478
1483
|
width: 16px;
|
|
1479
1484
|
height: 16px;
|
|
1480
1485
|
}
|
|
1481
1486
|
|
|
1482
1487
|
[data-theme="dark"] .file-header-comment-btn,
|
|
1483
|
-
[data-theme="dark"] .file-header-chat-btn
|
|
1488
|
+
[data-theme="dark"] .file-header-chat-btn,
|
|
1489
|
+
[data-theme="dark"] .file-header-summary-toggle {
|
|
1484
1490
|
color: #8b949e;
|
|
1485
1491
|
}
|
|
1486
1492
|
|
|
1487
1493
|
[data-theme="dark"] .file-header-comment-btn:hover,
|
|
1488
|
-
[data-theme="dark"] .file-header-chat-btn:hover
|
|
1494
|
+
[data-theme="dark"] .file-header-chat-btn:hover,
|
|
1495
|
+
[data-theme="dark"] .file-header-summary-toggle:not([disabled]):hover {
|
|
1489
1496
|
background-color: rgba(56, 139, 253, 0.15);
|
|
1490
1497
|
border-color: #58a6ff;
|
|
1491
1498
|
color: #58a6ff;
|
|
1492
1499
|
}
|
|
1493
1500
|
|
|
1501
|
+
/* Disabled state — no summaries available for this file */
|
|
1502
|
+
.file-header-summary-toggle[disabled] {
|
|
1503
|
+
opacity: 0.4;
|
|
1504
|
+
cursor: not-allowed;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
/* Off state — summaries hidden for this file. Shows the same slash overlay
|
|
1508
|
+
used by the toolbar toggle so the on/off state is visually distinct. */
|
|
1509
|
+
.file-header-summary-toggle.summaries-off:not([disabled])::after {
|
|
1510
|
+
content: '';
|
|
1511
|
+
position: absolute;
|
|
1512
|
+
top: 50%;
|
|
1513
|
+
left: 18%;
|
|
1514
|
+
right: 18%;
|
|
1515
|
+
height: 2px;
|
|
1516
|
+
background-color: currentColor;
|
|
1517
|
+
transform: rotate(-25deg);
|
|
1518
|
+
transform-origin: center;
|
|
1519
|
+
pointer-events: none;
|
|
1520
|
+
border-radius: 1px;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1494
1523
|
/* Has comments state - filled icon with accent color */
|
|
1495
1524
|
.file-header-comment-btn.has-comments {
|
|
1496
1525
|
color: var(--color-accent-primary, #0969da);
|
|
@@ -14720,3 +14749,571 @@ body.resizing * {
|
|
|
14720
14749
|
[data-theme="dark"] .status-failed .stack-progress-status-icon {
|
|
14721
14750
|
color: var(--color-danger, #f85149);
|
|
14722
14751
|
}
|
|
14752
|
+
|
|
14753
|
+
/* ==========================================================================
|
|
14754
|
+
Hunk Summary Annotations (Phase 5)
|
|
14755
|
+
========================================================================== */
|
|
14756
|
+
|
|
14757
|
+
.hunk-summary-row {
|
|
14758
|
+
background: transparent;
|
|
14759
|
+
}
|
|
14760
|
+
|
|
14761
|
+
.hunk-summary-cell {
|
|
14762
|
+
padding: 4px 0;
|
|
14763
|
+
border: none;
|
|
14764
|
+
}
|
|
14765
|
+
|
|
14766
|
+
.hunk-summary-annotation {
|
|
14767
|
+
display: flex;
|
|
14768
|
+
align-items: flex-start;
|
|
14769
|
+
gap: 10px;
|
|
14770
|
+
padding: 8px 12px;
|
|
14771
|
+
margin: 6px auto;
|
|
14772
|
+
font-size: 13px;
|
|
14773
|
+
line-height: 1.5;
|
|
14774
|
+
color: #0550ae;
|
|
14775
|
+
background: rgba(9, 105, 218, 0.06);
|
|
14776
|
+
border: 1px solid rgba(9, 105, 218, 0.18);
|
|
14777
|
+
border-left: 3px solid #0969da;
|
|
14778
|
+
border-radius: var(--radius-md, 6px);
|
|
14779
|
+
max-width: calc(100vw - var(--sidebar-width, 0px) - var(--right-panel-group-width, 0px) - 64px);
|
|
14780
|
+
box-sizing: border-box;
|
|
14781
|
+
word-break: break-word;
|
|
14782
|
+
}
|
|
14783
|
+
|
|
14784
|
+
[data-theme="dark"] .hunk-summary-annotation {
|
|
14785
|
+
color: #79c0ff;
|
|
14786
|
+
background: rgba(56, 139, 253, 0.10);
|
|
14787
|
+
border-color: rgba(56, 139, 253, 0.25);
|
|
14788
|
+
border-left-color: #58a6ff;
|
|
14789
|
+
}
|
|
14790
|
+
|
|
14791
|
+
.hunk-summary-icon {
|
|
14792
|
+
flex-shrink: 0;
|
|
14793
|
+
display: inline-flex;
|
|
14794
|
+
align-items: center;
|
|
14795
|
+
justify-content: center;
|
|
14796
|
+
width: 16px;
|
|
14797
|
+
height: 16px;
|
|
14798
|
+
margin-top: 1px;
|
|
14799
|
+
opacity: 0.85;
|
|
14800
|
+
}
|
|
14801
|
+
|
|
14802
|
+
.hunk-summary-text {
|
|
14803
|
+
flex: 1;
|
|
14804
|
+
min-width: 0;
|
|
14805
|
+
}
|
|
14806
|
+
|
|
14807
|
+
/* Review-level toggle hides every annotation row */
|
|
14808
|
+
body.summaries-hidden .hunk-summary-row,
|
|
14809
|
+
.d2h-file-wrapper.summaries-hidden-file .hunk-summary-row {
|
|
14810
|
+
display: none;
|
|
14811
|
+
}
|
|
14812
|
+
|
|
14813
|
+
/* ---------- Toolbar toggle states ---------- */
|
|
14814
|
+
/* Default toolbar buttons inherit a transparent border; the toggle button
|
|
14815
|
+
uses a fixed neutral border so the on-state color swap is visible. */
|
|
14816
|
+
.btn-summary-toggle {
|
|
14817
|
+
position: relative;
|
|
14818
|
+
border: 1px solid var(--color-border-primary, #d1d9e0);
|
|
14819
|
+
color: var(--color-text-secondary, #57606a);
|
|
14820
|
+
transition: color var(--transition-fast, 0.1s ease),
|
|
14821
|
+
border-color var(--transition-fast, 0.1s ease),
|
|
14822
|
+
background-color var(--transition-fast, 0.1s ease);
|
|
14823
|
+
}
|
|
14824
|
+
|
|
14825
|
+
/* Summaries are visible (default state when feature is on). */
|
|
14826
|
+
.btn-summary-toggle.active {
|
|
14827
|
+
color: var(--color-accent-primary, #0969da);
|
|
14828
|
+
border-color: var(--color-accent-primary, #0969da);
|
|
14829
|
+
background-color: rgba(9, 105, 218, 0.08);
|
|
14830
|
+
}
|
|
14831
|
+
|
|
14832
|
+
[data-theme="dark"] .btn-summary-toggle.active {
|
|
14833
|
+
color: var(--color-accent-primary, #58a6ff);
|
|
14834
|
+
border-color: var(--color-accent-primary, #58a6ff);
|
|
14835
|
+
background-color: rgba(56, 139, 253, 0.12);
|
|
14836
|
+
}
|
|
14837
|
+
|
|
14838
|
+
/* Summaries hidden — the off state is just the base button styling
|
|
14839
|
+
(gray secondary color, no background). Matches the tour-toggle pattern
|
|
14840
|
+
so the two buttons read consistently when off. */
|
|
14841
|
+
|
|
14842
|
+
/* "Generating" pulse — applied while the background summary job is running. */
|
|
14843
|
+
.btn-summary-toggle.generating {
|
|
14844
|
+
animation: summaryPulse 1.6s ease-in-out infinite;
|
|
14845
|
+
}
|
|
14846
|
+
|
|
14847
|
+
@keyframes summaryPulse {
|
|
14848
|
+
0%, 100% {
|
|
14849
|
+
box-shadow: 0 0 0 0 rgba(9, 105, 218, 0);
|
|
14850
|
+
}
|
|
14851
|
+
50% {
|
|
14852
|
+
box-shadow: 0 0 0 4px rgba(9, 105, 218, 0.18);
|
|
14853
|
+
}
|
|
14854
|
+
}
|
|
14855
|
+
|
|
14856
|
+
[data-theme="dark"] .btn-summary-toggle.generating {
|
|
14857
|
+
animation-name: summaryPulseDark;
|
|
14858
|
+
}
|
|
14859
|
+
|
|
14860
|
+
@keyframes summaryPulseDark {
|
|
14861
|
+
0%, 100% {
|
|
14862
|
+
box-shadow: 0 0 0 0 rgba(56, 139, 253, 0);
|
|
14863
|
+
}
|
|
14864
|
+
50% {
|
|
14865
|
+
box-shadow: 0 0 0 4px rgba(56, 139, 253, 0.28);
|
|
14866
|
+
}
|
|
14867
|
+
}
|
|
14868
|
+
|
|
14869
|
+
/* Custom hover label — shown alongside the native title in case the user's
|
|
14870
|
+
browser is suppressing native tooltips. Uses a `data-label` attr so the
|
|
14871
|
+
label tracks the on/off state. */
|
|
14872
|
+
.btn-summary-toggle[data-label]:hover::before {
|
|
14873
|
+
content: attr(data-label);
|
|
14874
|
+
position: absolute;
|
|
14875
|
+
top: calc(100% + 6px);
|
|
14876
|
+
right: 0;
|
|
14877
|
+
z-index: 200;
|
|
14878
|
+
padding: 4px 8px;
|
|
14879
|
+
font-size: 12px;
|
|
14880
|
+
line-height: 1.2;
|
|
14881
|
+
white-space: nowrap;
|
|
14882
|
+
color: var(--color-text-primary, #24292f);
|
|
14883
|
+
background: var(--color-bg-primary, #ffffff);
|
|
14884
|
+
border: 1px solid var(--color-border-primary, #d1d9e0);
|
|
14885
|
+
border-radius: var(--radius-sm, 4px);
|
|
14886
|
+
box-shadow: 0 2px 4px var(--color-shadow, rgba(27, 31, 36, 0.04));
|
|
14887
|
+
pointer-events: none;
|
|
14888
|
+
}
|
|
14889
|
+
|
|
14890
|
+
[data-theme="dark"] .btn-summary-toggle[data-label]:hover::before {
|
|
14891
|
+
color: var(--color-text-primary, #c9d1d9);
|
|
14892
|
+
background: var(--color-bg-primary, #0d1117);
|
|
14893
|
+
border-color: var(--color-border-primary, #30363d);
|
|
14894
|
+
}
|
|
14895
|
+
|
|
14896
|
+
.file-header-summary-toggle.summaries-off {
|
|
14897
|
+
opacity: 0.45;
|
|
14898
|
+
}
|
|
14899
|
+
|
|
14900
|
+
/* ==========================================================================
|
|
14901
|
+
Tour Annotations & Tour Bar (Phase 8)
|
|
14902
|
+
========================================================================== */
|
|
14903
|
+
|
|
14904
|
+
/* The inline annotation row that is mounted above the anchor line for each
|
|
14905
|
+
stop. Pale-yellow palette to differentiate from info-blue summaries. */
|
|
14906
|
+
.tour-annotation-row {
|
|
14907
|
+
background: transparent;
|
|
14908
|
+
}
|
|
14909
|
+
|
|
14910
|
+
.tour-annotation-cell {
|
|
14911
|
+
padding: 4px 0;
|
|
14912
|
+
border: none;
|
|
14913
|
+
}
|
|
14914
|
+
|
|
14915
|
+
.tour-annotation {
|
|
14916
|
+
display: flex;
|
|
14917
|
+
flex-direction: column;
|
|
14918
|
+
gap: 6px;
|
|
14919
|
+
padding: 10px 14px;
|
|
14920
|
+
margin: 6px auto;
|
|
14921
|
+
font-size: 13px;
|
|
14922
|
+
line-height: 1.45;
|
|
14923
|
+
color: #7d5700;
|
|
14924
|
+
/* Brighter amber matching the .tour-bar gradient so stops read as part
|
|
14925
|
+
of the same tour surface, not a muted variant of it. */
|
|
14926
|
+
background: linear-gradient(180deg, #fff8e1 0%, #fef3c7 100%);
|
|
14927
|
+
border: 1px solid rgba(212, 167, 44, 0.55);
|
|
14928
|
+
border-left: 3px solid #d4a72c;
|
|
14929
|
+
border-radius: var(--radius-md, 6px);
|
|
14930
|
+
max-width: calc(100vw - var(--sidebar-width, 0px) - var(--right-panel-group-width, 0px) - 64px);
|
|
14931
|
+
box-sizing: border-box;
|
|
14932
|
+
word-break: break-word;
|
|
14933
|
+
transition: box-shadow var(--transition-fast, 0.1s ease),
|
|
14934
|
+
border-color var(--transition-fast, 0.1s ease);
|
|
14935
|
+
}
|
|
14936
|
+
|
|
14937
|
+
[data-theme="dark"] .tour-annotation {
|
|
14938
|
+
color: #e3b341;
|
|
14939
|
+
/* Mirror the bar's dark-mode amber wash so light- and dark-mode tours
|
|
14940
|
+
share one visual language. */
|
|
14941
|
+
background: linear-gradient(180deg, rgba(187, 128, 9, 0.18) 0%, rgba(187, 128, 9, 0.28) 100%),
|
|
14942
|
+
var(--color-bg-primary, #0d1117);
|
|
14943
|
+
border-color: rgba(187, 128, 9, 0.55);
|
|
14944
|
+
border-left-color: #bb8009;
|
|
14945
|
+
}
|
|
14946
|
+
|
|
14947
|
+
.tour-annotation-header {
|
|
14948
|
+
display: flex;
|
|
14949
|
+
align-items: center;
|
|
14950
|
+
justify-content: space-between;
|
|
14951
|
+
gap: 8px;
|
|
14952
|
+
font-weight: 600;
|
|
14953
|
+
}
|
|
14954
|
+
|
|
14955
|
+
.tour-stop-marker {
|
|
14956
|
+
display: inline-flex;
|
|
14957
|
+
align-items: center;
|
|
14958
|
+
gap: 4px;
|
|
14959
|
+
font-size: 12px;
|
|
14960
|
+
text-transform: uppercase;
|
|
14961
|
+
letter-spacing: 0.04em;
|
|
14962
|
+
opacity: 0.9;
|
|
14963
|
+
}
|
|
14964
|
+
|
|
14965
|
+
.tour-stop-marker svg {
|
|
14966
|
+
width: 14px;
|
|
14967
|
+
height: 14px;
|
|
14968
|
+
}
|
|
14969
|
+
|
|
14970
|
+
.tour-annotation-title {
|
|
14971
|
+
margin: 0;
|
|
14972
|
+
font-size: 14px;
|
|
14973
|
+
font-weight: 600;
|
|
14974
|
+
line-height: 1.3;
|
|
14975
|
+
}
|
|
14976
|
+
|
|
14977
|
+
.tour-annotation-description {
|
|
14978
|
+
margin: 0;
|
|
14979
|
+
font-size: 13px;
|
|
14980
|
+
font-weight: 400;
|
|
14981
|
+
line-height: 1.45;
|
|
14982
|
+
opacity: 0.95;
|
|
14983
|
+
}
|
|
14984
|
+
|
|
14985
|
+
/* Wrap around the description <p>. Collapsed state uses the WebKit
|
|
14986
|
+
line-clamp triplet to cap visible content at ~3 lines; expanded state
|
|
14987
|
+
removes the clamp so the full description shows. The wrapper (not the
|
|
14988
|
+
inner <p>) carries the clamp so the JS overflow check has a stable
|
|
14989
|
+
element to measure (`scrollHeight > clientHeight`). */
|
|
14990
|
+
.tour-annotation-description-wrap {
|
|
14991
|
+
display: -webkit-box;
|
|
14992
|
+
-webkit-box-orient: vertical;
|
|
14993
|
+
-webkit-line-clamp: 3;
|
|
14994
|
+
line-clamp: 3;
|
|
14995
|
+
overflow: hidden;
|
|
14996
|
+
}
|
|
14997
|
+
|
|
14998
|
+
.tour-annotation-description-wrap.expanded {
|
|
14999
|
+
display: block;
|
|
15000
|
+
-webkit-line-clamp: unset;
|
|
15001
|
+
line-clamp: unset;
|
|
15002
|
+
overflow: visible;
|
|
15003
|
+
}
|
|
15004
|
+
|
|
15005
|
+
/* Footer holds only the "Show more"/"Show less" toggle when the
|
|
15006
|
+
description overflows the line-clamp; otherwise it's an empty stub.
|
|
15007
|
+
Chat about lives in the header now. */
|
|
15008
|
+
.tour-annotation-footer {
|
|
15009
|
+
display: flex;
|
|
15010
|
+
align-items: center;
|
|
15011
|
+
gap: 6px;
|
|
15012
|
+
margin-top: 4px;
|
|
15013
|
+
}
|
|
15014
|
+
|
|
15015
|
+
.tour-annotation-footer:empty {
|
|
15016
|
+
display: none;
|
|
15017
|
+
}
|
|
15018
|
+
|
|
15019
|
+
/* "Show more" / "Show less" toggle. Styled as a borderless amber link
|
|
15020
|
+
button so it reads as part of the tour annotation but doesn't compete
|
|
15021
|
+
with the primary Chat about action. */
|
|
15022
|
+
.tour-annotation-show-more-btn {
|
|
15023
|
+
appearance: none;
|
|
15024
|
+
background: none;
|
|
15025
|
+
border: 0;
|
|
15026
|
+
padding: 2px 4px;
|
|
15027
|
+
margin: 0;
|
|
15028
|
+
font: inherit;
|
|
15029
|
+
font-size: 12px;
|
|
15030
|
+
font-weight: 600;
|
|
15031
|
+
color: #7d5700;
|
|
15032
|
+
cursor: pointer;
|
|
15033
|
+
text-decoration: underline;
|
|
15034
|
+
text-underline-offset: 2px;
|
|
15035
|
+
border-radius: var(--radius-sm, 3px);
|
|
15036
|
+
}
|
|
15037
|
+
|
|
15038
|
+
.tour-annotation-show-more-btn:hover {
|
|
15039
|
+
color: #5b3f00;
|
|
15040
|
+
background: rgba(255, 255, 255, 0.45);
|
|
15041
|
+
}
|
|
15042
|
+
|
|
15043
|
+
.tour-annotation-show-more-btn:focus-visible {
|
|
15044
|
+
outline: 2px solid #d4a72c;
|
|
15045
|
+
outline-offset: 1px;
|
|
15046
|
+
}
|
|
15047
|
+
|
|
15048
|
+
[data-theme="dark"] .tour-annotation-show-more-btn {
|
|
15049
|
+
color: #e3b341;
|
|
15050
|
+
}
|
|
15051
|
+
|
|
15052
|
+
[data-theme="dark"] .tour-annotation-show-more-btn:hover {
|
|
15053
|
+
color: #f3c861;
|
|
15054
|
+
background: rgba(187, 128, 9, 0.18);
|
|
15055
|
+
}
|
|
15056
|
+
|
|
15057
|
+
/* The chat button on a tour-stop annotation reuses .ai-action / .ai-action-chat
|
|
15058
|
+
so its idle and hover treatments stay in sync with the rest of the app. We
|
|
15059
|
+
override surface colors (so it reads on the amber background) AND collapse
|
|
15060
|
+
it to an icon-only square — the button sits in the header next to the
|
|
15061
|
+
stop marker, where a labelled button would crowd the title row. The title
|
|
15062
|
+
attribute / aria-label carry the meaning for accessibility + tooltip. */
|
|
15063
|
+
.tour-annotation .tour-annotation-chat-btn {
|
|
15064
|
+
background: rgba(255, 255, 255, 0.6);
|
|
15065
|
+
color: #7d5700;
|
|
15066
|
+
border: 1px solid rgba(212, 167, 44, 0.55);
|
|
15067
|
+
padding: 2px;
|
|
15068
|
+
width: 22px;
|
|
15069
|
+
height: 22px;
|
|
15070
|
+
display: inline-flex;
|
|
15071
|
+
align-items: center;
|
|
15072
|
+
justify-content: center;
|
|
15073
|
+
flex: 0 0 auto;
|
|
15074
|
+
}
|
|
15075
|
+
|
|
15076
|
+
.tour-annotation .tour-annotation-chat-btn svg {
|
|
15077
|
+
width: 14px;
|
|
15078
|
+
height: 14px;
|
|
15079
|
+
}
|
|
15080
|
+
|
|
15081
|
+
.tour-annotation .tour-annotation-chat-btn:hover {
|
|
15082
|
+
background: rgba(255, 255, 255, 0.9);
|
|
15083
|
+
color: #5b3f00;
|
|
15084
|
+
border-color: #d4a72c;
|
|
15085
|
+
}
|
|
15086
|
+
|
|
15087
|
+
[data-theme="dark"] .tour-annotation .tour-annotation-chat-btn {
|
|
15088
|
+
background: rgba(187, 128, 9, 0.12);
|
|
15089
|
+
color: #e3b341;
|
|
15090
|
+
border-color: rgba(187, 128, 9, 0.55);
|
|
15091
|
+
}
|
|
15092
|
+
|
|
15093
|
+
[data-theme="dark"] .tour-annotation .tour-annotation-chat-btn:hover {
|
|
15094
|
+
background: rgba(187, 128, 9, 0.25);
|
|
15095
|
+
color: #f3c861;
|
|
15096
|
+
border-color: #e3b341;
|
|
15097
|
+
}
|
|
15098
|
+
|
|
15099
|
+
/* Emphasize the currently-active stop annotation. */
|
|
15100
|
+
body.tour-active .tour-annotation-row.active-stop .tour-annotation {
|
|
15101
|
+
box-shadow: 0 0 0 2px rgba(212, 167, 44, 0.55),
|
|
15102
|
+
0 2px 6px rgba(0, 0, 0, 0.08);
|
|
15103
|
+
border-color: #d4a72c;
|
|
15104
|
+
}
|
|
15105
|
+
|
|
15106
|
+
[data-theme="dark"] body.tour-active .tour-annotation-row.active-stop .tour-annotation {
|
|
15107
|
+
box-shadow: 0 0 0 2px rgba(187, 128, 9, 0.65),
|
|
15108
|
+
0 2px 6px rgba(0, 0, 0, 0.30);
|
|
15109
|
+
border-color: #e3b341;
|
|
15110
|
+
}
|
|
15111
|
+
|
|
15112
|
+
/* ---------- Sticky top tour bar ---------- */
|
|
15113
|
+
:root {
|
|
15114
|
+
/* Height assumed by the diff-toolbar / file-header offset rules below.
|
|
15115
|
+
Keep this in sync with the bar's computed height (icon + button row +
|
|
15116
|
+
vertical padding). The bar also sets `min-height: var(--tour-bar-height)`
|
|
15117
|
+
so its actual rendered height never drops below this value. */
|
|
15118
|
+
--tour-bar-height: 56px;
|
|
15119
|
+
}
|
|
15120
|
+
|
|
15121
|
+
/* While a tour is active, push the sticky `.diff-toolbar` and the
|
|
15122
|
+
per-file `.d2h-file-header` down by the bar's height so they stack
|
|
15123
|
+
below it instead of overlapping. The bar itself is `position: sticky;
|
|
15124
|
+
top: 0` inside the same scroll container (.main-layout .diff-view),
|
|
15125
|
+
so all three pin in order: tour bar (top: 0), diff toolbar (top:
|
|
15126
|
+
--tour-bar-height), file header (top: --toolbar-height + --tour-bar-height). */
|
|
15127
|
+
body.tour-active .diff-toolbar {
|
|
15128
|
+
top: var(--tour-bar-height);
|
|
15129
|
+
}
|
|
15130
|
+
|
|
15131
|
+
body.tour-active .d2h-file-header {
|
|
15132
|
+
top: calc(var(--toolbar-height, 0px) + var(--tour-bar-height));
|
|
15133
|
+
}
|
|
15134
|
+
|
|
15135
|
+
.tour-bar {
|
|
15136
|
+
/* Sticky inside the diff-view scroll container so the bar spans the
|
|
15137
|
+
diff width only — the file-tree sidebar (and its hide/show toggle in
|
|
15138
|
+
the diff toolbar) stays visible and clickable. Mounted as the first
|
|
15139
|
+
child of .diff-view via TourBar.mount(parent). */
|
|
15140
|
+
position: sticky;
|
|
15141
|
+
top: 0;
|
|
15142
|
+
/* z-index must exceed .diff-toolbar (5) and .d2h-file-header (4) so the
|
|
15143
|
+
bar paints above them at the top of the scrollport. */
|
|
15144
|
+
z-index: 6;
|
|
15145
|
+
/* Lock the rendered height to the var so the sticky-top offsets on
|
|
15146
|
+
.diff-toolbar and .d2h-file-header line up exactly with the bar's
|
|
15147
|
+
bottom edge (no gap, no overlap). flex-shrink:0 prevents the parent
|
|
15148
|
+
flex column (.diff-view) from squashing the bar below min-height. */
|
|
15149
|
+
box-sizing: border-box;
|
|
15150
|
+
flex-shrink: 0;
|
|
15151
|
+
min-height: var(--tour-bar-height);
|
|
15152
|
+
display: flex;
|
|
15153
|
+
align-items: center;
|
|
15154
|
+
justify-content: space-between;
|
|
15155
|
+
gap: 12px;
|
|
15156
|
+
padding: 10px 16px;
|
|
15157
|
+
/* Distinctive amber theme so the bar reads as a tour-mode chrome and
|
|
15158
|
+
not just another toolbar. Soft tinted background + a thicker amber
|
|
15159
|
+
bottom rule echo the per-stop annotation accent (#d4a72c). */
|
|
15160
|
+
background: linear-gradient(180deg, #fff8e1 0%, #fef3c7 100%);
|
|
15161
|
+
border-bottom: 2px solid #d4a72c;
|
|
15162
|
+
box-shadow: 0 2px 12px rgba(212, 167, 44, 0.18);
|
|
15163
|
+
font-size: 13px;
|
|
15164
|
+
color: #7d5700;
|
|
15165
|
+
}
|
|
15166
|
+
|
|
15167
|
+
[data-theme="dark"] .tour-bar {
|
|
15168
|
+
background: linear-gradient(180deg, rgba(187, 128, 9, 0.18) 0%, rgba(187, 128, 9, 0.28) 100%),
|
|
15169
|
+
var(--color-bg-primary, #0d1117);
|
|
15170
|
+
border-bottom-color: #bb8009;
|
|
15171
|
+
color: #e3b341;
|
|
15172
|
+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.40);
|
|
15173
|
+
}
|
|
15174
|
+
|
|
15175
|
+
.tour-bar__brand {
|
|
15176
|
+
display: inline-flex;
|
|
15177
|
+
align-items: center;
|
|
15178
|
+
gap: 8px;
|
|
15179
|
+
font-weight: 600;
|
|
15180
|
+
color: #7d5700;
|
|
15181
|
+
}
|
|
15182
|
+
|
|
15183
|
+
[data-theme="dark"] .tour-bar__brand {
|
|
15184
|
+
color: #e3b341;
|
|
15185
|
+
}
|
|
15186
|
+
|
|
15187
|
+
.tour-bar__brand svg {
|
|
15188
|
+
width: 16px;
|
|
15189
|
+
height: 16px;
|
|
15190
|
+
}
|
|
15191
|
+
|
|
15192
|
+
.tour-bar__progress {
|
|
15193
|
+
flex: 1;
|
|
15194
|
+
text-align: center;
|
|
15195
|
+
font-variant-numeric: tabular-nums;
|
|
15196
|
+
font-weight: 500;
|
|
15197
|
+
color: #6b4500;
|
|
15198
|
+
}
|
|
15199
|
+
|
|
15200
|
+
[data-theme="dark"] .tour-bar__progress {
|
|
15201
|
+
color: #e3b341;
|
|
15202
|
+
}
|
|
15203
|
+
|
|
15204
|
+
.tour-bar__nav {
|
|
15205
|
+
display: inline-flex;
|
|
15206
|
+
align-items: center;
|
|
15207
|
+
gap: 6px;
|
|
15208
|
+
}
|
|
15209
|
+
|
|
15210
|
+
.tour-bar__nav button {
|
|
15211
|
+
display: inline-flex;
|
|
15212
|
+
align-items: center;
|
|
15213
|
+
gap: 4px;
|
|
15214
|
+
padding: 4px 10px;
|
|
15215
|
+
font-size: 12px;
|
|
15216
|
+
font-weight: 500;
|
|
15217
|
+
color: #7d5700;
|
|
15218
|
+
background: rgba(255, 255, 255, 0.65);
|
|
15219
|
+
border: 1px solid rgba(212, 167, 44, 0.55);
|
|
15220
|
+
border-radius: var(--radius-sm, 4px);
|
|
15221
|
+
cursor: pointer;
|
|
15222
|
+
transition: background-color var(--transition-fast, 0.1s ease),
|
|
15223
|
+
border-color var(--transition-fast, 0.1s ease);
|
|
15224
|
+
}
|
|
15225
|
+
|
|
15226
|
+
.tour-bar__nav button:hover:not(:disabled) {
|
|
15227
|
+
background: rgba(255, 255, 255, 0.95);
|
|
15228
|
+
border-color: #d4a72c;
|
|
15229
|
+
}
|
|
15230
|
+
|
|
15231
|
+
.tour-bar__nav button:disabled {
|
|
15232
|
+
opacity: 0.45;
|
|
15233
|
+
cursor: not-allowed;
|
|
15234
|
+
}
|
|
15235
|
+
|
|
15236
|
+
.tour-bar__nav button svg {
|
|
15237
|
+
width: 12px;
|
|
15238
|
+
height: 12px;
|
|
15239
|
+
}
|
|
15240
|
+
|
|
15241
|
+
[data-theme="dark"] .tour-bar__nav button {
|
|
15242
|
+
color: #e3b341;
|
|
15243
|
+
background: rgba(13, 17, 23, 0.55);
|
|
15244
|
+
border-color: rgba(187, 128, 9, 0.55);
|
|
15245
|
+
}
|
|
15246
|
+
|
|
15247
|
+
[data-theme="dark"] .tour-bar__nav button:hover:not(:disabled) {
|
|
15248
|
+
background: rgba(13, 17, 23, 0.85);
|
|
15249
|
+
border-color: #e3b341;
|
|
15250
|
+
}
|
|
15251
|
+
|
|
15252
|
+
/* ---------- Toolbar tour-toggle button states ---------- */
|
|
15253
|
+
.btn-tour-toggle {
|
|
15254
|
+
position: relative;
|
|
15255
|
+
border: 1px solid var(--color-border-primary, #d1d9e0);
|
|
15256
|
+
color: var(--color-text-secondary, #57606a);
|
|
15257
|
+
transition: color var(--transition-fast, 0.1s ease),
|
|
15258
|
+
border-color var(--transition-fast, 0.1s ease),
|
|
15259
|
+
background-color var(--transition-fast, 0.1s ease);
|
|
15260
|
+
}
|
|
15261
|
+
|
|
15262
|
+
/* Tour active — pale-yellow highlight. */
|
|
15263
|
+
.btn-tour-toggle.active {
|
|
15264
|
+
color: #7d5700;
|
|
15265
|
+
border-color: #d4a72c;
|
|
15266
|
+
background-color: rgba(212, 167, 44, 0.12);
|
|
15267
|
+
}
|
|
15268
|
+
|
|
15269
|
+
[data-theme="dark"] .btn-tour-toggle.active {
|
|
15270
|
+
color: #e3b341;
|
|
15271
|
+
border-color: #bb8009;
|
|
15272
|
+
background-color: rgba(187, 128, 9, 0.20);
|
|
15273
|
+
}
|
|
15274
|
+
|
|
15275
|
+
/* Generating pulse while the background tour job is running. */
|
|
15276
|
+
.btn-tour-toggle.generating {
|
|
15277
|
+
animation: tourPulse 1.6s ease-in-out infinite;
|
|
15278
|
+
}
|
|
15279
|
+
|
|
15280
|
+
/* Subtle indicator that a newer tour is stashed and waiting for restart.
|
|
15281
|
+
Renders as a small dot in the corner of the toolbar button. */
|
|
15282
|
+
.btn-tour-toggle.tour-updated-pending::after {
|
|
15283
|
+
content: '';
|
|
15284
|
+
position: absolute;
|
|
15285
|
+
top: 4px;
|
|
15286
|
+
right: 4px;
|
|
15287
|
+
width: 6px;
|
|
15288
|
+
height: 6px;
|
|
15289
|
+
border-radius: 50%;
|
|
15290
|
+
background: #d4a72c;
|
|
15291
|
+
box-shadow: 0 0 0 2px var(--color-bg-primary, #ffffff);
|
|
15292
|
+
}
|
|
15293
|
+
|
|
15294
|
+
[data-theme="dark"] .btn-tour-toggle.tour-updated-pending::after {
|
|
15295
|
+
background: #e3b341;
|
|
15296
|
+
box-shadow: 0 0 0 2px var(--color-bg-primary, #0d1117);
|
|
15297
|
+
}
|
|
15298
|
+
|
|
15299
|
+
@keyframes tourPulse {
|
|
15300
|
+
0%, 100% {
|
|
15301
|
+
box-shadow: 0 0 0 0 rgba(212, 167, 44, 0);
|
|
15302
|
+
}
|
|
15303
|
+
50% {
|
|
15304
|
+
box-shadow: 0 0 0 4px rgba(212, 167, 44, 0.28);
|
|
15305
|
+
}
|
|
15306
|
+
}
|
|
15307
|
+
|
|
15308
|
+
[data-theme="dark"] .btn-tour-toggle.generating {
|
|
15309
|
+
animation-name: tourPulseDark;
|
|
15310
|
+
}
|
|
15311
|
+
|
|
15312
|
+
@keyframes tourPulseDark {
|
|
15313
|
+
0%, 100% {
|
|
15314
|
+
box-shadow: 0 0 0 0 rgba(187, 128, 9, 0);
|
|
15315
|
+
}
|
|
15316
|
+
50% {
|
|
15317
|
+
box-shadow: 0 0 0 4px rgba(187, 128, 9, 0.40);
|
|
15318
|
+
}
|
|
15319
|
+
}
|