@chrryai/pepper 1.7.47 → 2.2.69
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 +1 -1
- package/dist/extension.js +57 -60
- package/dist/extension.js.map +1 -1
- package/dist/extension.mjs +62 -65
- package/dist/extension.mjs.map +1 -1
- package/dist/index.d.mts +25 -25
- package/dist/index.d.ts +25 -25
- package/dist/index.js +57 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -65
- package/dist/index.mjs.map +1 -1
- package/dist/native.js +57 -60
- package/dist/native.js.map +1 -1
- package/dist/native.mjs +62 -65
- package/dist/native.mjs.map +1 -1
- package/dist/web.js +57 -60
- package/dist/web.js.map +1 -1
- package/dist/web.mjs +62 -65
- package/dist/web.mjs.map +1 -1
- package/package.json +39 -39
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ function App() {
|
|
|
35
35
|
function YourComponent() {
|
|
36
36
|
const { navigate, pathname } = useNavigation()
|
|
37
37
|
|
|
38
|
-
return <button onClick={() => navigate("/about")}>Go to About</button>
|
|
38
|
+
return <button type="button" onClick={() => navigate("/about")}>Go to About</button>
|
|
39
39
|
}
|
|
40
40
|
```
|
|
41
41
|
|
package/dist/extension.js
CHANGED
|
@@ -179,11 +179,8 @@ var ClientRouter = class {
|
|
|
179
179
|
*/
|
|
180
180
|
prefetch(url) {
|
|
181
181
|
if (typeof window === "undefined") return;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
});
|
|
185
|
-
} catch {
|
|
186
|
-
}
|
|
182
|
+
fetch(url, { method: "HEAD", mode: "no-cors" }).catch(() => {
|
|
183
|
+
});
|
|
187
184
|
}
|
|
188
185
|
/**
|
|
189
186
|
* Get current router state (useful for SSR hydration)
|
|
@@ -218,56 +215,8 @@ var ClientRouter = class {
|
|
|
218
215
|
};
|
|
219
216
|
var clientRouter = new ClientRouter();
|
|
220
217
|
|
|
221
|
-
// src/providers/HistoryRouterProvider.tsx
|
|
222
|
-
var import_react = require("react");
|
|
223
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
224
|
-
var HistoryRouterContext = (0, import_react.createContext)(
|
|
225
|
-
null
|
|
226
|
-
);
|
|
227
|
-
function useHistoryRouter() {
|
|
228
|
-
const context = (0, import_react.useContext)(HistoryRouterContext);
|
|
229
|
-
if (!context) {
|
|
230
|
-
throw new Error(
|
|
231
|
-
"useHistoryRouter must be used within HistoryRouterProvider"
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
return context;
|
|
235
|
-
}
|
|
236
|
-
function HistoryRouterProvider({
|
|
237
|
-
children
|
|
238
|
-
}) {
|
|
239
|
-
const [state, setState] = (0, import_react.useState)(() => clientRouter.getState());
|
|
240
|
-
const [updateTrigger, setUpdateTrigger] = (0, import_react.useState)(0);
|
|
241
|
-
(0, import_react.useEffect)(() => {
|
|
242
|
-
const unsubscribe = clientRouter.subscribe(() => {
|
|
243
|
-
const newState = clientRouter.getState();
|
|
244
|
-
setState(newState);
|
|
245
|
-
setUpdateTrigger((prev) => prev + 1);
|
|
246
|
-
});
|
|
247
|
-
return () => {
|
|
248
|
-
unsubscribe();
|
|
249
|
-
};
|
|
250
|
-
}, []);
|
|
251
|
-
const forceUpdate = () => {
|
|
252
|
-
setState(clientRouter.getState());
|
|
253
|
-
setUpdateTrigger((prev) => prev + 1);
|
|
254
|
-
};
|
|
255
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
256
|
-
HistoryRouterContext.Provider,
|
|
257
|
-
{
|
|
258
|
-
value: {
|
|
259
|
-
pathname: state.pathname,
|
|
260
|
-
searchParams: state.searchParams,
|
|
261
|
-
hash: state.hash,
|
|
262
|
-
forceUpdate
|
|
263
|
-
},
|
|
264
|
-
children
|
|
265
|
-
}
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
218
|
// src/hooks/useNavigation.ts
|
|
270
|
-
var
|
|
219
|
+
var import_react = require("react");
|
|
271
220
|
function useRouter() {
|
|
272
221
|
return {
|
|
273
222
|
push: clientRouter.push.bind(clientRouter),
|
|
@@ -279,8 +228,8 @@ function useRouter() {
|
|
|
279
228
|
};
|
|
280
229
|
}
|
|
281
230
|
function usePathname() {
|
|
282
|
-
const [pathname, setPathname] = (0,
|
|
283
|
-
(0,
|
|
231
|
+
const [pathname, setPathname] = (0, import_react.useState)(clientRouter.getState().pathname);
|
|
232
|
+
(0, import_react.useEffect)(() => {
|
|
284
233
|
const unsubscribe = clientRouter.subscribe(() => {
|
|
285
234
|
setPathname(clientRouter.getState().pathname);
|
|
286
235
|
});
|
|
@@ -291,10 +240,10 @@ function usePathname() {
|
|
|
291
240
|
return pathname;
|
|
292
241
|
}
|
|
293
242
|
function useSearchParams() {
|
|
294
|
-
const [searchParams, setSearchParams] = (0,
|
|
243
|
+
const [searchParams, setSearchParams] = (0, import_react.useState)(
|
|
295
244
|
clientRouter.getState().searchParams
|
|
296
245
|
);
|
|
297
|
-
(0,
|
|
246
|
+
(0, import_react.useEffect)(() => {
|
|
298
247
|
const unsubscribe = clientRouter.subscribe(() => {
|
|
299
248
|
setSearchParams(clientRouter.getState().searchParams);
|
|
300
249
|
});
|
|
@@ -305,8 +254,8 @@ function useSearchParams() {
|
|
|
305
254
|
return searchParams;
|
|
306
255
|
}
|
|
307
256
|
function useHash() {
|
|
308
|
-
const [hash, setHash] = (0,
|
|
309
|
-
(0,
|
|
257
|
+
const [hash, setHash] = (0, import_react.useState)(clientRouter.getState().hash);
|
|
258
|
+
(0, import_react.useEffect)(() => {
|
|
310
259
|
const unsubscribe = clientRouter.subscribe(() => {
|
|
311
260
|
setHash(clientRouter.getState().hash);
|
|
312
261
|
});
|
|
@@ -335,6 +284,54 @@ function useNavigation() {
|
|
|
335
284
|
hash
|
|
336
285
|
};
|
|
337
286
|
}
|
|
287
|
+
|
|
288
|
+
// src/providers/HistoryRouterProvider.tsx
|
|
289
|
+
var import_react2 = require("react");
|
|
290
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
291
|
+
var HistoryRouterContext = (0, import_react2.createContext)(
|
|
292
|
+
null
|
|
293
|
+
);
|
|
294
|
+
function useHistoryRouter() {
|
|
295
|
+
const context = (0, import_react2.useContext)(HistoryRouterContext);
|
|
296
|
+
if (!context) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
"useHistoryRouter must be used within HistoryRouterProvider"
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
return context;
|
|
302
|
+
}
|
|
303
|
+
function HistoryRouterProvider({
|
|
304
|
+
children
|
|
305
|
+
}) {
|
|
306
|
+
const [state, setState] = (0, import_react2.useState)(() => clientRouter.getState());
|
|
307
|
+
const [_updateTrigger, setUpdateTrigger] = (0, import_react2.useState)(0);
|
|
308
|
+
(0, import_react2.useEffect)(() => {
|
|
309
|
+
const unsubscribe = clientRouter.subscribe(() => {
|
|
310
|
+
const newState = clientRouter.getState();
|
|
311
|
+
setState(newState);
|
|
312
|
+
setUpdateTrigger((prev) => prev + 1);
|
|
313
|
+
});
|
|
314
|
+
return () => {
|
|
315
|
+
unsubscribe();
|
|
316
|
+
};
|
|
317
|
+
}, []);
|
|
318
|
+
const forceUpdate = () => {
|
|
319
|
+
setState(clientRouter.getState());
|
|
320
|
+
setUpdateTrigger((prev) => prev + 1);
|
|
321
|
+
};
|
|
322
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
323
|
+
HistoryRouterContext.Provider,
|
|
324
|
+
{
|
|
325
|
+
value: {
|
|
326
|
+
pathname: state.pathname,
|
|
327
|
+
searchParams: state.searchParams,
|
|
328
|
+
hash: state.hash,
|
|
329
|
+
forceUpdate
|
|
330
|
+
},
|
|
331
|
+
children
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
}
|
|
338
335
|
// Annotate the CommonJS export names for ESM import in node:
|
|
339
336
|
0 && (module.exports = {
|
|
340
337
|
ClientRouter,
|
package/dist/extension.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/extension.ts","../src/core/ClientRouter.ts","../src/providers/HistoryRouterProvider.tsx","../src/hooks/useNavigation.ts"],"sourcesContent":["/**\n * 🌶️ Pepper Router - Browser Extension\n * Optimized for browser extensions (Chrome, Firefox, etc.)\n */\n\nexport * from \"./index\"\n","// Types\nexport interface NavigateOptions {\n scroll?: boolean\n shallow?: boolean\n replace?: boolean // Use replaceState instead of pushState\n}\n\nexport interface RouterState {\n pathname: string\n searchParams: URLSearchParams\n hash: string\n}\n\n// SSR hydration state\ndeclare global {\n interface Window {\n __ROUTER_STATE__?: RouterState\n }\n}\n\nexport class ClientRouter {\n private listeners: Set<() => void> = new Set()\n private state: RouterState\n private isProgrammaticNavigation = false\n private supportsViewTransitions: boolean\n private lastNavigationTime = 0\n private navigationDebounceMs = 10 // Prevent double-tap navigation\n private isSwipeNavigation = false // Track mobile swipe gestures\n\n constructor() {\n // SSR hydration: Use server state if available\n this.state =\n typeof window !== \"undefined\" && window.__ROUTER_STATE__\n ? window.__ROUTER_STATE__\n : this.getCurrentState()\n\n // Cache View Transitions API support check\n this.supportsViewTransitions =\n typeof document !== \"undefined\" && \"startViewTransition\" in document\n\n // Log support status for debugging\n if (typeof window !== \"undefined\") {\n console.log(\n \"🌶️ Pepper Router: View Transitions supported:\",\n this.supportsViewTransitions,\n )\n }\n\n if (typeof window === \"undefined\") return\n\n // Listen to browser navigation events (passive for better performance)\n window.addEventListener(\"popstate\", this.handlePopState, { passive: true })\n window.addEventListener(\"hashchange\", this.handleHashChange, {\n passive: true,\n })\n\n // Detect mobile swipe gestures for back/forward navigation\n this.setupSwipeDetection()\n }\n\n private getCurrentState(): RouterState {\n if (typeof window === \"undefined\") {\n return {\n pathname: \"\",\n searchParams: new URLSearchParams(),\n hash: \"\",\n }\n }\n const url = new URL(window.location.href)\n const pathname = url.pathname === \"/index.html\" ? \"/\" : url.pathname || \"/\"\n\n return {\n pathname,\n searchParams: url.searchParams,\n hash: url.hash,\n }\n }\n\n private handlePopState = () => {\n // Ignore popstate during programmatic navigation\n if (this.isProgrammaticNavigation) {\n return\n }\n\n // Native browser back/forward (mobile swipe gestures, browser buttons)\n // Don't use view transitions - let browser handle it natively for smooth UX\n console.log(\"🌶️ Native back/forward navigation (no view transition)\")\n this.state = this.getCurrentState()\n this.notifyListeners()\n }\n\n private handleHashChange = () => {\n this.state = this.getCurrentState()\n this.notifyListeners()\n }\n\n private notifyListeners() {\n this.listeners.forEach((listener) => listener())\n }\n\n private setupSwipeDetection() {\n if (typeof window === \"undefined\") return\n\n let touchStartX = 0\n let touchStartY = 0\n\n window.addEventListener(\n \"touchstart\",\n (e) => {\n if (e.touches[0]) {\n touchStartX = e.touches[0].clientX\n touchStartY = e.touches[0].clientY\n }\n },\n { passive: true },\n )\n\n window.addEventListener(\n \"touchmove\",\n (e) => {\n if (!e.touches[0]) return\n\n const touchEndX = e.touches[0].clientX\n const touchEndY = e.touches[0].clientY\n\n const deltaX = touchEndX - touchStartX\n const deltaY = touchEndY - touchStartY\n\n // Detect horizontal swipe from screen edge (back/forward gesture)\n // iOS Safari: swipe from left edge = back, right edge = forward\n const isHorizontalSwipe = Math.abs(deltaX) > Math.abs(deltaY)\n const isFromEdge =\n touchStartX < 50 || touchStartX > window.innerWidth - 50\n\n if (isHorizontalSwipe && isFromEdge && Math.abs(deltaX) > 10) {\n this.isSwipeNavigation = true\n console.log(\"🌶️ Swipe gesture detected - disabling view transitions\")\n }\n },\n { passive: true },\n )\n\n window.addEventListener(\n \"touchend\",\n () => {\n // Reset swipe flag after a delay\n setTimeout(() => {\n this.isSwipeNavigation = false\n }, 500)\n },\n { passive: true },\n )\n }\n\n subscribe(listener: () => void) {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n push(href: string, options: NavigateOptions = {}) {\n if (typeof window === \"undefined\") return\n\n // Debounce: Prevent double navigation (mobile double-tap)\n const now = Date.now()\n if (now - this.lastNavigationTime < this.navigationDebounceMs) {\n console.log(\"🌶️ Navigation debounced (double-tap prevented)\")\n return\n }\n this.lastNavigationTime = now\n\n this.isProgrammaticNavigation = true\n const url = new URL(href, window.location.origin)\n\n const performNavigation = () => {\n // Support both push and replace\n if (options.replace) {\n window.history.replaceState({}, \"\", url.toString())\n } else {\n window.history.pushState({}, \"\", url.toString())\n }\n\n this.state = this.getCurrentState()\n\n if (options.scroll !== false) {\n window.scrollTo(0, 0)\n }\n\n this.notifyListeners()\n\n // Use queueMicrotask for better performance than setTimeout\n queueMicrotask(() => {\n this.isProgrammaticNavigation = false\n })\n }\n\n // Use cached View Transitions API check\n // Skip transitions during swipe gestures to avoid conflicts with native animations\n if (\n this.supportsViewTransitions &&\n !options.shallow &&\n !this.isSwipeNavigation\n ) {\n console.log(\"🌶️ Using View Transition for navigation to:\", href)\n document.startViewTransition(performNavigation)\n } else {\n if (this.isSwipeNavigation) {\n console.log(\"🌶️ Swipe navigation - skipping view transition\")\n }\n performNavigation()\n }\n }\n\n replace(href: string, options: NavigateOptions = {}) {\n // Delegate to push with replace option\n this.push(href, { ...options, replace: true })\n }\n\n /**\n * Prefetch a route to warm up the cache\n * Useful for hover/focus prefetching\n */\n prefetch(url: string) {\n if (typeof window === \"undefined\") return\n\n try {\n // HEAD request to warm up cache without downloading full content\n fetch(url, { method: \"HEAD\", mode: \"no-cors\" }).catch(() => {\n // Silently fail - prefetch is a hint, not critical\n })\n } catch {\n // Ignore prefetch errors\n }\n }\n\n /**\n * Get current router state (useful for SSR hydration)\n */\n getState(): RouterState {\n return this.state\n }\n\n refresh() {\n if (typeof window === \"undefined\") return\n // Force refresh by updating state and notifying listeners\n this.state = this.getCurrentState()\n this.notifyListeners()\n // Scroll to top on refresh\n window.scrollTo(0, 0)\n }\n\n back() {\n if (typeof window === \"undefined\") return\n window.history.back()\n }\n\n forward() {\n if (typeof window === \"undefined\") return\n window.history.forward()\n }\n\n destroy() {\n if (typeof window === \"undefined\") return\n window.removeEventListener(\"popstate\", this.handlePopState)\n window.removeEventListener(\"hashchange\", this.handleHashChange)\n this.listeners.clear()\n }\n\n // Get cached View Transitions support status\n hasViewTransitions() {\n return this.supportsViewTransitions\n }\n}\n\n// Create singleton instance\nexport const clientRouter = new ClientRouter()\n","/**\n * HistoryRouterProvider - Makes window.history changes trigger React re-renders\n *\n * This provider ensures that when clientRouter.push() is called,\n * all components using pathname/searchParams will re-render properly\n */\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n ReactNode,\n} from \"react\"\nimport { clientRouter } from \"../core/ClientRouter\"\n\ninterface HistoryRouterContextValue {\n pathname: string\n searchParams: URLSearchParams\n hash: string\n forceUpdate: () => void\n}\n\nconst HistoryRouterContext = createContext<HistoryRouterContextValue | null>(\n null,\n)\n\nexport function useHistoryRouter() {\n const context = useContext(HistoryRouterContext)\n if (!context) {\n throw new Error(\n \"useHistoryRouter must be used within HistoryRouterProvider\",\n )\n }\n return context\n}\n\ninterface HistoryRouterProviderProps {\n children: ReactNode\n}\n\n/**\n * HistoryRouterProvider\n *\n * Wraps the app and listens to window.history changes,\n * triggering React re-renders when navigation occurs\n *\n * @example\n * ```tsx\n * <HistoryRouterProvider>\n * <App />\n * </HistoryRouterProvider>\n * ```\n */\nexport function HistoryRouterProvider({\n children,\n}: HistoryRouterProviderProps) {\n const [state, setState] = useState(() => clientRouter.getState())\n const [updateTrigger, setUpdateTrigger] = useState(0)\n\n useEffect(() => {\n // Subscribe to router changes\n const unsubscribe = clientRouter.subscribe(() => {\n const newState = clientRouter.getState()\n setState(newState)\n setUpdateTrigger((prev: number) => prev + 1)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n const forceUpdate = () => {\n setState(clientRouter.getState())\n setUpdateTrigger((prev: number) => prev + 1)\n }\n\n return (\n <HistoryRouterContext.Provider\n value={{\n pathname: state.pathname,\n searchParams: state.searchParams,\n hash: state.hash,\n forceUpdate,\n }}\n >\n {children}\n </HistoryRouterContext.Provider>\n )\n}\n","import { useEffect, useState } from \"react\"\nimport { clientRouter, NavigateOptions } from \"../core/ClientRouter\"\n\nexport function useRouter() {\n return {\n push: clientRouter.push.bind(clientRouter),\n replace: clientRouter.replace.bind(clientRouter),\n back: clientRouter.back.bind(clientRouter),\n forward: clientRouter.forward.bind(clientRouter),\n refresh: clientRouter.refresh.bind(clientRouter),\n prefetch: clientRouter.prefetch.bind(clientRouter),\n }\n}\n\nexport function usePathname() {\n const [pathname, setPathname] = useState(clientRouter.getState().pathname)\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setPathname(clientRouter.getState().pathname)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return pathname\n}\n\nexport function useSearchParams() {\n const [searchParams, setSearchParams] = useState(\n clientRouter.getState().searchParams,\n )\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setSearchParams(clientRouter.getState().searchParams)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return searchParams\n}\n\nexport function useHash() {\n const [hash, setHash] = useState(clientRouter.getState().hash)\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setHash(clientRouter.getState().hash)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return hash\n}\n\n/**\n * Main navigation hook - provides all navigation methods and state\n *\n * @example\n * ```tsx\n * const { navigate, pathname, searchParams } = useNavigation()\n *\n * navigate('/about')\n * navigate('/search?q=pepper', { replace: true })\n * ```\n */\nexport function useNavigation() {\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const hash = useHash()\n\n return {\n // Navigation methods\n navigate: router.push,\n replace: router.replace,\n goBack: router.back,\n goForward: router.forward,\n refresh: router.refresh,\n prefetch: router.prefetch,\n\n // Current state\n pathname,\n searchParams,\n hash,\n }\n}\n"],"mappings":"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","names":["import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/extension.ts","../src/core/ClientRouter.ts","../src/hooks/useNavigation.ts","../src/providers/HistoryRouterProvider.tsx"],"sourcesContent":["/**\n * 🌶️ Pepper Router - Browser Extension\n * Optimized for browser extensions (Chrome, Firefox, etc.)\n */\n\nexport * from \"./index\"\n","// Types\nexport interface NavigateOptions {\n scroll?: boolean\n shallow?: boolean\n replace?: boolean // Use replaceState instead of pushState\n}\n\nexport interface RouterState {\n pathname: string\n searchParams: URLSearchParams\n hash: string\n}\n\n// SSR hydration state\ndeclare global {\n interface Window {\n __ROUTER_STATE__?: RouterState\n }\n}\n\nexport class ClientRouter {\n private listeners: Set<() => void> = new Set()\n private state: RouterState\n private isProgrammaticNavigation = false\n private supportsViewTransitions: boolean\n private lastNavigationTime = 0\n private navigationDebounceMs = 10 // Prevent double-tap navigation\n private isSwipeNavigation = false // Track mobile swipe gestures\n\n constructor() {\n // SSR hydration: Use server state if available\n this.state =\n typeof window !== \"undefined\" && window.__ROUTER_STATE__\n ? window.__ROUTER_STATE__\n : this.getCurrentState()\n\n // Cache View Transitions API support check\n this.supportsViewTransitions =\n typeof document !== \"undefined\" && \"startViewTransition\" in document\n\n // Log support status for debugging\n if (typeof window !== \"undefined\") {\n console.log(\n \"🌶️ Pepper Router: View Transitions supported:\",\n this.supportsViewTransitions,\n )\n }\n\n if (typeof window === \"undefined\") return\n\n // Listen to browser navigation events (passive for better performance)\n window.addEventListener(\"popstate\", this.handlePopState, { passive: true })\n window.addEventListener(\"hashchange\", this.handleHashChange, {\n passive: true,\n })\n\n // Detect mobile swipe gestures for back/forward navigation\n this.setupSwipeDetection()\n }\n\n private getCurrentState(): RouterState {\n if (typeof window === \"undefined\") {\n return {\n pathname: \"\",\n searchParams: new URLSearchParams(),\n hash: \"\",\n }\n }\n const url = new URL(window.location.href)\n const pathname = url.pathname === \"/index.html\" ? \"/\" : url.pathname || \"/\"\n\n return {\n pathname,\n searchParams: url.searchParams,\n hash: url.hash,\n }\n }\n\n private handlePopState = () => {\n // Ignore popstate during programmatic navigation\n if (this.isProgrammaticNavigation) {\n return\n }\n\n // Native browser back/forward (mobile swipe gestures, browser buttons)\n // Don't use view transitions - let browser handle it natively for smooth UX\n console.log(\"🌶️ Native back/forward navigation (no view transition)\")\n this.state = this.getCurrentState()\n this.notifyListeners()\n }\n\n private handleHashChange = () => {\n this.state = this.getCurrentState()\n this.notifyListeners()\n }\n\n private notifyListeners() {\n this.listeners.forEach((listener) => listener())\n }\n\n private setupSwipeDetection() {\n if (typeof window === \"undefined\") return\n\n let touchStartX = 0\n let touchStartY = 0\n\n window.addEventListener(\n \"touchstart\",\n (e) => {\n if (e.touches[0]) {\n touchStartX = e.touches[0].clientX\n touchStartY = e.touches[0].clientY\n }\n },\n { passive: true },\n )\n\n window.addEventListener(\n \"touchmove\",\n (e) => {\n if (!e.touches[0]) return\n\n const touchEndX = e.touches[0].clientX\n const touchEndY = e.touches[0].clientY\n\n const deltaX = touchEndX - touchStartX\n const deltaY = touchEndY - touchStartY\n\n // Detect horizontal swipe from screen edge (back/forward gesture)\n // iOS Safari: swipe from left edge = back, right edge = forward\n const isHorizontalSwipe = Math.abs(deltaX) > Math.abs(deltaY)\n const isFromEdge =\n touchStartX < 50 || touchStartX > window.innerWidth - 50\n\n if (isHorizontalSwipe && isFromEdge && Math.abs(deltaX) > 10) {\n this.isSwipeNavigation = true\n console.log(\"🌶️ Swipe gesture detected - disabling view transitions\")\n }\n },\n { passive: true },\n )\n\n window.addEventListener(\n \"touchend\",\n () => {\n // Reset swipe flag after a delay\n setTimeout(() => {\n this.isSwipeNavigation = false\n }, 500)\n },\n { passive: true },\n )\n }\n\n subscribe(listener: () => void) {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n push(href: string, options: NavigateOptions = {}) {\n if (typeof window === \"undefined\") return\n\n // Debounce: Prevent double navigation (mobile double-tap)\n const now = Date.now()\n if (now - this.lastNavigationTime < this.navigationDebounceMs) {\n console.log(\"🌶️ Navigation debounced (double-tap prevented)\")\n return\n }\n this.lastNavigationTime = now\n\n this.isProgrammaticNavigation = true\n const url = new URL(href, window.location.origin)\n\n const performNavigation = () => {\n // Support both push and replace\n if (options.replace) {\n window.history.replaceState({}, \"\", url.toString())\n } else {\n window.history.pushState({}, \"\", url.toString())\n }\n\n this.state = this.getCurrentState()\n\n if (options.scroll !== false) {\n window.scrollTo(0, 0)\n }\n\n this.notifyListeners()\n\n // Use queueMicrotask for better performance than setTimeout\n queueMicrotask(() => {\n this.isProgrammaticNavigation = false\n })\n }\n\n // Use cached View Transitions API check\n // Skip transitions during swipe gestures to avoid conflicts with native animations\n if (\n this.supportsViewTransitions &&\n !options.shallow &&\n !this.isSwipeNavigation\n ) {\n console.log(\"🌶️ Using View Transition for navigation to:\", href)\n document.startViewTransition(performNavigation)\n } else {\n if (this.isSwipeNavigation) {\n console.log(\"🌶️ Swipe navigation - skipping view transition\")\n }\n performNavigation()\n }\n }\n\n replace(href: string, options: NavigateOptions = {}) {\n // Delegate to push with replace option\n this.push(href, { ...options, replace: true })\n }\n\n /**\n * Prefetch a route to warm up the cache\n * Useful for hover/focus prefetching\n */\n prefetch(url: string) {\n if (typeof window === \"undefined\") return\n\n // HEAD request to warm up cache without downloading full content\n fetch(url, { method: \"HEAD\", mode: \"no-cors\" }).catch(() => {\n // Silently fail - prefetch is a hint, not critical\n })\n }\n\n /**\n * Get current router state (useful for SSR hydration)\n */\n getState(): RouterState {\n return this.state\n }\n\n refresh() {\n if (typeof window === \"undefined\") return\n // Force refresh by updating state and notifying listeners\n this.state = this.getCurrentState()\n this.notifyListeners()\n // Scroll to top on refresh\n window.scrollTo(0, 0)\n }\n\n back() {\n if (typeof window === \"undefined\") return\n window.history.back()\n }\n\n forward() {\n if (typeof window === \"undefined\") return\n window.history.forward()\n }\n\n destroy() {\n if (typeof window === \"undefined\") return\n window.removeEventListener(\"popstate\", this.handlePopState)\n window.removeEventListener(\"hashchange\", this.handleHashChange)\n this.listeners.clear()\n }\n\n // Get cached View Transitions support status\n hasViewTransitions() {\n return this.supportsViewTransitions\n }\n}\n\n// Create singleton instance\nexport const clientRouter = new ClientRouter()\n","import { useEffect, useState } from \"react\"\nimport { clientRouter } from \"../core/ClientRouter\"\n\nexport function useRouter() {\n return {\n push: clientRouter.push.bind(clientRouter),\n replace: clientRouter.replace.bind(clientRouter),\n back: clientRouter.back.bind(clientRouter),\n forward: clientRouter.forward.bind(clientRouter),\n refresh: clientRouter.refresh.bind(clientRouter),\n prefetch: clientRouter.prefetch.bind(clientRouter),\n }\n}\n\nexport function usePathname() {\n const [pathname, setPathname] = useState(clientRouter.getState().pathname)\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setPathname(clientRouter.getState().pathname)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return pathname\n}\n\nexport function useSearchParams() {\n const [searchParams, setSearchParams] = useState(\n clientRouter.getState().searchParams,\n )\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setSearchParams(clientRouter.getState().searchParams)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return searchParams\n}\n\nexport function useHash() {\n const [hash, setHash] = useState(clientRouter.getState().hash)\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setHash(clientRouter.getState().hash)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return hash\n}\n\n/**\n * Main navigation hook - provides all navigation methods and state\n *\n * @example\n * ```tsx\n * const { navigate, pathname, searchParams } = useNavigation()\n *\n * navigate('/about')\n * navigate('/search?q=pepper', { replace: true })\n * ```\n */\nexport function useNavigation() {\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const hash = useHash()\n\n return {\n // Navigation methods\n navigate: router.push,\n replace: router.replace,\n goBack: router.back,\n goForward: router.forward,\n refresh: router.refresh,\n prefetch: router.prefetch,\n\n // Current state\n pathname,\n searchParams,\n hash,\n }\n}\n","/**\n * HistoryRouterProvider - Makes window.history changes trigger React re-renders\n *\n * This provider ensures that when clientRouter.push() is called,\n * all components using pathname/searchParams will re-render properly\n */\n\nimport {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useState,\n} from \"react\"\nimport { clientRouter } from \"../core/ClientRouter\"\n\ninterface HistoryRouterContextValue {\n pathname: string\n searchParams: URLSearchParams\n hash: string\n forceUpdate: () => void\n}\n\nconst HistoryRouterContext = createContext<HistoryRouterContextValue | null>(\n null,\n)\n\nexport function useHistoryRouter() {\n const context = useContext(HistoryRouterContext)\n if (!context) {\n throw new Error(\n \"useHistoryRouter must be used within HistoryRouterProvider\",\n )\n }\n return context\n}\n\ninterface HistoryRouterProviderProps {\n children: ReactNode\n}\n\n/**\n * HistoryRouterProvider\n *\n * Wraps the app and listens to window.history changes,\n * triggering React re-renders when navigation occurs\n *\n * @example\n * ```tsx\n * <HistoryRouterProvider>\n * <App />\n * </HistoryRouterProvider>\n * ```\n */\nexport function HistoryRouterProvider({\n children,\n}: HistoryRouterProviderProps) {\n const [state, setState] = useState(() => clientRouter.getState())\n const [_updateTrigger, setUpdateTrigger] = useState(0)\n\n useEffect(() => {\n // Subscribe to router changes\n const unsubscribe = clientRouter.subscribe(() => {\n const newState = clientRouter.getState()\n setState(newState)\n setUpdateTrigger((prev: number) => prev + 1)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n const forceUpdate = () => {\n setState(clientRouter.getState())\n setUpdateTrigger((prev: number) => prev + 1)\n }\n\n return (\n <HistoryRouterContext.Provider\n value={{\n pathname: state.pathname,\n searchParams: state.searchParams,\n hash: state.hash,\n forceUpdate,\n }}\n >\n {children}\n </HistoryRouterContext.Provider>\n )\n}\n"],"mappings":"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","names":["import_react"]}
|
package/dist/extension.mjs
CHANGED
|
@@ -145,11 +145,8 @@ var ClientRouter = class {
|
|
|
145
145
|
*/
|
|
146
146
|
prefetch(url) {
|
|
147
147
|
if (typeof window === "undefined") return;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
});
|
|
151
|
-
} catch {
|
|
152
|
-
}
|
|
148
|
+
fetch(url, { method: "HEAD", mode: "no-cors" }).catch(() => {
|
|
149
|
+
});
|
|
153
150
|
}
|
|
154
151
|
/**
|
|
155
152
|
* Get current router state (useful for SSR hydration)
|
|
@@ -184,61 +181,8 @@ var ClientRouter = class {
|
|
|
184
181
|
};
|
|
185
182
|
var clientRouter = new ClientRouter();
|
|
186
183
|
|
|
187
|
-
// src/providers/HistoryRouterProvider.tsx
|
|
188
|
-
import {
|
|
189
|
-
createContext,
|
|
190
|
-
useContext,
|
|
191
|
-
useEffect,
|
|
192
|
-
useState
|
|
193
|
-
} from "react";
|
|
194
|
-
import { jsx } from "react/jsx-runtime";
|
|
195
|
-
var HistoryRouterContext = createContext(
|
|
196
|
-
null
|
|
197
|
-
);
|
|
198
|
-
function useHistoryRouter() {
|
|
199
|
-
const context = useContext(HistoryRouterContext);
|
|
200
|
-
if (!context) {
|
|
201
|
-
throw new Error(
|
|
202
|
-
"useHistoryRouter must be used within HistoryRouterProvider"
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
return context;
|
|
206
|
-
}
|
|
207
|
-
function HistoryRouterProvider({
|
|
208
|
-
children
|
|
209
|
-
}) {
|
|
210
|
-
const [state, setState] = useState(() => clientRouter.getState());
|
|
211
|
-
const [updateTrigger, setUpdateTrigger] = useState(0);
|
|
212
|
-
useEffect(() => {
|
|
213
|
-
const unsubscribe = clientRouter.subscribe(() => {
|
|
214
|
-
const newState = clientRouter.getState();
|
|
215
|
-
setState(newState);
|
|
216
|
-
setUpdateTrigger((prev) => prev + 1);
|
|
217
|
-
});
|
|
218
|
-
return () => {
|
|
219
|
-
unsubscribe();
|
|
220
|
-
};
|
|
221
|
-
}, []);
|
|
222
|
-
const forceUpdate = () => {
|
|
223
|
-
setState(clientRouter.getState());
|
|
224
|
-
setUpdateTrigger((prev) => prev + 1);
|
|
225
|
-
};
|
|
226
|
-
return /* @__PURE__ */ jsx(
|
|
227
|
-
HistoryRouterContext.Provider,
|
|
228
|
-
{
|
|
229
|
-
value: {
|
|
230
|
-
pathname: state.pathname,
|
|
231
|
-
searchParams: state.searchParams,
|
|
232
|
-
hash: state.hash,
|
|
233
|
-
forceUpdate
|
|
234
|
-
},
|
|
235
|
-
children
|
|
236
|
-
}
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
184
|
// src/hooks/useNavigation.ts
|
|
241
|
-
import { useEffect
|
|
185
|
+
import { useEffect, useState } from "react";
|
|
242
186
|
function useRouter() {
|
|
243
187
|
return {
|
|
244
188
|
push: clientRouter.push.bind(clientRouter),
|
|
@@ -250,8 +194,8 @@ function useRouter() {
|
|
|
250
194
|
};
|
|
251
195
|
}
|
|
252
196
|
function usePathname() {
|
|
253
|
-
const [pathname, setPathname] =
|
|
254
|
-
|
|
197
|
+
const [pathname, setPathname] = useState(clientRouter.getState().pathname);
|
|
198
|
+
useEffect(() => {
|
|
255
199
|
const unsubscribe = clientRouter.subscribe(() => {
|
|
256
200
|
setPathname(clientRouter.getState().pathname);
|
|
257
201
|
});
|
|
@@ -262,10 +206,10 @@ function usePathname() {
|
|
|
262
206
|
return pathname;
|
|
263
207
|
}
|
|
264
208
|
function useSearchParams() {
|
|
265
|
-
const [searchParams, setSearchParams] =
|
|
209
|
+
const [searchParams, setSearchParams] = useState(
|
|
266
210
|
clientRouter.getState().searchParams
|
|
267
211
|
);
|
|
268
|
-
|
|
212
|
+
useEffect(() => {
|
|
269
213
|
const unsubscribe = clientRouter.subscribe(() => {
|
|
270
214
|
setSearchParams(clientRouter.getState().searchParams);
|
|
271
215
|
});
|
|
@@ -276,8 +220,8 @@ function useSearchParams() {
|
|
|
276
220
|
return searchParams;
|
|
277
221
|
}
|
|
278
222
|
function useHash() {
|
|
279
|
-
const [hash, setHash] =
|
|
280
|
-
|
|
223
|
+
const [hash, setHash] = useState(clientRouter.getState().hash);
|
|
224
|
+
useEffect(() => {
|
|
281
225
|
const unsubscribe = clientRouter.subscribe(() => {
|
|
282
226
|
setHash(clientRouter.getState().hash);
|
|
283
227
|
});
|
|
@@ -306,6 +250,59 @@ function useNavigation() {
|
|
|
306
250
|
hash
|
|
307
251
|
};
|
|
308
252
|
}
|
|
253
|
+
|
|
254
|
+
// src/providers/HistoryRouterProvider.tsx
|
|
255
|
+
import {
|
|
256
|
+
createContext,
|
|
257
|
+
useContext,
|
|
258
|
+
useEffect as useEffect2,
|
|
259
|
+
useState as useState2
|
|
260
|
+
} from "react";
|
|
261
|
+
import { jsx } from "react/jsx-runtime";
|
|
262
|
+
var HistoryRouterContext = createContext(
|
|
263
|
+
null
|
|
264
|
+
);
|
|
265
|
+
function useHistoryRouter() {
|
|
266
|
+
const context = useContext(HistoryRouterContext);
|
|
267
|
+
if (!context) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
"useHistoryRouter must be used within HistoryRouterProvider"
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
return context;
|
|
273
|
+
}
|
|
274
|
+
function HistoryRouterProvider({
|
|
275
|
+
children
|
|
276
|
+
}) {
|
|
277
|
+
const [state, setState] = useState2(() => clientRouter.getState());
|
|
278
|
+
const [_updateTrigger, setUpdateTrigger] = useState2(0);
|
|
279
|
+
useEffect2(() => {
|
|
280
|
+
const unsubscribe = clientRouter.subscribe(() => {
|
|
281
|
+
const newState = clientRouter.getState();
|
|
282
|
+
setState(newState);
|
|
283
|
+
setUpdateTrigger((prev) => prev + 1);
|
|
284
|
+
});
|
|
285
|
+
return () => {
|
|
286
|
+
unsubscribe();
|
|
287
|
+
};
|
|
288
|
+
}, []);
|
|
289
|
+
const forceUpdate = () => {
|
|
290
|
+
setState(clientRouter.getState());
|
|
291
|
+
setUpdateTrigger((prev) => prev + 1);
|
|
292
|
+
};
|
|
293
|
+
return /* @__PURE__ */ jsx(
|
|
294
|
+
HistoryRouterContext.Provider,
|
|
295
|
+
{
|
|
296
|
+
value: {
|
|
297
|
+
pathname: state.pathname,
|
|
298
|
+
searchParams: state.searchParams,
|
|
299
|
+
hash: state.hash,
|
|
300
|
+
forceUpdate
|
|
301
|
+
},
|
|
302
|
+
children
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
}
|
|
309
306
|
export {
|
|
310
307
|
ClientRouter,
|
|
311
308
|
HistoryRouterProvider,
|
package/dist/extension.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/ClientRouter.ts","../src/providers/HistoryRouterProvider.tsx","../src/hooks/useNavigation.ts"],"sourcesContent":["// Types\nexport interface NavigateOptions {\n scroll?: boolean\n shallow?: boolean\n replace?: boolean // Use replaceState instead of pushState\n}\n\nexport interface RouterState {\n pathname: string\n searchParams: URLSearchParams\n hash: string\n}\n\n// SSR hydration state\ndeclare global {\n interface Window {\n __ROUTER_STATE__?: RouterState\n }\n}\n\nexport class ClientRouter {\n private listeners: Set<() => void> = new Set()\n private state: RouterState\n private isProgrammaticNavigation = false\n private supportsViewTransitions: boolean\n private lastNavigationTime = 0\n private navigationDebounceMs = 10 // Prevent double-tap navigation\n private isSwipeNavigation = false // Track mobile swipe gestures\n\n constructor() {\n // SSR hydration: Use server state if available\n this.state =\n typeof window !== \"undefined\" && window.__ROUTER_STATE__\n ? window.__ROUTER_STATE__\n : this.getCurrentState()\n\n // Cache View Transitions API support check\n this.supportsViewTransitions =\n typeof document !== \"undefined\" && \"startViewTransition\" in document\n\n // Log support status for debugging\n if (typeof window !== \"undefined\") {\n console.log(\n \"🌶️ Pepper Router: View Transitions supported:\",\n this.supportsViewTransitions,\n )\n }\n\n if (typeof window === \"undefined\") return\n\n // Listen to browser navigation events (passive for better performance)\n window.addEventListener(\"popstate\", this.handlePopState, { passive: true })\n window.addEventListener(\"hashchange\", this.handleHashChange, {\n passive: true,\n })\n\n // Detect mobile swipe gestures for back/forward navigation\n this.setupSwipeDetection()\n }\n\n private getCurrentState(): RouterState {\n if (typeof window === \"undefined\") {\n return {\n pathname: \"\",\n searchParams: new URLSearchParams(),\n hash: \"\",\n }\n }\n const url = new URL(window.location.href)\n const pathname = url.pathname === \"/index.html\" ? \"/\" : url.pathname || \"/\"\n\n return {\n pathname,\n searchParams: url.searchParams,\n hash: url.hash,\n }\n }\n\n private handlePopState = () => {\n // Ignore popstate during programmatic navigation\n if (this.isProgrammaticNavigation) {\n return\n }\n\n // Native browser back/forward (mobile swipe gestures, browser buttons)\n // Don't use view transitions - let browser handle it natively for smooth UX\n console.log(\"🌶️ Native back/forward navigation (no view transition)\")\n this.state = this.getCurrentState()\n this.notifyListeners()\n }\n\n private handleHashChange = () => {\n this.state = this.getCurrentState()\n this.notifyListeners()\n }\n\n private notifyListeners() {\n this.listeners.forEach((listener) => listener())\n }\n\n private setupSwipeDetection() {\n if (typeof window === \"undefined\") return\n\n let touchStartX = 0\n let touchStartY = 0\n\n window.addEventListener(\n \"touchstart\",\n (e) => {\n if (e.touches[0]) {\n touchStartX = e.touches[0].clientX\n touchStartY = e.touches[0].clientY\n }\n },\n { passive: true },\n )\n\n window.addEventListener(\n \"touchmove\",\n (e) => {\n if (!e.touches[0]) return\n\n const touchEndX = e.touches[0].clientX\n const touchEndY = e.touches[0].clientY\n\n const deltaX = touchEndX - touchStartX\n const deltaY = touchEndY - touchStartY\n\n // Detect horizontal swipe from screen edge (back/forward gesture)\n // iOS Safari: swipe from left edge = back, right edge = forward\n const isHorizontalSwipe = Math.abs(deltaX) > Math.abs(deltaY)\n const isFromEdge =\n touchStartX < 50 || touchStartX > window.innerWidth - 50\n\n if (isHorizontalSwipe && isFromEdge && Math.abs(deltaX) > 10) {\n this.isSwipeNavigation = true\n console.log(\"🌶️ Swipe gesture detected - disabling view transitions\")\n }\n },\n { passive: true },\n )\n\n window.addEventListener(\n \"touchend\",\n () => {\n // Reset swipe flag after a delay\n setTimeout(() => {\n this.isSwipeNavigation = false\n }, 500)\n },\n { passive: true },\n )\n }\n\n subscribe(listener: () => void) {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n push(href: string, options: NavigateOptions = {}) {\n if (typeof window === \"undefined\") return\n\n // Debounce: Prevent double navigation (mobile double-tap)\n const now = Date.now()\n if (now - this.lastNavigationTime < this.navigationDebounceMs) {\n console.log(\"🌶️ Navigation debounced (double-tap prevented)\")\n return\n }\n this.lastNavigationTime = now\n\n this.isProgrammaticNavigation = true\n const url = new URL(href, window.location.origin)\n\n const performNavigation = () => {\n // Support both push and replace\n if (options.replace) {\n window.history.replaceState({}, \"\", url.toString())\n } else {\n window.history.pushState({}, \"\", url.toString())\n }\n\n this.state = this.getCurrentState()\n\n if (options.scroll !== false) {\n window.scrollTo(0, 0)\n }\n\n this.notifyListeners()\n\n // Use queueMicrotask for better performance than setTimeout\n queueMicrotask(() => {\n this.isProgrammaticNavigation = false\n })\n }\n\n // Use cached View Transitions API check\n // Skip transitions during swipe gestures to avoid conflicts with native animations\n if (\n this.supportsViewTransitions &&\n !options.shallow &&\n !this.isSwipeNavigation\n ) {\n console.log(\"🌶️ Using View Transition for navigation to:\", href)\n document.startViewTransition(performNavigation)\n } else {\n if (this.isSwipeNavigation) {\n console.log(\"🌶️ Swipe navigation - skipping view transition\")\n }\n performNavigation()\n }\n }\n\n replace(href: string, options: NavigateOptions = {}) {\n // Delegate to push with replace option\n this.push(href, { ...options, replace: true })\n }\n\n /**\n * Prefetch a route to warm up the cache\n * Useful for hover/focus prefetching\n */\n prefetch(url: string) {\n if (typeof window === \"undefined\") return\n\n try {\n // HEAD request to warm up cache without downloading full content\n fetch(url, { method: \"HEAD\", mode: \"no-cors\" }).catch(() => {\n // Silently fail - prefetch is a hint, not critical\n })\n } catch {\n // Ignore prefetch errors\n }\n }\n\n /**\n * Get current router state (useful for SSR hydration)\n */\n getState(): RouterState {\n return this.state\n }\n\n refresh() {\n if (typeof window === \"undefined\") return\n // Force refresh by updating state and notifying listeners\n this.state = this.getCurrentState()\n this.notifyListeners()\n // Scroll to top on refresh\n window.scrollTo(0, 0)\n }\n\n back() {\n if (typeof window === \"undefined\") return\n window.history.back()\n }\n\n forward() {\n if (typeof window === \"undefined\") return\n window.history.forward()\n }\n\n destroy() {\n if (typeof window === \"undefined\") return\n window.removeEventListener(\"popstate\", this.handlePopState)\n window.removeEventListener(\"hashchange\", this.handleHashChange)\n this.listeners.clear()\n }\n\n // Get cached View Transitions support status\n hasViewTransitions() {\n return this.supportsViewTransitions\n }\n}\n\n// Create singleton instance\nexport const clientRouter = new ClientRouter()\n","/**\n * HistoryRouterProvider - Makes window.history changes trigger React re-renders\n *\n * This provider ensures that when clientRouter.push() is called,\n * all components using pathname/searchParams will re-render properly\n */\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n ReactNode,\n} from \"react\"\nimport { clientRouter } from \"../core/ClientRouter\"\n\ninterface HistoryRouterContextValue {\n pathname: string\n searchParams: URLSearchParams\n hash: string\n forceUpdate: () => void\n}\n\nconst HistoryRouterContext = createContext<HistoryRouterContextValue | null>(\n null,\n)\n\nexport function useHistoryRouter() {\n const context = useContext(HistoryRouterContext)\n if (!context) {\n throw new Error(\n \"useHistoryRouter must be used within HistoryRouterProvider\",\n )\n }\n return context\n}\n\ninterface HistoryRouterProviderProps {\n children: ReactNode\n}\n\n/**\n * HistoryRouterProvider\n *\n * Wraps the app and listens to window.history changes,\n * triggering React re-renders when navigation occurs\n *\n * @example\n * ```tsx\n * <HistoryRouterProvider>\n * <App />\n * </HistoryRouterProvider>\n * ```\n */\nexport function HistoryRouterProvider({\n children,\n}: HistoryRouterProviderProps) {\n const [state, setState] = useState(() => clientRouter.getState())\n const [updateTrigger, setUpdateTrigger] = useState(0)\n\n useEffect(() => {\n // Subscribe to router changes\n const unsubscribe = clientRouter.subscribe(() => {\n const newState = clientRouter.getState()\n setState(newState)\n setUpdateTrigger((prev: number) => prev + 1)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n const forceUpdate = () => {\n setState(clientRouter.getState())\n setUpdateTrigger((prev: number) => prev + 1)\n }\n\n return (\n <HistoryRouterContext.Provider\n value={{\n pathname: state.pathname,\n searchParams: state.searchParams,\n hash: state.hash,\n forceUpdate,\n }}\n >\n {children}\n </HistoryRouterContext.Provider>\n )\n}\n","import { useEffect, useState } from \"react\"\nimport { clientRouter, NavigateOptions } from \"../core/ClientRouter\"\n\nexport function useRouter() {\n return {\n push: clientRouter.push.bind(clientRouter),\n replace: clientRouter.replace.bind(clientRouter),\n back: clientRouter.back.bind(clientRouter),\n forward: clientRouter.forward.bind(clientRouter),\n refresh: clientRouter.refresh.bind(clientRouter),\n prefetch: clientRouter.prefetch.bind(clientRouter),\n }\n}\n\nexport function usePathname() {\n const [pathname, setPathname] = useState(clientRouter.getState().pathname)\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setPathname(clientRouter.getState().pathname)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return pathname\n}\n\nexport function useSearchParams() {\n const [searchParams, setSearchParams] = useState(\n clientRouter.getState().searchParams,\n )\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setSearchParams(clientRouter.getState().searchParams)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return searchParams\n}\n\nexport function useHash() {\n const [hash, setHash] = useState(clientRouter.getState().hash)\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setHash(clientRouter.getState().hash)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return hash\n}\n\n/**\n * Main navigation hook - provides all navigation methods and state\n *\n * @example\n * ```tsx\n * const { navigate, pathname, searchParams } = useNavigation()\n *\n * navigate('/about')\n * navigate('/search?q=pepper', { replace: true })\n * ```\n */\nexport function useNavigation() {\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const hash = useHash()\n\n return {\n // Navigation methods\n navigate: router.push,\n replace: router.replace,\n goBack: router.back,\n goForward: router.forward,\n refresh: router.refresh,\n prefetch: router.prefetch,\n\n // Current state\n pathname,\n searchParams,\n hash,\n }\n}\n"],"mappings":"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","names":["useEffect","useState","useState","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/ClientRouter.ts","../src/hooks/useNavigation.ts","../src/providers/HistoryRouterProvider.tsx"],"sourcesContent":["// Types\nexport interface NavigateOptions {\n scroll?: boolean\n shallow?: boolean\n replace?: boolean // Use replaceState instead of pushState\n}\n\nexport interface RouterState {\n pathname: string\n searchParams: URLSearchParams\n hash: string\n}\n\n// SSR hydration state\ndeclare global {\n interface Window {\n __ROUTER_STATE__?: RouterState\n }\n}\n\nexport class ClientRouter {\n private listeners: Set<() => void> = new Set()\n private state: RouterState\n private isProgrammaticNavigation = false\n private supportsViewTransitions: boolean\n private lastNavigationTime = 0\n private navigationDebounceMs = 10 // Prevent double-tap navigation\n private isSwipeNavigation = false // Track mobile swipe gestures\n\n constructor() {\n // SSR hydration: Use server state if available\n this.state =\n typeof window !== \"undefined\" && window.__ROUTER_STATE__\n ? window.__ROUTER_STATE__\n : this.getCurrentState()\n\n // Cache View Transitions API support check\n this.supportsViewTransitions =\n typeof document !== \"undefined\" && \"startViewTransition\" in document\n\n // Log support status for debugging\n if (typeof window !== \"undefined\") {\n console.log(\n \"🌶️ Pepper Router: View Transitions supported:\",\n this.supportsViewTransitions,\n )\n }\n\n if (typeof window === \"undefined\") return\n\n // Listen to browser navigation events (passive for better performance)\n window.addEventListener(\"popstate\", this.handlePopState, { passive: true })\n window.addEventListener(\"hashchange\", this.handleHashChange, {\n passive: true,\n })\n\n // Detect mobile swipe gestures for back/forward navigation\n this.setupSwipeDetection()\n }\n\n private getCurrentState(): RouterState {\n if (typeof window === \"undefined\") {\n return {\n pathname: \"\",\n searchParams: new URLSearchParams(),\n hash: \"\",\n }\n }\n const url = new URL(window.location.href)\n const pathname = url.pathname === \"/index.html\" ? \"/\" : url.pathname || \"/\"\n\n return {\n pathname,\n searchParams: url.searchParams,\n hash: url.hash,\n }\n }\n\n private handlePopState = () => {\n // Ignore popstate during programmatic navigation\n if (this.isProgrammaticNavigation) {\n return\n }\n\n // Native browser back/forward (mobile swipe gestures, browser buttons)\n // Don't use view transitions - let browser handle it natively for smooth UX\n console.log(\"🌶️ Native back/forward navigation (no view transition)\")\n this.state = this.getCurrentState()\n this.notifyListeners()\n }\n\n private handleHashChange = () => {\n this.state = this.getCurrentState()\n this.notifyListeners()\n }\n\n private notifyListeners() {\n this.listeners.forEach((listener) => listener())\n }\n\n private setupSwipeDetection() {\n if (typeof window === \"undefined\") return\n\n let touchStartX = 0\n let touchStartY = 0\n\n window.addEventListener(\n \"touchstart\",\n (e) => {\n if (e.touches[0]) {\n touchStartX = e.touches[0].clientX\n touchStartY = e.touches[0].clientY\n }\n },\n { passive: true },\n )\n\n window.addEventListener(\n \"touchmove\",\n (e) => {\n if (!e.touches[0]) return\n\n const touchEndX = e.touches[0].clientX\n const touchEndY = e.touches[0].clientY\n\n const deltaX = touchEndX - touchStartX\n const deltaY = touchEndY - touchStartY\n\n // Detect horizontal swipe from screen edge (back/forward gesture)\n // iOS Safari: swipe from left edge = back, right edge = forward\n const isHorizontalSwipe = Math.abs(deltaX) > Math.abs(deltaY)\n const isFromEdge =\n touchStartX < 50 || touchStartX > window.innerWidth - 50\n\n if (isHorizontalSwipe && isFromEdge && Math.abs(deltaX) > 10) {\n this.isSwipeNavigation = true\n console.log(\"🌶️ Swipe gesture detected - disabling view transitions\")\n }\n },\n { passive: true },\n )\n\n window.addEventListener(\n \"touchend\",\n () => {\n // Reset swipe flag after a delay\n setTimeout(() => {\n this.isSwipeNavigation = false\n }, 500)\n },\n { passive: true },\n )\n }\n\n subscribe(listener: () => void) {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n push(href: string, options: NavigateOptions = {}) {\n if (typeof window === \"undefined\") return\n\n // Debounce: Prevent double navigation (mobile double-tap)\n const now = Date.now()\n if (now - this.lastNavigationTime < this.navigationDebounceMs) {\n console.log(\"🌶️ Navigation debounced (double-tap prevented)\")\n return\n }\n this.lastNavigationTime = now\n\n this.isProgrammaticNavigation = true\n const url = new URL(href, window.location.origin)\n\n const performNavigation = () => {\n // Support both push and replace\n if (options.replace) {\n window.history.replaceState({}, \"\", url.toString())\n } else {\n window.history.pushState({}, \"\", url.toString())\n }\n\n this.state = this.getCurrentState()\n\n if (options.scroll !== false) {\n window.scrollTo(0, 0)\n }\n\n this.notifyListeners()\n\n // Use queueMicrotask for better performance than setTimeout\n queueMicrotask(() => {\n this.isProgrammaticNavigation = false\n })\n }\n\n // Use cached View Transitions API check\n // Skip transitions during swipe gestures to avoid conflicts with native animations\n if (\n this.supportsViewTransitions &&\n !options.shallow &&\n !this.isSwipeNavigation\n ) {\n console.log(\"🌶️ Using View Transition for navigation to:\", href)\n document.startViewTransition(performNavigation)\n } else {\n if (this.isSwipeNavigation) {\n console.log(\"🌶️ Swipe navigation - skipping view transition\")\n }\n performNavigation()\n }\n }\n\n replace(href: string, options: NavigateOptions = {}) {\n // Delegate to push with replace option\n this.push(href, { ...options, replace: true })\n }\n\n /**\n * Prefetch a route to warm up the cache\n * Useful for hover/focus prefetching\n */\n prefetch(url: string) {\n if (typeof window === \"undefined\") return\n\n // HEAD request to warm up cache without downloading full content\n fetch(url, { method: \"HEAD\", mode: \"no-cors\" }).catch(() => {\n // Silently fail - prefetch is a hint, not critical\n })\n }\n\n /**\n * Get current router state (useful for SSR hydration)\n */\n getState(): RouterState {\n return this.state\n }\n\n refresh() {\n if (typeof window === \"undefined\") return\n // Force refresh by updating state and notifying listeners\n this.state = this.getCurrentState()\n this.notifyListeners()\n // Scroll to top on refresh\n window.scrollTo(0, 0)\n }\n\n back() {\n if (typeof window === \"undefined\") return\n window.history.back()\n }\n\n forward() {\n if (typeof window === \"undefined\") return\n window.history.forward()\n }\n\n destroy() {\n if (typeof window === \"undefined\") return\n window.removeEventListener(\"popstate\", this.handlePopState)\n window.removeEventListener(\"hashchange\", this.handleHashChange)\n this.listeners.clear()\n }\n\n // Get cached View Transitions support status\n hasViewTransitions() {\n return this.supportsViewTransitions\n }\n}\n\n// Create singleton instance\nexport const clientRouter = new ClientRouter()\n","import { useEffect, useState } from \"react\"\nimport { clientRouter } from \"../core/ClientRouter\"\n\nexport function useRouter() {\n return {\n push: clientRouter.push.bind(clientRouter),\n replace: clientRouter.replace.bind(clientRouter),\n back: clientRouter.back.bind(clientRouter),\n forward: clientRouter.forward.bind(clientRouter),\n refresh: clientRouter.refresh.bind(clientRouter),\n prefetch: clientRouter.prefetch.bind(clientRouter),\n }\n}\n\nexport function usePathname() {\n const [pathname, setPathname] = useState(clientRouter.getState().pathname)\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setPathname(clientRouter.getState().pathname)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return pathname\n}\n\nexport function useSearchParams() {\n const [searchParams, setSearchParams] = useState(\n clientRouter.getState().searchParams,\n )\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setSearchParams(clientRouter.getState().searchParams)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return searchParams\n}\n\nexport function useHash() {\n const [hash, setHash] = useState(clientRouter.getState().hash)\n\n useEffect(() => {\n const unsubscribe = clientRouter.subscribe(() => {\n setHash(clientRouter.getState().hash)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n return hash\n}\n\n/**\n * Main navigation hook - provides all navigation methods and state\n *\n * @example\n * ```tsx\n * const { navigate, pathname, searchParams } = useNavigation()\n *\n * navigate('/about')\n * navigate('/search?q=pepper', { replace: true })\n * ```\n */\nexport function useNavigation() {\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const hash = useHash()\n\n return {\n // Navigation methods\n navigate: router.push,\n replace: router.replace,\n goBack: router.back,\n goForward: router.forward,\n refresh: router.refresh,\n prefetch: router.prefetch,\n\n // Current state\n pathname,\n searchParams,\n hash,\n }\n}\n","/**\n * HistoryRouterProvider - Makes window.history changes trigger React re-renders\n *\n * This provider ensures that when clientRouter.push() is called,\n * all components using pathname/searchParams will re-render properly\n */\n\nimport {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useState,\n} from \"react\"\nimport { clientRouter } from \"../core/ClientRouter\"\n\ninterface HistoryRouterContextValue {\n pathname: string\n searchParams: URLSearchParams\n hash: string\n forceUpdate: () => void\n}\n\nconst HistoryRouterContext = createContext<HistoryRouterContextValue | null>(\n null,\n)\n\nexport function useHistoryRouter() {\n const context = useContext(HistoryRouterContext)\n if (!context) {\n throw new Error(\n \"useHistoryRouter must be used within HistoryRouterProvider\",\n )\n }\n return context\n}\n\ninterface HistoryRouterProviderProps {\n children: ReactNode\n}\n\n/**\n * HistoryRouterProvider\n *\n * Wraps the app and listens to window.history changes,\n * triggering React re-renders when navigation occurs\n *\n * @example\n * ```tsx\n * <HistoryRouterProvider>\n * <App />\n * </HistoryRouterProvider>\n * ```\n */\nexport function HistoryRouterProvider({\n children,\n}: HistoryRouterProviderProps) {\n const [state, setState] = useState(() => clientRouter.getState())\n const [_updateTrigger, setUpdateTrigger] = useState(0)\n\n useEffect(() => {\n // Subscribe to router changes\n const unsubscribe = clientRouter.subscribe(() => {\n const newState = clientRouter.getState()\n setState(newState)\n setUpdateTrigger((prev: number) => prev + 1)\n })\n\n return () => {\n unsubscribe()\n }\n }, [])\n\n const forceUpdate = () => {\n setState(clientRouter.getState())\n setUpdateTrigger((prev: number) => prev + 1)\n }\n\n return (\n <HistoryRouterContext.Provider\n value={{\n pathname: state.pathname,\n searchParams: state.searchParams,\n hash: state.hash,\n forceUpdate,\n }}\n >\n {children}\n </HistoryRouterContext.Provider>\n )\n}\n"],"mappings":"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","names":["useEffect","useState","useState","useEffect"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -50,31 +50,6 @@ declare class ClientRouter {
|
|
|
50
50
|
}
|
|
51
51
|
declare const clientRouter: ClientRouter;
|
|
52
52
|
|
|
53
|
-
interface HistoryRouterContextValue {
|
|
54
|
-
pathname: string;
|
|
55
|
-
searchParams: URLSearchParams;
|
|
56
|
-
hash: string;
|
|
57
|
-
forceUpdate: () => void;
|
|
58
|
-
}
|
|
59
|
-
declare function useHistoryRouter(): HistoryRouterContextValue;
|
|
60
|
-
interface HistoryRouterProviderProps {
|
|
61
|
-
children: ReactNode;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* HistoryRouterProvider
|
|
65
|
-
*
|
|
66
|
-
* Wraps the app and listens to window.history changes,
|
|
67
|
-
* triggering React re-renders when navigation occurs
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```tsx
|
|
71
|
-
* <HistoryRouterProvider>
|
|
72
|
-
* <App />
|
|
73
|
-
* </HistoryRouterProvider>
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
|
-
declare function HistoryRouterProvider({ children, }: HistoryRouterProviderProps): react_jsx_runtime.JSX.Element;
|
|
77
|
-
|
|
78
53
|
declare function useRouter(): {
|
|
79
54
|
push: (href: string, options?: NavigateOptions) => void;
|
|
80
55
|
replace: (href: string, options?: NavigateOptions) => void;
|
|
@@ -109,4 +84,29 @@ declare function useNavigation(): {
|
|
|
109
84
|
hash: string;
|
|
110
85
|
};
|
|
111
86
|
|
|
87
|
+
interface HistoryRouterContextValue {
|
|
88
|
+
pathname: string;
|
|
89
|
+
searchParams: URLSearchParams;
|
|
90
|
+
hash: string;
|
|
91
|
+
forceUpdate: () => void;
|
|
92
|
+
}
|
|
93
|
+
declare function useHistoryRouter(): HistoryRouterContextValue;
|
|
94
|
+
interface HistoryRouterProviderProps {
|
|
95
|
+
children: ReactNode;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* HistoryRouterProvider
|
|
99
|
+
*
|
|
100
|
+
* Wraps the app and listens to window.history changes,
|
|
101
|
+
* triggering React re-renders when navigation occurs
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```tsx
|
|
105
|
+
* <HistoryRouterProvider>
|
|
106
|
+
* <App />
|
|
107
|
+
* </HistoryRouterProvider>
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare function HistoryRouterProvider({ children, }: HistoryRouterProviderProps): react_jsx_runtime.JSX.Element;
|
|
111
|
+
|
|
112
112
|
export { ClientRouter, HistoryRouterProvider, type NavigateOptions, type RouterState, clientRouter, useHash, useHistoryRouter, useNavigation, usePathname, useRouter, useSearchParams };
|
package/dist/index.d.ts
CHANGED
|
@@ -50,31 +50,6 @@ declare class ClientRouter {
|
|
|
50
50
|
}
|
|
51
51
|
declare const clientRouter: ClientRouter;
|
|
52
52
|
|
|
53
|
-
interface HistoryRouterContextValue {
|
|
54
|
-
pathname: string;
|
|
55
|
-
searchParams: URLSearchParams;
|
|
56
|
-
hash: string;
|
|
57
|
-
forceUpdate: () => void;
|
|
58
|
-
}
|
|
59
|
-
declare function useHistoryRouter(): HistoryRouterContextValue;
|
|
60
|
-
interface HistoryRouterProviderProps {
|
|
61
|
-
children: ReactNode;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* HistoryRouterProvider
|
|
65
|
-
*
|
|
66
|
-
* Wraps the app and listens to window.history changes,
|
|
67
|
-
* triggering React re-renders when navigation occurs
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```tsx
|
|
71
|
-
* <HistoryRouterProvider>
|
|
72
|
-
* <App />
|
|
73
|
-
* </HistoryRouterProvider>
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
|
-
declare function HistoryRouterProvider({ children, }: HistoryRouterProviderProps): react_jsx_runtime.JSX.Element;
|
|
77
|
-
|
|
78
53
|
declare function useRouter(): {
|
|
79
54
|
push: (href: string, options?: NavigateOptions) => void;
|
|
80
55
|
replace: (href: string, options?: NavigateOptions) => void;
|
|
@@ -109,4 +84,29 @@ declare function useNavigation(): {
|
|
|
109
84
|
hash: string;
|
|
110
85
|
};
|
|
111
86
|
|
|
87
|
+
interface HistoryRouterContextValue {
|
|
88
|
+
pathname: string;
|
|
89
|
+
searchParams: URLSearchParams;
|
|
90
|
+
hash: string;
|
|
91
|
+
forceUpdate: () => void;
|
|
92
|
+
}
|
|
93
|
+
declare function useHistoryRouter(): HistoryRouterContextValue;
|
|
94
|
+
interface HistoryRouterProviderProps {
|
|
95
|
+
children: ReactNode;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* HistoryRouterProvider
|
|
99
|
+
*
|
|
100
|
+
* Wraps the app and listens to window.history changes,
|
|
101
|
+
* triggering React re-renders when navigation occurs
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```tsx
|
|
105
|
+
* <HistoryRouterProvider>
|
|
106
|
+
* <App />
|
|
107
|
+
* </HistoryRouterProvider>
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare function HistoryRouterProvider({ children, }: HistoryRouterProviderProps): react_jsx_runtime.JSX.Element;
|
|
111
|
+
|
|
112
112
|
export { ClientRouter, HistoryRouterProvider, type NavigateOptions, type RouterState, clientRouter, useHash, useHistoryRouter, useNavigation, usePathname, useRouter, useSearchParams };
|