@industry-theme/file-city-panel 0.5.69 → 0.5.71

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.
@@ -257703,7 +257703,7 @@ const TrailLeaderLine = React.forwardRef(function TrailLeaderLine2({ containerRe
257703
257703
  const HEADER_BAND_OFFSET = 22;
257704
257704
  const bx = nodeRect.left - containerRect.left;
257705
257705
  const by = nodeRect.top - containerRect.top + HEADER_BAND_OFFSET;
257706
- const ELBOW_GAP = 60;
257706
+ const ELBOW_GAP = 30;
257707
257707
  const CORNER_RADIUS = 14;
257708
257708
  const elbowX = Math.max(
257709
257709
  canvasLeft + 8,
@@ -257806,19 +257806,61 @@ const TrailLeaderLine = React.forwardRef(function TrailLeaderLine2({ containerRe
257806
257806
  )
257807
257807
  ] });
257808
257808
  });
257809
- const TrailFilePath = React.forwardRef(function TrailFilePath2({ containerRef, buildings, cityCenter }, ref) {
257809
+ const CALLOUT_FADE_IN_MS = 350;
257810
+ const fileBasename = (path2) => {
257811
+ const i = path2.lastIndexOf("/");
257812
+ return i >= 0 ? path2.slice(i + 1) : path2;
257813
+ };
257814
+ const fileDirname = (path2) => {
257815
+ const i = path2.lastIndexOf("/");
257816
+ return i > 0 ? path2.slice(0, i) : "";
257817
+ };
257818
+ const TrailFilePath = React.forwardRef(function TrailFilePath2({
257819
+ containerRef,
257820
+ buildings,
257821
+ markerTitles,
257822
+ cityCenter,
257823
+ animate: animate2 = true,
257824
+ currentStepIndex = null,
257825
+ stepMs = 1100,
257826
+ dwellMs = 1400,
257827
+ endHoldMs = 2200
257828
+ }, ref) {
257810
257829
  const { theme: theme2 } = useTheme();
257811
257830
  const color2 = theme2.colors.accent ?? "#22d3ee";
257831
+ const fontFamily = theme2.fonts.body;
257832
+ const calloutFontSize = String(theme2.fontSizes[1] ?? 14);
257833
+ const calloutFontWeight = String(theme2.fontWeights.semibold ?? 600);
257834
+ const calloutBodyFontWeight = String(theme2.fontWeights.body ?? 400);
257835
+ const calloutLineDyPx = Math.round((theme2.fontSizes[1] ?? 14) * 1.45);
257836
+ const calloutLineDy = String(calloutLineDyPx);
257837
+ const surfaceColor = theme2.colors.backgroundSecondary ?? "#0f1419";
257838
+ const filenameColor = theme2.colors.text ?? "#f8fafc";
257839
+ const dirColor = theme2.colors.textMuted ?? "#94a3b8";
257812
257840
  const groupRef = React.useRef(null);
257841
+ const calloutTextRef = React.useRef(null);
257842
+ const calloutBgRef = React.useRef(null);
257843
+ const leadDotRef = React.useRef(null);
257813
257844
  const projectScratch = React.useRef(new THREE.Vector3());
257845
+ const animStartTsRef = React.useRef(
257846
+ typeof performance !== "undefined" ? performance.now() : 0
257847
+ );
257814
257848
  const buildingsRef = React.useRef(buildings);
257849
+ const markerTitlesRef = React.useRef(markerTitles);
257815
257850
  const cityCenterRef = React.useRef(cityCenter);
257851
+ const currentStepIndexRef = React.useRef(currentStepIndex);
257816
257852
  React.useEffect(() => {
257817
257853
  buildingsRef.current = buildings;
257818
257854
  }, [buildings]);
257855
+ React.useEffect(() => {
257856
+ markerTitlesRef.current = markerTitles;
257857
+ }, [markerTitles]);
257819
257858
  React.useEffect(() => {
257820
257859
  cityCenterRef.current = cityCenter;
257821
257860
  }, [cityCenter]);
257861
+ React.useEffect(() => {
257862
+ currentStepIndexRef.current = currentStepIndex;
257863
+ }, [currentStepIndex]);
257822
257864
  const hideAll = React.useCallback(() => {
257823
257865
  const g2 = groupRef.current;
257824
257866
  if (!g2) return;
@@ -257827,8 +257869,12 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({ containerRef, b
257827
257869
  React.useEffect(() => {
257828
257870
  if (buildings.length < 2) hideAll();
257829
257871
  }, [buildings, hideAll]);
257872
+ React.useEffect(() => {
257873
+ animStartTsRef.current = typeof performance !== "undefined" ? performance.now() : 0;
257874
+ }, [animate2, buildings, dwellMs, stepMs, endHoldMs]);
257830
257875
  const onCameraFrame = React.useCallback(
257831
257876
  (camera, size) => {
257877
+ var _a;
257832
257878
  const targets = buildingsRef.current;
257833
257879
  const center = cityCenterRef.current;
257834
257880
  const container = containerRef.current;
@@ -257857,31 +257903,80 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({ containerRef, b
257857
257903
  const y2 = canvasTop + (v.y * -0.5 + 0.5) * size.height;
257858
257904
  points.push({ x: x2, y: y2, visible });
257859
257905
  }
257906
+ const N = points.length;
257907
+ const slotMs = dwellMs + stepMs;
257908
+ const period = N >= 2 ? (N - 1) * slotMs + dwellMs + endHoldMs : 0;
257909
+ const rawFrozen = currentStepIndexRef.current;
257910
+ const frozenIndex = rawFrozen != null && rawFrozen >= 0 && rawFrozen < N ? rawFrozen : null;
257911
+ let revealedCount = animate2 ? 0 : N;
257912
+ let leadingTip = null;
257913
+ let traverseFrac = null;
257914
+ let dwellElapsed = 0;
257915
+ if (frozenIndex != null) {
257916
+ revealedCount = frozenIndex + 1;
257917
+ dwellElapsed = CALLOUT_FADE_IN_MS;
257918
+ } else if (animate2 && period > 0) {
257919
+ const elapsed = ((typeof performance !== "undefined" ? performance.now() : Date.now()) - animStartTsRef.current) % period;
257920
+ const traversalsEnd = (N - 1) * slotMs;
257921
+ if (elapsed >= traversalsEnd) {
257922
+ revealedCount = N;
257923
+ dwellElapsed = elapsed - traversalsEnd;
257924
+ } else {
257925
+ const slot = Math.floor(elapsed / slotMs);
257926
+ const tInSlot = elapsed - slot * slotMs;
257927
+ revealedCount = slot + 1;
257928
+ if (tInSlot >= dwellMs) {
257929
+ const t = tInSlot - dwellMs;
257930
+ const frac2 = stepMs > 0 ? t / stepMs : 1;
257931
+ traverseFrac = frac2;
257932
+ const eased = frac2 * frac2 * (3 - 2 * frac2);
257933
+ const a = points[slot];
257934
+ const b = points[slot + 1];
257935
+ leadingTip = {
257936
+ x: a.x + (b.x - a.x) * eased,
257937
+ y: a.y + (b.y - a.y) * eased,
257938
+ visible: a.visible && b.visible
257939
+ };
257940
+ } else {
257941
+ dwellElapsed = tInSlot;
257942
+ }
257943
+ }
257944
+ }
257945
+ const lastIndex = revealedCount - 1;
257860
257946
  let d = "";
257861
- for (let i = 0; i < points.length; i++) {
257947
+ let started = false;
257948
+ for (let i = 0; i <= lastIndex; i++) {
257862
257949
  const p2 = points[i];
257863
257950
  if (!p2.visible) {
257864
- d += "";
257951
+ started = false;
257865
257952
  continue;
257866
257953
  }
257867
- if (i === 0 || !points[i - 1].visible) {
257954
+ if (!started) {
257868
257955
  d += `M ${p2.x} ${p2.y} `;
257956
+ started = true;
257869
257957
  } else {
257870
257958
  d += `L ${p2.x} ${p2.y} `;
257871
257959
  }
257872
257960
  }
257873
- while (g2.children.length < 1 + targets.length * 2) {
257961
+ if (leadingTip && leadingTip.visible && started) {
257962
+ d += `L ${leadingTip.x} ${leadingTip.y} `;
257963
+ }
257964
+ const needed = 1 + targets.length * 2;
257965
+ if (g2.children.length === 0) {
257874
257966
  g2.appendChild(
257875
257967
  document.createElementNS("http://www.w3.org/2000/svg", "path")
257876
257968
  );
257969
+ }
257970
+ while (g2.children.length < needed) {
257971
+ const isCircleSlot = g2.children.length % 2 === 1;
257877
257972
  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")
257973
+ document.createElementNS(
257974
+ "http://www.w3.org/2000/svg",
257975
+ isCircleSlot ? "circle" : "text"
257976
+ )
257882
257977
  );
257883
257978
  }
257884
- while (g2.children.length > 1 + targets.length * 2) {
257979
+ while (g2.children.length > needed) {
257885
257980
  g2.removeChild(g2.children[g2.children.length - 1]);
257886
257981
  }
257887
257982
  const pathEl = g2.children[0];
@@ -257895,33 +257990,156 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({ containerRef, b
257895
257990
  const dot = g2.children[1 + i * 2];
257896
257991
  const label = g2.children[1 + i * 2 + 1];
257897
257992
  const p2 = points[i];
257898
- if (!p2.visible) {
257993
+ const revealed = !animate2 || i <= lastIndex;
257994
+ const isCurrent = animate2 && i === lastIndex;
257995
+ if (!p2.visible || !revealed) {
257899
257996
  dot.setAttribute("opacity", "0");
257900
257997
  label.setAttribute("opacity", "0");
257901
257998
  continue;
257902
257999
  }
257903
258000
  dot.setAttribute("cx", String(p2.x));
257904
258001
  dot.setAttribute("cy", String(p2.y));
257905
- dot.setAttribute("r", "6");
258002
+ dot.setAttribute("r", isCurrent ? "8" : "6");
257906
258003
  dot.setAttribute("fill", color2);
257907
- dot.setAttribute("stroke", "#0f1419");
258004
+ dot.setAttribute("stroke", surfaceColor);
257908
258005
  dot.setAttribute("stroke-width", "1.5");
257909
258006
  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);
258007
+ if (animate2) {
258008
+ label.setAttribute("opacity", "0");
258009
+ } else {
258010
+ label.setAttribute("x", String(p2.x));
258011
+ label.setAttribute("y", String(p2.y - 10));
258012
+ label.setAttribute("text-anchor", "middle");
258013
+ label.setAttribute("fill", color2);
258014
+ label.setAttribute("font-size", "11");
258015
+ label.setAttribute("font-weight", "600");
258016
+ label.setAttribute("opacity", "1");
258017
+ label.textContent = String(i + 1);
258018
+ }
258019
+ }
258020
+ const leadDot = leadDotRef.current;
258021
+ if (leadDot) {
258022
+ if (leadingTip && leadingTip.visible) {
258023
+ leadDot.setAttribute("cx", String(leadingTip.x));
258024
+ leadDot.setAttribute("cy", String(leadingTip.y));
258025
+ leadDot.setAttribute("r", "4");
258026
+ leadDot.setAttribute("fill", color2);
258027
+ leadDot.setAttribute("opacity", "0.85");
258028
+ } else {
258029
+ leadDot.setAttribute("opacity", "0");
258030
+ }
257918
258031
  }
258032
+ const SVG_NS = "http://www.w3.org/2000/svg";
258033
+ const paintCallout = (textEl, bgEl, point2, path2, title, opacity) => {
258034
+ if (!textEl || !bgEl) return;
258035
+ if (!point2 || !point2.visible || opacity <= 1e-3 || !path2) {
258036
+ textEl.setAttribute("opacity", "0");
258037
+ bgEl.setAttribute("opacity", "0");
258038
+ return;
258039
+ }
258040
+ while (textEl.children.length < 3) {
258041
+ textEl.appendChild(document.createElementNS(SVG_NS, "tspan"));
258042
+ }
258043
+ while (textEl.children.length > 3) {
258044
+ textEl.removeChild(textEl.lastChild);
258045
+ }
258046
+ const titleTspan = textEl.children[0];
258047
+ const nameTspan = textEl.children[1];
258048
+ const pathTspan = textEl.children[2];
258049
+ const name2 = fileBasename(path2);
258050
+ const dir = fileDirname(path2);
258051
+ const trimmedTitle = (title == null ? void 0 : title.trim()) ?? "";
258052
+ const lineCount = (trimmedTitle ? 1 : 0) + 1 + (dir ? 1 : 0);
258053
+ const BOTTOM_LINE_OFFSET = 24;
258054
+ const topLineY = point2.y - BOTTOM_LINE_OFFSET - (lineCount - 1) * calloutLineDyPx;
258055
+ textEl.setAttribute("text-anchor", "middle");
258056
+ textEl.setAttribute("font-family", fontFamily);
258057
+ textEl.setAttribute("opacity", String(opacity));
258058
+ titleTspan.textContent = trimmedTitle;
258059
+ titleTspan.setAttribute("x", String(point2.x));
258060
+ titleTspan.setAttribute("y", String(topLineY));
258061
+ titleTspan.setAttribute("fill", filenameColor);
258062
+ titleTspan.setAttribute("font-size", calloutFontSize);
258063
+ titleTspan.setAttribute("font-weight", calloutFontWeight);
258064
+ nameTspan.textContent = name2;
258065
+ nameTspan.setAttribute("x", String(point2.x));
258066
+ nameTspan.setAttribute(
258067
+ "dy",
258068
+ trimmedTitle ? calloutLineDy : "0"
258069
+ );
258070
+ nameTspan.setAttribute(
258071
+ "fill",
258072
+ trimmedTitle ? dirColor : filenameColor
258073
+ );
258074
+ nameTspan.setAttribute("font-size", calloutFontSize);
258075
+ nameTspan.setAttribute(
258076
+ "font-weight",
258077
+ trimmedTitle ? calloutBodyFontWeight : calloutFontWeight
258078
+ );
258079
+ pathTspan.textContent = dir;
258080
+ pathTspan.setAttribute("x", String(point2.x));
258081
+ pathTspan.setAttribute("dy", dir ? calloutLineDy : "0");
258082
+ pathTspan.setAttribute("fill", dirColor);
258083
+ pathTspan.setAttribute("font-size", calloutFontSize);
258084
+ pathTspan.setAttribute("font-weight", calloutFontWeight);
258085
+ const bbox = textEl.getBBox();
258086
+ const padX = 14;
258087
+ const padY = 10;
258088
+ bgEl.setAttribute("x", String(bbox.x - padX));
258089
+ bgEl.setAttribute("y", String(bbox.y - padY));
258090
+ bgEl.setAttribute("width", String(bbox.width + padX * 2));
258091
+ bgEl.setAttribute("height", String(bbox.height + padY * 2));
258092
+ bgEl.setAttribute("rx", "8");
258093
+ bgEl.setAttribute("fill", surfaceColor);
258094
+ bgEl.setAttribute("stroke", color2);
258095
+ bgEl.setAttribute("stroke-width", "1.5");
258096
+ bgEl.setAttribute("opacity", String(0.94 * opacity));
258097
+ };
258098
+ let calloutOpacity = 0;
258099
+ if (frozenIndex != null && lastIndex >= 0) {
258100
+ calloutOpacity = 1;
258101
+ } else if (animate2 && lastIndex >= 0 && lastIndex < targets.length) {
258102
+ if (traverseFrac !== null) {
258103
+ const eased = traverseFrac * traverseFrac * (3 - 2 * traverseFrac);
258104
+ calloutOpacity = 1 - eased;
258105
+ } else {
258106
+ const t = Math.min(1, dwellElapsed / CALLOUT_FADE_IN_MS);
258107
+ calloutOpacity = t * t * (3 - 2 * t);
258108
+ }
258109
+ } else if (!animate2 && lastIndex >= 0) {
258110
+ calloutOpacity = 1;
258111
+ }
258112
+ paintCallout(
258113
+ calloutTextRef.current,
258114
+ calloutBgRef.current,
258115
+ lastIndex >= 0 ? points[lastIndex] : null,
258116
+ lastIndex >= 0 ? targets[lastIndex].path : null,
258117
+ lastIndex >= 0 ? ((_a = markerTitlesRef.current) == null ? void 0 : _a[lastIndex]) ?? null : null,
258118
+ calloutOpacity
258119
+ );
257919
258120
  g2.setAttribute("opacity", "1");
257920
258121
  },
257921
- [containerRef, hideAll, color2]
258122
+ [
258123
+ animate2,
258124
+ containerRef,
258125
+ hideAll,
258126
+ color2,
258127
+ stepMs,
258128
+ dwellMs,
258129
+ endHoldMs,
258130
+ fontFamily,
258131
+ calloutFontSize,
258132
+ calloutFontWeight,
258133
+ calloutBodyFontWeight,
258134
+ calloutLineDy,
258135
+ calloutLineDyPx,
258136
+ surfaceColor,
258137
+ filenameColor,
258138
+ dirColor
258139
+ ]
257922
258140
  );
257923
258141
  React.useImperativeHandle(ref, () => ({ onCameraFrame }), [onCameraFrame]);
257924
- return /* @__PURE__ */ jsx(
258142
+ return /* @__PURE__ */ jsxs(
257925
258143
  "svg",
257926
258144
  {
257927
258145
  width: "100%",
@@ -257935,7 +258153,12 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({ containerRef, b
257935
258153
  // overlays (z 1900) so the path only paints over the city.
257936
258154
  zIndex: 26
257937
258155
  },
257938
- children: /* @__PURE__ */ jsx("g", { ref: groupRef, opacity: 0 })
258156
+ children: [
258157
+ /* @__PURE__ */ jsx("g", { ref: groupRef, opacity: 0 }),
258158
+ /* @__PURE__ */ jsx("circle", { ref: leadDotRef, opacity: 0, pointerEvents: "none" }),
258159
+ /* @__PURE__ */ jsx("rect", { ref: calloutBgRef, opacity: 0, pointerEvents: "none" }),
258160
+ /* @__PURE__ */ jsx("text", { ref: calloutTextRef, opacity: 0, pointerEvents: "none" })
258161
+ ]
257939
258162
  }
257940
258163
  );
257941
258164
  });
@@ -257946,6 +258169,7 @@ const RESIZE_HANDLE_WIDTH = 6;
257946
258169
  const TrailMarkdownOverlay = ({
257947
258170
  eyebrow,
257948
258171
  title,
258172
+ subtitle,
257949
258173
  markdown: markdown2,
257950
258174
  slideIdPrefix,
257951
258175
  bottomOffset,
@@ -258053,7 +258277,7 @@ const TrailMarkdownOverlay = ({
258053
258277
  to { transform: translateX(0); }
258054
258278
  }
258055
258279
  ` }),
258056
- eyebrow || title ? /* @__PURE__ */ jsxs(
258280
+ eyebrow || title || subtitle ? /* @__PURE__ */ jsxs(
258057
258281
  "div",
258058
258282
  {
258059
258283
  style: {
@@ -258092,6 +258316,22 @@ const TrailMarkdownOverlay = ({
258092
258316
  title,
258093
258317
  children: title
258094
258318
  }
258319
+ ) : null,
258320
+ subtitle ? /* @__PURE__ */ jsx(
258321
+ "span",
258322
+ {
258323
+ style: {
258324
+ fontSize: theme2.fontSizes[0],
258325
+ color: theme2.colors.textSecondary,
258326
+ fontFamily: theme2.fonts.monospace,
258327
+ overflow: "hidden",
258328
+ textOverflow: "ellipsis",
258329
+ whiteSpace: "nowrap",
258330
+ marginTop: 2
258331
+ },
258332
+ title: subtitle,
258333
+ children: subtitle
258334
+ }
258095
258335
  ) : null
258096
258336
  ]
258097
258337
  }
@@ -258495,32 +258735,15 @@ const TrailSnippetView = ({
258495
258735
  if (!fileObject || !slice) {
258496
258736
  return /* @__PURE__ */ jsx("div", { style: { padding: 16, color: theme2.colors.textSecondary }, children: "Loading…" });
258497
258737
  }
258498
- const rangeLabel = slice.sliceStart === slice.sliceEnd ? `Line ${slice.sliceStart}` : `Lines ${slice.sliceStart}–${slice.sliceEnd}`;
258499
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", minHeight: 0 }, children: [
258500
- /* @__PURE__ */ jsx(
258501
- "div",
258502
- {
258503
- style: {
258504
- padding: "4px 14px 6px",
258505
- fontFamily: theme2.fonts.monospace,
258506
- fontSize: theme2.fontSizes[0],
258507
- color: theme2.colors.textSecondary,
258508
- letterSpacing: 0.4,
258509
- textTransform: "uppercase"
258510
- },
258511
- children: rangeLabel
258512
- }
258513
- ),
258514
- /* @__PURE__ */ jsx(
258515
- File$2,
258516
- {
258517
- file: fileObject,
258518
- options,
258519
- selectedLines: slice.focusOffset != null ? { start: slice.focusOffset, end: slice.focusOffset } : void 0,
258520
- style: pierreStyle$1
258521
- }
258522
- )
258523
- ] });
258738
+ return /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", minHeight: 0 }, children: /* @__PURE__ */ jsx(
258739
+ File$2,
258740
+ {
258741
+ file: fileObject,
258742
+ options,
258743
+ selectedLines: slice.focusOffset != null ? { start: slice.focusOffset, end: slice.focusOffset } : void 0,
258744
+ style: pierreStyle$1
258745
+ }
258746
+ ) });
258524
258747
  };
258525
258748
  const buildBackgroundCSS = (color2) => `
258526
258749
  :host {
@@ -258645,40 +258868,23 @@ const TrailDiffSnippetView = ({
258645
258868
  if (!fileDiff) {
258646
258869
  return /* @__PURE__ */ jsx("div", { style: { padding: 16, color: theme2.colors.error }, children: "Failed to compute diff" });
258647
258870
  }
258648
- const rangeLabel = startLine != null && endLine != null ? startLine === endLine ? `Line ${startLine}` : `Lines ${startLine}–${endLine}` : null;
258649
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", minHeight: 0 }, children: [
258650
- rangeLabel && /* @__PURE__ */ jsx(
258651
- "div",
258652
- {
258653
- style: {
258654
- padding: "4px 14px 6px",
258655
- fontFamily: theme2.fonts.monospace,
258656
- fontSize: theme2.fontSizes[0],
258657
- color: theme2.colors.textSecondary,
258658
- letterSpacing: 0.4,
258659
- textTransform: "uppercase"
258660
- },
258661
- children: rangeLabel
258662
- }
258663
- ),
258664
- /* @__PURE__ */ jsx(
258665
- FileDiff$2,
258666
- {
258667
- fileDiff,
258668
- options: { ...pierreOptionsBase, diffStyle },
258669
- selectedLines: slice.focusOffset != null ? { start: slice.focusOffset, end: slice.focusOffset } : void 0,
258670
- style: background ? {
258671
- ...pierreStyle,
258672
- // Pierre derives addition/deletion/context surfaces by
258673
- // `color-mix`ing from --diffs-bg, which is keyed off
258674
- // --diffs-light-bg / --diffs-dark-bg. Overriding the
258675
- // source variables recolors the whole palette coherently.
258676
- ["--diffs-light-bg"]: background,
258677
- ["--diffs-dark-bg"]: background
258678
- } : pierreStyle
258679
- }
258680
- )
258681
- ] });
258871
+ return /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", minHeight: 0 }, children: /* @__PURE__ */ jsx(
258872
+ FileDiff$2,
258873
+ {
258874
+ fileDiff,
258875
+ options: { ...pierreOptionsBase, diffStyle },
258876
+ selectedLines: slice.focusOffset != null ? { start: slice.focusOffset, end: slice.focusOffset } : void 0,
258877
+ style: background ? {
258878
+ ...pierreStyle,
258879
+ // Pierre derives addition/deletion/context surfaces by
258880
+ // `color-mix`ing from --diffs-bg, which is keyed off
258881
+ // --diffs-light-bg / --diffs-dark-bg. Overriding the
258882
+ // source variables recolors the whole palette coherently.
258883
+ ["--diffs-light-bg"]: background,
258884
+ ["--diffs-dark-bg"]: background
258885
+ } : pierreStyle
258886
+ }
258887
+ ) });
258682
258888
  };
258683
258889
  const pierreOptionsBase = {
258684
258890
  disableFileHeader: true
@@ -258687,10 +258893,11 @@ const pierreStyle = {
258687
258893
  display: "block"
258688
258894
  };
258689
258895
  const SEQUENCE_DRAWER_HEIGHT_PCT = 45;
258690
- const SNIPPET_PANE_WIDTH_PX = 460;
258896
+ const SNIPPET_PANE_RIGHT_GUTTER_PX = 16;
258897
+ const SNIPPET_PANE_LEADER_BUFFER_PX = 60;
258691
258898
  const PANEL_TRANSITION_MS = 280;
258692
258899
  const THREE_D_TOGGLE_DISABLED = true;
258693
- const FileCityTrailExplorerPanel = ({ context, actions }) => {
258900
+ const FileCityTrailExplorerPanel = ({ context, actions, defaultViewMode, defaultShowSequenceDrawer }) => {
258694
258901
  var _a;
258695
258902
  const tree = context.fileTree.data;
258696
258903
  const lineCounts = ((_a = context.lineCounts.data) == null ? void 0 : _a.lineCounts) ?? null;
@@ -258746,7 +258953,9 @@ const FileCityTrailExplorerPanel = ({ context, actions }) => {
258746
258953
  onSelectMarker: setSelectedMarkerId,
258747
258954
  cityData,
258748
258955
  hasLineCounts: lineCounts != null && Object.keys(lineCounts).length > 0,
258749
- readFile: actions.readFile
258956
+ readFile: actions.readFile,
258957
+ defaultViewMode,
258958
+ defaultShowSequenceDrawer
258750
258959
  }
258751
258960
  );
258752
258961
  };
@@ -258758,7 +258967,9 @@ const FileCityTrailSequenceLayout = ({
258758
258967
  onSelectMarker,
258759
258968
  cityData,
258760
258969
  hasLineCounts,
258761
- readFile
258970
+ readFile,
258971
+ defaultViewMode = "city",
258972
+ defaultShowSequenceDrawer = true
258762
258973
  }) => {
258763
258974
  var _a, _b;
258764
258975
  const { theme: theme2 } = useTheme();
@@ -258769,8 +258980,12 @@ const FileCityTrailSequenceLayout = ({
258769
258980
  return;
258770
258981
  }, [hasLineCounts]);
258771
258982
  const effectiveShow3D = false;
258772
- const [viewMode, setViewMode] = React.useState("city");
258773
- const [showSequenceDrawer, setShowSequenceDrawer] = React.useState(true);
258983
+ const [viewMode, setViewMode] = React.useState(
258984
+ defaultViewMode
258985
+ );
258986
+ const [showSequenceDrawer, setShowSequenceDrawer] = React.useState(
258987
+ defaultShowSequenceDrawer
258988
+ );
258774
258989
  const [drawerHeightOverridePct, setDrawerHeightOverridePct] = React.useState(null);
258775
258990
  const [isResizingDrawer, setIsResizingDrawer] = React.useState(false);
258776
258991
  const drawerHeightPct = !showSequenceDrawer ? 0 : drawerHeightOverridePct ?? SEQUENCE_DRAWER_HEIGHT_PCT;
@@ -258937,17 +259152,43 @@ const FileCityTrailSequenceLayout = ({
258937
259152
  z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2
258938
259153
  };
258939
259154
  }, [cityData]);
258940
- const trailPathBuildings = React.useMemo(() => {
258941
- if (!cityData || markersForThisRepo.length === 0) return [];
258942
- const buildingByPath = new Map(cityData.buildings.map((b) => [b.path, b]));
258943
- const out = [];
259155
+ const { trailPathBuildings, trailPathTitles, trailPathMarkerIds } = React.useMemo(() => {
259156
+ var _a2;
259157
+ if (!cityData || markersForThisRepo.length === 0) {
259158
+ return {
259159
+ trailPathBuildings: [],
259160
+ trailPathTitles: [],
259161
+ trailPathMarkerIds: []
259162
+ };
259163
+ }
259164
+ const buildingByPath = new Map(
259165
+ cityData.buildings.map((b) => [b.path, b])
259166
+ );
259167
+ const buildings = [];
259168
+ const titles = [];
259169
+ const markerIds = [];
258944
259170
  for (const m of markersForThisRepo) {
258945
259171
  if (!m.sourcePath) continue;
258946
- const b = buildingByPath.get(m.sourcePath) ?? cityData.buildings.find((cb) => cb.path.endsWith(`/${m.sourcePath}`));
258947
- if (b) out.push(b);
259172
+ const b = buildingByPath.get(m.sourcePath) ?? cityData.buildings.find(
259173
+ (cb) => cb.path.endsWith(`/${m.sourcePath}`)
259174
+ );
259175
+ if (b) {
259176
+ buildings.push(b);
259177
+ titles.push(((_a2 = m.label) == null ? void 0 : _a2.trim()) || null);
259178
+ markerIds.push(m.id);
259179
+ }
258948
259180
  }
258949
- return out;
259181
+ return {
259182
+ trailPathBuildings: buildings,
259183
+ trailPathTitles: titles,
259184
+ trailPathMarkerIds: markerIds
259185
+ };
258950
259186
  }, [cityData, markersForThisRepo]);
259187
+ const trailPathCurrentStepIndex = React.useMemo(() => {
259188
+ if (!selectedMarkerId) return null;
259189
+ const idx = trailPathMarkerIds.indexOf(selectedMarkerId);
259190
+ return idx >= 0 ? idx : null;
259191
+ }, [selectedMarkerId, trailPathMarkerIds]);
258951
259192
  const onCameraFrame = React.useCallback(
258952
259193
  (camera, size) => {
258953
259194
  var _a2, _b2;
@@ -258992,13 +259233,18 @@ const FileCityTrailSequenceLayout = ({
258992
259233
  return () => observer.disconnect();
258993
259234
  }, [snippetPaneEl]);
258994
259235
  const overlayMarkdown = ((_a = selectedMarker == null ? void 0 : selectedMarker.description) == null ? void 0 : _a.trim()) ? {
258995
- eyebrow: "Marker",
259236
+ eyebrow: null,
258996
259237
  title: selectedMarker.label ?? selectedMarker.id,
259238
+ // File path under the title — the panel header already names
259239
+ // the trail, so swapping the "Marker" eyebrow for the marker's
259240
+ // sourcePath gives the user the more useful piece of context.
259241
+ subtitle: selectedMarker.sourcePath ?? null,
258997
259242
  markdown: selectedMarker.description,
258998
259243
  slideIdPrefix: `trail-${trail2.id}-marker-${selectedMarker.id}`
258999
259244
  } : ((_b = trail2.summary) == null ? void 0 : _b.trim()) ? {
259000
259245
  eyebrow: null,
259001
259246
  title: null,
259247
+ subtitle: null,
259002
259248
  markdown: trail2.summary,
259003
259249
  slideIdPrefix: `trail-${trail2.id}-summary`
259004
259250
  } : null;
@@ -259082,6 +259328,7 @@ const FileCityTrailSequenceLayout = ({
259082
259328
  {
259083
259329
  trail: trail2,
259084
259330
  markerCount: markersForThisRepo.length,
259331
+ activeMarkerIndex: stepperIndex,
259085
259332
  show3D: effectiveShow3D,
259086
259333
  onToggle3D: () => setShow3D((v) => !v),
259087
259334
  hideToggle: THREE_D_TOGGLE_DISABLED,
@@ -259094,13 +259341,16 @@ const FileCityTrailSequenceLayout = ({
259094
259341
  // Drawer-open is the default start state, so it no longer
259095
259342
  // counts as "non-idle". Exit is offered only when the user
259096
259343
  // has drilled into something the start view doesn't show.
259097
- selectedMarkerId != null || snippetExpanded || viewMode !== "city"
259344
+ // The "idle" view-mode is the host-supplied default — when
259345
+ // the host opens the panel in 'doc', staying in 'doc' is
259346
+ // idle and switching to 'city' is the non-idle drill-in.
259347
+ selectedMarkerId != null || snippetExpanded || viewMode !== defaultViewMode
259098
259348
  ),
259099
259349
  onExitTrail: () => {
259100
259350
  onSelectMarker(null);
259101
259351
  setDrawerHeightOverridePct(null);
259102
259352
  setSnippetExpanded(false);
259103
- setViewMode("city");
259353
+ setViewMode(defaultViewMode);
259104
259354
  },
259105
259355
  viewMode,
259106
259356
  onSetViewMode: setViewMode
@@ -259149,6 +259399,8 @@ const FileCityTrailSequenceLayout = ({
259149
259399
  ref: filePathRef,
259150
259400
  containerRef,
259151
259401
  buildings: trailPathBuildings,
259402
+ markerTitles: trailPathTitles,
259403
+ currentStepIndex: trailPathCurrentStepIndex,
259152
259404
  cityCenter
259153
259405
  }
259154
259406
  ),
@@ -259167,6 +259419,7 @@ const FileCityTrailSequenceLayout = ({
259167
259419
  {
259168
259420
  eyebrow: overlayMarkdown.eyebrow,
259169
259421
  title: overlayMarkdown.title,
259422
+ subtitle: overlayMarkdown.subtitle,
259170
259423
  markdown: overlayMarkdown.markdown,
259171
259424
  slideIdPrefix: overlayMarkdown.slideIdPrefix,
259172
259425
  bottomOffset: `${drawerHeightPct}%`,
@@ -259208,7 +259461,8 @@ const FileCityTrailSequenceLayout = ({
259208
259461
  drawerHeightPct,
259209
259462
  disableBottomTransition: isResizingDrawer,
259210
259463
  expanded: snippetExpanded,
259211
- onToggleExpanded: () => setSnippetExpanded((v) => !v)
259464
+ onToggleExpanded: () => setSnippetExpanded((v) => !v),
259465
+ markdownOverlayWidth
259212
259466
  }
259213
259467
  ) : null
259214
259468
  ]
@@ -259375,7 +259629,8 @@ const SnippetSidePane = React.forwardRef(function SnippetSidePane2({
259375
259629
  drawerHeightPct,
259376
259630
  disableBottomTransition,
259377
259631
  expanded,
259378
- onToggleExpanded
259632
+ onToggleExpanded,
259633
+ markdownOverlayWidth
259379
259634
  }, ref) {
259380
259635
  const { theme: theme2 } = useTheme();
259381
259636
  if (!marker.sourcePath) {
@@ -259389,6 +259644,7 @@ const SnippetSidePane = React.forwardRef(function SnippetSidePane2({
259389
259644
  disableBottomTransition,
259390
259645
  expanded,
259391
259646
  onToggleExpanded,
259647
+ markdownOverlayWidth,
259392
259648
  children: /* @__PURE__ */ jsx(
259393
259649
  "div",
259394
259650
  {
@@ -259414,6 +259670,7 @@ const SnippetSidePane = React.forwardRef(function SnippetSidePane2({
259414
259670
  disableBottomTransition,
259415
259671
  expanded,
259416
259672
  onToggleExpanded,
259673
+ markdownOverlayWidth,
259417
259674
  children: snippet2.kind === "slice" ? /* @__PURE__ */ jsx(
259418
259675
  TrailSnippetView,
259419
259676
  {
@@ -259455,10 +259712,12 @@ const SidePaneFrame = React.forwardRef(
259455
259712
  drawerHeightPct,
259456
259713
  disableBottomTransition,
259457
259714
  expanded,
259458
- onToggleExpanded
259715
+ onToggleExpanded,
259716
+ markdownOverlayWidth
259459
259717
  }, ref) {
259460
259718
  const { theme: theme2 } = useTheme();
259461
259719
  const [widthPx, setWidthPx] = React.useState(null);
259720
+ const defaultLeftInset = (markdownOverlayWidth ?? 0) + SNIPPET_PANE_LEADER_BUFFER_PX;
259462
259721
  const [isResizing, setIsResizing] = React.useState(false);
259463
259722
  const innerRef = React.useRef(null);
259464
259723
  const setInnerRef = React.useCallback(
@@ -259497,7 +259756,7 @@ const SidePaneFrame = React.forwardRef(
259497
259756
  const parentWidth = (parent == null ? void 0 : parent.clientWidth) ?? window.innerWidth;
259498
259757
  const maxWidth = Math.max(
259499
259758
  SNIPPET_PANE_MIN_WIDTH_PX,
259500
- parentWidth - 32
259759
+ parentWidth - defaultLeftInset - SNIPPET_PANE_RIGHT_GUTTER_PX
259501
259760
  );
259502
259761
  const dx = e.clientX - drag2.startX;
259503
259762
  const next2 = Math.min(
@@ -259506,7 +259765,7 @@ const SidePaneFrame = React.forwardRef(
259506
259765
  );
259507
259766
  setWidthPx(next2);
259508
259767
  },
259509
- []
259768
+ [defaultLeftInset]
259510
259769
  );
259511
259770
  const handleResizePointerUp = React.useCallback(
259512
259771
  (e) => {
@@ -259526,9 +259785,15 @@ const SidePaneFrame = React.forwardRef(
259526
259785
  style: {
259527
259786
  position: "absolute",
259528
259787
  top: 16,
259529
- right: 16,
259788
+ right: SNIPPET_PANE_RIGHT_GUTTER_PX,
259530
259789
  bottom: expanded ? `calc(${drawerHeightPct}% + 16px)` : "50%",
259531
- width: widthPx ?? SNIPPET_PANE_WIDTH_PX,
259790
+ // Auto-fill the right column when the user hasn't dragged an
259791
+ // explicit width: anchor to the markdown overlay's right edge
259792
+ // (plus a leader-line buffer) and the city container's right
259793
+ // gutter. Once `widthPx` is set, switch to an explicit width
259794
+ // and let `right` keep the right anchor.
259795
+ left: widthPx == null ? defaultLeftInset : void 0,
259796
+ width: widthPx ?? void 0,
259532
259797
  minWidth: SNIPPET_PANE_MIN_WIDTH_PX,
259533
259798
  backgroundColor: theme2.colors.background,
259534
259799
  border: `1px solid ${theme2.colors.border}`,
@@ -259591,33 +259856,77 @@ const SidePaneFrame = React.forwardRef(
259591
259856
  flexShrink: 0
259592
259857
  },
259593
259858
  children: [
259594
- /* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
259595
- /* @__PURE__ */ jsx(
259596
- "div",
259597
- {
259598
- style: {
259599
- fontFamily: theme2.fonts.heading,
259600
- fontSize: theme2.fontSizes[0],
259601
- fontWeight: theme2.fontWeights.semibold,
259602
- color: theme2.colors.text
259603
- },
259604
- children: marker.label ?? marker.id
259605
- }
259606
- ),
259607
- marker.sourcePath ? /* @__PURE__ */ jsx(
259608
- "div",
259609
- {
259610
- style: {
259611
- fontSize: theme2.fontSizes[0],
259612
- color: theme2.colors.textSecondary,
259613
- fontFamily: theme2.fonts.monospace,
259614
- marginTop: 2,
259615
- wordBreak: "break-all"
259616
- },
259617
- children: marker.sourcePath
259618
- }
259619
- ) : null
259620
- ] }),
259859
+ /* @__PURE__ */ jsx("div", { style: { minWidth: 0, flex: 1 }, children: (() => {
259860
+ const path2 = marker.sourcePath ?? "";
259861
+ const slashIdx = path2.lastIndexOf("/");
259862
+ const filename = slashIdx >= 0 ? path2.slice(slashIdx + 1) : path2;
259863
+ const directory = slashIdx >= 0 ? path2.slice(0, slashIdx + 1) : null;
259864
+ const snippet2 = marker.snippet;
259865
+ const rangeLabel = snippet2 ? snippet2.startLine === snippet2.endLine ? `${snippet2.startLine}` : `${snippet2.startLine}–${snippet2.endLine}` : null;
259866
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
259867
+ /* @__PURE__ */ jsxs(
259868
+ "div",
259869
+ {
259870
+ style: {
259871
+ display: "flex",
259872
+ alignItems: "baseline",
259873
+ gap: 8,
259874
+ minWidth: 0
259875
+ },
259876
+ children: [
259877
+ /* @__PURE__ */ jsx(
259878
+ "div",
259879
+ {
259880
+ style: {
259881
+ fontFamily: theme2.fonts.monospace,
259882
+ fontSize: theme2.fontSizes[1],
259883
+ fontWeight: theme2.fontWeights.semibold,
259884
+ color: theme2.colors.text,
259885
+ overflow: "hidden",
259886
+ textOverflow: "ellipsis",
259887
+ whiteSpace: "nowrap",
259888
+ minWidth: 0,
259889
+ flexShrink: 1
259890
+ },
259891
+ title: path2 || void 0,
259892
+ children: filename || marker.id
259893
+ }
259894
+ ),
259895
+ rangeLabel ? /* @__PURE__ */ jsx(
259896
+ "div",
259897
+ {
259898
+ style: {
259899
+ fontFamily: theme2.fonts.monospace,
259900
+ fontSize: theme2.fontSizes[0],
259901
+ color: theme2.colors.textSecondary,
259902
+ letterSpacing: 0.4,
259903
+ textTransform: "uppercase",
259904
+ flexShrink: 0
259905
+ },
259906
+ children: rangeLabel
259907
+ }
259908
+ ) : null
259909
+ ]
259910
+ }
259911
+ ),
259912
+ directory ? /* @__PURE__ */ jsx(
259913
+ "div",
259914
+ {
259915
+ style: {
259916
+ fontSize: theme2.fontSizes[0],
259917
+ color: theme2.colors.textSecondary,
259918
+ fontFamily: theme2.fonts.monospace,
259919
+ marginTop: 2,
259920
+ overflow: "hidden",
259921
+ textOverflow: "ellipsis",
259922
+ whiteSpace: "nowrap"
259923
+ },
259924
+ title: directory,
259925
+ children: directory
259926
+ }
259927
+ ) : null
259928
+ ] });
259929
+ })() }),
259621
259930
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 6, flexShrink: 0 }, children: [
259622
259931
  /* @__PURE__ */ jsx(
259623
259932
  "button",
@@ -259914,6 +260223,7 @@ const TrailDocumentCard = ({
259914
260223
  const TrailHeader = ({
259915
260224
  trail: trail2,
259916
260225
  markerCount,
260226
+ activeMarkerIndex,
259917
260227
  show3D,
259918
260228
  onToggle3D,
259919
260229
  hideToggle = false,
@@ -259993,20 +260303,34 @@ const TrailHeader = ({
259993
260303
  children: trail2.title
259994
260304
  }
259995
260305
  ),
259996
- /* @__PURE__ */ jsxs(
260306
+ /* @__PURE__ */ jsx(
259997
260307
  "div",
259998
260308
  {
260309
+ "aria-label": `${markerCount} marker${markerCount === 1 ? "" : "s"} in this repo`,
259999
260310
  style: {
260000
- fontFamily: theme2.fonts.body,
260001
- fontSize: theme2.fontSizes[0],
260002
- color: theme2.colors.textSecondary
260311
+ display: "flex",
260312
+ alignItems: "center",
260313
+ height: theme2.fontSizes[0]
260003
260314
  },
260004
- children: [
260005
- markerCount,
260006
- " marker",
260007
- markerCount === 1 ? "" : "s",
260008
- " in this repo"
260009
- ]
260315
+ children: Array.from({ length: markerCount }).map((_, i) => {
260316
+ const isActive = i === activeMarkerIndex;
260317
+ return /* @__PURE__ */ jsx(
260318
+ "span",
260319
+ {
260320
+ style: {
260321
+ width: 6,
260322
+ height: 6,
260323
+ borderRadius: "50%",
260324
+ background: isActive ? theme2.colors.accent : theme2.colors.textSecondary,
260325
+ // Group dots in fives for at-a-glance countability — a
260326
+ // wider gap before every 5th index (after the first)
260327
+ // creates visual columns of 5 like tally marks.
260328
+ marginLeft: i === 0 ? 0 : i % 5 === 0 ? 11 : 5
260329
+ }
260330
+ },
260331
+ i
260332
+ );
260333
+ })
260010
260334
  }
260011
260335
  )
260012
260336
  ]