@industry-theme/file-city-panel 0.5.60 → 0.5.62

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.
Files changed (35) hide show
  1. package/dist/index.d.ts +13 -3
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/panels/FileCitySequenceExplorerPanel/FileCitySequenceExplorer/FileCitySequenceExplorer.d.ts +6 -0
  4. package/dist/panels/FileCitySequenceExplorerPanel/FileCitySequenceExplorer/FileCitySequenceExplorer.d.ts.map +1 -1
  5. package/dist/panels/FileCitySequenceExplorerPanel/FileCitySequenceExplorer/styles.d.ts +1 -2
  6. package/dist/panels/FileCitySequenceExplorerPanel/FileCitySequenceExplorer/styles.d.ts.map +1 -1
  7. package/dist/panels/FileCitySequenceExplorerPanel/FileCitySequenceExplorerPanel.d.ts +5 -0
  8. package/dist/panels/FileCitySequenceExplorerPanel/FileCitySequenceExplorerPanel.d.ts.map +1 -1
  9. package/dist/panels/FileCityTrailExplorerPanel/FileCityTrailExplorerPanel.d.ts +23 -0
  10. package/dist/panels/FileCityTrailExplorerPanel/FileCityTrailExplorerPanel.d.ts.map +1 -0
  11. package/dist/panels/FileCityTrailExplorerPanel/buildSequenceViewInputs.d.ts +29 -0
  12. package/dist/panels/FileCityTrailExplorerPanel/buildSequenceViewInputs.d.ts.map +1 -0
  13. package/dist/panels/FileCityTrailExplorerPanel/cameraFraming.d.ts +42 -0
  14. package/dist/panels/FileCityTrailExplorerPanel/cameraFraming.d.ts.map +1 -0
  15. package/dist/panels/FileCityTrailExplorerPanel/index.d.ts +2 -0
  16. package/dist/panels/FileCityTrailExplorerPanel/index.d.ts.map +1 -0
  17. package/dist/panels/FileCityTrailExplorerPanel/overlays/TrailDiffSnippetView.d.ts +45 -0
  18. package/dist/panels/FileCityTrailExplorerPanel/overlays/TrailDiffSnippetView.d.ts.map +1 -0
  19. package/dist/panels/FileCityTrailExplorerPanel/overlays/TrailLeaderLine.d.ts +39 -0
  20. package/dist/panels/FileCityTrailExplorerPanel/overlays/TrailLeaderLine.d.ts.map +1 -0
  21. package/dist/panels/FileCityTrailExplorerPanel/overlays/TrailMarkdownOverlay.d.ts +50 -0
  22. package/dist/panels/FileCityTrailExplorerPanel/overlays/TrailMarkdownOverlay.d.ts.map +1 -0
  23. package/dist/panels/FileCityTrailExplorerPanel/overlays/TrailSnippetView.d.ts +35 -0
  24. package/dist/panels/FileCityTrailExplorerPanel/overlays/TrailSnippetView.d.ts.map +1 -0
  25. package/dist/panels/utils/styles.d.ts +22 -0
  26. package/dist/panels/utils/styles.d.ts.map +1 -0
  27. package/dist/panels.bundle.js +1904 -250
  28. package/dist/panels.bundle.js.map +1 -1
  29. package/dist/types/FileCityTrailExplorerPanel.d.ts +83 -0
  30. package/dist/types/FileCityTrailExplorerPanel.d.ts.map +1 -0
  31. package/dist/types/Trail.d.ts +508 -0
  32. package/dist/types/Trail.d.ts.map +1 -0
  33. package/dist/types/index.d.ts +2 -0
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/package.json +1 -1
@@ -253815,12 +253815,12 @@ const PierreFileView = ({
253815
253815
  File$2,
253816
253816
  {
253817
253817
  file: fileObject,
253818
- options: background ? buildPierreOptions$1(background) : pierreOptions$1,
253819
- style: pierreStyle$2
253818
+ options: background ? buildPierreOptions$2(background) : pierreOptions$2,
253819
+ style: pierreStyle$4
253820
253820
  }
253821
253821
  );
253822
253822
  };
253823
- const buildBackgroundCSS$1 = (color2) => `
253823
+ const buildBackgroundCSS$2 = (color2) => `
253824
253824
  :host {
253825
253825
  background: ${color2} !important;
253826
253826
  }
@@ -253835,19 +253835,19 @@ const buildBackgroundCSS$1 = (color2) => `
253835
253835
  background: ${color2} !important;
253836
253836
  }
253837
253837
  `;
