@industry-theme/file-city-panel 0.5.71 → 0.5.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TrailFilePath.d.ts","sourceRoot":"","sources":["../../../../src/panels/FileCityTrailExplorerPanel/overlays/TrailFilePath.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,kFAAkF;IAClF,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACrD;;;;OAIG;IACH,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;IACtD;;;;OAIG;IACH,UAAU,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5C;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"TrailFilePath.d.ts","sourceRoot":"","sources":["../../../../src/panels/FileCityTrailExplorerPanel/overlays/TrailFilePath.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,kFAAkF;IAClF,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACrD;;;;OAIG;IACH,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;IACtD;;;;OAIG;IACH,UAAU,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5C;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAmCD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,gGAksBxB,CAAC"}
|
package/dist/panels.bundle.js
CHANGED
|
@@ -257807,6 +257807,10 @@ const TrailLeaderLine = React.forwardRef(function TrailLeaderLine2({ containerRe
|
|
|
257807
257807
|
] });
|
|
257808
257808
|
});
|
|
257809
257809
|
const CALLOUT_FADE_IN_MS = 350;
|
|
257810
|
+
const TITLE_TYPEWRITER_CPS = 28;
|
|
257811
|
+
const TITLE_TYPEWRITER_HOLD_MS = 500;
|
|
257812
|
+
const TITLE_INTER_HOLD_MS = 220;
|
|
257813
|
+
const TITLE_CARET = "▍";
|
|
257810
257814
|
const fileBasename = (path2) => {
|
|
257811
257815
|
const i = path2.lastIndexOf("/");
|
|
257812
257816
|
return i >= 0 ? path2.slice(i + 1) : path2;
|
|
@@ -257823,7 +257827,13 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
257823
257827
|
animate: animate2 = true,
|
|
257824
257828
|
currentStepIndex = null,
|
|
257825
257829
|
stepMs = 1100,
|
|
257826
|
-
|
|
257830
|
+
// Dwell budget covers: fade-in (350ms) + initial hold (500ms) +
|
|
257831
|
+
// filename typewriter (~ name.length * 36ms at 28cps) + inter-hold
|
|
257832
|
+
// (220ms) + title typewriter (~ title.length * 36ms) + a digest
|
|
257833
|
+
// beat after typing finishes so the user can read the full
|
|
257834
|
+
// callout before the dot walks on. 3500ms leaves ~1.5s of digest
|
|
257835
|
+
// for typical 10-char filenames + 15-char titles.
|
|
257836
|
+
dwellMs = 3500,
|
|
257827
257837
|
endHoldMs = 2200
|
|
257828
257838
|
}, ref) {
|
|
257829
257839
|
const { theme: theme2 } = useTheme();
|
|
@@ -257845,6 +257855,8 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
257845
257855
|
const animStartTsRef = React.useRef(
|
|
257846
257856
|
typeof performance !== "undefined" ? performance.now() : 0
|
|
257847
257857
|
);
|
|
257858
|
+
const hoverFrozenElapsedRef = React.useRef(null);
|
|
257859
|
+
const periodRef = React.useRef(0);
|
|
257848
257860
|
const buildingsRef = React.useRef(buildings);
|
|
257849
257861
|
const markerTitlesRef = React.useRef(markerTitles);
|
|
257850
257862
|
const cityCenterRef = React.useRef(cityCenter);
|
|
@@ -257874,7 +257886,7 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
257874
257886
|
}, [animate2, buildings, dwellMs, stepMs, endHoldMs]);
|
|
257875
257887
|
const onCameraFrame = React.useCallback(
|
|
257876
257888
|
(camera, size) => {
|
|
257877
|
-
var _a;
|
|
257889
|
+
var _a, _b;
|
|
257878
257890
|
const targets = buildingsRef.current;
|
|
257879
257891
|
const center = cityCenterRef.current;
|
|
257880
257892
|
const container = containerRef.current;
|
|
@@ -257906,6 +257918,7 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
257906
257918
|
const N = points.length;
|
|
257907
257919
|
const slotMs = dwellMs + stepMs;
|
|
257908
257920
|
const period = N >= 2 ? (N - 1) * slotMs + dwellMs + endHoldMs : 0;
|
|
257921
|
+
periodRef.current = period;
|
|
257909
257922
|
const rawFrozen = currentStepIndexRef.current;
|
|
257910
257923
|
const frozenIndex = rawFrozen != null && rawFrozen >= 0 && rawFrozen < N ? rawFrozen : null;
|
|
257911
257924
|
let revealedCount = animate2 ? 0 : N;
|
|
@@ -257916,7 +257929,7 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
257916
257929
|
revealedCount = frozenIndex + 1;
|
|
257917
257930
|
dwellElapsed = CALLOUT_FADE_IN_MS;
|
|
257918
257931
|
} else if (animate2 && period > 0) {
|
|
257919
|
-
const elapsed = ((typeof performance !== "undefined" ? performance.now() : Date.now()) - animStartTsRef.current) % period;
|
|
257932
|
+
const elapsed = hoverFrozenElapsedRef.current !== null ? hoverFrozenElapsedRef.current : ((typeof performance !== "undefined" ? performance.now() : Date.now()) - animStartTsRef.current) % period;
|
|
257920
257933
|
const traversalsEnd = (N - 1) * slotMs;
|
|
257921
257934
|
if (elapsed >= traversalsEnd) {
|
|
257922
257935
|
revealedCount = N;
|
|
@@ -258030,43 +258043,56 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
258030
258043
|
}
|
|
258031
258044
|
}
|
|
258032
258045
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
258033
|
-
const paintCallout = (textEl, bgEl, point2, path2, title, opacity) => {
|
|
258046
|
+
const paintCallout = (textEl, bgEl, point2, path2, title, opacity, nameRevealChars2, titleRevealChars2) => {
|
|
258034
258047
|
if (!textEl || !bgEl) return;
|
|
258035
258048
|
if (!point2 || !point2.visible || opacity <= 1e-3 || !path2) {
|
|
258036
258049
|
textEl.setAttribute("opacity", "0");
|
|
258037
258050
|
bgEl.setAttribute("opacity", "0");
|
|
258051
|
+
bgEl.setAttribute("pointer-events", "none");
|
|
258052
|
+
textEl.setAttribute("pointer-events", "none");
|
|
258038
258053
|
return;
|
|
258039
258054
|
}
|
|
258055
|
+
const interactive = opacity >= 0.6;
|
|
258056
|
+
bgEl.setAttribute(
|
|
258057
|
+
"pointer-events",
|
|
258058
|
+
interactive ? "auto" : "none"
|
|
258059
|
+
);
|
|
258060
|
+
textEl.setAttribute(
|
|
258061
|
+
"pointer-events",
|
|
258062
|
+
interactive ? "auto" : "none"
|
|
258063
|
+
);
|
|
258040
258064
|
while (textEl.children.length < 3) {
|
|
258041
258065
|
textEl.appendChild(document.createElementNS(SVG_NS, "tspan"));
|
|
258042
258066
|
}
|
|
258043
258067
|
while (textEl.children.length > 3) {
|
|
258044
258068
|
textEl.removeChild(textEl.lastChild);
|
|
258045
258069
|
}
|
|
258046
|
-
const
|
|
258070
|
+
const pathTspan = textEl.children[0];
|
|
258047
258071
|
const nameTspan = textEl.children[1];
|
|
258048
|
-
const
|
|
258072
|
+
const titleTspan = textEl.children[2];
|
|
258049
258073
|
const name2 = fileBasename(path2);
|
|
258050
258074
|
const dir = fileDirname(path2);
|
|
258051
258075
|
const trimmedTitle = (title == null ? void 0 : title.trim()) ?? "";
|
|
258052
|
-
const
|
|
258076
|
+
const displayTitle = trimmedTitle && titleRevealChars2 !== null ? titleRevealChars2 < 0 ? "" : titleRevealChars2 >= trimmedTitle.length ? trimmedTitle : trimmedTitle.slice(0, titleRevealChars2) + TITLE_CARET : trimmedTitle;
|
|
258077
|
+
const displayName = nameRevealChars2 !== null ? nameRevealChars2 < 0 ? "" : nameRevealChars2 >= name2.length ? name2 : name2.slice(0, nameRevealChars2) + TITLE_CARET : name2;
|
|
258078
|
+
const lineCount = (dir ? 1 : 0) + 1 + (trimmedTitle ? 1 : 0);
|
|
258053
258079
|
const BOTTOM_LINE_OFFSET = 24;
|
|
258054
258080
|
const topLineY = point2.y - BOTTOM_LINE_OFFSET - (lineCount - 1) * calloutLineDyPx;
|
|
258055
258081
|
textEl.setAttribute("text-anchor", "middle");
|
|
258056
258082
|
textEl.setAttribute("font-family", fontFamily);
|
|
258057
258083
|
textEl.setAttribute("opacity", String(opacity));
|
|
258058
|
-
|
|
258059
|
-
|
|
258060
|
-
|
|
258061
|
-
|
|
258062
|
-
|
|
258063
|
-
|
|
258084
|
+
pathTspan.textContent = dir;
|
|
258085
|
+
pathTspan.setAttribute("text-anchor", "middle");
|
|
258086
|
+
pathTspan.setAttribute("x", String(point2.x));
|
|
258087
|
+
pathTspan.setAttribute("y", String(topLineY));
|
|
258088
|
+
pathTspan.removeAttribute("dy");
|
|
258089
|
+
pathTspan.setAttribute("fill", color2);
|
|
258090
|
+
pathTspan.setAttribute("font-size", calloutFontSize);
|
|
258091
|
+
pathTspan.setAttribute("font-weight", calloutFontWeight);
|
|
258064
258092
|
nameTspan.textContent = name2;
|
|
258065
|
-
nameTspan.setAttribute("
|
|
258066
|
-
nameTspan.
|
|
258067
|
-
|
|
258068
|
-
trimmedTitle ? calloutLineDy : "0"
|
|
258069
|
-
);
|
|
258093
|
+
nameTspan.setAttribute("text-anchor", "start");
|
|
258094
|
+
nameTspan.removeAttribute("y");
|
|
258095
|
+
nameTspan.setAttribute("dy", dir ? calloutLineDy : "0");
|
|
258070
258096
|
nameTspan.setAttribute(
|
|
258071
258097
|
"fill",
|
|
258072
258098
|
trimmedTitle ? dirColor : filenameColor
|
|
@@ -258076,12 +258102,20 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
258076
258102
|
"font-weight",
|
|
258077
258103
|
trimmedTitle ? calloutBodyFontWeight : calloutFontWeight
|
|
258078
258104
|
);
|
|
258079
|
-
|
|
258080
|
-
|
|
258081
|
-
|
|
258082
|
-
|
|
258083
|
-
|
|
258084
|
-
|
|
258105
|
+
const nameWidth = typeof nameTspan.getComputedTextLength === "function" ? nameTspan.getComputedTextLength() : 0;
|
|
258106
|
+
nameTspan.setAttribute("x", String(point2.x - nameWidth / 2));
|
|
258107
|
+
titleTspan.textContent = trimmedTitle;
|
|
258108
|
+
titleTspan.setAttribute("text-anchor", "start");
|
|
258109
|
+
titleTspan.removeAttribute("y");
|
|
258110
|
+
titleTspan.setAttribute(
|
|
258111
|
+
"dy",
|
|
258112
|
+
trimmedTitle ? calloutLineDy : "0"
|
|
258113
|
+
);
|
|
258114
|
+
titleTspan.setAttribute("fill", filenameColor);
|
|
258115
|
+
titleTspan.setAttribute("font-size", calloutFontSize);
|
|
258116
|
+
titleTspan.setAttribute("font-weight", calloutFontWeight);
|
|
258117
|
+
const titleWidth = typeof titleTspan.getComputedTextLength === "function" ? titleTspan.getComputedTextLength() : 0;
|
|
258118
|
+
titleTspan.setAttribute("x", String(point2.x - titleWidth / 2));
|
|
258085
258119
|
const bbox = textEl.getBBox();
|
|
258086
258120
|
const padX = 14;
|
|
258087
258121
|
const padY = 10;
|
|
@@ -258094,6 +258128,12 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
258094
258128
|
bgEl.setAttribute("stroke", color2);
|
|
258095
258129
|
bgEl.setAttribute("stroke-width", "1.5");
|
|
258096
258130
|
bgEl.setAttribute("opacity", String(0.94 * opacity));
|
|
258131
|
+
if (displayName !== name2) {
|
|
258132
|
+
nameTspan.textContent = displayName;
|
|
258133
|
+
}
|
|
258134
|
+
if (displayTitle !== trimmedTitle) {
|
|
258135
|
+
titleTspan.textContent = displayTitle;
|
|
258136
|
+
}
|
|
258097
258137
|
};
|
|
258098
258138
|
let calloutOpacity = 0;
|
|
258099
258139
|
if (frozenIndex != null && lastIndex >= 0) {
|
|
@@ -258109,13 +258149,49 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
258109
258149
|
} else if (!animate2 && lastIndex >= 0) {
|
|
258110
258150
|
calloutOpacity = 1;
|
|
258111
258151
|
}
|
|
258152
|
+
const activePath = lastIndex >= 0 ? targets[lastIndex].path : null;
|
|
258153
|
+
const activeName = activePath ? fileBasename(activePath) : "";
|
|
258154
|
+
const activeTitle = lastIndex >= 0 ? (((_a = markerTitlesRef.current) == null ? void 0 : _a[lastIndex]) ?? "").trim() : "";
|
|
258155
|
+
let nameRevealChars = null;
|
|
258156
|
+
let titleRevealChars = null;
|
|
258157
|
+
if (animate2 && frozenIndex == null && traverseFrac === null && lastIndex >= 0) {
|
|
258158
|
+
const charsPerMs = TITLE_TYPEWRITER_CPS / 1e3;
|
|
258159
|
+
const nameStart = CALLOUT_FADE_IN_MS + TITLE_TYPEWRITER_HOLD_MS;
|
|
258160
|
+
const nameDuration = activeName.length > 0 ? activeName.length / charsPerMs : 0;
|
|
258161
|
+
const nameDone = nameStart + nameDuration;
|
|
258162
|
+
const titleStart = nameDone + TITLE_INTER_HOLD_MS;
|
|
258163
|
+
if (activeName.length > 0) {
|
|
258164
|
+
if (dwellElapsed < nameStart) {
|
|
258165
|
+
nameRevealChars = 0;
|
|
258166
|
+
} else if (dwellElapsed < nameDone) {
|
|
258167
|
+
const typed = Math.floor(
|
|
258168
|
+
(dwellElapsed - nameStart) * charsPerMs
|
|
258169
|
+
);
|
|
258170
|
+
nameRevealChars = typed >= activeName.length ? null : Math.max(0, typed);
|
|
258171
|
+
} else {
|
|
258172
|
+
nameRevealChars = null;
|
|
258173
|
+
}
|
|
258174
|
+
}
|
|
258175
|
+
if (activeTitle.length > 0) {
|
|
258176
|
+
if (dwellElapsed < titleStart) {
|
|
258177
|
+
titleRevealChars = -1;
|
|
258178
|
+
} else {
|
|
258179
|
+
const typed = Math.floor(
|
|
258180
|
+
(dwellElapsed - titleStart) * charsPerMs
|
|
258181
|
+
);
|
|
258182
|
+
titleRevealChars = typed >= activeTitle.length ? null : Math.max(0, typed);
|
|
258183
|
+
}
|
|
258184
|
+
}
|
|
258185
|
+
}
|
|
258112
258186
|
paintCallout(
|
|
258113
258187
|
calloutTextRef.current,
|
|
258114
258188
|
calloutBgRef.current,
|
|
258115
258189
|
lastIndex >= 0 ? points[lastIndex] : null,
|
|
258116
|
-
|
|
258117
|
-
lastIndex >= 0 ? ((
|
|
258118
|
-
calloutOpacity
|
|
258190
|
+
activePath,
|
|
258191
|
+
lastIndex >= 0 ? ((_b = markerTitlesRef.current) == null ? void 0 : _b[lastIndex]) ?? null : null,
|
|
258192
|
+
calloutOpacity,
|
|
258193
|
+
nameRevealChars,
|
|
258194
|
+
titleRevealChars
|
|
258119
258195
|
);
|
|
258120
258196
|
g2.setAttribute("opacity", "1");
|
|
258121
258197
|
},
|
|
@@ -258139,6 +258215,19 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
258139
258215
|
]
|
|
258140
258216
|
);
|
|
258141
258217
|
React.useImperativeHandle(ref, () => ({ onCameraFrame }), [onCameraFrame]);
|
|
258218
|
+
const handleCalloutEnter = React.useCallback(() => {
|
|
258219
|
+
if (hoverFrozenElapsedRef.current !== null) return;
|
|
258220
|
+
const period = periodRef.current;
|
|
258221
|
+
if (period <= 0) return;
|
|
258222
|
+
const now2 = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
258223
|
+
hoverFrozenElapsedRef.current = (now2 - animStartTsRef.current) % period;
|
|
258224
|
+
}, []);
|
|
258225
|
+
const handleCalloutLeave = React.useCallback(() => {
|
|
258226
|
+
if (hoverFrozenElapsedRef.current === null) return;
|
|
258227
|
+
const now2 = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
258228
|
+
animStartTsRef.current = now2 - hoverFrozenElapsedRef.current;
|
|
258229
|
+
hoverFrozenElapsedRef.current = null;
|
|
258230
|
+
}, []);
|
|
258142
258231
|
return /* @__PURE__ */ jsxs(
|
|
258143
258232
|
"svg",
|
|
258144
258233
|
{
|
|
@@ -258156,8 +258245,26 @@ const TrailFilePath = React.forwardRef(function TrailFilePath2({
|
|
|
258156
258245
|
children: [
|
|
258157
258246
|
/* @__PURE__ */ jsx("g", { ref: groupRef, opacity: 0 }),
|
|
258158
258247
|
/* @__PURE__ */ jsx("circle", { ref: leadDotRef, opacity: 0, pointerEvents: "none" }),
|
|
258159
|
-
/* @__PURE__ */ jsx(
|
|
258160
|
-
|
|
258248
|
+
/* @__PURE__ */ jsx(
|
|
258249
|
+
"rect",
|
|
258250
|
+
{
|
|
258251
|
+
ref: calloutBgRef,
|
|
258252
|
+
opacity: 0,
|
|
258253
|
+
pointerEvents: "auto",
|
|
258254
|
+
onMouseEnter: handleCalloutEnter,
|
|
258255
|
+
onMouseLeave: handleCalloutLeave
|
|
258256
|
+
}
|
|
258257
|
+
),
|
|
258258
|
+
/* @__PURE__ */ jsx(
|
|
258259
|
+
"text",
|
|
258260
|
+
{
|
|
258261
|
+
ref: calloutTextRef,
|
|
258262
|
+
opacity: 0,
|
|
258263
|
+
pointerEvents: "auto",
|
|
258264
|
+
onMouseEnter: handleCalloutEnter,
|
|
258265
|
+
onMouseLeave: handleCalloutLeave
|
|
258266
|
+
}
|
|
258267
|
+
)
|
|
258161
258268
|
]
|
|
258162
258269
|
}
|
|
258163
258270
|
);
|
|
@@ -258993,20 +259100,24 @@ const FileCityTrailSequenceLayout = ({
|
|
|
258993
259100
|
if (!selectedMarkerId) return null;
|
|
258994
259101
|
return trail2.markers.find((m) => m.id === selectedMarkerId) ?? null;
|
|
258995
259102
|
}, [trail2.markers, selectedMarkerId]);
|
|
258996
|
-
const
|
|
259103
|
+
const markerByBuildingPath = React.useMemo(() => {
|
|
258997
259104
|
const map2 = /* @__PURE__ */ new Map();
|
|
259105
|
+
if (!cityData) return map2;
|
|
258998
259106
|
for (const m of markersForThisRepo) {
|
|
258999
|
-
|
|
259107
|
+
const cityPath = markerCityPath(m, trail2);
|
|
259108
|
+
if (!cityPath) continue;
|
|
259109
|
+
const b = cityData.buildings.find((cb) => cb.path === cityPath) ?? cityData.buildings.find((cb) => cb.path.endsWith(`/${cityPath}`));
|
|
259110
|
+
if (b) map2.set(b.path, m);
|
|
259000
259111
|
}
|
|
259001
259112
|
return map2;
|
|
259002
|
-
}, [markersForThisRepo]);
|
|
259113
|
+
}, [markersForThisRepo, cityData, trail2]);
|
|
259003
259114
|
const handleBuildingClick = React.useCallback(
|
|
259004
259115
|
(building) => {
|
|
259005
|
-
const marker =
|
|
259116
|
+
const marker = markerByBuildingPath.get(building.path);
|
|
259006
259117
|
if (!marker) return;
|
|
259007
259118
|
onSelectMarker(selectedMarkerId === marker.id ? null : marker.id);
|
|
259008
259119
|
},
|
|
259009
|
-
[
|
|
259120
|
+
[markerByBuildingPath, selectedMarkerId, onSelectMarker]
|
|
259010
259121
|
);
|
|
259011
259122
|
const stepperIndex = React.useMemo(() => {
|
|
259012
259123
|
if (!selectedMarkerId) return -1;
|
|
@@ -259042,37 +259153,39 @@ const FileCityTrailSequenceLayout = ({
|
|
|
259042
259153
|
const firstMarkerByPath = /* @__PURE__ */ new Map();
|
|
259043
259154
|
const order2 = [];
|
|
259044
259155
|
for (const m of markersForThisRepo) {
|
|
259045
|
-
|
|
259046
|
-
|
|
259047
|
-
counts.
|
|
259048
|
-
|
|
259049
|
-
|
|
259050
|
-
|
|
259051
|
-
|
|
259052
|
-
|
|
259053
|
-
|
|
259054
|
-
|
|
259055
|
-
|
|
259056
|
-
|
|
259156
|
+
const cityPath = markerCityPath(m, trail2);
|
|
259157
|
+
if (!cityPath) continue;
|
|
259158
|
+
const existing = counts.get(cityPath) ?? 0;
|
|
259159
|
+
counts.set(cityPath, existing + 1);
|
|
259160
|
+
if (!firstMarkerByPath.has(cityPath)) {
|
|
259161
|
+
firstMarkerByPath.set(cityPath, m.id);
|
|
259162
|
+
order2.push(cityPath);
|
|
259163
|
+
}
|
|
259164
|
+
}
|
|
259165
|
+
const selectedCityPath = selectedMarker ? markerCityPath(selectedMarker, trail2) : null;
|
|
259166
|
+
return order2.map((cityPath) => ({
|
|
259167
|
+
sourcePath: cityPath,
|
|
259168
|
+
markerCount: counts.get(cityPath) ?? 0,
|
|
259169
|
+
isActive: selectedCityPath === cityPath,
|
|
259057
259170
|
onClick: () => {
|
|
259058
|
-
const markerId = firstMarkerByPath.get(
|
|
259171
|
+
const markerId = firstMarkerByPath.get(cityPath);
|
|
259059
259172
|
if (markerId) onSelectMarker(markerId);
|
|
259060
259173
|
},
|
|
259061
259174
|
onHoverChange: setHoveredFilePath
|
|
259062
259175
|
}));
|
|
259063
|
-
}, [markersForThisRepo, selectedMarker, onSelectMarker]);
|
|
259176
|
+
}, [markersForThisRepo, selectedMarker, onSelectMarker, trail2]);
|
|
259064
259177
|
const trailFilesHighlightLayers = React.useMemo(() => {
|
|
259065
259178
|
if (!cityData) return null;
|
|
259066
|
-
const
|
|
259067
|
-
if (
|
|
259179
|
+
const cityPaths = markersForThisRepo.map((m) => markerCityPath(m, trail2)).filter((p2) => typeof p2 === "string" && p2.length > 0);
|
|
259180
|
+
if (cityPaths.length === 0) return null;
|
|
259068
259181
|
const buildingByPath = new Map(cityData.buildings.map((b) => [b.path, b]));
|
|
259069
259182
|
const byColor = /* @__PURE__ */ new Map();
|
|
259070
259183
|
const seen = /* @__PURE__ */ new Set();
|
|
259071
|
-
for (const
|
|
259072
|
-
if (seen.has(
|
|
259073
|
-
const building = buildingByPath.get(
|
|
259184
|
+
for (const cityPath of cityPaths) {
|
|
259185
|
+
if (seen.has(cityPath)) continue;
|
|
259186
|
+
const building = buildingByPath.get(cityPath) ?? cityData.buildings.find((b) => b.path.endsWith(`/${cityPath}`));
|
|
259074
259187
|
if (!building) continue;
|
|
259075
|
-
seen.add(
|
|
259188
|
+
seen.add(cityPath);
|
|
259076
259189
|
const color2 = building.color ?? getFileColor(building.path);
|
|
259077
259190
|
if (!color2) continue;
|
|
259078
259191
|
const list2 = byColor.get(color2) ?? [];
|
|
@@ -259094,7 +259207,7 @@ const FileCityTrailSequenceLayout = ({
|
|
|
259094
259207
|
renderStrategy: "fill"
|
|
259095
259208
|
}))
|
|
259096
259209
|
}));
|
|
259097
|
-
}, [markersForThisRepo, cityData]);
|
|
259210
|
+
}, [markersForThisRepo, cityData, trail2]);
|
|
259098
259211
|
const trailFilesActive = trailFilesHighlightLayers !== null && trailFilesHighlightLayers.length > 0;
|
|
259099
259212
|
const hoveredFileHighlightLayer = React.useMemo(() => {
|
|
259100
259213
|
if (!hoveredFilePath || !cityData) return null;
|
|
@@ -259119,12 +259232,49 @@ const FileCityTrailSequenceLayout = ({
|
|
|
259119
259232
|
]
|
|
259120
259233
|
};
|
|
259121
259234
|
}, [hoveredFilePath, cityData, theme2.colors.accent]);
|
|
259235
|
+
const districtTintLayers = React.useMemo(() => {
|
|
259236
|
+
if (!cityData) return null;
|
|
259237
|
+
const repos = trail2.repos ?? [];
|
|
259238
|
+
if (repos.length <= 1) return null;
|
|
259239
|
+
const palette = ["#3b82f6", "#a855f7", "#f97316", "#10b981", "#eab308"];
|
|
259240
|
+
const layers = [];
|
|
259241
|
+
repos.forEach((repo, i) => {
|
|
259242
|
+
const prefix = `${repo.name}/`;
|
|
259243
|
+
const paths = [];
|
|
259244
|
+
for (const b of cityData.buildings) {
|
|
259245
|
+
if (b.path === repo.name || b.path.startsWith(prefix)) {
|
|
259246
|
+
paths.push(b.path);
|
|
259247
|
+
} else if (b.path.includes(`/${prefix}`)) {
|
|
259248
|
+
paths.push(b.path);
|
|
259249
|
+
}
|
|
259250
|
+
}
|
|
259251
|
+
if (paths.length === 0) return;
|
|
259252
|
+
layers.push({
|
|
259253
|
+
// The layer-system id is internal — using the PURL here is fine
|
|
259254
|
+
// (and uniquely scopes by canonical identity even if two repos
|
|
259255
|
+
// happened to share a `name`).
|
|
259256
|
+
id: `district-${repo.id}`,
|
|
259257
|
+
name: `District: ${repo.name}`,
|
|
259258
|
+
enabled: true,
|
|
259259
|
+
color: palette[i % palette.length],
|
|
259260
|
+
opacity: 0.18,
|
|
259261
|
+
priority: 10,
|
|
259262
|
+
items: paths.map((path2) => ({
|
|
259263
|
+
path: path2,
|
|
259264
|
+
type: "file",
|
|
259265
|
+
renderStrategy: "fill"
|
|
259266
|
+
}))
|
|
259267
|
+
});
|
|
259268
|
+
});
|
|
259269
|
+
return layers.length > 0 ? layers : null;
|
|
259270
|
+
}, [cityData, trail2.repos]);
|
|
259122
259271
|
const cityHighlightLayers = React.useMemo(() => {
|
|
259123
259272
|
const layers = [];
|
|
259273
|
+
if (districtTintLayers) layers.push(...districtTintLayers);
|
|
259124
259274
|
if (trailFilesHighlightLayers) layers.push(...trailFilesHighlightLayers);
|
|
259125
259275
|
if (hoveredFileHighlightLayer) layers.push(hoveredFileHighlightLayer);
|
|
259126
259276
|
return layers.length > 0 ? layers : void 0;
|
|
259127
|
-
}, [trailFilesHighlightLayers, hoveredFileHighlightLayer]);
|
|
259277
|
+
}, [districtTintLayers, trailFilesHighlightLayers, hoveredFileHighlightLayer]);
|
|
259128
259278
|
const containerRef = React.useRef(null);
|
|
259129
259279
|
const snippetPaneRef = React.useRef(null);
|
|
259130
259280
|
const leaderLineRef = React.useRef(null);
|
|
@@ -259141,10 +259291,11 @@ const FileCityTrailSequenceLayout = ({
|
|
|
259141
259291
|
const [snippetPaneWidth, setSnippetPaneWidth] = React.useState(null);
|
|
259142
259292
|
const [containerSize, setContainerSize] = React.useState(null);
|
|
259143
259293
|
const selectedBuilding = React.useMemo(() => {
|
|
259144
|
-
if (!cityData || !
|
|
259145
|
-
const path2 = selectedMarker
|
|
259294
|
+
if (!cityData || !selectedMarker) return null;
|
|
259295
|
+
const path2 = markerCityPath(selectedMarker, trail2);
|
|
259296
|
+
if (!path2) return null;
|
|
259146
259297
|
return cityData.buildings.find((b) => b.path === path2) ?? cityData.buildings.find((b) => b.path.endsWith(`/${path2}`)) ?? null;
|
|
259147
|
-
}, [cityData, selectedMarker]);
|
|
259298
|
+
}, [cityData, selectedMarker, trail2]);
|
|
259148
259299
|
const cityCenter = React.useMemo(() => {
|
|
259149
259300
|
if (!cityData) return null;
|
|
259150
259301
|
return {
|
|
@@ -259168,10 +259319,9 @@ const FileCityTrailSequenceLayout = ({
|
|
|
259168
259319
|
const titles = [];
|
|
259169
259320
|
const markerIds = [];
|
|
259170
259321
|
for (const m of markersForThisRepo) {
|
|
259171
|
-
|
|
259172
|
-
|
|
259173
|
-
|
|
259174
|
-
);
|
|
259322
|
+
const cityPath = markerCityPath(m, trail2);
|
|
259323
|
+
if (!cityPath) continue;
|
|
259324
|
+
const b = buildingByPath.get(cityPath) ?? cityData.buildings.find((cb) => cb.path.endsWith(`/${cityPath}`));
|
|
259175
259325
|
if (b) {
|
|
259176
259326
|
buildings.push(b);
|
|
259177
259327
|
titles.push(((_a2 = m.label) == null ? void 0 : _a2.trim()) || null);
|
|
@@ -259183,7 +259333,7 @@ const FileCityTrailSequenceLayout = ({
|
|
|
259183
259333
|
trailPathTitles: titles,
|
|
259184
259334
|
trailPathMarkerIds: markerIds
|
|
259185
259335
|
};
|
|
259186
|
-
}, [cityData, markersForThisRepo]);
|
|
259336
|
+
}, [cityData, markersForThisRepo, trail2]);
|
|
259187
259337
|
const trailPathCurrentStepIndex = React.useMemo(() => {
|
|
259188
259338
|
if (!selectedMarkerId) return null;
|
|
259189
259339
|
const idx = trailPathMarkerIds.indexOf(selectedMarkerId);
|
|
@@ -259382,7 +259532,7 @@ const FileCityTrailSequenceLayout = ({
|
|
|
259382
259532
|
cityData,
|
|
259383
259533
|
width: "100%",
|
|
259384
259534
|
height: "100%",
|
|
259385
|
-
selectedPath:
|
|
259535
|
+
selectedPath: selectedMarker ? markerCityPath(selectedMarker, trail2) : null,
|
|
259386
259536
|
onBuildingClick: handleBuildingClick,
|
|
259387
259537
|
onCameraFrame,
|
|
259388
259538
|
showControls: false,
|
|
@@ -260485,6 +260635,15 @@ function filterMarkersForRepo(trail2, repository) {
|
|
|
260485
260635
|
}
|
|
260486
260636
|
return trail2.markers.filter((m) => m.repo === repository.id);
|
|
260487
260637
|
}
|
|
260638
|
+
function markerCityPath(marker, trail2) {
|
|
260639
|
+
if (!marker.sourcePath) return null;
|
|
260640
|
+
const repos = trail2.repos ?? [];
|
|
260641
|
+
if (repos.length <= 1) return marker.sourcePath;
|
|
260642
|
+
if (!marker.repo) return null;
|
|
260643
|
+
const repo = repos.find((r2) => r2.id === marker.repo);
|
|
260644
|
+
if (!repo) return null;
|
|
260645
|
+
return `${repo.name}/${marker.sourcePath}`;
|
|
260646
|
+
}
|
|
260488
260647
|
const focusBuildingTool = {
|
|
260489
260648
|
name: "focus_building",
|
|
260490
260649
|
description: "Focuses the camera on a specific file (building) in the file city visualization",
|