@industry-theme/file-city-panel 0.5.63 → 0.5.65

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.
@@ -257701,14 +257701,30 @@ const TrailLeaderLine = React.forwardRef(function TrailLeaderLine2({ containerRe
257701
257701
  const sx = canvasLeft + (v.x * 0.5 + 0.5) * size.width;
257702
257702
  const sy = canvasTop + (v.y * -0.5 + 0.5) * size.height;
257703
257703
  const HEADER_BAND_OFFSET = 22;
257704
- const nodeCenterX = nodeRect.left + nodeRect.width / 2 - containerRect.left;
257705
- const aimRight = nodeCenterX < sx;
257706
- const bx = aimRight ? nodeRect.right - containerRect.left : nodeRect.left - containerRect.left;
257704
+ const bx = nodeRect.left - containerRect.left;
257707
257705
  const by = nodeRect.top - containerRect.top + HEADER_BAND_OFFSET;
257708
- const dxRaw = (bx - sx) * 0.5;
257709
- const dxAbs = Math.max(80, Math.abs(dxRaw));
257710
- const dx = dxRaw < 0 ? -dxAbs : dxAbs;
257711
- const d = `M ${sx} ${sy} C ${sx + dx} ${sy}, ${bx - dx} ${by}, ${bx} ${by}`;
257706
+ const ELBOW_GAP = 60;
257707
+ const CORNER_RADIUS = 14;
257708
+ const elbowX = Math.max(
257709
+ canvasLeft + 8,
257710
+ Math.min(sx, bx) - ELBOW_GAP
257711
+ );
257712
+ const goingUp = by < sy;
257713
+ const r2 = Math.min(
257714
+ CORNER_RADIUS,
257715
+ Math.abs(sy - by) / 2,
257716
+ Math.abs(sx - elbowX) / 2,
257717
+ Math.abs(bx - elbowX) / 2
257718
+ );
257719
+ const cornerSign = goingUp ? -1 : 1;
257720
+ const d = [
257721
+ `M ${sx} ${sy}`,
257722
+ `L ${elbowX + r2} ${sy}`,
257723
+ `Q ${elbowX} ${sy} ${elbowX} ${sy + cornerSign * r2}`,
257724
+ `L ${elbowX} ${by - cornerSign * r2}`,
257725
+ `Q ${elbowX} ${by} ${elbowX + r2} ${by}`,
257726
+ `L ${bx} ${by}`
257727
+ ].join(" ");
257712
257728
  const pathEl = pathRef.current;
257713
257729
  const buildingEl = buildingMarkerRef.current;
257714
257730
  const nodeMarkerEl = nodeMarkerRef.current;
@@ -257790,6 +257806,139 @@ const TrailLeaderLine = React.forwardRef(function TrailLeaderLine2({ containerRe
257790
257806
  )
257791
257807
  ] });
257792
257808
  });
