@rangojs/router 0.0.0-experimental.259 → 0.0.0-experimental.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +294 -28
- package/dist/bin/rango.js +355 -47
- package/dist/vite/index.js +1658 -1239
- package/package.json +3 -3
- package/skills/cache-guide/SKILL.md +9 -5
- package/skills/caching/SKILL.md +4 -4
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +40 -29
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +229 -15
- package/skills/middleware/SKILL.md +109 -30
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +189 -19
- package/skills/rango/SKILL.md +1 -2
- package/skills/response-routes/SKILL.md +3 -3
- package/skills/route/SKILL.md +44 -3
- package/skills/router-setup/SKILL.md +80 -3
- package/skills/theme/SKILL.md +5 -4
- package/skills/typesafety/SKILL.md +59 -16
- package/skills/use-cache/SKILL.md +16 -2
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +56 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +29 -48
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +19 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +66 -443
- package/src/browser/navigation-client.ts +34 -62
- package/src/browser/navigation-store.ts +4 -33
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/partial-update.ts +103 -151
- package/src/browser/prefetch/cache.ts +67 -0
- package/src/browser/prefetch/fetch.ts +137 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +154 -44
- package/src/browser/react/NavigationProvider.tsx +32 -0
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +29 -11
- package/src/browser/react/location-state.ts +6 -4
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +23 -45
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +21 -64
- package/src/browser/react/use-navigation.ts +7 -32
- package/src/browser/react/use-params.ts +5 -34
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +3 -6
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +75 -114
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +46 -22
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +458 -405
- package/src/browser/types.ts +21 -35
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +38 -13
- package/src/build/generate-route-types.ts +4 -0
- package/src/build/index.ts +1 -0
- package/src/build/route-trie.ts +19 -3
- package/src/build/route-types/codegen.ts +13 -4
- package/src/build/route-types/include-resolution.ts +13 -0
- package/src/build/route-types/per-module-writer.ts +15 -3
- package/src/build/route-types/router-processing.ts +170 -18
- package/src/build/runtime-discovery.ts +13 -1
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +136 -123
- package/src/cache/cache-scope.ts +76 -83
- package/src/cache/cf/cf-cache-store.ts +12 -7
- package/src/cache/document-cache.ts +93 -69
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +43 -69
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +140 -117
- package/src/cache/taint.ts +30 -3
- package/src/cache/types.ts +1 -115
- package/src/client.rsc.tsx +0 -1
- package/src/client.tsx +53 -76
- package/src/errors.ts +6 -1
- package/src/handle.ts +1 -1
- package/src/handles/MetaTags.tsx +5 -2
- package/src/host/cookie-handler.ts +8 -3
- package/src/host/index.ts +0 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +53 -10
- package/src/index.ts +73 -43
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +60 -18
- package/src/prerender.ts +76 -18
- package/src/reverse.ts +11 -7
- package/src/root-error-boundary.tsx +30 -26
- package/src/route-definition/dsl-helpers.ts +9 -6
- package/src/route-definition/index.ts +0 -3
- package/src/route-definition/redirect.ts +15 -3
- package/src/route-map-builder.ts +38 -2
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +7 -0
- package/src/router/content-negotiation.ts +1 -1
- package/src/router/debug-manifest.ts +16 -3
- package/src/router/handler-context.ts +96 -17
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +6 -11
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +62 -54
- package/src/router/match-context.ts +3 -0
- package/src/router/match-handlers.ts +185 -11
- package/src/router/match-middleware/background-revalidation.ts +65 -85
- package/src/router/match-middleware/cache-lookup.ts +78 -10
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/match-result.ts +0 -9
- package/src/router/metrics.ts +233 -13
- package/src/router/middleware-types.ts +34 -39
- package/src/router/middleware.ts +290 -130
- package/src/router/pattern-matching.ts +61 -10
- package/src/router/prerender-match.ts +36 -6
- package/src/router/preview-match.ts +7 -1
- package/src/router/revalidation.ts +61 -2
- package/src/router/router-context.ts +15 -0
- package/src/router/router-interfaces.ts +158 -40
- package/src/router/router-options.ts +223 -1
- package/src/router/router-registry.ts +5 -2
- package/src/router/segment-resolution/fresh.ts +165 -242
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +102 -98
- package/src/router/segment-resolution/revalidation.ts +394 -272
- package/src/router/segment-resolution/static-store.ts +2 -2
- package/src/router/segment-resolution.ts +1 -3
- package/src/router/segment-wrappers.ts +3 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +20 -2
- package/src/router/types.ts +7 -1
- package/src/router.ts +203 -18
- package/src/rsc/handler-context.ts +13 -2
- package/src/rsc/handler.ts +489 -438
- package/src/rsc/helpers.ts +125 -5
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/manifest-init.ts +3 -2
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +245 -19
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +47 -43
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +166 -66
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +20 -2
- package/src/search-params.ts +38 -23
- package/src/server/context.ts +61 -7
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +84 -12
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +275 -49
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +67 -28
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +4 -18
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +6 -1
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +22 -0
- package/src/types/handler-context.ts +103 -16
- package/src/types/index.ts +1 -1
- package/src/types/loader-types.ts +9 -6
- package/src/types/route-config.ts +17 -26
- package/src/types/route-entry.ts +28 -0
- package/src/types/segments.ts +0 -5
- package/src/urls/include-helper.ts +49 -8
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +30 -12
- package/src/urls/path-helper.ts +17 -2
- package/src/urls/pattern-types.ts +21 -1
- package/src/urls/response-types.ts +29 -7
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +27 -9
- package/src/vite/discovery/bundle-postprocess.ts +32 -52
- package/src/vite/discovery/discover-routers.ts +52 -26
- package/src/vite/discovery/prerender-collection.ts +58 -41
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/state.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/index.ts +10 -51
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/expose-internal-ids.ts +4 -3
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/plugins/version-plugin.ts +188 -18
- package/src/vite/rango.ts +61 -36
- package/src/vite/router-discovery.ts +173 -100
- package/src/vite/utils/prerender-utils.ts +81 -0
- package/src/vite/utils/shared-utils.ts +19 -9
- package/skills/testing/SKILL.md +0 -226
- package/src/browser/lru-cache.ts +0 -61
- package/src/browser/react/prefetch.ts +0 -27
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/route-definition/route-function.ts +0 -119
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/{CLAUDE.md → AGENTS.md} +0 -0
|
@@ -11,12 +11,7 @@ import { createEventController } from "./event-controller.js";
|
|
|
11
11
|
import { createNavigationClient } from "./navigation-client.js";
|
|
12
12
|
import { createServerActionBridge } from "./server-action-bridge.js";
|
|
13
13
|
import { createNavigationBridge } from "./navigation-bridge.js";
|
|
14
|
-
import {
|
|
15
|
-
NavigationProvider,
|
|
16
|
-
initHandleDataSync,
|
|
17
|
-
initSegmentsSync,
|
|
18
|
-
} from "./react/index.js";
|
|
19
|
-
import { initThemeConfigSync } from "../theme/theme-context.js";
|
|
14
|
+
import { NavigationProvider } from "./react/index.js";
|
|
20
15
|
import type {
|
|
21
16
|
RscPayload,
|
|
22
17
|
RscBrowserDependencies,
|
|
@@ -26,6 +21,11 @@ import type {
|
|
|
26
21
|
} from "./types.js";
|
|
27
22
|
import type { EventController } from "./event-controller.js";
|
|
28
23
|
import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
|
|
24
|
+
import { initRangoState } from "./rango-state.js";
|
|
25
|
+
import {
|
|
26
|
+
isInterceptSegment,
|
|
27
|
+
splitInterceptSegments,
|
|
28
|
+
} from "./intercept-utils.js";
|
|
29
29
|
|
|
30
30
|
// Vite HMR types are provided by vite/client
|
|
31
31
|
|
|
@@ -110,6 +110,8 @@ export interface BrowserAppContext {
|
|
|
110
110
|
initialTheme?: Theme;
|
|
111
111
|
/** Whether connection warmup is enabled */
|
|
112
112
|
warmupEnabled?: boolean;
|
|
113
|
+
/** App version for prefetch version mismatch detection */
|
|
114
|
+
version?: string;
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
// Module-level state for the initialized app
|
|
@@ -166,16 +168,6 @@ export async function initBrowserApp(
|
|
|
166
168
|
initialLocation: new URL(window.location.href),
|
|
167
169
|
});
|
|
168
170
|
|
|
169
|
-
// Initialize segments state BEFORE hydration to avoid mismatch
|
|
170
|
-
initSegmentsSync(
|
|
171
|
-
initialPayload.metadata?.matched,
|
|
172
|
-
initialPayload.metadata?.pathname,
|
|
173
|
-
initialPayload.metadata?.params,
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
// Initialize theme config for MetaTags (must match SSR state)
|
|
177
|
-
initThemeConfigSync(effectiveThemeConfig);
|
|
178
|
-
|
|
179
171
|
// Initialize event controller with segment order (even without handles)
|
|
180
172
|
eventController.setHandleData({}, initialPayload.metadata?.matched);
|
|
181
173
|
|
|
@@ -191,12 +183,11 @@ export async function initBrowserApp(
|
|
|
191
183
|
for await (const handleData of handlesGenerator) {
|
|
192
184
|
lastHandleData = handleData;
|
|
193
185
|
}
|
|
194
|
-
// Initialize
|
|
186
|
+
// Initialize event controller with initial handle state before hydration.
|
|
195
187
|
eventController.setHandleData(
|
|
196
188
|
lastHandleData,
|
|
197
189
|
initialPayload.metadata?.matched,
|
|
198
190
|
);
|
|
199
|
-
initHandleDataSync(lastHandleData, initialPayload.metadata?.matched);
|
|
200
191
|
|
|
201
192
|
// Update the initial cache entry with the processed handleData
|
|
202
193
|
// The cache entry was created by createNavigationStore but without handleData
|
|
@@ -210,6 +201,10 @@ export async function initBrowserApp(
|
|
|
210
201
|
const rootLayout = initialPayload.metadata?.rootLayout;
|
|
211
202
|
const version = initialPayload.metadata?.version;
|
|
212
203
|
|
|
204
|
+
// Initialize the localStorage state key for browser HTTP cache invalidation.
|
|
205
|
+
// Uses the build version so a new deploy automatically busts all cached prefetches.
|
|
206
|
+
initRangoState(version ?? "0");
|
|
207
|
+
|
|
213
208
|
// Create a bound renderSegments that includes rootLayout
|
|
214
209
|
const renderSegments = (
|
|
215
210
|
segments: ResolvedSegment[],
|
|
@@ -270,11 +265,14 @@ export async function initBrowserApp(
|
|
|
270
265
|
});
|
|
271
266
|
const streamingToken = handle.startStreaming();
|
|
272
267
|
|
|
268
|
+
const interceptSourceUrl = store.getInterceptSourceUrl();
|
|
269
|
+
|
|
273
270
|
try {
|
|
274
271
|
const { payload, streamComplete } = await client.fetchPartial({
|
|
275
272
|
targetUrl: window.location.href,
|
|
276
273
|
segmentIds: [],
|
|
277
274
|
previousUrl: store.getSegmentState().currentUrl,
|
|
275
|
+
interceptSourceUrl: interceptSourceUrl || undefined,
|
|
278
276
|
hmr: true,
|
|
279
277
|
});
|
|
280
278
|
|
|
@@ -282,10 +280,22 @@ export async function initBrowserApp(
|
|
|
282
280
|
const segments = payload.metadata.segments || [];
|
|
283
281
|
const matched = payload.metadata.matched || [];
|
|
284
282
|
|
|
283
|
+
// Derive intercept state from the returned payload, not the
|
|
284
|
+
// pre-fetch store snapshot. If the HMR edit removed intercept
|
|
285
|
+
// behavior, the response won't contain intercept segments.
|
|
286
|
+
const responseIsIntercept = segments.some(isInterceptSegment);
|
|
287
|
+
|
|
288
|
+
// Sync store intercept state with what the server returned
|
|
289
|
+
if (!responseIsIntercept && interceptSourceUrl) {
|
|
290
|
+
store.setInterceptSourceUrl(null);
|
|
291
|
+
}
|
|
292
|
+
|
|
285
293
|
store.setSegmentIds(matched);
|
|
286
294
|
store.setCurrentUrl(window.location.href);
|
|
287
295
|
|
|
288
|
-
const historyKey = generateHistoryKey(window.location.href
|
|
296
|
+
const historyKey = generateHistoryKey(window.location.href, {
|
|
297
|
+
intercept: responseIsIntercept,
|
|
298
|
+
});
|
|
289
299
|
store.setHistoryKey(historyKey);
|
|
290
300
|
const currentHandleData = eventController.getHandleState().data;
|
|
291
301
|
store.cacheSegmentsForHistory(
|
|
@@ -294,18 +304,22 @@ export async function initBrowserApp(
|
|
|
294
304
|
currentHandleData,
|
|
295
305
|
);
|
|
296
306
|
|
|
307
|
+
const { main, intercept } = splitInterceptSegments(segments);
|
|
297
308
|
store.emitUpdate({
|
|
298
|
-
root: renderSegments(
|
|
309
|
+
root: renderSegments(main, {
|
|
310
|
+
interceptSegments: intercept.length > 0 ? intercept : undefined,
|
|
311
|
+
}),
|
|
299
312
|
metadata: payload.metadata,
|
|
300
313
|
});
|
|
301
314
|
}
|
|
302
315
|
|
|
303
316
|
await streamComplete;
|
|
317
|
+
handle.complete(new URL(window.location.href));
|
|
318
|
+
console.log("[RSCRouter] HMR: RSC stream complete");
|
|
304
319
|
} finally {
|
|
305
320
|
streamingToken.end();
|
|
321
|
+
handle[Symbol.dispose]();
|
|
306
322
|
}
|
|
307
|
-
handle.complete(new URL(window.location.href));
|
|
308
|
-
console.log("[RSCRouter] HMR: RSC stream complete");
|
|
309
323
|
});
|
|
310
324
|
}
|
|
311
325
|
|
|
@@ -319,6 +333,7 @@ export async function initBrowserApp(
|
|
|
319
333
|
themeConfig: effectiveThemeConfig,
|
|
320
334
|
initialTheme: effectiveInitialTheme,
|
|
321
335
|
warmupEnabled: initialPayload.metadata?.warmupEnabled ?? true,
|
|
336
|
+
version,
|
|
322
337
|
};
|
|
323
338
|
browserAppContext = context;
|
|
324
339
|
|
|
@@ -383,8 +398,16 @@ export function RSCRouter(_props: RSCRouterProps): React.ReactElement {
|
|
|
383
398
|
themeConfig,
|
|
384
399
|
initialTheme,
|
|
385
400
|
warmupEnabled,
|
|
401
|
+
version,
|
|
386
402
|
} = getBrowserAppContext();
|
|
387
403
|
|
|
404
|
+
// Signal that the React tree has hydrated. useEffect only fires after
|
|
405
|
+
// hydration completes, so this attribute is a stable readiness marker
|
|
406
|
+
// that does not depend on React internals like __reactFiber.
|
|
407
|
+
React.useEffect(() => {
|
|
408
|
+
document.documentElement.dataset.hydrated = "";
|
|
409
|
+
}, []);
|
|
410
|
+
|
|
388
411
|
return (
|
|
389
412
|
<NavigationProvider
|
|
390
413
|
store={store}
|
|
@@ -394,6 +417,7 @@ export function RSCRouter(_props: RSCRouterProps): React.ReactElement {
|
|
|
394
417
|
themeConfig={themeConfig}
|
|
395
418
|
initialTheme={initialTheme}
|
|
396
419
|
warmupEnabled={warmupEnabled}
|
|
420
|
+
version={version}
|
|
397
421
|
/>
|
|
398
422
|
);
|
|
399
423
|
}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* - Supports hash link scrolling
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { debugLog } from "./logging.js";
|
|
12
|
+
|
|
11
13
|
const SCROLL_STORAGE_KEY = "rsc-router-scroll-positions";
|
|
12
14
|
|
|
13
15
|
/**
|
|
@@ -139,12 +141,13 @@ export function initScrollRestoration(options?: {
|
|
|
139
141
|
|
|
140
142
|
window.addEventListener("pagehide", handlePageHide);
|
|
141
143
|
|
|
142
|
-
|
|
144
|
+
debugLog(
|
|
143
145
|
"[Scroll] Initialized, loaded positions:",
|
|
144
146
|
Object.keys(savedScrollPositions).length,
|
|
145
147
|
);
|
|
146
148
|
|
|
147
149
|
return () => {
|
|
150
|
+
cancelScrollRestorationPolling();
|
|
148
151
|
window.removeEventListener("pagehide", handlePageHide);
|
|
149
152
|
window.history.scrollRestoration = "auto";
|
|
150
153
|
initialized = false;
|
|
@@ -267,13 +270,13 @@ export function restoreScrollPosition(options?: {
|
|
|
267
270
|
|
|
268
271
|
if (canScrollToPosition) {
|
|
269
272
|
window.scrollTo(0, savedY);
|
|
270
|
-
|
|
273
|
+
debugLog("[Scroll] Restored position:", savedY, "for key:", key);
|
|
271
274
|
return true;
|
|
272
275
|
}
|
|
273
276
|
|
|
274
277
|
// Scroll as far as we can for now
|
|
275
278
|
window.scrollTo(0, maxScrollY);
|
|
276
|
-
|
|
279
|
+
debugLog("[Scroll] Partial restore to:", maxScrollY, "target:", savedY);
|
|
277
280
|
|
|
278
281
|
// Poll while streaming until we can scroll to target position
|
|
279
282
|
if (options?.retryIfStreaming && options?.isStreaming?.()) {
|
|
@@ -282,14 +285,14 @@ export function restoreScrollPosition(options?: {
|
|
|
282
285
|
pendingPollInterval = setInterval(() => {
|
|
283
286
|
// Stop if we've exceeded the timeout
|
|
284
287
|
if (Date.now() - startTime > SCROLL_POLL_TIMEOUT_MS) {
|
|
285
|
-
|
|
288
|
+
debugLog("[Scroll] Polling timeout, giving up");
|
|
286
289
|
cancelScrollRestorationPolling();
|
|
287
290
|
return;
|
|
288
291
|
}
|
|
289
292
|
|
|
290
293
|
// Stop if streaming ended
|
|
291
294
|
if (!options.isStreaming?.()) {
|
|
292
|
-
|
|
295
|
+
debugLog("[Scroll] Streaming ended, stopping poll");
|
|
293
296
|
cancelScrollRestorationPolling();
|
|
294
297
|
return;
|
|
295
298
|
}
|
|
@@ -299,7 +302,7 @@ export function restoreScrollPosition(options?: {
|
|
|
299
302
|
document.documentElement.scrollHeight - window.innerHeight;
|
|
300
303
|
if (savedY <= currentMaxScrollY) {
|
|
301
304
|
window.scrollTo(0, savedY);
|
|
302
|
-
|
|
305
|
+
debugLog("[Scroll] Poll restored position:", savedY);
|
|
303
306
|
cancelScrollRestorationPolling();
|
|
304
307
|
}
|
|
305
308
|
}, SCROLL_POLL_INTERVAL_MS);
|
|
@@ -322,7 +325,7 @@ export function scrollToHash(): boolean {
|
|
|
322
325
|
const element = document.getElementById(id);
|
|
323
326
|
if (element) {
|
|
324
327
|
element.scrollIntoView();
|
|
325
|
-
|
|
328
|
+
debugLog("[Scroll] Scrolled to hash element:", id);
|
|
326
329
|
return true;
|
|
327
330
|
}
|
|
328
331
|
} catch (e) {
|