@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 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
- try {
183
- fetch(url, { method: "HEAD", mode: "no-cors" }).catch(() => {
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 import_react2 = require("react");
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, import_react2.useState)(clientRouter.getState().pathname);
283
- (0, import_react2.useEffect)(() => {
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, import_react2.useState)(
243
+ const [searchParams, setSearchParams] = (0, import_react.useState)(
295
244
  clientRouter.getState().searchParams
296
245
  );
297
- (0, import_react2.useEffect)(() => {
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, import_react2.useState)(clientRouter.getState().hash);
309
- (0, import_react2.useEffect)(() => {
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,
@@ -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"]}
@@ -145,11 +145,8 @@ var ClientRouter = class {
145
145
  */
146
146
  prefetch(url) {
147
147
  if (typeof window === "undefined") return;
148
- try {
149
- fetch(url, { method: "HEAD", mode: "no-cors" }).catch(() => {
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 as useEffect2, useState as useState2 } from "react";
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] = useState2(clientRouter.getState().pathname);
254
- useEffect2(() => {
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] = useState2(
209
+ const [searchParams, setSearchParams] = useState(
266
210
  clientRouter.getState().searchParams
267
211
  );
268
- useEffect2(() => {
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] = useState2(clientRouter.getState().hash);
280
- useEffect2(() => {
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,
@@ -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 };