257809
+ const TrailFilePath = React.forwardRef(function TrailFilePath2({ containerRef, buildings, cityCenter }, ref) {
257810
+ const { theme: theme2 } = useTheme();
257811
+ const color2 = theme2.colors.accent ?? "#22d3ee";
257812
+ const groupRef = React.useRef(null);
257813
+ const projectScratch = React.useRef(new THREE.Vector3());
257814
+ const buildingsRef = React.useRef(buildings);
257815
+ const cityCenterRef = React.useRef(cityCenter);
257816
+ React.useEffect(() => {
257817
+ buildingsRef.current = buildings;
257818
+ }, [buildings]);
257819
+ React.useEffect(() => {
257820
+ cityCenterRef.current = cityCenter;
257821
+ }, [cityCenter]);
257822
+ const hideAll = React.useCallback(() => {
257823
+ const g2 = groupRef.current;
257824
+ if (!g2) return;
257825
+ g2.setAttribute("opacity", "0");
257826
+ }, []);
257827
+ React.useEffect(() => {
257828
+ if (buildings.length < 2) hideAll();
257829
+ }, [buildings, hideAll]);
257830
+ const onCameraFrame = React.useCallback(
257831
+ (camera, size) => {
257832
+ const targets = buildingsRef.current;
257833
+ const center = cityCenterRef.current;
257834
+ const container = containerRef.current;
257835
+ const g2 = groupRef.current;
257836
+ if (!targets || targets.length < 2 || !center || !container || !g2 || size.width === 0) {
257837
+ hideAll();
257838
+ return;
257839
+ }
257840
+ const containerRect = container.getBoundingClientRect();
257841
+ const canvasEl = container.querySelector("canvas");
257842
+ if (!canvasEl) {
257843
+ hideAll();
257844
+ return;
257845
+ }
257846
+ const canvasRect = canvasEl.getBoundingClientRect();
257847
+ const canvasLeft = canvasRect.left - containerRect.left;
257848
+ const canvasTop = canvasRect.top - containerRect.top;
257849
+ const points = [];
257850
+ const v = projectScratch.current;
257851
+ for (const b of targets) {
257852
+ v.set(b.position.x - center.x, 0, b.position.z - center.z).project(
257853
+ camera
257854
+ );
257855
+ const visible = v.z <= 1;
257856
+ const x2 = canvasLeft + (v.x * 0.5 + 0.5) * size.width;
257857
+ const y2 = canvasTop + (v.y * -0.5 + 0.5) * size.height;
257858
+ points.push({ x: x2, y: y2, visible });
257859
+ }
257860
+ let d = "";
257861
+ for (let i = 0; i < points.length; i++) {
257862
+ const p2 = points[i];
257863
+ if (!p2.visible) {
257864
+ d += "";
257865
+ continue;
257866
+ }
257867
+ if (i === 0 || !points[i - 1].visible) {
257868
+ d += `M ${p2.x} ${p2.y} `;
257869
+ } else {
257870
+ d += `L ${p2.x} ${p2.y} `;
257871
+ }
257872
+ }
257873
+ while (g2.children.length < 1 + targets.length * 2) {
257874
+ g2.appendChild(
257875
+ document.createElementNS("http://www.w3.org/2000/svg", "path")
257876
+ );
257877
+ g2.appendChild(
257878
+ document.createElementNS("http://www.w3.org/2000/svg", "circle")
257879
+ );
257880
+ g2.appendChild(
257881
+ document.createElementNS("http://www.w3.org/2000/svg", "text")
257882
+ );
257883
+ }
257884
+ while (g2.children.length > 1 + targets.length * 2) {
257885
+ g2.removeChild(g2.children[g2.children.length - 1]);
257886
+ }
257887
+ const pathEl = g2.children[0];
257888
+ pathEl.setAttribute("d", d.trim() || "M 0 0");
257889
+ pathEl.setAttribute("fill", "none");
257890
+ pathEl.setAttribute("stroke", color2);
257891
+ pathEl.setAttribute("stroke-width", "2");
257892
+ pathEl.setAttribute("stroke-dasharray", "6 4");
257893
+ pathEl.setAttribute("opacity", "0.75");
257894
+ for (let i = 0; i < targets.length; i++) {
257895
+ const dot = g2.children[1 + i * 2];
257896
+ const label = g2.children[1 + i * 2 + 1];
257897
+ const p2 = points[i];
257898
+ if (!p2.visible) {
257899
+ dot.setAttribute("opacity", "0");
257900
+ label.setAttribute("opacity", "0");
257901
+ continue;
257902
+ }
257903
+ dot.setAttribute("cx", String(p2.x));
257904
+ dot.setAttribute("cy", String(p2.y));
257905
+ dot.setAttribute("r", "6");
257906
+ dot.setAttribute("fill", color2);
257907
+ dot.setAttribute("stroke", "#0f1419");
257908
+ dot.setAttribute("stroke-width", "1.5");
257909
+ dot.setAttribute("opacity", "1");
257910
+ label.setAttribute("x", String(p2.x));
257911
+ label.setAttribute("y", String(p2.y - 10));
257912
+ label.setAttribute("text-anchor", "middle");
257913
+ label.setAttribute("fill", color2);
257914
+ label.setAttribute("font-size", "11");
257915
+ label.setAttribute("font-weight", "600");
257916
+ label.setAttribute("opacity", "1");
257917
+ label.textContent = String(i + 1);
257918
+ }
257919
+ g2.setAttribute("opacity", "1");
257920
+ },
257921
+ [containerRef, hideAll, color2]
257922
+ );
257923
+ React.useImperativeHandle(ref, () => ({ onCameraFrame }), [onCameraFrame]);
257924
+ return /* @__PURE__ */ jsx(
257925
+ "svg",
257926
+ {
257927
+ width: "100%",
257928
+ height: "100%",
257929
+ style: {
257930
+ position: "absolute",
257931
+ inset: 0,
257932
+ pointerEvents: "none",
257933
+ overflow: "visible",
257934
+ // Above the city canvas (auto z-index) but below the floating
257935
+ // overlays (z 1900) so the path only paints over the city.
257936
+ zIndex: 26
257937
+ },
257938
+ children: /* @__PURE__ */ jsx("g", { ref: groupRef, opacity: 0 })
257939
+ }
257940
+ );
257941
+ });
257793
257942
  const PANEL_WIDTH_PCT = 38;
257794
257943
  const FLOAT_INSET = 16;
257795
257944
  const MIN_WIDTH_PX = 360;
@@ -257970,8 +258119,8 @@ const TrailMarkdownOverlay = ({
257970
258119
  ) })
257971
258120
  }
257972
258121
  ),
257973
- files && files.length > 0 ? /* @__PURE__ */ jsx(TrailFilesList, { files }) : null,
257974
258122
  nav ? /* @__PURE__ */ jsx(TrailMarkdownOverlayFooter, { nav }) : null,