253838
- const pierreOptions$1 = {
253838
+ const pierreOptions$2 = {
253839
253839
  disableFileHeader: true
253840
253840
  };
253841
- const buildPierreOptions$1 = (background) => ({
253841
+ const buildPierreOptions$2 = (background) => ({
253842
253842
  disableFileHeader: true,
253843
- unsafeCSS: buildBackgroundCSS$1(background)
253843
+ unsafeCSS: buildBackgroundCSS$2(background)
253844
253844
  });
253845
- const pierreStyle$2 = {
253845
+ const pierreStyle$4 = {
253846
253846
  display: "block"
253847
253847
  };
253848
- const MIN_WIDTH_PX$3 = 320;
253848
+ const MIN_WIDTH_PX$4 = 320;
253849
253849
  const MIN_LEFT_GAP_PX$2 = 80;
253850
- const RESIZE_HANDLE_WIDTH$3 = 6;
253850
+ const RESIZE_HANDLE_WIDTH$4 = 6;
253851
253851
  const FileOverlay = ({
253852
253852
  filePath,
253853
253853
  fileName,
@@ -253876,7 +253876,7 @@ const FileOverlay = ({
253876
253876
  setIsResizing(true);
253877
253877
  const onMove = (ev) => {
253878
253878
  const next2 = Math.max(
253879
- MIN_WIDTH_PX$3,
253879
+ MIN_WIDTH_PX$4,
253880
253880
  Math.min(parentRect.width - MIN_LEFT_GAP_PX$2, parentRect.right - ev.clientX)
253881
253881
  );
253882
253882
  setWidthPx(next2);
@@ -253926,8 +253926,8 @@ const FileOverlay = ({
253926
253926
  position: "absolute",
253927
253927
  top: 0,
253928
253928
  bottom: 0,
253929
- left: -RESIZE_HANDLE_WIDTH$3 / 2,
253930
- width: RESIZE_HANDLE_WIDTH$3,
253929
+ left: -RESIZE_HANDLE_WIDTH$4 / 2,
253930
+ width: RESIZE_HANDLE_WIDTH$4,
253931
253931
  cursor: "col-resize",
253932
253932
  zIndex: 1,
253933
253933
  background: isResizing ? `color-mix(in srgb, ${theme2.colors.primary} 40%, transparent)` : "transparent"
@@ -254911,7 +254911,7 @@ const PierreSnippetView = ({
254911
254911
  [slice, onOpenComposer]
254912
254912
  );
254913
254913
  const options = React.useMemo(() => {
254914
- const base2 = background ? buildPierreOptions(background) : pierreOptions;
254914
+ const base2 = background ? buildPierreOptions$1(background) : pierreOptions$1;
254915
254915
  return {
254916
254916
  ...base2,
254917
254917
  onPostRender,
@@ -254950,12 +254950,12 @@ const PierreSnippetView = ({
254950
254950
  lineAnnotations,
254951
254951
  renderAnnotation,
254952
254952
  selectedLines: slice.focusOffset != null ? { start: slice.focusOffset, end: slice.focusOffset } : void 0,
254953
- style: pierreStyle$1
254953
+ style: pierreStyle$3
254954
254954
  }
254955
254955
  )
254956
254956
  ] });
254957
254957
  };
254958
- const buildBackgroundCSS = (color2) => `
254958
+ const buildBackgroundCSS$1 = (color2) => `
254959
254959
  :host {
254960
254960
  background: ${color2} !important;
254961
254961
  }
@@ -254970,17 +254970,17 @@ const buildBackgroundCSS = (color2) => `
254970
254970
  background: ${color2} !important;
254971
254971
  }
254972
254972
  `;
254973
- const pierreOptions = {
254973
+ const pierreOptions$1 = {
254974
254974
  disableFileHeader: true
254975
254975
  };
254976
- const buildPierreOptions = (background) => ({
254976
+ const buildPierreOptions$1 = (background) => ({
254977
254977
  disableFileHeader: true,
254978
- unsafeCSS: buildBackgroundCSS(background)
254978
+ unsafeCSS: buildBackgroundCSS$1(background)
254979
254979
  });
254980
- const pierreStyle$1 = {
254980
+ const pierreStyle$3 = {
254981
254981
  display: "block"
254982
254982
  };
254983
- const sliceWindow = (oldContents, newContents, startLine, endLine, focusLine, contextLines) => {
254983
+ const sliceWindow$1 = (oldContents, newContents, startLine, endLine, focusLine, contextLines) => {
254984
254984
  if (startLine == null || endLine == null) {
254985
254985
  return {
254986
254986
  oldText: oldContents,
@@ -255019,7 +255019,7 @@ const PierreSnippetDiffView = ({
255019
255019
  }) => {
255020
255020
  const { theme: theme2 } = useTheme();
255021
255021
  const slice = React.useMemo(
255022
- () => sliceWindow(
255022
+ () => sliceWindow$1(
255023
255023
  oldContents,
255024
255024
  newContents,
255025
255025
  startLine,
@@ -255062,10 +255062,10 @@ const PierreSnippetDiffView = ({
255062
255062
  FileDiff$2,
255063
255063
  {
255064
255064
  fileDiff,
255065
- options: { ...pierreOptionsBase, diffStyle },
255065
+ options: { ...pierreOptionsBase$1, diffStyle },
255066
255066
  selectedLines: slice.focusOffset != null ? { start: slice.focusOffset, end: slice.focusOffset } : void 0,
255067
255067
  style: background ? {
255068
- ...pierreStyle,
255068
+ ...pierreStyle$2,
255069
255069
  // Pierre derives addition/deletion/context/separator surfaces
255070
255070
  // by `color-mix`ing from --diffs-bg, which is keyed off
255071
255071
  // --diffs-light-bg / --diffs-dark-bg. Overriding the source
@@ -255074,15 +255074,15 @@ const PierreSnippetDiffView = ({
255074
255074
  // anchored to our theme background.
255075
255075
  ["--diffs-light-bg"]: background,
255076
255076
  ["--diffs-dark-bg"]: background
255077
- } : pierreStyle
255077
+ } : pierreStyle$2
255078
255078
  }
255079
255079
  )
255080
255080
  ] });
255081
255081
  };
255082
- const pierreOptionsBase = {
255082
+ const pierreOptionsBase$1 = {
255083
255083
  disableFileHeader: true
255084
255084
  };
255085
- const pierreStyle = {
255085
+ const pierreStyle$2 = {
255086
255086
  display: "block"
255087
255087
  };
255088
255088
  const toUiNote = (n) => {
@@ -255101,12 +255101,12 @@ const toUiNote = (n) => {
255101
255101
  createdAt: new Date(n.createdAt).getTime()
255102
255102
  };
255103
255103
  };
255104
- const PANEL_WIDTH_PCT$2 = 38;
255105
- const FLOAT_INSET$2 = 16;
255106
- const TOP_INSET$2 = 72;
255107
- const MIN_WIDTH_PX$2 = 360;
255104
+ const PANEL_WIDTH_PCT$3 = 38;
255105
+ const FLOAT_INSET$3 = 16;
255106
+ const TOP_INSET$3 = 72;
255107
+ const MIN_WIDTH_PX$3 = 360;
255108
255108
  const MIN_LEFT_GAP_PX$1 = 80;
255109
- const RESIZE_HANDLE_WIDTH$2 = 6;
255109
+ const RESIZE_HANDLE_WIDTH$3 = 6;
255110
255110
  const SequenceEventDetailOverlay = React.forwardRef(function SequenceEventDetailOverlay2({
255111
255111
  event,
255112
255112
  absolutePath,
@@ -255254,15 +255254,15 @@ const SequenceEventDetailOverlay = React.forwardRef(function SequenceEventDetail
255254
255254
  },
255255
255255
  [deleteSequenceNote, payloadId]
255256
255256
  );
255257
- const detailWidthCss = widthPx != null ? `${widthPx}px` : `calc(${PANEL_WIDTH_PCT$2}% - ${FLOAT_INSET$2}px)`;
255257
+ const detailWidthCss = widthPx != null ? `${widthPx}px` : `calc(${PANEL_WIDTH_PCT$3}% - ${FLOAT_INSET$3}px)`;
255258
255258
  const NOTES_PANEL_WIDTH = 320;
255259
255259
  const NOTES_PANEL_GAP = 12;
255260
255260
  const notesPanelStyle = {
255261
255261
  position: "absolute",
255262
- top: TOP_INSET$2,
255263
- bottom: `calc(${bottomOffsetCss} + ${FLOAT_INSET$2}px)`,
255262
+ top: TOP_INSET$3,
255263
+ bottom: `calc(${bottomOffsetCss} + ${FLOAT_INSET$3}px)`,
255264
255264
  width: NOTES_PANEL_WIDTH,
255265
- right: `calc(${detailWidthCss} + ${FLOAT_INSET$2}px + ${NOTES_PANEL_GAP}px)`,
255265
+ right: `calc(${detailWidthCss} + ${FLOAT_INSET$3}px + ${NOTES_PANEL_GAP}px)`,
255266
255266
  // Above the markdown overlay (z 1900) so an open thread wins when the
255267
255267
  // snippet drawer is dragged wide enough that the notes panel reaches the
255268
255268
  // left side of the workspace. The leader-line dot (z 1901) anchors at the
@@ -255286,10 +255286,10 @@ const SequenceEventDetailOverlay = React.forwardRef(function SequenceEventDetail
255286
255286
  setIsResizing(true);
255287
255287
  const onMove = (ev) => {
255288
255288
  const next2 = Math.max(
255289
- MIN_WIDTH_PX$2,
255289
+ MIN_WIDTH_PX$3,
255290
255290
  Math.min(
255291
- parentRect.width - MIN_LEFT_GAP_PX$1 - FLOAT_INSET$2,
255292
- parentRect.right - FLOAT_INSET$2 - ev.clientX
255291
+ parentRect.width - MIN_LEFT_GAP_PX$1 - FLOAT_INSET$3,
255292
+ parentRect.right - FLOAT_INSET$3 - ev.clientX
255293
255293
  )
255294
255294
  );
255295
255295
  setWidthPx(next2);
@@ -255310,14 +255310,14 @@ const SequenceEventDetailOverlay = React.forwardRef(function SequenceEventDetail
255310
255310
  onAnimationEnd: () => setHasEntered(true),
255311
255311
  style: {
255312
255312
  position: "absolute",
255313
- top: TOP_INSET$2,
255314
- right: FLOAT_INSET$2,
255313
+ top: TOP_INSET$3,
255314
+ right: FLOAT_INSET$3,
255315
255315
  // Anchor the bottom edge (mirroring the markdown overlay) so the
255316
255316
  // panel has a *definite* height — otherwise transient placeholder
255317
255317
  // states like "Loading…" collapse the panel to the content size.
255318
- bottom: `calc(${bottomOffsetCss} + ${FLOAT_INSET$2}px)`,
255319
- width: widthPx != null ? `${widthPx}px` : `calc(${PANEL_WIDTH_PCT$2}% - ${FLOAT_INSET$2}px)`,
255320
- minWidth: MIN_WIDTH_PX$2,
255318
+ bottom: `calc(${bottomOffsetCss} + ${FLOAT_INSET$3}px)`,
255319
+ width: widthPx != null ? `${widthPx}px` : `calc(${PANEL_WIDTH_PCT$3}% - ${FLOAT_INSET$3}px)`,
255320
+ minWidth: MIN_WIDTH_PX$3,
255321
255321
  backgroundColor: theme2.colors.background,
255322
255322
  border: `1px solid ${theme2.colors.border}`,
255323
255323
  borderRadius: 12,
@@ -255347,8 +255347,8 @@ const SequenceEventDetailOverlay = React.forwardRef(function SequenceEventDetail
255347
255347
  position: "absolute",
255348
255348
  top: 0,
255349
255349
  bottom: 0,
255350
- left: -RESIZE_HANDLE_WIDTH$2 / 2,
255351
- width: RESIZE_HANDLE_WIDTH$2,
255350
+ left: -RESIZE_HANDLE_WIDTH$3 / 2,
255351
+ width: RESIZE_HANDLE_WIDTH$3,
255352
255352
  cursor: "col-resize",
255353
255353
  zIndex: 1,
255354
255354
  background: isResizing ? `color-mix(in srgb, ${theme2.colors.primary} 40%, transparent)` : "transparent"
@@ -255572,12 +255572,12 @@ const iconButtonStyle = (color2, disabled = false) => ({
255572
255572
  alignItems: "center",
255573
255573
  opacity: disabled ? 0.5 : 1
255574
255574
  });
255575
- const PANEL_WIDTH_PCT$1 = 38;
255576
- const FLOAT_INSET$1 = 16;
255577
- const TOP_INSET$1 = 72;
255578
- const MIN_WIDTH_PX$1 = 360;
255575
+ const PANEL_WIDTH_PCT$2 = 38;
255576
+ const FLOAT_INSET$2 = 16;
255577
+ const TOP_INSET$2 = 72;
255578
+ const MIN_WIDTH_PX$2 = 360;
255579
255579
  const MIN_LEFT_GAP_PX = 80;
255580
- const RESIZE_HANDLE_WIDTH$1 = 6;
255580
+ const RESIZE_HANDLE_WIDTH$2 = 6;
255581
255581
  const SequenceFilesOverlay = ({
255582
255582
  steps,
255583
255583
  totalEvents,
@@ -255608,10 +255608,10 @@ const SequenceFilesOverlay = ({
255608
255608
  setIsResizing(true);
255609
255609
  const onMove = (ev) => {
255610
255610
  const next2 = Math.max(
255611
- MIN_WIDTH_PX$1,
255611
+ MIN_WIDTH_PX$2,
255612
255612
  Math.min(
255613
- parentRect.width - MIN_LEFT_GAP_PX - FLOAT_INSET$1,
255614
- parentRect.right - FLOAT_INSET$1 - ev.clientX
255613
+ parentRect.width - MIN_LEFT_GAP_PX - FLOAT_INSET$2,
255614
+ parentRect.right - FLOAT_INSET$2 - ev.clientX
255615
255615
  )
255616
255616
  );
255617
255617
  setWidthPx(next2);
@@ -255631,11 +255631,11 @@ const SequenceFilesOverlay = ({
255631
255631
  onAnimationEnd: () => setHasEntered(true),
255632
255632
  style: {
255633
255633
  position: "absolute",
255634
- top: TOP_INSET$1,
255635
- right: FLOAT_INSET$1,
255636
- bottom: `calc(${bottomOffsetCss} + ${FLOAT_INSET$1}px)`,
255637
- width: widthPx != null ? `${widthPx}px` : `calc(${PANEL_WIDTH_PCT$1}% - ${FLOAT_INSET$1}px)`,
255638
- minWidth: MIN_WIDTH_PX$1,
255634
+ top: TOP_INSET$2,
255635
+ right: FLOAT_INSET$2,
255636
+ bottom: `calc(${bottomOffsetCss} + ${FLOAT_INSET$2}px)`,
255637
+ width: widthPx != null ? `${widthPx}px` : `calc(${PANEL_WIDTH_PCT$2}% - ${FLOAT_INSET$2}px)`,
255638
+ minWidth: MIN_WIDTH_PX$2,
255639
255639
  backgroundColor: theme2.colors.background,
255640
255640
  border: `1px solid ${theme2.colors.border}`,
255641
255641
  borderRadius: 12,
@@ -255678,8 +255678,8 @@ const SequenceFilesOverlay = ({
255678
255678
  position: "absolute",
255679
255679
  top: 0,
255680
255680
  bottom: 0,
255681
- left: -RESIZE_HANDLE_WIDTH$1 / 2,
255682
- width: RESIZE_HANDLE_WIDTH$1,
255681
+ left: -RESIZE_HANDLE_WIDTH$2 / 2,
255682
+ width: RESIZE_HANDLE_WIDTH$2,
255683
255683
  cursor: "col-resize",
255684
255684
  zIndex: 1,
255685
255685
  background: isResizing ? `color-mix(in srgb, ${theme2.colors.primary} 40%, transparent)` : "transparent"
@@ -256358,11 +256358,11 @@ const primaryButton = (theme2) => ({
256358
256358
  fontSize: theme2.fontSizes[0],
256359
256359
  fontWeight: 600
256360
256360
  });
256361
- const PANEL_WIDTH_PCT = 38;
256362
- const FLOAT_INSET = 16;
256363
- const TOP_INSET = 72;
256364
- const MIN_WIDTH_PX = 360;
256365
- const RESIZE_HANDLE_WIDTH = 6;
256361
+ const PANEL_WIDTH_PCT$1 = 38;
256362
+ const FLOAT_INSET$1 = 16;
256363
+ const TOP_INSET$1 = 72;
256364
+ const MIN_WIDTH_PX$1 = 360;
256365
+ const RESIZE_HANDLE_WIDTH$1 = 6;
256366
256366
  const SequenceMarkdownOverlay = ({
256367
256367
  eyebrow,
256368
256368
  title,
@@ -256449,9 +256449,9 @@ const SequenceMarkdownOverlay = ({
256449
256449
  if (!drag2) return;
256450
256450
  const parent = (_a = containerRef.current) == null ? void 0 : _a.parentElement;
256451
256451
  const parentWidth = (parent == null ? void 0 : parent.clientWidth) ?? window.innerWidth;
256452
- const maxWidth = Math.max(MIN_WIDTH_PX, parentWidth - FLOAT_INSET * 2);
256452
+ const maxWidth = Math.max(MIN_WIDTH_PX$1, parentWidth - FLOAT_INSET$1 * 2);
256453
256453
  const dx = e.clientX - drag2.startX;
256454
- const next2 = Math.min(maxWidth, Math.max(MIN_WIDTH_PX, drag2.startWidth + dx));
256454
+ const next2 = Math.min(maxWidth, Math.max(MIN_WIDTH_PX$1, drag2.startWidth + dx));
256455
256455
  setWidthPx(next2);
256456
256456
  },
256457
256457
  []
@@ -256475,16 +256475,16 @@ const SequenceMarkdownOverlay = ({
256475
256475
  onAnimationEnd: () => setHasEntered(true),
256476
256476
  style: {
256477
256477
  position: "absolute",
256478
- top: TOP_INSET,
256479
- left: FLOAT_INSET,
256478
+ top: TOP_INSET$1,
256479
+ left: FLOAT_INSET$1,
256480
256480
  // Anchor the bottom edge too so the column has a *definite* height —
256481
256481
  // `IndustryMarkdownSlide` renders with `height: 100%` and only
256482
256482
  // engages its internal scroll when the chain of ancestors hands it a
256483
256483
  // resolvable pixel height. A `maxHeight`-only cap on a content-sized
256484
256484
  // flex column doesn't qualify across browsers.
256485
- bottom: `calc(${bottomOffsetCss} + ${FLOAT_INSET}px)`,
256486
- width: widthPx != null ? widthPx : `calc(${PANEL_WIDTH_PCT}% - ${FLOAT_INSET}px)`,
256487
- minWidth: MIN_WIDTH_PX,
256485
+ bottom: `calc(${bottomOffsetCss} + ${FLOAT_INSET$1}px)`,
256486
+ width: widthPx != null ? widthPx : `calc(${PANEL_WIDTH_PCT$1}% - ${FLOAT_INSET$1}px)`,
256487
+ minWidth: MIN_WIDTH_PX$1,
256488
256488
  backgroundColor: theme2.colors.background,
256489
256489
  border: `1px solid ${theme2.colors.border}`,
256490
256490
  borderRadius: 12,
@@ -256704,7 +256704,7 @@ const SequenceMarkdownOverlay = ({
256704
256704
  top: 0,
256705
256705
  right: 0,
256706
256706
  bottom: 0,
256707
- width: RESIZE_HANDLE_WIDTH,
256707
+ width: RESIZE_HANDLE_WIDTH$1,
256708
256708
  cursor: "ew-resize",
256709
256709
  // Subtle painted line on hover/drag so users discover it without
256710
256710
  // bias-distracting the rest of the time.
@@ -257577,188 +257577,1815 @@ const Placeholder = ({ building }) => {
257577
257577
  }
257578
257578
  );
257579
257579
  };
257580
- const focusBuildingTool = {
257581
- name: "focus_building",
257582
- description: "Focuses the camera on a specific file (building) in the file city visualization",
257583
- inputs: {
257584
- type: "object",
257585
- properties: {
257586
- filePath: {
257587
- type: "string",
257588
- description: "Path to the file to focus on"
257589
- },
257590
- animate: {
257591
- type: "boolean",
257592
- description: "Whether to animate the camera transition"
257593
- }
257594
- },
257595
- required: ["filePath"]
257596
- },
257597
- outputs: {
257598
- type: "object",
257599
- properties: {
257600
- success: { type: "boolean" },
257601
- message: { type: "string" }
257602
- }
257603
- },
257604
- tags: ["navigation", "camera", "focus"],
257605
- tool_call_template: {
257606
- call_template_type: "panel_event",
257607
- event_type: "industry-theme.file-city-panel:focus-building"
257608
- }
257609
- };
257610
- const selectDistrictTool = {
257611
- name: "select_district",
257612
- description: "Selects and highlights a directory (district) in the file city visualization",
257613
- inputs: {
257614
- type: "object",
257615
- properties: {
257616
- directoryPath: {
257617
- type: "string",
257618
- description: "Path to the directory to select"
257619
- },
257620
- expandChildren: {
257621
- type: "boolean",
257622
- description: "Whether to expand and show child buildings"
257623
- }
257624
- },
257625
- required: ["directoryPath"]
257626
- },
257627
- outputs: {
257628
- type: "object",
257629
- properties: {
257630
- success: { type: "boolean" },
257631
- selectedDistrict: { type: "string" }
257580
+ function buildSequenceViewInputs(payload, view) {
257581
+ var _a;
257582
+ const markersById = /* @__PURE__ */ new Map();
257583
+ for (const m of payload.markers) markersById.set(m.id, m);
257584
+ const events2 = [];
257585
+ for (const ref of view.markers) {
257586
+ const marker = markersById.get(ref.markerId);
257587
+ if (!marker) {
257588
+ console.warn(
257589
+ `[trail] sequence view references unknown markerId: ${ref.markerId}`
257590
+ );
257591
+ continue;
257632
257592
  }
257633
- },
257634
- tags: ["selection", "district", "directory"],
257635
- tool_call_template: {
257636
- call_template_type: "panel_event",
257637
- event_type: "industry-theme.file-city-panel:select-district"
257593
+ events2.push({
257594
+ id: marker.id,
257595
+ name: ref.name,
257596
+ label: marker.label,
257597
+ participant: ref.participant,
257598
+ moveEvent: ref.moveEvent,
257599
+ type: ref.type,
257600
+ sourcePath: marker.sourcePath
257601
+ });
257638
257602
  }
257639
- };
257640
- const resetViewTool = {
257641
- name: "reset_view",
257642
- description: "Resets the file city view to the default camera position",
257643
- inputs: {
257644
- type: "object",
257645
- properties: {
257646
- animate: {
257647
- type: "boolean",
257648
- description: "Whether to animate the camera reset"
257649
- }
257650
- }
257651
- },
257652
- outputs: {
257653
- type: "object",
257654
- properties: {
257655
- success: { type: "boolean" }
257603
+ if (view.actors) {
257604
+ for (const actor of view.actors) {
257605
+ events2.push({
257606
+ id: `__actor:${actor.name}`,
257607
+ name: actor.name,
257608
+ label: actor.label
257609
+ });
257656
257610
  }
257657
- },
257658
- tags: ["navigation", "camera", "reset"],
257659
- tool_call_template: {
257660
- call_template_type: "panel_event",
257661
- event_type: "industry-theme.file-city-panel:reset-view"
257662
- }
257663
- };
257664
- const codeCityPanelTools = [
257665
- focusBuildingTool,
257666
- selectDistrictTool,
257667
- resetViewTool
257668
- ];
257669
- const codeCityPanelToolsMetadata = {
257670
- id: "industry-theme.file-city-panel",
257671
- name: "File City Panel",
257672
- description: "Tools provided by the file city visualization panel extension",
257673
- tools: codeCityPanelTools
257674
- };
257675
- const NpmIcon = ({ size }) => /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "#CB3837", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z" }) });
257676
- const YarnIcon = ({ size }) => /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "#2C8EBB", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M12 0C5.375 0 0 5.375 0 12s5.375 12 12 12 12-5.375 12-12S18.625 0 12 0zm.768 4.105c.183 0 .363.053.525.157.125.083.287.185.755 1.154.31-.088.468-.042.551-.019.204.056.366.19.463.375.477.917.542 2.553.334 3.605-.241 1.232-.755 2.029-1.131 2.576.324.329.778.899 1.117 1.825.278.774.31 1.478.273 2.015a5.51 5.51 0 0 0 .602-.329c.593-.366 1.487-.917 2.553-.931.714-.009 1.269.445 1.353 1.103a1.23 1.23 0 0 1-.945 1.362c-.649.158-.95.278-1.821.843-1.232.797-2.539 1.242-3.012 1.39a1.686 1.686 0 0 1-.704.343c-.737.181-3.266.315-3.466.315h-.046c-.783 0-1.214-.241-1.45-.491-.658.329-1.51.19-2.122-.134a1.078 1.078 0 0 1-.58-1.153 1.243 1.243 0 0 1-.153-.195c-.162-.25-.528-.936-.454-1.946.056-.723.556-1.367.88-1.71a5.522 5.522 0 0 1 .408-2.256c.306-.727.885-1.348 1.32-1.737-.32-.537-.644-1.367-.329-2.21.227-.602.412-.936.82-1.08h-.005c.199-.074.389-.153.486-.259a3.418 3.418 0 0 1 2.298-1.103c.037-.093.079-.185.125-.283.31-.658.639-1.029 1.024-1.168a.94.94 0 0 1 .328-.06z" }) });
257677
- const PnpmIcon = ({ size }) => /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "#F69220", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M0 0v7.5h7.5V0zm8.25 0v7.5h7.498V0zm8.25 0v7.5H24V0zM8.25 8.25v7.5h7.498v-7.5zm8.25 0v7.5H24v-7.5zM0 16.5V24h7.5v-7.5zm8.25 0V24h7.498v-7.5zm8.25 0V24H24v-7.5z" }) });
257678
- const BunIcon = ({ size }) => /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "#FBF0DF", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M12 22.596c6.628 0 12-4.338 12-9.688 0-3.318-2.057-6.248-5.219-7.986-1.286-.715-2.297-1.357-3.139-1.89C14.058 2.025 13.08 1.404 12 1.404c-1.097 0-2.334.785-3.966 1.821a49.92 49.92 0 0 1-2.816 1.697C2.057 6.66 0 9.59 0 12.908c0 5.35 5.372 9.687 12 9.687v.001Z" }) });
257679
- const PackageManagerIcon = ({
257680
- packageManager,
257681
- size = 16,
257682
- color: color2
257683
- }) => {
257684
- switch (packageManager) {
257685
- case "npm":
257686
- return /* @__PURE__ */ jsx(NpmIcon, { size });
257687
- case "yarn":
257688
- return /* @__PURE__ */ jsx(YarnIcon, { size });
257689
- case "pnpm":
257690
- return /* @__PURE__ */ jsx(PnpmIcon, { size });
257691
- case "bun":
257692
- return /* @__PURE__ */ jsx(BunIcon, { size });
257693
- default:
257694
- return /* @__PURE__ */ jsx(Package$1, { size, color: color2 });
257695
257611
  }
257696
- };
257697
- const ProjectInfoHeader = ({
257698
- project,
257699
- onOpen,
257700
- compact = false
257701
- }) => {
257612
+ return {
257613
+ events: events2,
257614
+ edges: view.edges,
257615
+ layoutOptions: {
257616
+ laneOrder: (_a = view.layout) == null ? void 0 : _a.laneOrder
257617
+ }
257618
+ };
257619
+ }
257620
+ const FOV_DEG = 50;
257621
+ const FOV_RAD = FOV_DEG * Math.PI / 180;
257622
+ const TAN_HALF_FOV = Math.tan(FOV_RAD / 2);
257623
+ const FIT_PADDING = 1.08;
257624
+ function computeCameraFraming(inputs) {
257625
+ const { canvasW, canvasH, cityBounds, leftInset, rightInset, topInset, bottomInset } = inputs;
257626
+ if (canvasW <= 0 || canvasH <= 0) return null;
257627
+ const citySize = Math.max(
257628
+ cityBounds.maxX - cityBounds.minX,
257629
+ cityBounds.maxZ - cityBounds.minZ
257630
+ );
257631
+ const effectiveAspect = Math.min(1, canvasW / canvasH);
257632
+ const baseline = citySize / (2 * TAN_HALF_FOV * effectiveAspect) * FIT_PADDING;
257633
+ const visW = canvasW - leftInset - rightInset;
257634
+ const visH = canvasH - topInset - bottomInset;
257635
+ if (visW <= 0 || visH <= 0) return null;
257636
+ const fraction = Math.min(visW / canvasW, visH / canvasH);
257637
+ const height = baseline / fraction;
257638
+ const vH = 2 * height * TAN_HALF_FOV;
257639
+ const vW = vH * (canvasW / canvasH);
257640
+ const dxPx = canvasW / 2 - (leftInset + (canvasW - rightInset)) / 2;
257641
+ const dyPx = canvasH / 2 - (topInset + (canvasH - bottomInset)) / 2;
257642
+ const targetX = dxPx * (vW / canvasW);
257643
+ const targetZ = dyPx * (vH / canvasH);
257644
+ return { targetX, targetZ, height };
257645
+ }
257646
+ const TrailLeaderLine = React.forwardRef(function TrailLeaderLine2({ containerRef, building, cityCenter, targetRef }, ref) {
257702
257647
  const { theme: theme2 } = useTheme();
257703
- const totalDeps = (project.dependencyCount || 0) + (project.devDependencyCount || 0);
257704
- const fontSizeSmall = theme2.fontSizes[0];
257705
- const fontSizeMedium = theme2.fontSizes[1];
257706
- const fontSizeLarge = theme2.fontSizes[2] || theme2.fontSizes[1];
257707
- return /* @__PURE__ */ jsxs(
257708
- "div",
257709
- {
257710
- style: {
257711
- display: "flex",
257712
- flexDirection: "column",
257713
- gap: compact ? 4 : 8,
257714
- padding: compact ? "8px 12px" : "12px 16px",
257715
- backgroundColor: theme2.colors.background,
257716
- borderBottom: `1px solid ${theme2.colors.border}`,
257717
- fontFamily: theme2.fonts.body,
257718
- minHeight: compact ? 48 : "auto"
257719
- },
257720
- children: [
257721
- /* @__PURE__ */ jsxs(
257722
- "div",
257723
- {
257724
- style: {
257725
- display: "flex",
257726
- alignItems: "center",
257727
- justifyContent: "space-between",
257728
- gap: 12
257729
- },
257730
- children: [
257731
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, flex: 1, minWidth: 0 }, children: [
257732
- project.packageManager && /* @__PURE__ */ jsx("div", { style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(
257733
- PackageManagerIcon,
257734
- {
257735
- packageManager: project.packageManager,
257736
- size: compact ? 18 : 20,
257737
- color: theme2.colors.textSecondary
257738
- }
257739
- ) }),
257740
- /* @__PURE__ */ jsx(
257741
- "span",
257742
- {
257743
- style: {
257744
- fontSize: compact ? fontSizeMedium : fontSizeLarge,
257745
- fontWeight: 600,
257746
- color: theme2.colors.text,
257747
- overflow: "hidden",
257748
- textOverflow: "ellipsis",
257749
- whiteSpace: "nowrap"
257750
- },
257751
- title: project.name,
257752
- children: project.name
257753
- }
257754
- ),
257755
- project.version && /* @__PURE__ */ jsxs(
257756
- "span",
257757
- {
257758
- style: {
257759
- fontSize: fontSizeSmall,
257760
- color: theme2.colors.textSecondary,
257761
- backgroundColor: theme2.colors.backgroundLight,
257648
+ const color2 = theme2.colors.primary ?? "#22d3ee";
257649
+ const pathRef = React.useRef(null);
257650
+ const buildingMarkerRef = React.useRef(null);
257651
+ const nodeMarkerRef = React.useRef(null);
257652
+ const projectScratch = React.useRef(new THREE.Vector3());
257653
+ const buildingRef = React.useRef(building);
257654
+ const cityCenterRef = React.useRef(cityCenter);
257655
+ React.useEffect(() => {
257656
+ buildingRef.current = building;
257657
+ }, [building]);
257658
+ React.useEffect(() => {
257659
+ cityCenterRef.current = cityCenter;
257660
+ }, [cityCenter]);
257661
+ const hideAll = React.useCallback(() => {
257662
+ var _a, _b, _c;
257663
+ (_a = pathRef.current) == null ? void 0 : _a.setAttribute("opacity", "0");
257664
+ (_b = buildingMarkerRef.current) == null ? void 0 : _b.setAttribute("opacity", "0");
257665
+ (_c = nodeMarkerRef.current) == null ? void 0 : _c.setAttribute("opacity", "0");
257666
+ }, []);
257667
+ React.useEffect(() => {
257668
+ if (!building) hideAll();
257669
+ }, [building, hideAll]);
257670
+ const onCameraFrame = React.useCallback(
257671
+ (camera, size) => {
257672
+ const target = buildingRef.current;
257673
+ const center = cityCenterRef.current;
257674
+ const container = containerRef.current;
257675
+ const nodeEl = targetRef.current;
257676
+ if (!target || !center || !nodeEl || !container || size.width === 0) {
257677
+ hideAll();
257678
+ return;
257679
+ }
257680
+ const containerRect = container.getBoundingClientRect();
257681
+ const nodeRect = nodeEl.getBoundingClientRect();
257682
+ const canvasEl = container.querySelector("canvas");
257683
+ if (!canvasEl) {
257684
+ hideAll();
257685
+ return;
257686
+ }
257687
+ const canvasRect = canvasEl.getBoundingClientRect();
257688
+ const canvasLeft = canvasRect.left - containerRect.left;
257689
+ const canvasTop = canvasRect.top - containerRect.top;
257690
+ const v = projectScratch.current;
257691
+ v.set(
257692
+ target.position.x - center.x,
257693
+ 0,
257694
+ target.position.z - center.z
257695
+ ).project(camera);
257696
+ const behindCamera = v.z > 1;
257697
+ if (behindCamera) {
257698
+ hideAll();
257699
+ return;
257700
+ }
257701
+ const sx = canvasLeft + (v.x * 0.5 + 0.5) * size.width;
257702
+ const sy = canvasTop + (v.y * -0.5 + 0.5) * size.height;
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;
257707
+ 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}`;
257712
+ const pathEl = pathRef.current;
257713
+ const buildingEl = buildingMarkerRef.current;
257714
+ const nodeMarkerEl = nodeMarkerRef.current;
257715
+ if (pathEl) {
257716
+ pathEl.setAttribute("d", d);
257717
+ pathEl.setAttribute("stroke", color2);
257718
+ pathEl.setAttribute("opacity", "0.85");
257719
+ }
257720
+ if (buildingEl) {
257721
+ buildingEl.setAttribute("x", String(sx - 3.5));
257722
+ buildingEl.setAttribute("y", String(sy - 3.5));
257723
+ buildingEl.setAttribute("fill", color2);
257724
+ buildingEl.setAttribute("opacity", "1");
257725
+ }
257726
+ if (nodeMarkerEl) {
257727
+ nodeMarkerEl.setAttribute("cx", String(bx));
257728
+ nodeMarkerEl.setAttribute("cy", String(by));
257729
+ nodeMarkerEl.setAttribute("fill", color2);
257730
+ nodeMarkerEl.setAttribute("opacity", "1");
257731
+ }
257732
+ },
257733
+ [containerRef, targetRef, hideAll, color2]
257734
+ );
257735
+ React.useImperativeHandle(ref, () => ({ onCameraFrame }), [onCameraFrame]);
257736
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
257737
+ /* @__PURE__ */ jsxs(
257738
+ "svg",
257739
+ {
257740
+ width: "100%",
257741
+ height: "100%",
257742
+ style: {
257743
+ position: "absolute",
257744
+ inset: 0,
257745
+ pointerEvents: "none",
257746
+ overflow: "visible",
257747
+ // Above the 3D canvas (auto z-index) but below the floating
257748
+ // overlays (z 1900), so the line only paints over the city.
257749
+ zIndex: 25
257750
+ },
257751
+ children: [
257752
+ /* @__PURE__ */ jsx(
257753
+ "path",
257754
+ {
257755
+ ref: pathRef,
257756
+ fill: "none",
257757
+ strokeWidth: 1.75,
257758
+ strokeDasharray: "5 4",
257759
+ opacity: 0
257760
+ }
257761
+ ),
257762
+ /* @__PURE__ */ jsx(
257763
+ "rect",
257764
+ {
257765
+ ref: buildingMarkerRef,
257766
+ width: 7,
257767
+ height: 7,
257768
+ stroke: "#0f1419",
257769
+ strokeWidth: 1.25,
257770
+ opacity: 0
257771
+ }
257772
+ )
257773
+ ]
257774
+ }
257775
+ ),
257776
+ /* @__PURE__ */ jsx(
257777
+ "svg",
257778
+ {
257779
+ width: "100%",
257780
+ height: "100%",
257781
+ style: {
257782
+ position: "absolute",
257783
+ inset: 0,
257784
+ pointerEvents: "none",
257785
+ overflow: "visible",
257786
+ zIndex: 1901
257787
+ },
257788
+ children: /* @__PURE__ */ jsx("circle", { ref: nodeMarkerRef, r: 3.5, opacity: 0 })
257789
+ }
257790
+ )
257791
+ ] });
257792
+ });
257793
+ const PANEL_WIDTH_PCT = 38;
257794
+ const FLOAT_INSET = 16;
257795
+ const TOP_INSET = 16;
257796
+ const MIN_WIDTH_PX = 360;
257797
+ const RESIZE_HANDLE_WIDTH = 6;
257798
+ const TrailMarkdownOverlay = ({
257799
+ eyebrow,
257800
+ title,
257801
+ markdown: markdown2,
257802
+ slideIdPrefix,
257803
+ bottomOffset,
257804
+ containerRef: externalContainerRef,
257805
+ nav
257806
+ }) => {
257807
+ const { theme: theme2 } = useTheme();
257808
+ const body = markdown2.trim();
257809
+ const bottomOffsetCss = typeof bottomOffset === "number" ? `${bottomOffset}px` : bottomOffset;
257810
+ const [hasEntered, setHasEntered] = React.useState(false);
257811
+ const [widthPx, setWidthPx] = React.useState(null);
257812
+ const [isResizing, setIsResizing] = React.useState(false);
257813
+ const containerRef = React.useRef(null);
257814
+ const setContainerRef = React.useCallback(
257815
+ (node2) => {
257816
+ containerRef.current = node2;
257817
+ if (typeof externalContainerRef === "function") {
257818
+ externalContainerRef(node2);
257819
+ } else if (externalContainerRef) {
257820
+ externalContainerRef.current = node2;
257821
+ }
257822
+ },
257823
+ [externalContainerRef]
257824
+ );
257825
+ const dragStateRef = React.useRef(null);
257826
+ const handleResizePointerDown = React.useCallback(
257827
+ (e) => {
257828
+ if (e.button !== 0) return;
257829
+ const el = containerRef.current;
257830
+ if (!el) return;
257831
+ e.preventDefault();
257832
+ dragStateRef.current = {
257833
+ startX: e.clientX,
257834
+ startWidth: el.getBoundingClientRect().width
257835
+ };
257836
+ setIsResizing(true);
257837
+ e.currentTarget.setPointerCapture(e.pointerId);
257838
+ },
257839
+ []
257840
+ );
257841
+ const handleResizePointerMove = React.useCallback(
257842
+ (e) => {
257843
+ var _a;
257844
+ const drag2 = dragStateRef.current;
257845
+ if (!drag2) return;
257846
+ const parent = (_a = containerRef.current) == null ? void 0 : _a.parentElement;
257847
+ const parentWidth = (parent == null ? void 0 : parent.clientWidth) ?? window.innerWidth;
257848
+ const maxWidth = Math.max(MIN_WIDTH_PX, parentWidth - FLOAT_INSET * 2);
257849
+ const dx = e.clientX - drag2.startX;
257850
+ const next2 = Math.min(maxWidth, Math.max(MIN_WIDTH_PX, drag2.startWidth + dx));
257851
+ setWidthPx(next2);
257852
+ },
257853
+ []
257854
+ );
257855
+ const handleResizePointerUp = React.useCallback(
257856
+ (e) => {
257857
+ if (!dragStateRef.current) return;
257858
+ dragStateRef.current = null;
257859
+ setIsResizing(false);
257860
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) {
257861
+ e.currentTarget.releasePointerCapture(e.pointerId);
257862
+ }
257863
+ },
257864
+ []
257865
+ );
257866
+ if (!body) return null;
257867
+ return /* @__PURE__ */ jsxs(
257868
+ "div",
257869
+ {
257870
+ ref: setContainerRef,
257871
+ onAnimationEnd: () => setHasEntered(true),
257872
+ style: {
257873
+ position: "absolute",
257874
+ top: TOP_INSET,
257875
+ left: FLOAT_INSET,
257876
+ bottom: `calc(${bottomOffsetCss} + ${FLOAT_INSET}px)`,
257877
+ width: widthPx != null ? widthPx : `calc(${PANEL_WIDTH_PCT}% - ${FLOAT_INSET}px)`,
257878
+ minWidth: MIN_WIDTH_PX,
257879
+ backgroundColor: theme2.colors.background,
257880
+ border: `1px solid ${theme2.colors.border}`,
257881
+ borderRadius: 12,
257882
+ overflow: "hidden",
257883
+ boxShadow: "0 12px 32px rgba(0, 0, 0, 0.28)",
257884
+ display: "flex",
257885
+ flexDirection: "column",
257886
+ zIndex: 1900,
257887
+ animation: hasEntered || isResizing ? void 0 : "trailMarkdownOverlaySlideIn 220ms ease-out",
257888
+ userSelect: isResizing ? "none" : void 0
257889
+ },
257890
+ children: [
257891
+ /* @__PURE__ */ jsx("style", { children: `
257892
+ @keyframes trailMarkdownOverlaySlideIn {
257893
+ from { transform: translateX(-100%); }
257894
+ to { transform: translateX(0); }
257895
+ }
257896
+ ` }),
257897
+ /* @__PURE__ */ jsxs(
257898
+ "div",
257899
+ {
257900
+ style: {
257901
+ padding: "10px 14px",
257902
+ borderBottom: `1px solid ${theme2.colors.border}`,
257903
+ display: "flex",
257904
+ flexDirection: "column",
257905
+ color: theme2.colors.text,
257906
+ fontFamily: theme2.fonts.body,
257907
+ flexShrink: 0,
257908
+ minWidth: 0
257909
+ },
257910
+ children: [
257911
+ /* @__PURE__ */ jsx(
257912
+ "span",
257913
+ {
257914
+ style: {
257915
+ fontSize: theme2.fontSizes[0],
257916
+ color: theme2.colors.textSecondary,
257917
+ letterSpacing: 0.4,
257918
+ textTransform: "uppercase"
257919
+ },
257920
+ children: eyebrow
257921
+ }
257922
+ ),
257923
+ /* @__PURE__ */ jsx(
257924
+ "span",
257925
+ {
257926
+ style: {
257927
+ fontSize: theme2.fontSizes[1],
257928
+ fontWeight: 600,
257929
+ overflow: "hidden",
257930
+ textOverflow: "ellipsis",
257931
+ whiteSpace: "nowrap"
257932
+ },
257933
+ title,
257934
+ children: title
257935
+ }
257936
+ )
257937
+ ]
257938
+ }
257939
+ ),
257940
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, minHeight: 0, position: "relative" }, children: /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0 }, children: /* @__PURE__ */ jsx(
257941
+ IndustryMarkdownSlide,
257942
+ {
257943
+ content: body,
257944
+ slideIdPrefix,
257945
+ slideIndex: 0,
257946
+ isVisible: true,
257947
+ theme: theme2,
257948
+ transparentBackground: true,
257949
+ enableKeyboardScrolling: false
257950
+ }
257951
+ ) }) }),
257952
+ nav ? /* @__PURE__ */ jsx(TrailMarkdownOverlayFooter, { nav }) : null,
257953
+ /* @__PURE__ */ jsx(
257954
+ "div",
257955
+ {
257956
+ role: "separator",
257957
+ "aria-orientation": "vertical",
257958
+ "aria-label": "Resize markdown panel",
257959
+ onPointerDown: handleResizePointerDown,
257960
+ onPointerMove: handleResizePointerMove,
257961
+ onPointerUp: handleResizePointerUp,
257962
+ onPointerCancel: handleResizePointerUp,
257963
+ style: {
257964
+ position: "absolute",
257965
+ top: 0,
257966
+ right: 0,
257967
+ bottom: 0,
257968
+ width: RESIZE_HANDLE_WIDTH,
257969
+ cursor: "ew-resize",
257970
+ backgroundColor: isResizing ? theme2.colors.accent : "transparent",
257971
+ transition: isResizing ? void 0 : "background-color 120ms ease",
257972
+ touchAction: "none"
257973
+ },
257974
+ onMouseEnter: (e) => {
257975
+ if (isResizing) return;
257976
+ e.currentTarget.style.backgroundColor = theme2.colors.border;
257977
+ },
257978
+ onMouseLeave: (e) => {
257979
+ if (isResizing) return;
257980
+ e.currentTarget.style.backgroundColor = "transparent";
257981
+ }
257982
+ }
257983
+ )
257984
+ ]
257985
+ }
257986
+ );
257987
+ };
257988
+ const TrailMarkdownOverlayFooter = ({
257989
+ nav
257990
+ }) => {
257991
+ const { theme: theme2 } = useTheme();
257992
+ const idle = nav.position < 0;
257993
+ const canPrev = !idle && nav.position > 0;
257994
+ const canNext = !idle && nav.position < nav.total - 1;
257995
+ const stepButtonStyle = (enabled) => ({
257996
+ background: "transparent",
257997
+ border: `1px solid ${theme2.colors.muted}`,
257998
+ borderRadius: theme2.radii[2],
257999
+ color: enabled ? theme2.colors.text : theme2.colors.textTertiary,
258000
+ cursor: enabled ? "pointer" : "not-allowed",
258001
+ padding: "4px 10px",
258002
+ fontFamily: theme2.fonts.body,
258003
+ fontSize: theme2.fontSizes[0],
258004
+ lineHeight: 1.2,
258005
+ opacity: enabled ? 1 : 0.5
258006
+ });
258007
+ return /* @__PURE__ */ jsx(
258008
+ "div",
258009
+ {
258010
+ style: {
258011
+ padding: "8px 14px",
258012
+ borderTop: `1px solid ${theme2.colors.border}`,
258013
+ display: "flex",
258014
+ alignItems: "center",
258015
+ justifyContent: idle ? "center" : "space-between",
258016
+ gap: 8,
258017
+ flexShrink: 0
258018
+ },
258019
+ children: idle ? /* @__PURE__ */ jsx(
258020
+ "button",
258021
+ {
258022
+ type: "button",
258023
+ onClick: nav.onStart,
258024
+ disabled: nav.total === 0,
258025
+ title: nav.total === 0 ? "No markers in this repo" : "Jump to the first marker",
258026
+ style: {
258027
+ background: theme2.colors.accent,
258028
+ color: theme2.colors.background,
258029
+ border: "none",
258030
+ borderRadius: theme2.radii[3],
258031
+ padding: "6px 14px",
258032
+ fontFamily: theme2.fonts.body,
258033
+ fontSize: theme2.fontSizes[0],
258034
+ fontWeight: theme2.fontWeights.semibold,
258035
+ cursor: nav.total === 0 ? "not-allowed" : "pointer",
258036
+ opacity: nav.total === 0 ? 0.5 : 1,
258037
+ lineHeight: 1.2
258038
+ },
258039
+ children: "Start trail"
258040
+ }
258041
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
258042
+ /* @__PURE__ */ jsx(
258043
+ "button",
258044
+ {
258045
+ type: "button",
258046
+ onClick: nav.onPrev,
258047
+ disabled: !canPrev,
258048
+ "aria-label": "Previous marker",
258049
+ title: "Previous marker",
258050
+ style: stepButtonStyle(canPrev),
258051
+ children: "◀ Prev"
258052
+ }
258053
+ ),
258054
+ /* @__PURE__ */ jsxs(
258055
+ "span",
258056
+ {
258057
+ style: {
258058
+ fontFamily: theme2.fonts.body,
258059
+ fontSize: theme2.fontSizes[0],
258060
+ color: theme2.colors.textSecondary,
258061
+ fontVariantNumeric: "tabular-nums"
258062
+ },
258063
+ children: [
258064
+ nav.position + 1,
258065
+ " / ",
258066
+ nav.total
258067
+ ]
258068
+ }
258069
+ ),
258070
+ /* @__PURE__ */ jsx(
258071
+ "button",
258072
+ {
258073
+ type: "button",
258074
+ onClick: nav.onNext,
258075
+ disabled: !canNext,
258076
+ "aria-label": "Next marker",
258077
+ title: "Next marker",
258078
+ style: stepButtonStyle(canNext),
258079
+ children: "Next ▶"
258080
+ }
258081
+ )
258082
+ ] })
258083
+ }
258084
+ );
258085
+ };
258086
+ const TrailSnippetView = ({
258087
+ filePath,
258088
+ fileName,
258089
+ startLine,
258090
+ endLine,
258091
+ focusLine,
258092
+ contextLines = 2,
258093
+ readFile,
258094
+ background
258095
+ }) => {
258096
+ const { theme: theme2 } = useTheme();
258097
+ const [contents, setContents] = React.useState(null);
258098
+ const [error, setError] = React.useState(null);
258099
+ React.useEffect(() => {
258100
+ let cancelled = false;
258101
+ setContents(null);
258102
+ setError(null);
258103
+ readFile(filePath).then((content2) => {
258104
+ if (cancelled) return;
258105
+ setContents(content2);
258106
+ }).catch((err) => {
258107
+ if (cancelled) return;
258108
+ setError(err instanceof Error ? err.message : "Failed to read file");
258109
+ });
258110
+ return () => {
258111
+ cancelled = true;
258112
+ };
258113
+ }, [filePath, readFile]);
258114
+ const slice = React.useMemo(() => {
258115
+ if (contents == null) return null;
258116
+ const allLines = contents.split("\n");
258117
+ const total = allLines.length;
258118
+ const safeStart = Math.max(1, Math.min(startLine, total));
258119
+ const safeEnd = Math.max(safeStart, Math.min(endLine, total));
258120
+ const sliceStart = Math.max(1, safeStart - contextLines);
258121
+ const sliceEnd = Math.min(total, safeEnd + contextLines);
258122
+ return {
258123
+ contents: allLines.slice(sliceStart - 1, sliceEnd).join("\n"),
258124
+ sliceStart,
258125
+ sliceEnd,
258126
+ focusOffset: focusLine == null ? null : Math.max(1, focusLine - sliceStart + 1)
258127
+ };
258128
+ }, [contents, startLine, endLine, contextLines, focusLine]);
258129
+ const fileObject = React.useMemo(
258130
+ () => slice ? { name: fileName, contents: slice.contents } : null,
258131
+ [fileName, slice]
258132
+ );
258133
+ const lineNumberOffset = slice ? slice.sliceStart - 1 : 0;
258134
+ const onPostRender = React.useCallback(
258135
+ (fileContainer) => {
258136
+ if (lineNumberOffset === 0) return;
258137
+ const root2 = fileContainer.shadowRoot ?? fileContainer;
258138
+ const items = root2.querySelectorAll(
258139
+ "[data-column-number][data-line-index]"
258140
+ );
258141
+ items.forEach((el) => {
258142
+ const idxStr = el.dataset.lineIndex;
258143
+ if (idxStr == null) return;
258144
+ const idx = Number.parseInt(idxStr, 10);
258145
+ if (Number.isNaN(idx)) return;
258146
+ const display = String(idx + 1 + lineNumberOffset);
258147
+ const span = el.querySelector(
258148
+ "[data-line-number-content]"
258149
+ );
258150
+ if (span && span.textContent !== display) {
258151
+ span.textContent = display;
258152
+ }
258153
+ });
258154
+ },
258155
+ [lineNumberOffset]
258156
+ );
258157
+ const options = React.useMemo(() => {
258158
+ const base2 = background ? buildPierreOptions(background) : pierreOptions;
258159
+ return { ...base2, onPostRender };
258160
+ }, [background, onPostRender]);
258161
+ if (error) {
258162
+ return /* @__PURE__ */ jsx("div", { style: { padding: 16, color: theme2.colors.error }, children: error });
258163
+ }
258164
+ if (!fileObject || !slice) {
258165
+ return /* @__PURE__ */ jsx("div", { style: { padding: 16, color: theme2.colors.textSecondary }, children: "Loading…" });
258166
+ }
258167
+ const rangeLabel = slice.sliceStart === slice.sliceEnd ? `Line ${slice.sliceStart}` : `Lines ${slice.sliceStart}–${slice.sliceEnd}`;
258168
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", minHeight: 0 }, children: [
258169
+ /* @__PURE__ */ jsx(
258170
+ "div",
258171
+ {
258172
+ style: {
258173
+ padding: "4px 14px 6px",
258174
+ fontFamily: theme2.fonts.monospace,
258175
+ fontSize: theme2.fontSizes[0],
258176
+ color: theme2.colors.textSecondary,
258177
+ letterSpacing: 0.4,
258178
+ textTransform: "uppercase"
258179
+ },
258180
+ children: rangeLabel
258181
+ }
258182
+ ),
258183
+ /* @__PURE__ */ jsx(
258184
+ File$2,
258185
+ {
258186
+ file: fileObject,
258187
+ options,
258188
+ selectedLines: slice.focusOffset != null ? { start: slice.focusOffset, end: slice.focusOffset } : void 0,
258189
+ style: pierreStyle$1
258190
+ }
258191
+ )
258192
+ ] });
258193
+ };
258194
+ const buildBackgroundCSS = (color2) => `
258195
+ :host {
258196
+ background: ${color2} !important;
258197
+ }
258198
+ pre, code,
258199
+ [data-gutter], [data-content],
258200
+ [data-line], [data-column-number],
258201
+ [data-gutter-buffer], [data-line-annotation], [data-no-newline],
258202
+ [data-separator], [data-separator-wrapper] {
258203
+ background: ${color2} !important;
258204
+ }
258205
+ [data-line] span {
258206
+ background: ${color2} !important;
258207
+ }
258208
+ `;
258209
+ const pierreOptions = {
258210
+ disableFileHeader: true
258211
+ };
258212
+ const buildPierreOptions = (background) => ({
258213
+ disableFileHeader: true,
258214
+ unsafeCSS: buildBackgroundCSS(background)
258215
+ });
258216
+ const pierreStyle$1 = {
258217
+ display: "block"
258218
+ };
258219
+ const sliceWindow = (oldContents, newContents, startLine, endLine, focusLine, contextLines) => {
258220
+ if (startLine == null || endLine == null) {
258221
+ return {
258222
+ oldText: oldContents,
258223
+ newText: newContents,
258224
+ windowStart: 1,
258225
+ focusOffset: focusLine ?? null
258226
+ };
258227
+ }
258228
+ const oldLines = oldContents.split("\n");
258229
+ const newLines = newContents.split("\n");
258230
+ const totalNew = newLines.length;
258231
+ const totalOld = oldLines.length;
258232
+ const safeStart = Math.max(1, Math.min(startLine, totalNew));
258233
+ const safeEnd = Math.max(safeStart, Math.min(endLine, totalNew));
258234
+ const sliceStart = Math.max(1, safeStart - contextLines);
258235
+ const sliceEnd = Math.min(
258236
+ Math.max(totalOld, totalNew),
258237
+ safeEnd + contextLines
258238
+ );
258239
+ const sliceOldEnd = Math.min(totalOld, sliceEnd);
258240
+ const sliceNewEnd = Math.min(totalNew, sliceEnd);
258241
+ return {
258242
+ oldText: oldLines.slice(sliceStart - 1, sliceOldEnd).join("\n"),
258243
+ newText: newLines.slice(sliceStart - 1, sliceNewEnd).join("\n"),
258244
+ windowStart: sliceStart,
258245
+ focusOffset: focusLine == null ? null : Math.max(1, focusLine - sliceStart + 1)
258246
+ };
258247
+ };
258248
+ const TrailDiffSnippetView = ({
258249
+ filePath,
258250
+ fileName,
258251
+ oldContents,
258252
+ newContents,
258253
+ readFile,
258254
+ startLine,
258255
+ endLine,
258256
+ focusLine,
258257
+ contextLines = 2,
258258
+ background,
258259
+ diffStyle = "unified"
258260
+ }) => {
258261
+ const { theme: theme2 } = useTheme();
258262
+ const [resolvedNew, setResolvedNew] = React.useState(
258263
+ newContents ?? null
258264
+ );
258265
+ const [error, setError] = React.useState(null);
258266
+ React.useEffect(() => {
258267
+ if (newContents !== void 0) {
258268
+ setResolvedNew(newContents);
258269
+ setError(null);
258270
+ return;
258271
+ }
258272
+ let cancelled = false;
258273
+ setResolvedNew(null);
258274
+ setError(null);
258275
+ readFile(filePath).then((content2) => {
258276
+ if (cancelled) return;
258277
+ setResolvedNew(content2);
258278
+ }).catch((err) => {
258279
+ if (cancelled) return;
258280
+ setError(err instanceof Error ? err.message : "Failed to read file");
258281
+ });
258282
+ return () => {
258283
+ cancelled = true;
258284
+ };
258285
+ }, [filePath, newContents, readFile]);
258286
+ const slice = React.useMemo(
258287
+ () => resolvedNew == null ? null : sliceWindow(
258288
+ oldContents,
258289
+ resolvedNew,
258290
+ startLine,
258291
+ endLine,
258292
+ focusLine,
258293
+ contextLines
258294
+ ),
258295
+ [oldContents, resolvedNew, startLine, endLine, focusLine, contextLines]
258296
+ );
258297
+ const fileDiff = React.useMemo(() => {
258298
+ if (!slice) return null;
258299
+ try {
258300
+ return parseDiffFromFile(
258301
+ { name: fileName, contents: slice.oldText },
258302
+ { name: fileName, contents: slice.newText }
258303
+ );
258304
+ } catch {
258305
+ return null;
258306
+ }
258307
+ }, [fileName, slice]);
258308
+ if (error) {
258309
+ return /* @__PURE__ */ jsx("div", { style: { padding: 16, color: theme2.colors.error }, children: error });
258310
+ }
258311
+ if (!slice) {
258312
+ return /* @__PURE__ */ jsx("div", { style: { padding: 16, color: theme2.colors.textSecondary }, children: "Loading…" });
258313
+ }
258314
+ if (!fileDiff) {
258315
+ return /* @__PURE__ */ jsx("div", { style: { padding: 16, color: theme2.colors.error }, children: "Failed to compute diff" });
258316
+ }
258317
+ const rangeLabel = startLine != null && endLine != null ? startLine === endLine ? `Line ${startLine}` : `Lines ${startLine}–${endLine}` : null;
258318
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", minHeight: 0 }, children: [
258319
+ rangeLabel && /* @__PURE__ */ jsx(
258320
+ "div",
258321
+ {
258322
+ style: {
258323
+ padding: "4px 14px 6px",
258324
+ fontFamily: theme2.fonts.monospace,
258325
+ fontSize: theme2.fontSizes[0],
258326
+ color: theme2.colors.textSecondary,
258327
+ letterSpacing: 0.4,
258328
+ textTransform: "uppercase"
258329
+ },
258330
+ children: rangeLabel
258331
+ }
258332
+ ),
258333
+ /* @__PURE__ */ jsx(
258334
+ FileDiff$2,
258335
+ {
258336
+ fileDiff,
258337
+ options: { ...pierreOptionsBase, diffStyle },
258338
+ selectedLines: slice.focusOffset != null ? { start: slice.focusOffset, end: slice.focusOffset } : void 0,
258339
+ style: background ? {
258340
+ ...pierreStyle,
258341
+ // Pierre derives addition/deletion/context surfaces by
258342
+ // `color-mix`ing from --diffs-bg, which is keyed off
258343
+ // --diffs-light-bg / --diffs-dark-bg. Overriding the
258344
+ // source variables recolors the whole palette coherently.
258345
+ ["--diffs-light-bg"]: background,
258346
+ ["--diffs-dark-bg"]: background
258347
+ } : pierreStyle
258348
+ }
258349
+ )
258350
+ ] });
258351
+ };
258352
+ const pierreOptionsBase = {
258353
+ disableFileHeader: true
258354
+ };
258355
+ const pierreStyle = {
258356
+ display: "block"
258357
+ };
258358
+ const SEQUENCE_DRAWER_HEIGHT_PCT = 38;
258359
+ const SNIPPET_PANE_WIDTH_PX = 460;
258360
+ const HEADER_HEIGHT_PX = 48;
258361
+ const THREE_D_TOGGLE_DISABLED = true;
258362
+ const FileCityTrailExplorerPanel = ({ context, actions }) => {
258363
+ var _a;
258364
+ const tree = context.fileTree.data;
258365
+ const lineCounts = ((_a = context.lineCounts.data) == null ? void 0 : _a.lineCounts) ?? null;
258366
+ const trail2 = context.trail.data;
258367
+ const repository = context.repository;
258368
+ const cityData = React.useMemo(
258369
+ () => tree ? buildCityDataFromContext({ fileTree: tree, lineCounts }) : null,
258370
+ [tree, lineCounts]
258371
+ );
258372
+ const [selectedMarkerId, setSelectedMarkerId] = React.useState(null);
258373
+ React.useEffect(() => {
258374
+ setSelectedMarkerId(null);
258375
+ }, [trail2 == null ? void 0 : trail2.id]);
258376
+ const markersForThisRepo = React.useMemo(() => {
258377
+ if (!trail2) return [];
258378
+ return filterMarkersForRepo(trail2, repository);
258379
+ }, [trail2, repository]);
258380
+ if (!trail2) {
258381
+ return /* @__PURE__ */ jsx(
258382
+ FileCityTrailExplorerEmptyState,
258383
+ {
258384
+ message: "No trail loaded.",
258385
+ cityData
258386
+ }
258387
+ );
258388
+ }
258389
+ const view = trail2.views[0];
258390
+ if (!view) {
258391
+ return /* @__PURE__ */ jsx(
258392
+ FileCityTrailExplorerEmptyState,
258393
+ {
258394
+ message: "Trail has no views.",
258395
+ cityData
258396
+ }
258397
+ );
258398
+ }
258399
+ if (view.kind !== "sequence") {
258400
+ return /* @__PURE__ */ jsx(
258401
+ FileCityTrailExplorerEmptyState,
258402
+ {
258403
+ message: `Unsupported view kind: ${view.kind}. v1 only renders kind:'sequence'.`,
258404
+ cityData
258405
+ }
258406
+ );
258407
+ }
258408
+ return /* @__PURE__ */ jsx(
258409
+ FileCityTrailSequenceLayout,
258410
+ {
258411
+ trail: trail2,
258412
+ view,
258413
+ markersForThisRepo,
258414
+ selectedMarkerId,
258415
+ onSelectMarker: setSelectedMarkerId,
258416
+ cityData,
258417
+ hasLineCounts: lineCounts != null && Object.keys(lineCounts).length > 0,
258418
+ readFile: actions.readFile
258419
+ }
258420
+ );
258421
+ };
258422
+ const FileCityTrailSequenceLayout = ({
258423
+ trail: trail2,
258424
+ view,
258425
+ markersForThisRepo,
258426
+ selectedMarkerId,
258427
+ onSelectMarker,
258428
+ cityData,
258429
+ hasLineCounts,
258430
+ readFile
258431
+ }) => {
258432
+ var _a, _b;
258433
+ const { theme: theme2 } = useTheme();
258434
+ const [show3D, setShow3D] = React.useState(
258435
+ false
258436
+ );
258437
+ React.useEffect(() => {
258438
+ return;
258439
+ }, [hasLineCounts]);
258440
+ const effectiveShow3D = false;
258441
+ const selectedMarker = React.useMemo(() => {
258442
+ if (!selectedMarkerId) return null;
258443
+ return trail2.markers.find((m) => m.id === selectedMarkerId) ?? null;
258444
+ }, [trail2.markers, selectedMarkerId]);
258445
+ const markerBySourcePathForThisRepo = React.useMemo(() => {
258446
+ const map2 = /* @__PURE__ */ new Map();
258447
+ for (const m of markersForThisRepo) {
258448
+ if (m.sourcePath) map2.set(m.sourcePath, m);
258449
+ }
258450
+ return map2;
258451
+ }, [markersForThisRepo]);
258452
+ const handleBuildingClick = React.useCallback(
258453
+ (building) => {
258454
+ const marker = markerBySourcePathForThisRepo.get(building.path);
258455
+ if (!marker) return;
258456
+ onSelectMarker(selectedMarkerId === marker.id ? null : marker.id);
258457
+ },
258458
+ [markerBySourcePathForThisRepo, selectedMarkerId, onSelectMarker]
258459
+ );
258460
+ const stepperIndex = React.useMemo(() => {
258461
+ if (!selectedMarkerId) return -1;
258462
+ return markersForThisRepo.findIndex((m) => m.id === selectedMarkerId);
258463
+ }, [markersForThisRepo, selectedMarkerId]);
258464
+ const handleStartTrail = React.useCallback(() => {
258465
+ const first = markersForThisRepo[0];
258466
+ if (first) onSelectMarker(first.id);
258467
+ }, [markersForThisRepo, onSelectMarker]);
258468
+ const handleStepPrev = React.useCallback(() => {
258469
+ if (stepperIndex <= 0) return;
258470
+ const target = markersForThisRepo[stepperIndex - 1];
258471
+ if (target) onSelectMarker(target.id);
258472
+ }, [markersForThisRepo, stepperIndex, onSelectMarker]);
258473
+ const handleStepNext = React.useCallback(() => {
258474
+ if (stepperIndex < 0 || stepperIndex >= markersForThisRepo.length - 1) return;
258475
+ const target = markersForThisRepo[stepperIndex + 1];
258476
+ if (target) onSelectMarker(target.id);
258477
+ }, [markersForThisRepo, stepperIndex, onSelectMarker]);
258478
+ const trailFilesHighlightLayers = React.useMemo(() => {
258479
+ if (!cityData) return null;
258480
+ const sourcePaths = markersForThisRepo.map((m) => m.sourcePath).filter((p2) => typeof p2 === "string" && p2.length > 0);
258481
+ if (sourcePaths.length === 0) return null;
258482
+ const buildingByPath = new Map(cityData.buildings.map((b) => [b.path, b]));
258483
+ const byColor = /* @__PURE__ */ new Map();
258484
+ const seen = /* @__PURE__ */ new Set();
258485
+ for (const sourcePath of sourcePaths) {
258486
+ if (seen.has(sourcePath)) continue;
258487
+ const building = buildingByPath.get(sourcePath) ?? cityData.buildings.find((b) => b.path.endsWith(`/${sourcePath}`));
258488
+ if (!building) continue;
258489
+ seen.add(sourcePath);
258490
+ const color2 = building.color ?? getFileColor(building.path);
258491
+ if (!color2) continue;
258492
+ const list2 = byColor.get(color2) ?? [];
258493
+ list2.push(building.path);
258494
+ byColor.set(color2, list2);
258495
+ }
258496
+ if (byColor.size === 0) return null;
258497
+ let i = 0;
258498
+ return Array.from(byColor.entries()).map(([color2, paths]) => ({
258499
+ id: `trail-files-${i++}`,
258500
+ name: "Trail files",
258501
+ enabled: true,
258502
+ color: color2,
258503
+ opacity: 1,
258504
+ priority: 50,
258505
+ items: paths.map((path2) => ({
258506
+ path: path2,
258507
+ type: "file",
258508
+ renderStrategy: "fill"
258509
+ }))
258510
+ }));
258511
+ }, [markersForThisRepo, cityData]);
258512
+ const trailFilesActive = trailFilesHighlightLayers !== null && trailFilesHighlightLayers.length > 0;
258513
+ const containerRef = React.useRef(null);
258514
+ const snippetPaneRef = React.useRef(null);
258515
+ const leaderLineRef = React.useRef(null);
258516
+ const [markdownOverlayWidth, setMarkdownOverlayWidth] = React.useState(null);
258517
+ const [snippetPaneEl, setSnippetPaneEl] = React.useState(null);
258518
+ const snippetPaneCallbackRef = React.useCallback(
258519
+ (node2) => {
258520
+ snippetPaneRef.current = node2;
258521
+ setSnippetPaneEl(node2);
258522
+ },
258523
+ []
258524
+ );
258525
+ const [snippetPaneWidth, setSnippetPaneWidth] = React.useState(null);
258526
+ const [containerSize, setContainerSize] = React.useState(null);
258527
+ const selectedBuilding = React.useMemo(() => {
258528
+ if (!cityData || !(selectedMarker == null ? void 0 : selectedMarker.sourcePath)) return null;
258529
+ const path2 = selectedMarker.sourcePath;
258530
+ return cityData.buildings.find((b) => b.path === path2) ?? cityData.buildings.find((b) => b.path.endsWith(`/${path2}`)) ?? null;
258531
+ }, [cityData, selectedMarker]);
258532
+ const cityCenter = React.useMemo(() => {
258533
+ if (!cityData) return null;
258534
+ return {
258535
+ x: (cityData.bounds.minX + cityData.bounds.maxX) / 2,
258536
+ z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2
258537
+ };
258538
+ }, [cityData]);
258539
+ const onCameraFrame = React.useCallback(
258540
+ (camera, size) => {
258541
+ var _a2;
258542
+ (_a2 = leaderLineRef.current) == null ? void 0 : _a2.onCameraFrame(camera, size);
258543
+ },
258544
+ []
258545
+ );
258546
+ React.useEffect(() => {
258547
+ const el = containerRef.current;
258548
+ if (!el) return;
258549
+ const update = () => {
258550
+ const rect = el.getBoundingClientRect();
258551
+ setContainerSize({ width: rect.width, height: rect.height });
258552
+ };
258553
+ update();
258554
+ const observer = new ResizeObserver(update);
258555
+ observer.observe(el);
258556
+ return () => observer.disconnect();
258557
+ }, []);
258558
+ const [markdownOverlayEl, setMarkdownOverlayEl] = React.useState(null);
258559
+ React.useEffect(() => {
258560
+ if (!markdownOverlayEl) {
258561
+ setMarkdownOverlayWidth(null);
258562
+ return;
258563
+ }
258564
+ const update = () => setMarkdownOverlayWidth(markdownOverlayEl.getBoundingClientRect().width);
258565
+ update();
258566
+ const observer = new ResizeObserver(update);
258567
+ observer.observe(markdownOverlayEl);
258568
+ return () => observer.disconnect();
258569
+ }, [markdownOverlayEl]);
258570
+ React.useEffect(() => {
258571
+ if (!snippetPaneEl) {
258572
+ setSnippetPaneWidth(null);
258573
+ return;
258574
+ }
258575
+ const update = () => setSnippetPaneWidth(snippetPaneEl.getBoundingClientRect().width);
258576
+ update();
258577
+ const observer = new ResizeObserver(update);
258578
+ observer.observe(snippetPaneEl);
258579
+ return () => observer.disconnect();
258580
+ }, [snippetPaneEl]);
258581
+ const overlayMarkdown = ((_a = selectedMarker == null ? void 0 : selectedMarker.description) == null ? void 0 : _a.trim()) ? {
258582
+ eyebrow: "Marker",
258583
+ title: selectedMarker.label ?? selectedMarker.id,
258584
+ markdown: selectedMarker.description,
258585
+ slideIdPrefix: `trail-${trail2.id}-marker-${selectedMarker.id}`
258586
+ } : ((_b = trail2.summary) == null ? void 0 : _b.trim()) ? {
258587
+ eyebrow: "Trail",
258588
+ title: trail2.title,
258589
+ markdown: trail2.summary,
258590
+ slideIdPrefix: `trail-${trail2.id}-summary`
258591
+ } : null;
258592
+ const hasOverlayMarkdown = overlayMarkdown != null;
258593
+ const showSnippetPane = !!(selectedMarker == null ? void 0 : selectedMarker.snippet);
258594
+ React.useEffect(() => {
258595
+ if (!cityData || !containerSize) return;
258596
+ let cancelled = false;
258597
+ let waitId = null;
258598
+ const apply = () => {
258599
+ if (cancelled) return;
258600
+ if (hasOverlayMarkdown && markdownOverlayWidth == null) return;
258601
+ if (showSnippetPane && snippetPaneWidth == null) return;
258602
+ const FLOAT_INSET2 = 16;
258603
+ const leftInset = hasOverlayMarkdown ? FLOAT_INSET2 + (markdownOverlayWidth ?? 0) : 0;
258604
+ const snippetW = snippetPaneWidth ?? SNIPPET_PANE_WIDTH_PX;
258605
+ const rightInset = showSnippetPane ? snippetW + FLOAT_INSET2 : 0;
258606
+ const topInset = FLOAT_INSET2;
258607
+ const bottomInset = SEQUENCE_DRAWER_HEIGHT_PCT / 100 * containerSize.height;
258608
+ const result = computeCameraFraming({
258609
+ canvasW: containerSize.width,
258610
+ canvasH: containerSize.height,
258611
+ cityBounds: cityData.bounds,
258612
+ leftInset,
258613
+ rightInset,
258614
+ topInset,
258615
+ bottomInset
258616
+ });
258617
+ if (!result) return;
258618
+ setCameraFlatView(result.targetX, result.targetZ, result.height, {
258619
+ duration: 400
258620
+ });
258621
+ };
258622
+ const handle2 = setTimeout(() => {
258623
+ if (getCameraPosition() != null) {
258624
+ apply();
258625
+ return;
258626
+ }
258627
+ let waited = 0;
258628
+ waitId = setInterval(() => {
258629
+ waited += 50;
258630
+ if (cancelled) return;
258631
+ if (getCameraPosition() != null) {
258632
+ if (waitId) clearInterval(waitId);
258633
+ waitId = null;
258634
+ apply();
258635
+ } else if (waited >= 3e3) {
258636
+ if (waitId) clearInterval(waitId);
258637
+ waitId = null;
258638
+ }
258639
+ }, 50);
258640
+ }, 350);
258641
+ return () => {
258642
+ cancelled = true;
258643
+ clearTimeout(handle2);
258644
+ if (waitId) clearInterval(waitId);
258645
+ };
258646
+ }, [
258647
+ cityData,
258648
+ containerSize,
258649
+ showSnippetPane,
258650
+ snippetPaneWidth,
258651
+ hasOverlayMarkdown,
258652
+ markdownOverlayWidth
258653
+ ]);
258654
+ return /* @__PURE__ */ jsxs(
258655
+ "div",
258656
+ {
258657
+ style: {
258658
+ position: "relative",
258659
+ width: "100%",
258660
+ height: "100%",
258661
+ display: "flex",
258662
+ flexDirection: "column",
258663
+ backgroundColor: theme2.colors.background,
258664
+ color: theme2.colors.text
258665
+ },
258666
+ children: [
258667
+ /* @__PURE__ */ jsx(
258668
+ TrailHeader,
258669
+ {
258670
+ trail: trail2,
258671
+ markerCount: markersForThisRepo.length,
258672
+ show3D: effectiveShow3D,
258673
+ onToggle3D: () => setShow3D((v) => !v),
258674
+ hideToggle: THREE_D_TOGGLE_DISABLED
258675
+ }
258676
+ ),
258677
+ /* @__PURE__ */ jsxs(
258678
+ "div",
258679
+ {
258680
+ ref: containerRef,
258681
+ style: {
258682
+ flex: 1,
258683
+ position: "relative",
258684
+ minHeight: 0,
258685
+ overflow: "hidden"
258686
+ },
258687
+ children: [
258688
+ cityData ? /* @__PURE__ */ jsx(
258689
+ FileCity3D,
258690
+ {
258691
+ cityData,
258692
+ width: "100%",
258693
+ height: "100%",
258694
+ selectedPath: (selectedMarker == null ? void 0 : selectedMarker.sourcePath) ?? null,
258695
+ onBuildingClick: handleBuildingClick,
258696
+ onCameraFrame,
258697
+ showControls: false,
258698
+ backgroundColor: theme2.colors.background,
258699
+ textColor: theme2.colors.textMuted,
258700
+ isGrown: effectiveShow3D,
258701
+ highlightLayers: trailFilesHighlightLayers ?? void 0,
258702
+ defaultBuildingColor: trailFilesActive ? theme2.colors.textTertiary : void 0
258703
+ }
258704
+ ) : /* @__PURE__ */ jsx(CityLoadingPlaceholder, {}),
258705
+ /* @__PURE__ */ jsx(
258706
+ TrailLeaderLine,
258707
+ {
258708
+ ref: leaderLineRef,
258709
+ containerRef,
258710
+ building: selectedBuilding,
258711
+ cityCenter,
258712
+ targetRef: snippetPaneRef
258713
+ }
258714
+ ),
258715
+ overlayMarkdown ? /* @__PURE__ */ jsx(
258716
+ TrailMarkdownOverlay,
258717
+ {
258718
+ eyebrow: overlayMarkdown.eyebrow,
258719
+ title: overlayMarkdown.title,
258720
+ markdown: overlayMarkdown.markdown,
258721
+ slideIdPrefix: overlayMarkdown.slideIdPrefix,
258722
+ bottomOffset: `${SEQUENCE_DRAWER_HEIGHT_PCT}%`,
258723
+ containerRef: setMarkdownOverlayEl,
258724
+ nav: markersForThisRepo.length > 0 ? {
258725
+ position: stepperIndex,
258726
+ total: markersForThisRepo.length,
258727
+ onStart: handleStartTrail,
258728
+ onPrev: handleStepPrev,
258729
+ onNext: handleStepNext
258730
+ } : void 0
258731
+ }
258732
+ ) : null,
258733
+ /* @__PURE__ */ jsx(
258734
+ SequenceDrawer,
258735
+ {
258736
+ trail: trail2,
258737
+ view,
258738
+ selectedMarkerId,
258739
+ onSelectMarker
258740
+ }
258741
+ ),
258742
+ (selectedMarker == null ? void 0 : selectedMarker.snippet) ? /* @__PURE__ */ jsx(
258743
+ SnippetSidePane,
258744
+ {
258745
+ ref: snippetPaneCallbackRef,
258746
+ marker: selectedMarker,
258747
+ snippet: selectedMarker.snippet,
258748
+ readFile,
258749
+ onClose: () => onSelectMarker(null)
258750
+ }
258751
+ ) : null
258752
+ ]
258753
+ }
258754
+ )
258755
+ ]
258756
+ }
258757
+ );
258758
+ };
258759
+ const SequenceDrawer = ({
258760
+ trail: trail2,
258761
+ view,
258762
+ selectedMarkerId,
258763
+ onSelectMarker
258764
+ }) => {
258765
+ const { theme: theme2 } = useTheme();
258766
+ const inputs = React.useMemo(
258767
+ () => buildSequenceViewInputs(trail2, view),
258768
+ [trail2, view]
258769
+ );
258770
+ const handleNodeClick2 = React.useCallback(
258771
+ (nodeId) => {
258772
+ if (nodeId.startsWith("__actor:")) return;
258773
+ onSelectMarker(selectedMarkerId === nodeId ? null : nodeId);
258774
+ },
258775
+ [onSelectMarker, selectedMarkerId]
258776
+ );
258777
+ return /* @__PURE__ */ jsx(
258778
+ "div",
258779
+ {
258780
+ style: {
258781
+ position: "absolute",
258782
+ left: 0,
258783
+ right: 0,
258784
+ bottom: 0,
258785
+ height: `${SEQUENCE_DRAWER_HEIGHT_PCT}%`,
258786
+ backgroundColor: withAlpha(theme2.colors.background, 92),
258787
+ borderTop: `1px solid ${theme2.colors.border}`,
258788
+ zIndex: 10
258789
+ },
258790
+ children: /* @__PURE__ */ jsx(
258791
+ SequenceDiagramRenderer,
258792
+ {
258793
+ events: inputs.events,
258794
+ edges: inputs.edges,
258795
+ layoutOptions: inputs.layoutOptions,
258796
+ width: "100%",
258797
+ height: "100%",
258798
+ showControls: false,
258799
+ showBackground: false,
258800
+ stickyHeaders: true,
258801
+ selectedNodeId: selectedMarkerId ?? void 0,
258802
+ onNodeClick: handleNodeClick2
258803
+ }
258804
+ )
258805
+ }
258806
+ );
258807
+ };
258808
+ const SnippetSidePane = React.forwardRef(function SnippetSidePane2({ marker, snippet: snippet2, readFile, onClose }, ref) {
258809
+ const { theme: theme2 } = useTheme();
258810
+ if (!marker.sourcePath) {
258811
+ return /* @__PURE__ */ jsx(SidePaneFrame, { ref, marker, onClose, children: /* @__PURE__ */ jsx(
258812
+ "div",
258813
+ {
258814
+ style: {
258815
+ fontSize: theme2.fontSizes[0],
258816
+ color: theme2.colors.textSecondary,
258817
+ padding: 8
258818
+ },
258819
+ children: "Marker has no sourcePath; snippet cannot be rendered."
258820
+ }
258821
+ ) });
258822
+ }
258823
+ const fileName = marker.sourcePath.split("/").pop() ?? marker.sourcePath;
258824
+ return /* @__PURE__ */ jsx(SidePaneFrame, { ref, marker, onClose, children: snippet2.kind === "slice" ? /* @__PURE__ */ jsx(
258825
+ TrailSnippetView,
258826
+ {
258827
+ filePath: marker.sourcePath,
258828
+ fileName,
258829
+ startLine: snippet2.startLine,
258830
+ endLine: snippet2.endLine,
258831
+ focusLine: snippet2.focusLine,
258832
+ contextLines: snippet2.contextLines,
258833
+ readFile,
258834
+ background: theme2.colors.background
258835
+ }
258836
+ ) : /* @__PURE__ */ jsx(
258837
+ TrailDiffSnippetView,
258838
+ {
258839
+ filePath: marker.sourcePath,
258840
+ fileName,
258841
+ oldContents: snippet2.oldContents,
258842
+ newContents: snippet2.newContents,
258843
+ readFile,
258844
+ startLine: snippet2.startLine,
258845
+ endLine: snippet2.endLine,
258846
+ focusLine: snippet2.focusLine,
258847
+ contextLines: snippet2.contextLines,
258848
+ diffStyle: snippet2.diffStyle,
258849
+ background: theme2.colors.background
258850
+ }
258851
+ ) });
258852
+ });
258853
+ const SNIPPET_PANE_MIN_WIDTH_PX = 320;
258854
+ const SNIPPET_PANE_RESIZE_HANDLE_WIDTH = 6;
258855
+ const SidePaneFrame = React.forwardRef(
258856
+ function SidePaneFrame2({ marker, onClose, children: children2 }, ref) {
258857
+ const { theme: theme2 } = useTheme();
258858
+ const [widthPx, setWidthPx] = React.useState(null);
258859
+ const [isResizing, setIsResizing] = React.useState(false);
258860
+ const innerRef = React.useRef(null);
258861
+ const setInnerRef = React.useCallback(
258862
+ (node2) => {
258863
+ innerRef.current = node2;
258864
+ if (typeof ref === "function") {
258865
+ ref(node2);
258866
+ } else if (ref) {
258867
+ ref.current = node2;
258868
+ }
258869
+ },
258870
+ [ref]
258871
+ );
258872
+ const dragStateRef = React.useRef(null);
258873
+ const handleResizePointerDown = React.useCallback(
258874
+ (e) => {
258875
+ if (e.button !== 0) return;
258876
+ const el = innerRef.current;
258877
+ if (!el) return;
258878
+ e.preventDefault();
258879
+ dragStateRef.current = {
258880
+ startX: e.clientX,
258881
+ startWidth: el.getBoundingClientRect().width
258882
+ };
258883
+ setIsResizing(true);
258884
+ e.currentTarget.setPointerCapture(e.pointerId);
258885
+ },
258886
+ []
258887
+ );
258888
+ const handleResizePointerMove = React.useCallback(
258889
+ (e) => {
258890
+ var _a;
258891
+ const drag2 = dragStateRef.current;
258892
+ if (!drag2) return;
258893
+ const parent = (_a = innerRef.current) == null ? void 0 : _a.parentElement;
258894
+ const parentWidth = (parent == null ? void 0 : parent.clientWidth) ?? window.innerWidth;
258895
+ const maxWidth = Math.max(
258896
+ SNIPPET_PANE_MIN_WIDTH_PX,
258897
+ parentWidth - 32
258898
+ );
258899
+ const dx = e.clientX - drag2.startX;
258900
+ const next2 = Math.min(
258901
+ maxWidth,
258902
+ Math.max(SNIPPET_PANE_MIN_WIDTH_PX, drag2.startWidth - dx)
258903
+ );
258904
+ setWidthPx(next2);
258905
+ },
258906
+ []
258907
+ );
258908
+ const handleResizePointerUp = React.useCallback(
258909
+ (e) => {
258910
+ if (!dragStateRef.current) return;
258911
+ dragStateRef.current = null;
258912
+ setIsResizing(false);
258913
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) {
258914
+ e.currentTarget.releasePointerCapture(e.pointerId);
258915
+ }
258916
+ },
258917
+ []
258918
+ );
258919
+ return /* @__PURE__ */ jsxs(
258920
+ "div",
258921
+ {
258922
+ ref: setInnerRef,
258923
+ style: {
258924
+ position: "absolute",
258925
+ top: 16,
258926
+ right: 16,
258927
+ bottom: `calc(${SEQUENCE_DRAWER_HEIGHT_PCT}% + 16px)`,
258928
+ width: widthPx ?? SNIPPET_PANE_WIDTH_PX,
258929
+ minWidth: SNIPPET_PANE_MIN_WIDTH_PX,
258930
+ backgroundColor: theme2.colors.background,
258931
+ border: `1px solid ${theme2.colors.border}`,
258932
+ borderRadius: theme2.radii[5],
258933
+ boxShadow: theme2.shadows[3],
258934
+ display: "flex",
258935
+ flexDirection: "column",
258936
+ overflow: "hidden",
258937
+ zIndex: 1900,
258938
+ userSelect: isResizing ? "none" : void 0
258939
+ },
258940
+ children: [
258941
+ /* @__PURE__ */ jsx(
258942
+ "div",
258943
+ {
258944
+ role: "separator",
258945
+ "aria-orientation": "vertical",
258946
+ "aria-label": "Resize snippet panel",
258947
+ onPointerDown: handleResizePointerDown,
258948
+ onPointerMove: handleResizePointerMove,
258949
+ onPointerUp: handleResizePointerUp,
258950
+ onPointerCancel: handleResizePointerUp,
258951
+ style: {
258952
+ position: "absolute",
258953
+ top: 0,
258954
+ left: 0,
258955
+ bottom: 0,
258956
+ width: SNIPPET_PANE_RESIZE_HANDLE_WIDTH,
258957
+ cursor: "ew-resize",
258958
+ backgroundColor: isResizing ? theme2.colors.accent : "transparent",
258959
+ transition: isResizing ? void 0 : "background-color 120ms ease",
258960
+ touchAction: "none",
258961
+ zIndex: 1
258962
+ },
258963
+ onMouseEnter: (e) => {
258964
+ if (isResizing) return;
258965
+ e.currentTarget.style.backgroundColor = theme2.colors.border;
258966
+ },
258967
+ onMouseLeave: (e) => {
258968
+ if (isResizing) return;
258969
+ e.currentTarget.style.backgroundColor = "transparent";
258970
+ }
258971
+ }
258972
+ ),
258973
+ /* @__PURE__ */ jsxs(
258974
+ "div",
258975
+ {
258976
+ style: {
258977
+ display: "flex",
258978
+ alignItems: "flex-start",
258979
+ justifyContent: "space-between",
258980
+ gap: 8,
258981
+ padding: "10px 14px",
258982
+ borderBottom: `1px solid ${theme2.colors.border}`,
258983
+ flexShrink: 0
258984
+ },
258985
+ children: [
258986
+ /* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
258987
+ /* @__PURE__ */ jsx(
258988
+ "div",
258989
+ {
258990
+ style: {
258991
+ fontFamily: theme2.fonts.heading,
258992
+ fontSize: theme2.fontSizes[0],
258993
+ fontWeight: theme2.fontWeights.semibold,
258994
+ color: theme2.colors.text
258995
+ },
258996
+ children: marker.label ?? marker.id
258997
+ }
258998
+ ),
258999
+ marker.sourcePath ? /* @__PURE__ */ jsx(
259000
+ "div",
259001
+ {
259002
+ style: {
259003
+ fontSize: theme2.fontSizes[0],
259004
+ color: theme2.colors.textSecondary,
259005
+ fontFamily: theme2.fonts.monospace,
259006
+ marginTop: 2,
259007
+ wordBreak: "break-all"
259008
+ },
259009
+ children: marker.sourcePath
259010
+ }
259011
+ ) : null
259012
+ ] }),
259013
+ /* @__PURE__ */ jsx(
259014
+ "button",
259015
+ {
259016
+ onClick: onClose,
259017
+ style: {
259018
+ background: "transparent",
259019
+ color: theme2.colors.text,
259020
+ border: `1px solid ${theme2.colors.muted}`,
259021
+ borderRadius: theme2.radii[2],
259022
+ padding: "2px 8px",
259023
+ cursor: "pointer",
259024
+ fontSize: theme2.fontSizes[0],
259025
+ fontFamily: theme2.fonts.body,
259026
+ flexShrink: 0
259027
+ },
259028
+ children: "Close"
259029
+ }
259030
+ )
259031
+ ]
259032
+ }
259033
+ ),
259034
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: children2 })
259035
+ ]
259036
+ }
259037
+ );
259038
+ }
259039
+ );
259040
+ const TrailHeader = ({
259041
+ trail: trail2,
259042
+ markerCount,
259043
+ show3D,
259044
+ onToggle3D,
259045
+ hideToggle = false
259046
+ }) => {
259047
+ const { theme: theme2 } = useTheme();
259048
+ return /* @__PURE__ */ jsxs(
259049
+ "div",
259050
+ {
259051
+ style: {
259052
+ height: HEADER_HEIGHT_PX,
259053
+ padding: "8px 16px",
259054
+ borderBottom: `1px solid ${theme2.colors.border}`,
259055
+ display: "flex",
259056
+ flexDirection: "row",
259057
+ alignItems: "center",
259058
+ justifyContent: "space-between",
259059
+ gap: 12,
259060
+ flexShrink: 0
259061
+ },
259062
+ children: [
259063
+ /* @__PURE__ */ jsxs(
259064
+ "div",
259065
+ {
259066
+ style: {
259067
+ display: "flex",
259068
+ flexDirection: "column",
259069
+ justifyContent: "center",
259070
+ gap: 2,
259071
+ minWidth: 0
259072
+ },
259073
+ children: [
259074
+ /* @__PURE__ */ jsx(
259075
+ "div",
259076
+ {
259077
+ style: {
259078
+ fontFamily: theme2.fonts.heading,
259079
+ fontSize: theme2.fontSizes[1],
259080
+ fontWeight: theme2.fontWeights.semibold,
259081
+ color: theme2.colors.text
259082
+ },
259083
+ children: trail2.title
259084
+ }
259085
+ ),
259086
+ /* @__PURE__ */ jsxs(
259087
+ "div",
259088
+ {
259089
+ style: {
259090
+ fontFamily: theme2.fonts.body,
259091
+ fontSize: theme2.fontSizes[0],
259092
+ color: theme2.colors.textSecondary
259093
+ },
259094
+ children: [
259095
+ markerCount,
259096
+ " marker",
259097
+ markerCount === 1 ? "" : "s",
259098
+ " in this repo"
259099
+ ]
259100
+ }
259101
+ )
259102
+ ]
259103
+ }
259104
+ ),
259105
+ hideToggle ? null : /* @__PURE__ */ jsx(
259106
+ "button",
259107
+ {
259108
+ type: "button",
259109
+ onClick: onToggle3D,
259110
+ "aria-pressed": show3D,
259111
+ title: show3D ? "Switch to flat (2D) view" : "Switch to 3D view",
259112
+ style: {
259113
+ flexShrink: 0,
259114
+ fontFamily: theme2.fonts.body,
259115
+ fontSize: theme2.fontSizes[0],
259116
+ fontWeight: theme2.fontWeights.medium,
259117
+ color: show3D ? theme2.colors.background : theme2.colors.text,
259118
+ background: show3D ? theme2.colors.accent : "transparent",
259119
+ border: `1px solid ${show3D ? theme2.colors.accent : theme2.colors.muted}`,
259120
+ borderRadius: theme2.radii[3],
259121
+ padding: "4px 10px",
259122
+ cursor: "pointer",
259123
+ lineHeight: 1.2
259124
+ },
259125
+ children: "3D"
259126
+ }
259127
+ )
259128
+ ]
259129
+ }
259130
+ );
259131
+ };
259132
+ const FileCityTrailExplorerEmptyState = ({
259133
+ message,
259134
+ cityData
259135
+ }) => {
259136
+ const { theme: theme2 } = useTheme();
259137
+ return /* @__PURE__ */ jsxs(
259138
+ "div",
259139
+ {
259140
+ style: {
259141
+ position: "relative",
259142
+ width: "100%",
259143
+ height: "100%",
259144
+ backgroundColor: theme2.colors.background,
259145
+ color: theme2.colors.text
259146
+ },
259147
+ children: [
259148
+ cityData ? /* @__PURE__ */ jsx(
259149
+ FileCity3D,
259150
+ {
259151
+ cityData,
259152
+ width: "100%",
259153
+ height: "100%",
259154
+ showControls: false,
259155
+ backgroundColor: theme2.colors.background,
259156
+ textColor: theme2.colors.textMuted
259157
+ }
259158
+ ) : null,
259159
+ /* @__PURE__ */ jsx(
259160
+ "div",
259161
+ {
259162
+ style: {
259163
+ position: "absolute",
259164
+ inset: 0,
259165
+ display: "flex",
259166
+ alignItems: "center",
259167
+ justifyContent: "center",
259168
+ padding: 16,
259169
+ backgroundColor: cityData ? withAlpha(theme2.colors.background, 60) : void 0,
259170
+ color: theme2.colors.textSecondary,
259171
+ fontFamily: theme2.fonts.body,
259172
+ fontSize: theme2.fontSizes[1]
259173
+ },
259174
+ children: message
259175
+ }
259176
+ )
259177
+ ]
259178
+ }
259179
+ );
259180
+ };
259181
+ const CityLoadingPlaceholder = () => {
259182
+ const { theme: theme2 } = useTheme();
259183
+ return /* @__PURE__ */ jsx(
259184
+ "div",
259185
+ {
259186
+ style: {
259187
+ width: "100%",
259188
+ height: "100%",
259189
+ display: "flex",
259190
+ alignItems: "center",
259191
+ justifyContent: "center",
259192
+ color: theme2.colors.textMuted,
259193
+ fontFamily: theme2.fonts.body,
259194
+ fontSize: theme2.fontSizes[0]
259195
+ },
259196
+ children: "Building the city…"
259197
+ }
259198
+ );
259199
+ };
259200
+ function filterMarkersForRepo(trail2, repository) {
259201
+ const repos = trail2.repos ?? [];
259202
+ if (repos.length <= 1 || !repository) {
259203
+ return trail2.markers;
259204
+ }
259205
+ return trail2.markers.filter((m) => m.repo === repository.id);
259206
+ }
259207
+ const focusBuildingTool = {
259208
+ name: "focus_building",
259209
+ description: "Focuses the camera on a specific file (building) in the file city visualization",
259210
+ inputs: {
259211
+ type: "object",
259212
+ properties: {
259213
+ filePath: {
259214
+ type: "string",
259215
+ description: "Path to the file to focus on"
259216
+ },
259217
+ animate: {
259218
+ type: "boolean",
259219
+ description: "Whether to animate the camera transition"
259220
+ }
259221
+ },
259222
+ required: ["filePath"]
259223
+ },
259224
+ outputs: {
259225
+ type: "object",
259226
+ properties: {
259227
+ success: { type: "boolean" },
259228
+ message: { type: "string" }
259229
+ }
259230
+ },
259231
+ tags: ["navigation", "camera", "focus"],
259232
+ tool_call_template: {
259233
+ call_template_type: "panel_event",
259234
+ event_type: "industry-theme.file-city-panel:focus-building"
259235
+ }
259236
+ };
259237
+ const selectDistrictTool = {
259238
+ name: "select_district",
259239
+ description: "Selects and highlights a directory (district) in the file city visualization",
259240
+ inputs: {
259241
+ type: "object",
259242
+ properties: {
259243
+ directoryPath: {
259244
+ type: "string",
259245
+ description: "Path to the directory to select"
259246
+ },
259247
+ expandChildren: {
259248
+ type: "boolean",
259249
+ description: "Whether to expand and show child buildings"
259250
+ }
259251
+ },
259252
+ required: ["directoryPath"]
259253
+ },
259254
+ outputs: {
259255
+ type: "object",
259256
+ properties: {
259257
+ success: { type: "boolean" },
259258
+ selectedDistrict: { type: "string" }
259259
+ }
259260
+ },
259261
+ tags: ["selection", "district", "directory"],
259262
+ tool_call_template: {
259263
+ call_template_type: "panel_event",
259264
+ event_type: "industry-theme.file-city-panel:select-district"
259265
+ }
259266
+ };
259267
+ const resetViewTool = {
259268
+ name: "reset_view",
259269
+ description: "Resets the file city view to the default camera position",
259270
+ inputs: {
259271
+ type: "object",
259272
+ properties: {
259273
+ animate: {
259274
+ type: "boolean",
259275
+ description: "Whether to animate the camera reset"
259276
+ }
259277
+ }
259278
+ },
259279
+ outputs: {
259280
+ type: "object",
259281
+ properties: {
259282
+ success: { type: "boolean" }
259283
+ }
259284
+ },
259285
+ tags: ["navigation", "camera", "reset"],
259286
+ tool_call_template: {
259287
+ call_template_type: "panel_event",
259288
+ event_type: "industry-theme.file-city-panel:reset-view"
259289
+ }
259290
+ };
259291
+ const codeCityPanelTools = [
259292
+ focusBuildingTool,
259293
+ selectDistrictTool,
259294
+ resetViewTool
259295
+ ];
259296
+ const codeCityPanelToolsMetadata = {
259297
+ id: "industry-theme.file-city-panel",
259298
+ name: "File City Panel",
259299
+ description: "Tools provided by the file city visualization panel extension",
259300
+ tools: codeCityPanelTools
259301
+ };
259302
+ const NpmIcon = ({ size }) => /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "#CB3837", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z" }) });
259303
+ const YarnIcon = ({ size }) => /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "#2C8EBB", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M12 0C5.375 0 0 5.375 0 12s5.375 12 12 12 12-5.375 12-12S18.625 0 12 0zm.768 4.105c.183 0 .363.053.525.157.125.083.287.185.755 1.154.31-.088.468-.042.551-.019.204.056.366.19.463.375.477.917.542 2.553.334 3.605-.241 1.232-.755 2.029-1.131 2.576.324.329.778.899 1.117 1.825.278.774.31 1.478.273 2.015a5.51 5.51 0 0 0 .602-.329c.593-.366 1.487-.917 2.553-.931.714-.009 1.269.445 1.353 1.103a1.23 1.23 0 0 1-.945 1.362c-.649.158-.95.278-1.821.843-1.232.797-2.539 1.242-3.012 1.39a1.686 1.686 0 0 1-.704.343c-.737.181-3.266.315-3.466.315h-.046c-.783 0-1.214-.241-1.45-.491-.658.329-1.51.19-2.122-.134a1.078 1.078 0 0 1-.58-1.153 1.243 1.243 0 0 1-.153-.195c-.162-.25-.528-.936-.454-1.946.056-.723.556-1.367.88-1.71a5.522 5.522 0 0 1 .408-2.256c.306-.727.885-1.348 1.32-1.737-.32-.537-.644-1.367-.329-2.21.227-.602.412-.936.82-1.08h-.005c.199-.074.389-.153.486-.259a3.418 3.418 0 0 1 2.298-1.103c.037-.093.079-.185.125-.283.31-.658.639-1.029 1.024-1.168a.94.94 0 0 1 .328-.06z" }) });
259304
+ const PnpmIcon = ({ size }) => /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "#F69220", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M0 0v7.5h7.5V0zm8.25 0v7.5h7.498V0zm8.25 0v7.5H24V0zM8.25 8.25v7.5h7.498v-7.5zm8.25 0v7.5H24v-7.5zM0 16.5V24h7.5v-7.5zm8.25 0V24h7.498v-7.5zm8.25 0V24H24v-7.5z" }) });
259305
+ const BunIcon = ({ size }) => /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "#FBF0DF", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M12 22.596c6.628 0 12-4.338 12-9.688 0-3.318-2.057-6.248-5.219-7.986-1.286-.715-2.297-1.357-3.139-1.89C14.058 2.025 13.08 1.404 12 1.404c-1.097 0-2.334.785-3.966 1.821a49.92 49.92 0 0 1-2.816 1.697C2.057 6.66 0 9.59 0 12.908c0 5.35 5.372 9.687 12 9.687v.001Z" }) });
259306
+ const PackageManagerIcon = ({
259307
+ packageManager,
259308
+ size = 16,
259309
+ color: color2
259310
+ }) => {
259311
+ switch (packageManager) {
259312
+ case "npm":
259313
+ return /* @__PURE__ */ jsx(NpmIcon, { size });
259314
+ case "yarn":
259315
+ return /* @__PURE__ */ jsx(YarnIcon, { size });
259316
+ case "pnpm":
259317
+ return /* @__PURE__ */ jsx(PnpmIcon, { size });
259318
+ case "bun":
259319
+ return /* @__PURE__ */ jsx(BunIcon, { size });
259320
+ default:
259321
+ return /* @__PURE__ */ jsx(Package$1, { size, color: color2 });
259322
+ }
259323
+ };
259324
+ const ProjectInfoHeader = ({
259325
+ project,
259326
+ onOpen,
259327
+ compact = false
259328
+ }) => {
259329
+ const { theme: theme2 } = useTheme();
259330
+ const totalDeps = (project.dependencyCount || 0) + (project.devDependencyCount || 0);
259331
+ const fontSizeSmall = theme2.fontSizes[0];
259332
+ const fontSizeMedium = theme2.fontSizes[1];
259333
+ const fontSizeLarge = theme2.fontSizes[2] || theme2.fontSizes[1];
259334
+ return /* @__PURE__ */ jsxs(
259335
+ "div",
259336
+ {
259337
+ style: {
259338
+ display: "flex",
259339
+ flexDirection: "column",
259340
+ gap: compact ? 4 : 8,
259341
+ padding: compact ? "8px 12px" : "12px 16px",
259342
+ backgroundColor: theme2.colors.background,
259343
+ borderBottom: `1px solid ${theme2.colors.border}`,
259344
+ fontFamily: theme2.fonts.body,
259345
+ minHeight: compact ? 48 : "auto"
259346
+ },
259347
+ children: [
259348
+ /* @__PURE__ */ jsxs(
259349
+ "div",
259350
+ {
259351
+ style: {
259352
+ display: "flex",
259353
+ alignItems: "center",
259354
+ justifyContent: "space-between",
259355
+ gap: 12
259356
+ },
259357
+ children: [
259358
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, flex: 1, minWidth: 0 }, children: [
259359
+ project.packageManager && /* @__PURE__ */ jsx("div", { style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(
259360
+ PackageManagerIcon,
259361
+ {
259362
+ packageManager: project.packageManager,
259363
+ size: compact ? 18 : 20,
259364
+ color: theme2.colors.textSecondary
259365
+ }
259366
+ ) }),
259367
+ /* @__PURE__ */ jsx(
259368
+ "span",
259369
+ {
259370
+ style: {
259371
+ fontSize: compact ? fontSizeMedium : fontSizeLarge,
259372
+ fontWeight: 600,
259373
+ color: theme2.colors.text,
259374
+ overflow: "hidden",
259375
+ textOverflow: "ellipsis",
259376
+ whiteSpace: "nowrap"
259377
+ },
259378
+ title: project.name,
259379
+ children: project.name
259380
+ }
259381
+ ),
259382
+ project.version && /* @__PURE__ */ jsxs(
259383
+ "span",
259384
+ {
259385
+ style: {
259386
+ fontSize: fontSizeSmall,
259387
+ color: theme2.colors.textSecondary,
259388
+ backgroundColor: theme2.colors.backgroundLight,
257762
259389
  padding: "2px 6px",
257763
259390
  borderRadius: "4px",
257764
259391
  flexShrink: 0
@@ -258301,6 +259928,32 @@ const panels = [
258301
259928
  onUnmount: async (_context) => {
258302
259929
  console.log("File City Explorer Panel unmounting");
258303
259930
  }
259931
+ },
259932
+ {
259933
+ metadata: {
259934
+ id: "principal-ade.file-city-trail-explorer-panel",
259935
+ name: "File City Trail Explorer",
259936
+ icon: "🥾",
259937
+ version: "0.1.0",
259938
+ author: "Principal AI",
259939
+ description: "3D file-city visualization for trails — the new authored-walkthrough medium. See docs/TRAIL_RENAME.md.",
259940
+ // Required slice — the explorer cannot render without a tree. Other
259941
+ // slices declared on FileCityTrailExplorerPanelContext are
259942
+ // host-supplied with `data: null` when no trail is active.
259943
+ slices: ["fileTree"]
259944
+ },
259945
+ component: FileCityTrailExplorerPanel,
259946
+ onMount: async (context) => {
259947
+ var _a;
259948
+ console.log(
259949
+ "File City Trail Explorer Panel mounted",
259950
+ (_a = context.currentScope.repository) == null ? void 0 : _a.path
259951
+ );
259952
+ await context.refresh("repository", "fileTree");
259953
+ },
259954
+ onUnmount: async (_context) => {
259955
+ console.log("File City Trail Explorer Panel unmounting");
259956
+ }
258304
259957
  }
258305
259958
  ];
258306
259959
  const onPackageLoad = async () => {
@@ -258319,6 +259972,7 @@ export {
258319
259972
  FileCardList,
258320
259973
  FileCitySequenceExplorer,
258321
259974
  FileCitySequenceExplorerPanel,
259975
+ FileCityTrailExplorerPanel,
258322
259976
  GitChangesCardList,
258323
259977
  MockTTSAdapter,
258324
259978
  PrChangesCardList,