258123
+ files && files.length > 0 ? /* @__PURE__ */ jsx(TrailFilesList, { files }) : null,
257975
258124
  /* @__PURE__ */ jsx(
257976
258125
  "div",
257977
258126
  {
@@ -258533,9 +258682,8 @@ const pierreOptionsBase = {
258533
258682
  const pierreStyle = {
258534
258683
  display: "block"
258535
258684
  };
258536
- const SEQUENCE_DRAWER_HEIGHT_PCT = 35;
258685
+ const SEQUENCE_DRAWER_HEIGHT_PCT = 45;
258537
258686
  const SNIPPET_PANE_WIDTH_PX = 460;
258538
- const HEADER_HEIGHT_PX = 48;
258539
258687
  const PANEL_TRANSITION_MS = 280;
258540
258688
  const THREE_D_TOGGLE_DISABLED = true;
258541
258689
  const FileCityTrailExplorerPanel = ({ context, actions }) => {
@@ -258617,6 +258765,7 @@ const FileCityTrailSequenceLayout = ({
258617
258765
  return;
258618
258766
  }, [hasLineCounts]);
258619
258767
  const effectiveShow3D = false;
258768
+ const [viewMode, setViewMode] = React.useState("city");
258620
258769
  const [showSequenceDrawer, setShowSequenceDrawer] = React.useState(false);
258621
258770
  const [drawerHeightOverridePct, setDrawerHeightOverridePct] = React.useState(null);
258622
258771
  const [isResizingDrawer, setIsResizingDrawer] = React.useState(false);
@@ -258648,6 +258797,10 @@ const FileCityTrailSequenceLayout = ({
258648
258797
  const first = markersForThisRepo[0];
258649
258798
  if (first) onSelectMarker(first.id);
258650
258799
  }, [markersForThisRepo, onSelectMarker]);
258800
+ const [snippetExpanded, setSnippetExpanded] = React.useState(false);
258801
+ React.useEffect(() => {
258802
+ setSnippetExpanded(false);
258803
+ }, [selectedMarkerId]);
258651
258804
  const handleStepPrev = React.useCallback(() => {
258652
258805
  if (stepperIndex < 0) return;
258653
258806
  if (stepperIndex === 0) {
@@ -258756,6 +258909,7 @@ const FileCityTrailSequenceLayout = ({
258756
258909
  const containerRef = React.useRef(null);
258757
258910
  const snippetPaneRef = React.useRef(null);
258758
258911
  const leaderLineRef = React.useRef(null);
258912
+ const filePathRef = React.useRef(null);
258759
258913
  const [markdownOverlayWidth, setMarkdownOverlayWidth] = React.useState(null);
258760
258914
  const [snippetPaneEl, setSnippetPaneEl] = React.useState(null);
258761
258915
  const snippetPaneCallbackRef = React.useCallback(
@@ -258779,10 +258933,22 @@ const FileCityTrailSequenceLayout = ({
258779
258933
  z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2
258780
258934
  };
258781
258935
  }, [cityData]);
258936
+ const trailPathBuildings = React.useMemo(() => {
258937
+ if (!cityData || markersForThisRepo.length === 0) return [];
258938
+ const buildingByPath = new Map(cityData.buildings.map((b) => [b.path, b]));
258939
+ const out = [];
258940
+ for (const m of markersForThisRepo) {
258941
+ if (!m.sourcePath) continue;
258942
+ const b = buildingByPath.get(m.sourcePath) ?? cityData.buildings.find((cb) => cb.path.endsWith(`/${m.sourcePath}`));
258943
+ if (b) out.push(b);
258944
+ }
258945
+ return out;
258946
+ }, [cityData, markersForThisRepo]);
258782
258947
  const onCameraFrame = React.useCallback(
258783
258948
  (camera, size) => {
258784
- var _a2;
258949
+ var _a2, _b2;
258785
258950
  (_a2 = leaderLineRef.current) == null ? void 0 : _a2.onCameraFrame(camera, size);
258951
+ (_b2 = filePathRef.current) == null ? void 0 : _b2.onCameraFrame(camera, size);
258786
258952
  },
258787
258953
  []
258788
258954
  );
@@ -258844,10 +259010,9 @@ const FileCityTrailSequenceLayout = ({
258844
259010
  if (showSnippetPane && snippetPaneWidth == null) return;
258845
259011
  const FLOAT_INSET2 = 16;
258846
259012
  const leftInset = hasOverlayMarkdown ? markdownOverlayWidth ?? 0 : 0;
258847
- const snippetW = snippetPaneWidth ?? SNIPPET_PANE_WIDTH_PX;
258848
- const rightInset = showSnippetPane ? snippetW + FLOAT_INSET2 : 0;
258849
- const topInset = FLOAT_INSET2;
258850
- const bottomInset = drawerHeightPct / 100 * containerSize.height;
259013
+ const rightInset = 0;
259014
+ const topInset = showSnippetPane ? containerSize.height * 0.5 + FLOAT_INSET2 : FLOAT_INSET2;
259015
+ const bottomInset = showSnippetPane ? 0 : drawerHeightPct / 100 * containerSize.height;
258851
259016
  const result = computeCameraFraming({
258852
259017
  canvasW: containerSize.width,
258853
259018
  canvasH: containerSize.height,
@@ -258920,10 +259085,29 @@ const FileCityTrailSequenceLayout = ({
258920
259085
  onToggleSequenceDrawer: () => {
258921
259086
  setDrawerHeightOverridePct(null);
258922
259087
  setShowSequenceDrawer((v) => !v);
258923
- }
259088
+ },
259089
+ canExit: selectedMarkerId != null || showSequenceDrawer || snippetExpanded || viewMode !== "city",
259090
+ onExitTrail: () => {
259091
+ onSelectMarker(null);
259092
+ setShowSequenceDrawer(false);
259093
+ setDrawerHeightOverridePct(null);
259094
+ setSnippetExpanded(false);
259095
+ setViewMode("city");
259096
+ },
259097
+ viewMode,
259098
+ onSetViewMode: setViewMode
258924
259099
  }
258925
259100
  ),
258926
- /* @__PURE__ */ jsxs(
259101
+ viewMode === "doc" ? /* @__PURE__ */ jsx(
259102
+ TrailDocumentView,
259103
+ {
259104
+ markers: markersForThisRepo,
259105
+ summary: trail2.summary,
259106
+ readFile,
259107
+ selectedMarkerId,
259108
+ onSelectMarker
259109
+ }
259110
+ ) : /* @__PURE__ */ jsxs(
258927
259111
  "div",
258928
259112
  {
258929
259113
  ref: containerRef,
@@ -258951,6 +259135,15 @@ const FileCityTrailSequenceLayout = ({
258951
259135
  defaultBuildingColor: trailFilesActive ? theme2.colors.textTertiary : void 0
258952
259136
  }
258953
259137
  ) : /* @__PURE__ */ jsx(CityLoadingPlaceholder, {}),
259138
+ /* @__PURE__ */ jsx(
259139
+ TrailFilePath,
259140
+ {
259141
+ ref: filePathRef,
259142
+ containerRef,
259143
+ buildings: trailPathBuildings,
259144
+ cityCenter
259145
+ }
259146
+ ),
258954
259147
  /* @__PURE__ */ jsx(
258955
259148
  TrailLeaderLine,
258956
259149
  {
@@ -258978,7 +259171,7 @@ const FileCityTrailSequenceLayout = ({
258978
259171
  onPrev: handleStepPrev,
258979
259172
  onNext: handleStepNext
258980
259173
  } : void 0,
258981
- files: trailFiles
259174
+ files: showSequenceDrawer ? void 0 : trailFiles
258982
259175
  }
258983
259176
  ) : null,
258984
259177
  /* @__PURE__ */ jsx(
@@ -259005,7 +259198,9 @@ const FileCityTrailSequenceLayout = ({
259005
259198
  readFile,
259006
259199
  onClose: () => onSelectMarker(null),
259007
259200
  drawerHeightPct,
259008
- disableBottomTransition: isResizingDrawer
259201
+ disableBottomTransition: isResizingDrawer,
259202
+ expanded: snippetExpanded,
259203
+ onToggleExpanded: () => setSnippetExpanded((v) => !v)
259009
259204
  }
259010
259205
  ) : null
259011
259206
  ]
@@ -259167,7 +259362,9 @@ const SnippetSidePane = React.forwardRef(function SnippetSidePane2({
259167
259362
  readFile,
259168
259363
  onClose,
259169
259364
  drawerHeightPct,
259170
- disableBottomTransition
259365
+ disableBottomTransition,
259366
+ expanded,
259367
+ onToggleExpanded
259171
259368
  }, ref) {
259172
259369
  const { theme: theme2 } = useTheme();
259173
259370
  if (!marker.sourcePath) {
@@ -259179,6 +259376,8 @@ const SnippetSidePane = React.forwardRef(function SnippetSidePane2({
259179
259376
  onClose,
259180
259377
  drawerHeightPct,
259181
259378
  disableBottomTransition,
259379
+ expanded,
259380
+ onToggleExpanded,
259182
259381
  children: /* @__PURE__ */ jsx(
259183
259382
  "div",
259184
259383
  {
@@ -259202,6 +259401,8 @@ const SnippetSidePane = React.forwardRef(function SnippetSidePane2({
259202
259401
  onClose,
259203
259402
  drawerHeightPct,
259204
259403
  disableBottomTransition,
259404
+ expanded,
259405
+ onToggleExpanded,
259205
259406
  children: snippet2.kind === "slice" ? /* @__PURE__ */ jsx(
259206
259407
  TrailSnippetView,
259207
259408
  {
@@ -259236,7 +259437,15 @@ const SnippetSidePane = React.forwardRef(function SnippetSidePane2({
259236
259437
  const SNIPPET_PANE_MIN_WIDTH_PX = 320;
259237
259438
  const SNIPPET_PANE_RESIZE_HANDLE_WIDTH = 6;
259238
259439
  const SidePaneFrame = React.forwardRef(
259239
- function SidePaneFrame2({ marker, onClose, children: children2, drawerHeightPct, disableBottomTransition }, ref) {
259440
+ function SidePaneFrame2({
259441
+ marker,
259442
+ onClose,
259443
+ children: children2,
259444
+ drawerHeightPct,
259445
+ disableBottomTransition,
259446
+ expanded,
259447
+ onToggleExpanded
259448
+ }, ref) {
259240
259449
  const { theme: theme2 } = useTheme();
259241
259450
  const [widthPx, setWidthPx] = React.useState(null);
259242
259451
  const [isResizing, setIsResizing] = React.useState(false);
@@ -259307,7 +259516,7 @@ const SidePaneFrame = React.forwardRef(
259307
259516
  position: "absolute",
259308
259517
  top: 16,
259309
259518
  right: 16,
259310
- bottom: `calc(${drawerHeightPct}% + 16px)`,
259519
+ bottom: expanded ? `calc(${drawerHeightPct}% + 16px)` : "50%",
259311
259520
  width: widthPx ?? SNIPPET_PANE_WIDTH_PX,
259312
259521
  minWidth: SNIPPET_PANE_MIN_WIDTH_PX,
259313
259522
  backgroundColor: theme2.colors.background,
@@ -259398,24 +259607,45 @@ const SidePaneFrame = React.forwardRef(
259398
259607
  }
259399
259608
  ) : null
259400
259609
  ] }),
259401
- /* @__PURE__ */ jsx(
259402
- "button",
259403
- {
259404
- onClick: onClose,
259405
- style: {
259406
- background: "transparent",
259407
- color: theme2.colors.text,
259408
- border: `1px solid ${theme2.colors.muted}`,
259409
- borderRadius: theme2.radii[2],
259410
- padding: "2px 8px",
259411
- cursor: "pointer",
259412
- fontSize: theme2.fontSizes[0],
259413
- fontFamily: theme2.fonts.body,
259414
- flexShrink: 0
259415
- },
259416
- children: "Close"
259417
- }
259418
- )
259610
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 6, flexShrink: 0 }, children: [
259611
+ /* @__PURE__ */ jsx(
259612
+ "button",
259613
+ {
259614
+ onClick: onToggleExpanded,
259615
+ "aria-pressed": expanded,
259616
+ title: expanded ? "Collapse to top half (city visible below)" : "Expand to fill the right column",
259617
+ style: {
259618
+ background: "transparent",
259619
+ color: theme2.colors.text,
259620
+ border: `1px solid ${theme2.colors.muted}`,
259621
+ borderRadius: theme2.radii[2],
259622
+ padding: "2px 8px",
259623
+ cursor: "pointer",
259624
+ fontSize: theme2.fontSizes[0],
259625
+ fontFamily: theme2.fonts.body,
259626
+ lineHeight: 1.2
259627
+ },
259628
+ children: expanded ? "▾" : "▴"
259629
+ }
259630
+ ),
259631
+ /* @__PURE__ */ jsx(
259632
+ "button",
259633
+ {
259634
+ onClick: onClose,
259635
+ style: {
259636
+ background: "transparent",
259637
+ color: theme2.colors.text,
259638
+ border: `1px solid ${theme2.colors.muted}`,
259639
+ borderRadius: theme2.radii[2],
259640
+ padding: "2px 8px",
259641
+ cursor: "pointer",
259642
+ fontSize: theme2.fontSizes[0],
259643
+ fontFamily: theme2.fonts.body
259644
+ },
259645
+ children: "Close"
259646
+ }
259647
+ )
259648
+ ] })
259419
259649
  ]
259420
259650
  }
259421
259651
  ),
@@ -259425,6 +259655,251 @@ const SidePaneFrame = React.forwardRef(
259425
259655
  );
259426
259656
  }
259427
259657
  );
259658
+ const TrailDocumentView = ({
259659
+ markers,
259660
+ summary,
259661
+ readFile,
259662
+ selectedMarkerId,
259663
+ onSelectMarker
259664
+ }) => {
259665
+ const { theme: theme2 } = useTheme();
259666
+ return /* @__PURE__ */ jsx(
259667
+ "div",
259668
+ {
259669
+ style: {
259670
+ flex: 1,
259671
+ minHeight: 0,
259672
+ overflowY: "auto",
259673
+ overflowX: "hidden",
259674
+ padding: "24px 32px 64px",
259675
+ background: theme2.colors.background
259676
+ },
259677
+ children: /* @__PURE__ */ jsxs("div", { style: { maxWidth: 880, margin: "0 auto" }, children: [
259678
+ summary ? /* @__PURE__ */ jsx(
259679
+ "div",
259680
+ {
259681
+ style: {
259682
+ fontFamily: theme2.fonts.body,
259683
+ fontSize: theme2.fontSizes[1],
259684
+ lineHeight: 1.6,
259685
+ color: theme2.colors.text,
259686
+ whiteSpace: "pre-wrap",
259687
+ marginBottom: 32,
259688
+ paddingBottom: 24,
259689
+ borderBottom: `1px solid ${theme2.colors.border}`
259690
+ },
259691
+ children: summary
259692
+ }
259693
+ ) : null,
259694
+ markers.length === 0 ? /* @__PURE__ */ jsx(
259695
+ "div",
259696
+ {
259697
+ style: {
259698
+ fontFamily: theme2.fonts.body,
259699
+ fontSize: theme2.fontSizes[1],
259700
+ color: theme2.colors.textSecondary
259701
+ },
259702
+ children: "No markers in this repo."
259703
+ }
259704
+ ) : markers.map((marker, idx) => /* @__PURE__ */ jsx(
259705
+ TrailDocumentCard,
259706
+ {
259707
+ index: idx,
259708
+ total: markers.length,
259709
+ marker,
259710
+ readFile,
259711
+ isActive: marker.id === selectedMarkerId,
259712
+ onActivate: () => onSelectMarker(
259713
+ marker.id === selectedMarkerId ? null : marker.id
259714
+ )
259715
+ },
259716
+ marker.id
259717
+ ))
259718
+ ] })
259719
+ }
259720
+ );
259721
+ };
259722
+ const TrailDocumentCard = ({
259723
+ index: index2,
259724
+ total,
259725
+ marker,
259726
+ readFile,
259727
+ isActive,
259728
+ onActivate
259729
+ }) => {
259730
+ const { theme: theme2 } = useTheme();
259731
+ const fileName = marker.sourcePath ? marker.sourcePath.split("/").pop() ?? marker.sourcePath : null;
259732
+ const headingLabel = marker.label ?? `Marker ${index2 + 1}`;
259733
+ const snippet2 = marker.snippet;
259734
+ const [snippetExpanded, setSnippetExpanded] = React.useState(false);
259735
+ return /* @__PURE__ */ jsxs(
259736
+ "div",
259737
+ {
259738
+ style: {
259739
+ marginBottom: 32,
259740
+ border: `1px solid ${isActive ? theme2.colors.accent : theme2.colors.border}`,
259741
+ borderRadius: theme2.radii[3],
259742
+ background: theme2.colors.background,
259743
+ boxShadow: isActive ? `0 0 0 2px ${withAlpha(theme2.colors.accent, 0.18)}` : "none",
259744
+ overflow: "hidden"
259745
+ },
259746
+ children: [
259747
+ /* @__PURE__ */ jsxs(
259748
+ "button",
259749
+ {
259750
+ type: "button",
259751
+ onClick: onActivate,
259752
+ title: isActive ? "Clear selection" : "Select this marker on the city view",
259753
+ style: {
259754
+ display: "flex",
259755
+ flexDirection: "column",
259756
+ alignItems: "flex-start",
259757
+ gap: 4,
259758
+ width: "100%",
259759
+ textAlign: "left",
259760
+ padding: "16px 20px",
259761
+ background: "transparent",
259762
+ border: "none",
259763
+ borderBottom: `1px solid ${theme2.colors.border}`,
259764
+ cursor: "pointer",
259765
+ color: theme2.colors.text
259766
+ },
259767
+ children: [
259768
+ /* @__PURE__ */ jsxs(
259769
+ "div",
259770
+ {
259771
+ style: {
259772
+ fontFamily: theme2.fonts.body,
259773
+ fontSize: theme2.fontSizes[0],
259774
+ color: theme2.colors.textSecondary,
259775
+ letterSpacing: "0.04em",
259776
+ textTransform: "uppercase"
259777
+ },
259778
+ children: [
259779
+ index2 + 1,
259780
+ " / ",
259781
+ total
259782
+ ]
259783
+ }
259784
+ ),
259785
+ /* @__PURE__ */ jsx(
259786
+ "div",
259787
+ {
259788
+ style: {
259789
+ fontFamily: theme2.fonts.heading,
259790
+ fontSize: theme2.fontSizes[2],
259791
+ fontWeight: theme2.fontWeights.semibold,
259792
+ color: theme2.colors.text
259793
+ },
259794
+ children: headingLabel
259795
+ }
259796
+ ),
259797
+ marker.sourcePath ? /* @__PURE__ */ jsx(
259798
+ "div",
259799
+ {
259800
+ style: {
259801
+ fontFamily: theme2.fonts.monospace,
259802
+ fontSize: theme2.fontSizes[0],
259803
+ color: theme2.colors.textSecondary
259804
+ },
259805
+ children: marker.sourcePath
259806
+ }
259807
+ ) : null
259808
+ ]
259809
+ }
259810
+ ),
259811
+ marker.description ? /* @__PURE__ */ jsx(
259812
+ "div",
259813
+ {
259814
+ style: {
259815
+ fontFamily: theme2.fonts.body,
259816
+ fontSize: theme2.fontSizes[1],
259817
+ lineHeight: 1.6,
259818
+ color: theme2.colors.text,
259819
+ whiteSpace: "pre-wrap",
259820
+ padding: "16px 20px",
259821
+ borderBottom: snippet2 ? `1px solid ${theme2.colors.border}` : "none"
259822
+ },
259823
+ children: marker.description
259824
+ }
259825
+ ) : null,
259826
+ snippet2 && marker.sourcePath ? /* @__PURE__ */ jsxs("div", { style: { background: theme2.colors.background }, children: [
259827
+ /* @__PURE__ */ jsxs(
259828
+ "button",
259829
+ {
259830
+ type: "button",
259831
+ onClick: () => setSnippetExpanded((v) => !v),
259832
+ "aria-expanded": snippetExpanded,
259833
+ title: snippetExpanded ? "Hide snippet" : "Show snippet",
259834
+ style: {
259835
+ display: "flex",
259836
+ alignItems: "center",
259837
+ gap: 8,
259838
+ width: "100%",
259839
+ textAlign: "left",
259840
+ padding: "10px 20px",
259841
+ background: "transparent",
259842
+ border: "none",
259843
+ borderBottom: snippetExpanded ? `1px solid ${theme2.colors.border}` : "none",
259844
+ cursor: "pointer",
259845
+ fontFamily: theme2.fonts.body,
259846
+ fontSize: theme2.fontSizes[0],
259847
+ fontWeight: theme2.fontWeights.medium,
259848
+ color: theme2.colors.textSecondary,
259849
+ letterSpacing: "0.04em",
259850
+ textTransform: "uppercase"
259851
+ },
259852
+ children: [
259853
+ /* @__PURE__ */ jsx(
259854
+ "span",
259855
+ {
259856
+ "aria-hidden": true,
259857
+ style: {
259858
+ display: "inline-block",
259859
+ width: 10,
259860
+ transform: snippetExpanded ? "rotate(90deg)" : "rotate(0deg)",
259861
+ transition: "transform 120ms ease"
259862
+ },
259863
+ children: "▶"
259864
+ }
259865
+ ),
259866
+ snippetExpanded ? "Hide snippet" : "Show snippet"
259867
+ ]
259868
+ }
259869
+ ),
259870
+ snippetExpanded ? /* @__PURE__ */ jsx("div", { style: { padding: 12 }, children: snippet2.kind === "slice" ? /* @__PURE__ */ jsx(
259871
+ TrailSnippetView,
259872
+ {
259873
+ filePath: marker.sourcePath,
259874
+ fileName: fileName ?? marker.sourcePath,
259875
+ startLine: snippet2.startLine,
259876
+ endLine: snippet2.endLine,
259877
+ focusLine: snippet2.focusLine,
259878
+ contextLines: snippet2.contextLines,
259879
+ readFile,
259880
+ background: theme2.colors.background
259881
+ }
259882
+ ) : /* @__PURE__ */ jsx(
259883
+ TrailDiffSnippetView,
259884
+ {
259885
+ filePath: marker.sourcePath,
259886
+ fileName: fileName ?? marker.sourcePath,
259887
+ oldContents: snippet2.oldContents,
259888
+ newContents: snippet2.newContents,
259889
+ readFile,
259890
+ startLine: snippet2.startLine,
259891
+ endLine: snippet2.endLine,
259892
+ focusLine: snippet2.focusLine,
259893
+ contextLines: snippet2.contextLines,
259894
+ diffStyle: snippet2.diffStyle,
259895
+ background: theme2.colors.background
259896
+ }
259897
+ ) }) : null
259898
+ ] }) : null
259899
+ ]
259900
+ }
259901
+ );
259902
+ };
259428
259903
  const TrailHeader = ({
259429
259904
  trail: trail2,
259430
259905
  markerCount,
@@ -259432,7 +259907,11 @@ const TrailHeader = ({
259432
259907
  onToggle3D,
259433
259908
  hideToggle = false,
259434
259909
  showSequenceDrawer,
259435
- onToggleSequenceDrawer
259910
+ onToggleSequenceDrawer,
259911
+ canExit,
259912
+ onExitTrail,
259913
+ viewMode,
259914
+ onSetViewMode
259436
259915
  }) => {
259437
259916
  const { theme: theme2 } = useTheme();
259438
259917
  const toggleButtonStyle = (active) => ({
@@ -259448,11 +259927,28 @@ const TrailHeader = ({
259448
259927
  cursor: "pointer",
259449
259928
  lineHeight: 1.2
259450
259929
  });
259930
+ const segmentStyle = (active, side) => ({
259931
+ flexShrink: 0,
259932
+ fontFamily: theme2.fonts.body,
259933
+ fontSize: theme2.fontSizes[0],
259934
+ fontWeight: theme2.fontWeights.medium,
259935
+ color: active ? theme2.colors.background : theme2.colors.text,
259936
+ background: active ? theme2.colors.accent : "transparent",
259937
+ border: `1px solid ${active ? theme2.colors.accent : theme2.colors.muted}`,
259938
+ borderTopLeftRadius: side === "left" ? theme2.radii[3] : 0,
259939
+ borderBottomLeftRadius: side === "left" ? theme2.radii[3] : 0,
259940
+ borderTopRightRadius: side === "right" ? theme2.radii[3] : 0,
259941
+ borderBottomRightRadius: side === "right" ? theme2.radii[3] : 0,
259942
+ marginLeft: side === "right" ? -1 : 0,
259943
+ padding: "4px 10px",
259944
+ cursor: "pointer",
259945
+ lineHeight: 1.2
259946
+ });
259947
+ const isDoc = viewMode === "doc";
259451
259948
  return /* @__PURE__ */ jsxs(
259452
259949
  "div",
259453
259950
  {
259454
259951
  style: {
259455
- height: HEADER_HEIGHT_PX,
259456
259952
  padding: "8px 16px",
259457
259953
  borderBottom: `1px solid ${theme2.colors.border}`,
259458
259954
  display: "flex",
@@ -259506,7 +260002,7 @@ const TrailHeader = ({
259506
260002
  }
259507
260003
  ),
259508
260004
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
259509
- /* @__PURE__ */ jsx(
260005
+ isDoc ? null : /* @__PURE__ */ jsx(
259510
260006
  "button",
259511
260007
  {
259512
260008
  type: "button",
@@ -259517,7 +260013,31 @@ const TrailHeader = ({
259517
260013
  children: "Diagram"
259518
260014
  }
259519
260015
  ),
259520
- hideToggle ? null : /* @__PURE__ */ jsx(
260016
+ /* @__PURE__ */ jsx(
260017
+ "button",
260018
+ {
260019
+ type: "button",
260020
+ onClick: onExitTrail,
260021
+ disabled: !canExit,
260022
+ title: canExit ? "Return to the trail summary" : "Already at the trail summary",
260023
+ style: {
260024
+ flexShrink: 0,
260025
+ fontFamily: theme2.fonts.body,
260026
+ fontSize: theme2.fontSizes[0],
260027
+ fontWeight: theme2.fontWeights.medium,
260028
+ color: canExit ? theme2.colors.text : theme2.colors.textTertiary,
260029
+ background: "transparent",
260030
+ border: `1px solid ${theme2.colors.muted}`,
260031
+ borderRadius: theme2.radii[3],
260032
+ padding: "4px 10px",
260033
+ cursor: canExit ? "pointer" : "not-allowed",
260034
+ opacity: canExit ? 1 : 0.5,
260035
+ lineHeight: 1.2
260036
+ },
260037
+ children: "Exit"
260038
+ }
260039
+ ),
260040
+ hideToggle || isDoc ? null : /* @__PURE__ */ jsx(
259521
260041
  "button",
259522
260042
  {
259523
260043
  type: "button",
@@ -259527,7 +260047,31 @@ const TrailHeader = ({
259527
260047
  style: toggleButtonStyle(show3D),
259528
260048
  children: "3D"
259529
260049
  }
259530
- )
260050
+ ),
260051
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", marginLeft: 6 }, children: [
260052
+ /* @__PURE__ */ jsx(
260053
+ "button",
260054
+ {
260055
+ type: "button",
260056
+ onClick: () => onSetViewMode("city"),
260057
+ "aria-pressed": !isDoc,
260058
+ title: "City view — 3D buildings + sequence drawer",
260059
+ style: segmentStyle(!isDoc, "left"),
260060
+ children: "City"
260061
+ }
260062
+ ),
260063
+ /* @__PURE__ */ jsx(
260064
+ "button",
260065
+ {
260066
+ type: "button",
260067
+ onClick: () => onSetViewMode("doc"),
260068
+ "aria-pressed": isDoc,
260069
+ title: "Document view — scroll all markers as one page",
260070
+ style: segmentStyle(isDoc, "right"),
260071
+ children: "Doc"
260072
+ }
260073
+ )
260074
+ ] })
259531
260075
  ] })
259532
260076
  ]
259533
260077
  }