@textjs/app 0.0.0-stage → 1.0.2
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 +34 -2
- package/bin/text.js +29 -0
- package/bin/text.ts +13 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/cli/build.d.ts +39 -0
- package/dist/cli/build.d.ts.map +1 -0
- package/dist/cli/build.js +157 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/config.d.ts +22 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +73 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/dev.d.ts +12 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +49 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/index.d.ts +26 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +192 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/run.d.ts +31 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +111 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/start.d.ts +14 -0
- package/dist/cli/start.d.ts.map +1 -0
- package/dist/cli/start.js +183 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli/ui.d.ts +35 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +73 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/client.d.ts +34 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +93 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +143 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +44 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +26 -0
- package/dist/context.js.map +1 -0
- package/dist/dev-errors.d.ts +68 -0
- package/dist/dev-errors.d.ts.map +1 -0
- package/dist/dev-errors.js +121 -0
- package/dist/dev-errors.js.map +1 -0
- package/dist/devtools.d.ts +64 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +186 -0
- package/dist/devtools.js.map +1 -0
- package/dist/frame-mirror.d.ts +19 -0
- package/dist/frame-mirror.d.ts.map +1 -0
- package/dist/frame-mirror.js +141 -0
- package/dist/frame-mirror.js.map +1 -0
- package/dist/frame-protocol.d.ts +175 -0
- package/dist/frame-protocol.d.ts.map +1 -0
- package/dist/frame-protocol.js +369 -0
- package/dist/frame-protocol.js.map +1 -0
- package/dist/history.d.ts +54 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +152 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/link.d.ts +32 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +54 -0
- package/dist/link.js.map +1 -0
- package/dist/live-player.d.ts +83 -0
- package/dist/live-player.d.ts.map +1 -0
- package/dist/live-player.js +413 -0
- package/dist/live-player.js.map +1 -0
- package/dist/live-protocol.d.ts +132 -0
- package/dist/live-protocol.d.ts.map +1 -0
- package/dist/live-protocol.js +278 -0
- package/dist/live-protocol.js.map +1 -0
- package/dist/live-stub.d.ts +10 -0
- package/dist/live-stub.d.ts.map +1 -0
- package/dist/live-stub.js +32 -0
- package/dist/live-stub.js.map +1 -0
- package/dist/mcp/cli.d.ts +22 -0
- package/dist/mcp/cli.d.ts.map +1 -0
- package/dist/mcp/cli.js +177 -0
- package/dist/mcp/cli.js.map +1 -0
- package/dist/mcp/frame.d.ts +58 -0
- package/dist/mcp/frame.d.ts.map +1 -0
- package/dist/mcp/frame.js +169 -0
- package/dist/mcp/frame.js.map +1 -0
- package/dist/mcp/index.d.ts +15 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +15 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/inspect.d.ts +95 -0
- package/dist/mcp/inspect.d.ts.map +1 -0
- package/dist/mcp/inspect.js +386 -0
- package/dist/mcp/inspect.js.map +1 -0
- package/dist/mcp/keys.d.ts +20 -0
- package/dist/mcp/keys.d.ts.map +1 -0
- package/dist/mcp/keys.js +87 -0
- package/dist/mcp/keys.js.map +1 -0
- package/dist/mcp/logs.d.ts +63 -0
- package/dist/mcp/logs.d.ts.map +1 -0
- package/dist/mcp/logs.js +217 -0
- package/dist/mcp/logs.js.map +1 -0
- package/dist/mcp/server.d.ts +64 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +485 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/session.d.ts +230 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +742 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/navigation-errors.d.ts +28 -0
- package/dist/navigation-errors.d.ts.map +1 -0
- package/dist/navigation-errors.js +43 -0
- package/dist/navigation-errors.js.map +1 -0
- package/dist/navigation.d.ts +21 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +33 -0
- package/dist/navigation.js.map +1 -0
- package/dist/router.d.ts +76 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +296 -0
- package/dist/router.js.map +1 -0
- package/dist/routing.d.ts +101 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +286 -0
- package/dist/routing.js.map +1 -0
- package/dist/server/adapters/build-step.d.ts +34 -0
- package/dist/server/adapters/build-step.d.ts.map +1 -0
- package/dist/server/adapters/build-step.js +112 -0
- package/dist/server/adapters/build-step.js.map +1 -0
- package/dist/server/adapters/bun.d.ts +8 -0
- package/dist/server/adapters/bun.d.ts.map +1 -0
- package/dist/server/adapters/bun.js +89 -0
- package/dist/server/adapters/bun.js.map +1 -0
- package/dist/server/adapters/bundle.d.ts +21 -0
- package/dist/server/adapters/bundle.d.ts.map +1 -0
- package/dist/server/adapters/bundle.js +86 -0
- package/dist/server/adapters/bundle.js.map +1 -0
- package/dist/server/adapters/cloudflare.d.ts +29 -0
- package/dist/server/adapters/cloudflare.d.ts.map +1 -0
- package/dist/server/adapters/cloudflare.js +105 -0
- package/dist/server/adapters/cloudflare.js.map +1 -0
- package/dist/server/adapters/index.d.ts +10 -0
- package/dist/server/adapters/index.d.ts.map +1 -0
- package/dist/server/adapters/index.js +24 -0
- package/dist/server/adapters/index.js.map +1 -0
- package/dist/server/adapters/runtime-node.d.ts +16 -0
- package/dist/server/adapters/runtime-node.d.ts.map +1 -0
- package/dist/server/adapters/runtime-node.js +29 -0
- package/dist/server/adapters/runtime-node.js.map +1 -0
- package/dist/server/adapters/runtime.d.ts +42 -0
- package/dist/server/adapters/runtime.d.ts.map +1 -0
- package/dist/server/adapters/runtime.js +115 -0
- package/dist/server/adapters/runtime.js.map +1 -0
- package/dist/server/adapters/shared.d.ts +66 -0
- package/dist/server/adapters/shared.d.ts.map +1 -0
- package/dist/server/adapters/shared.js +124 -0
- package/dist/server/adapters/shared.js.map +1 -0
- package/dist/server/adapters/static.d.ts +9 -0
- package/dist/server/adapters/static.d.ts.map +1 -0
- package/dist/server/adapters/static.js +70 -0
- package/dist/server/adapters/static.js.map +1 -0
- package/dist/server/adapters/vercel.d.ts +43 -0
- package/dist/server/adapters/vercel.d.ts.map +1 -0
- package/dist/server/adapters/vercel.js +119 -0
- package/dist/server/adapters/vercel.js.map +1 -0
- package/dist/server/bun-jsx.d.ts +18 -0
- package/dist/server/bun-jsx.d.ts.map +1 -0
- package/dist/server/bun-jsx.js +56 -0
- package/dist/server/bun-jsx.js.map +1 -0
- package/dist/server/cache-route.d.ts +38 -0
- package/dist/server/cache-route.d.ts.map +1 -0
- package/dist/server/cache-route.js +109 -0
- package/dist/server/cache-route.js.map +1 -0
- package/dist/server/cache.d.ts +131 -0
- package/dist/server/cache.d.ts.map +1 -0
- package/dist/server/cache.js +409 -0
- package/dist/server/cache.js.map +1 -0
- package/dist/server/frames.d.ts +45 -0
- package/dist/server/frames.d.ts.map +1 -0
- package/dist/server/frames.js +294 -0
- package/dist/server/frames.js.map +1 -0
- package/dist/server/http.d.ts +80 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +453 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/jsx-dev-runtime.d.ts +5 -0
- package/dist/server/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/server/jsx-dev-runtime.js +6 -0
- package/dist/server/jsx-dev-runtime.js.map +1 -0
- package/dist/server/jsx-runtime.d.ts +10 -0
- package/dist/server/jsx-runtime.d.ts.map +1 -0
- package/dist/server/jsx-runtime.js +11 -0
- package/dist/server/jsx-runtime.js.map +1 -0
- package/dist/server/live-app.d.ts +87 -0
- package/dist/server/live-app.d.ts.map +1 -0
- package/dist/server/live-app.js +307 -0
- package/dist/server/live-app.js.map +1 -0
- package/dist/server/live-build.d.ts +70 -0
- package/dist/server/live-build.d.ts.map +1 -0
- package/dist/server/live-build.js +222 -0
- package/dist/server/live-build.js.map +1 -0
- package/dist/server/live-dev.d.ts +62 -0
- package/dist/server/live-dev.d.ts.map +1 -0
- package/dist/server/live-dev.js +313 -0
- package/dist/server/live-dev.js.map +1 -0
- package/dist/server/live-manifest.d.ts +23 -0
- package/dist/server/live-manifest.d.ts.map +1 -0
- package/dist/server/live-manifest.js +7 -0
- package/dist/server/live-manifest.js.map +1 -0
- package/dist/server/live-prod.d.ts +30 -0
- package/dist/server/live-prod.d.ts.map +1 -0
- package/dist/server/live-prod.js +97 -0
- package/dist/server/live-prod.js.map +1 -0
- package/dist/server/live-routes.d.ts +72 -0
- package/dist/server/live-routes.d.ts.map +1 -0
- package/dist/server/live-routes.js +274 -0
- package/dist/server/live-routes.js.map +1 -0
- package/dist/server/live-web.d.ts +16 -0
- package/dist/server/live-web.d.ts.map +1 -0
- package/dist/server/live-web.js +159 -0
- package/dist/server/live-web.js.map +1 -0
- package/dist/server/live.d.ts +138 -0
- package/dist/server/live.d.ts.map +1 -0
- package/dist/server/live.js +542 -0
- package/dist/server/live.js.map +1 -0
- package/dist/server/middleware-node.d.ts +48 -0
- package/dist/server/middleware-node.d.ts.map +1 -0
- package/dist/server/middleware-node.js +167 -0
- package/dist/server/middleware-node.js.map +1 -0
- package/dist/server/middleware-runtime.d.ts +97 -0
- package/dist/server/middleware-runtime.d.ts.map +1 -0
- package/dist/server/middleware-runtime.js +447 -0
- package/dist/server/middleware-runtime.js.map +1 -0
- package/dist/server/middleware.d.ts +145 -0
- package/dist/server/middleware.d.ts.map +1 -0
- package/dist/server/middleware.js +365 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/ppr.d.ts +22 -0
- package/dist/server/ppr.d.ts.map +1 -0
- package/dist/server/ppr.js +58 -0
- package/dist/server/ppr.js.map +1 -0
- package/dist/server/scan.d.ts +48 -0
- package/dist/server/scan.d.ts.map +1 -0
- package/dist/server/scan.js +161 -0
- package/dist/server/scan.js.map +1 -0
- package/dist/server/server-components.d.ts +65 -0
- package/dist/server/server-components.d.ts.map +1 -0
- package/dist/server/server-components.js +215 -0
- package/dist/server/server-components.js.map +1 -0
- package/dist/terminal.d.ts +5 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +5 -0
- package/dist/terminal.js.map +1 -0
- package/dist/testing.d.ts +5 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +5 -0
- package/dist/testing.js.map +1 -0
- package/dist/vite-plugin.d.ts +51 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +403 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +130 -4
- package/src/cli/build.ts +182 -0
- package/src/cli/config.ts +72 -0
- package/src/cli/dev.ts +54 -0
- package/src/cli/index.ts +185 -0
- package/src/cli/run.ts +124 -0
- package/src/cli/start.ts +194 -0
- package/src/cli/ui.ts +93 -0
- package/src/client.tsx +122 -0
- package/src/config.ts +155 -0
- package/src/context.ts +69 -0
- package/src/dev-errors.ts +167 -0
- package/src/devtools.tsx +419 -0
- package/src/frame-mirror.ts +145 -0
- package/src/frame-protocol.ts +435 -0
- package/src/history.ts +201 -0
- package/src/index.ts +9 -0
- package/src/link.tsx +108 -0
- package/src/live-player.ts +449 -0
- package/src/live-protocol.ts +317 -0
- package/src/live-stub.tsx +32 -0
- package/src/mcp/cli.ts +188 -0
- package/src/mcp/frame.ts +224 -0
- package/src/mcp/index.ts +15 -0
- package/src/mcp/inspect.ts +445 -0
- package/src/mcp/keys.ts +86 -0
- package/src/mcp/logs.ts +225 -0
- package/src/mcp/server.ts +595 -0
- package/src/mcp/session.ts +821 -0
- package/src/navigation-errors.ts +60 -0
- package/src/navigation.ts +44 -0
- package/src/router.tsx +497 -0
- package/src/routing.ts +344 -0
- package/src/server/adapters/build-step.ts +135 -0
- package/src/server/adapters/bun.ts +92 -0
- package/src/server/adapters/bundle.ts +97 -0
- package/src/server/adapters/cloudflare.ts +111 -0
- package/src/server/adapters/index.ts +29 -0
- package/src/server/adapters/runtime-node.ts +34 -0
- package/src/server/adapters/runtime.ts +145 -0
- package/src/server/adapters/shared.ts +159 -0
- package/src/server/adapters/static.ts +68 -0
- package/src/server/adapters/vercel.ts +149 -0
- package/src/server/bun-jsx.ts +65 -0
- package/src/server/cache-route.ts +139 -0
- package/src/server/cache.ts +498 -0
- package/src/server/frames.ts +326 -0
- package/src/server/http.ts +467 -0
- package/src/server/jsx-dev-runtime.ts +9 -0
- package/src/server/jsx-runtime.ts +13 -0
- package/src/server/live-app.tsx +374 -0
- package/src/server/live-build.ts +258 -0
- package/src/server/live-dev.ts +357 -0
- package/src/server/live-manifest.ts +25 -0
- package/src/server/live-prod.ts +120 -0
- package/src/server/live-routes.ts +298 -0
- package/src/server/live-web.ts +170 -0
- package/src/server/live.ts +551 -0
- package/src/server/middleware-node.ts +168 -0
- package/src/server/middleware-runtime.ts +478 -0
- package/src/server/middleware.ts +451 -0
- package/src/server/ppr.ts +62 -0
- package/src/server/scan.ts +190 -0
- package/src/server/server-components.ts +224 -0
- package/src/terminal.ts +5 -0
- package/src/testing.ts +5 -0
- package/src/vite-plugin.ts +413 -0
package/src/history.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router histories: `window.history` (pushState / replaceState / popstate)
|
|
3
|
+
* and an in-memory history for tests and embedded previews.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { normalizeBasePath } from "./config";
|
|
7
|
+
|
|
8
|
+
export interface RouterLocation {
|
|
9
|
+
pathname: string;
|
|
10
|
+
/** Including the leading `?` (or `""`). */
|
|
11
|
+
search: string;
|
|
12
|
+
/** Including the leading `#` (or `""`). */
|
|
13
|
+
hash: string;
|
|
14
|
+
/** `pathname + search + hash`. */
|
|
15
|
+
href: string;
|
|
16
|
+
/** Unique per history entry. */
|
|
17
|
+
key: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type HistoryAction = "push" | "replace" | "pop";
|
|
21
|
+
|
|
22
|
+
export interface RouterHistory {
|
|
23
|
+
readonly location: RouterLocation;
|
|
24
|
+
readonly action: HistoryAction;
|
|
25
|
+
push(href: string, state?: unknown): void;
|
|
26
|
+
replace(href: string, state?: unknown): void;
|
|
27
|
+
go(delta: number): void;
|
|
28
|
+
back(): void;
|
|
29
|
+
forward(): void;
|
|
30
|
+
/** Subscribe to location changes (returns an unsubscribe function). */
|
|
31
|
+
listen(listener: (location: RouterLocation, action: HistoryAction) => void): () => void;
|
|
32
|
+
/** Stable getter for `useSyncExternalStore`. */
|
|
33
|
+
getLocation(): RouterLocation;
|
|
34
|
+
/** Turn an app href into a document URL (applies `basePath`). */
|
|
35
|
+
createHref(href: string): string;
|
|
36
|
+
dispose?(): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let keyCounter = 0;
|
|
40
|
+
const nextKey = (): string => (++keyCounter).toString(36);
|
|
41
|
+
|
|
42
|
+
/** Parse an app-relative href against the current pathname. */
|
|
43
|
+
export function parseHref(href: string, from = "/"): Omit<RouterLocation, "key"> {
|
|
44
|
+
const url = new URL(href, `http://text.local${from.startsWith("/") ? from : `/${from}`}`);
|
|
45
|
+
let pathname = url.pathname.replace(/\/{2,}/g, "/");
|
|
46
|
+
if (pathname.length > 1 && pathname.endsWith("/")) pathname = pathname.slice(0, -1);
|
|
47
|
+
const search = url.search === "?" ? "" : url.search;
|
|
48
|
+
return { pathname, search, hash: url.hash, href: pathname + search + url.hash };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** `true` for absolute URLs on another origin (`https://…`, `mailto:`). */
|
|
52
|
+
export function isExternalHref(href: string, origin?: string): boolean {
|
|
53
|
+
if (!/^[a-z][a-z0-9+.-]*:/i.test(href) && !href.startsWith("//")) return false;
|
|
54
|
+
if (origin === undefined) return true;
|
|
55
|
+
try {
|
|
56
|
+
return new URL(href, origin).origin !== origin;
|
|
57
|
+
} catch {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function makeLocation(href: string, from: string): RouterLocation {
|
|
63
|
+
return { ...parseHref(href, from), key: nextKey() };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class Emitter {
|
|
67
|
+
private readonly listeners = new Set<(location: RouterLocation, action: HistoryAction) => void>();
|
|
68
|
+
listen(listener: (location: RouterLocation, action: HistoryAction) => void): () => void {
|
|
69
|
+
this.listeners.add(listener);
|
|
70
|
+
return () => this.listeners.delete(listener);
|
|
71
|
+
}
|
|
72
|
+
emit(location: RouterLocation, action: HistoryAction): void {
|
|
73
|
+
for (const l of [...this.listeners]) l(location, action);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface MemoryHistory extends RouterHistory {
|
|
78
|
+
readonly entries: readonly RouterLocation[];
|
|
79
|
+
readonly index: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface MemoryHistoryOptions {
|
|
83
|
+
initialEntries?: string[];
|
|
84
|
+
initialIndex?: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** An in-memory history: full App Router navigation without touching `window.location`. */
|
|
88
|
+
export function createMemoryHistory(initial: string | MemoryHistoryOptions = "/"): MemoryHistory {
|
|
89
|
+
const opts: MemoryHistoryOptions = typeof initial === "string" ? { initialEntries: [initial] } : initial;
|
|
90
|
+
const raw = opts.initialEntries && opts.initialEntries.length > 0 ? opts.initialEntries : ["/"];
|
|
91
|
+
let entries: RouterLocation[] = raw.map((h) => makeLocation(h, "/"));
|
|
92
|
+
let index = Math.max(0, Math.min(entries.length - 1, opts.initialIndex ?? entries.length - 1));
|
|
93
|
+
let action: HistoryAction = "pop";
|
|
94
|
+
const emitter = new Emitter();
|
|
95
|
+
const current = (): RouterLocation => entries[index] as RouterLocation;
|
|
96
|
+
const go = (delta: number): void => {
|
|
97
|
+
const next = Math.max(0, Math.min(entries.length - 1, index + delta));
|
|
98
|
+
if (next === index) return;
|
|
99
|
+
index = next;
|
|
100
|
+
action = "pop";
|
|
101
|
+
emitter.emit(current(), action);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
get location() {
|
|
106
|
+
return current();
|
|
107
|
+
},
|
|
108
|
+
get action() {
|
|
109
|
+
return action;
|
|
110
|
+
},
|
|
111
|
+
get entries() {
|
|
112
|
+
return entries;
|
|
113
|
+
},
|
|
114
|
+
get index() {
|
|
115
|
+
return index;
|
|
116
|
+
},
|
|
117
|
+
getLocation: () => current(),
|
|
118
|
+
createHref: (href) => parseHref(href, current().pathname).href,
|
|
119
|
+
push(href) {
|
|
120
|
+
const loc = makeLocation(href, current().pathname);
|
|
121
|
+
entries = [...entries.slice(0, index + 1), loc];
|
|
122
|
+
index = entries.length - 1;
|
|
123
|
+
action = "push";
|
|
124
|
+
emitter.emit(loc, action);
|
|
125
|
+
},
|
|
126
|
+
replace(href) {
|
|
127
|
+
const loc = makeLocation(href, current().pathname);
|
|
128
|
+
entries = entries.slice();
|
|
129
|
+
entries[index] = loc;
|
|
130
|
+
action = "replace";
|
|
131
|
+
emitter.emit(loc, action);
|
|
132
|
+
},
|
|
133
|
+
go,
|
|
134
|
+
back: () => go(-1),
|
|
135
|
+
forward: () => go(1),
|
|
136
|
+
listen: (l) => emitter.listen(l),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface BrowserHistoryOptions {
|
|
141
|
+
/** Mount point, e.g. `"/console"`. */
|
|
142
|
+
basePath?: string;
|
|
143
|
+
window?: Window;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Sync the App Router with `window.location` and the browser Back / Forward buttons. */
|
|
147
|
+
export function createBrowserHistory(options: BrowserHistoryOptions = {}): RouterHistory {
|
|
148
|
+
const win = options.window ?? (typeof window !== "undefined" ? window : undefined);
|
|
149
|
+
if (!win) return createMemoryHistory("/");
|
|
150
|
+
const base = normalizeBasePath(options.basePath);
|
|
151
|
+
const emitter = new Emitter();
|
|
152
|
+
let action: HistoryAction = "pop";
|
|
153
|
+
|
|
154
|
+
const strip = (pathname: string): string => {
|
|
155
|
+
if (base && (pathname === base || pathname.startsWith(`${base}/`))) return pathname.slice(base.length) || "/";
|
|
156
|
+
return pathname;
|
|
157
|
+
};
|
|
158
|
+
const read = (): RouterLocation => {
|
|
159
|
+
const l = win.location;
|
|
160
|
+
const state = win.history.state as { __textjs?: string } | null;
|
|
161
|
+
return { ...parseHref(strip(l.pathname) + l.search + l.hash, "/"), key: state?.__textjs ?? "default" };
|
|
162
|
+
};
|
|
163
|
+
let current = read();
|
|
164
|
+
const createHref = (href: string): string => base + parseHref(href, current.pathname).href;
|
|
165
|
+
|
|
166
|
+
const onPop = (): void => {
|
|
167
|
+
current = read();
|
|
168
|
+
action = "pop";
|
|
169
|
+
emitter.emit(current, action);
|
|
170
|
+
};
|
|
171
|
+
win.addEventListener("popstate", onPop);
|
|
172
|
+
|
|
173
|
+
const write = (href: string, state: unknown, kind: "push" | "replace"): void => {
|
|
174
|
+
const key = nextKey();
|
|
175
|
+
const data = { ...(typeof state === "object" && state !== null ? state : {}), __textjs: key };
|
|
176
|
+
const url = createHref(href);
|
|
177
|
+
if (kind === "push") win.history.pushState(data, "", url);
|
|
178
|
+
else win.history.replaceState(data, "", url);
|
|
179
|
+
current = { ...parseHref(href, current.pathname), key };
|
|
180
|
+
action = kind;
|
|
181
|
+
emitter.emit(current, action);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
get location() {
|
|
186
|
+
return current;
|
|
187
|
+
},
|
|
188
|
+
get action() {
|
|
189
|
+
return action;
|
|
190
|
+
},
|
|
191
|
+
getLocation: () => current,
|
|
192
|
+
createHref,
|
|
193
|
+
push: (href, state) => write(href, state, "push"),
|
|
194
|
+
replace: (href, state) => write(href, state, "replace"),
|
|
195
|
+
go: (delta) => win.history.go(delta),
|
|
196
|
+
back: () => win.history.back(),
|
|
197
|
+
forward: () => win.history.forward(),
|
|
198
|
+
listen: (l) => emitter.listen(l),
|
|
199
|
+
dispose: () => win.removeEventListener("popstate", onPop),
|
|
200
|
+
};
|
|
201
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ▼ Text.js — The Console Framework for the Web.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from "@textjs/react";
|
|
6
|
+
export { VERSION, ENGINE_NAME, BRAND_MARK } from "@textjs/core";
|
|
7
|
+
export { defineConfig, type TextConfig, type Metadata, type ViewportConfig, type Rewrite, type ProxyTarget } from "./config";
|
|
8
|
+
export { useAppTheme, type AppShellState } from "./context";
|
|
9
|
+
export type { PageProps, LayoutProps, ErrorComponentProps } from "./router";
|
package/src/link.tsx
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@textjs/app/link` — focusable client-side navigation for console apps.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useRef, type ReactNode } from "react";
|
|
6
|
+
import { Text, useFocus, useInput, useTheme, type TextProps } from "@textjs/react";
|
|
7
|
+
import { useOptionalRouterState } from "./context";
|
|
8
|
+
import { parseHref } from "./history";
|
|
9
|
+
|
|
10
|
+
export interface LinkProps extends Omit<TextProps, "children" | "allowLink"> {
|
|
11
|
+
href: string;
|
|
12
|
+
/** Replace the current history entry instead of pushing. */
|
|
13
|
+
replace?: boolean;
|
|
14
|
+
/** Include in Tab order; `Enter` / `Space` navigate while focused (default `true`). */
|
|
15
|
+
focusable?: boolean;
|
|
16
|
+
autoFocus?: boolean;
|
|
17
|
+
/** Focus id for `useFocusManager().focus(id)`. */
|
|
18
|
+
id?: string;
|
|
19
|
+
/** Preload lazy route modules on mount (default `true`). */
|
|
20
|
+
prefetch?: boolean;
|
|
21
|
+
/** Single key that follows the link from anywhere, e.g. `"1"`. */
|
|
22
|
+
shortcut?: string;
|
|
23
|
+
/** Class names applied when `href` matches the current pathname. */
|
|
24
|
+
activeClassName?: string;
|
|
25
|
+
/** Called before navigating. */
|
|
26
|
+
onNavigate?: () => void;
|
|
27
|
+
children?: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* ```tsx
|
|
32
|
+
* <Link href="/deployments">Deployments</Link>
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function Link({
|
|
36
|
+
href,
|
|
37
|
+
replace = false,
|
|
38
|
+
focusable = true,
|
|
39
|
+
autoFocus,
|
|
40
|
+
id,
|
|
41
|
+
prefetch = true,
|
|
42
|
+
shortcut,
|
|
43
|
+
activeClassName,
|
|
44
|
+
onNavigate,
|
|
45
|
+
className,
|
|
46
|
+
children,
|
|
47
|
+
...textProps
|
|
48
|
+
}: LinkProps): ReactNode {
|
|
49
|
+
const theme = useTheme();
|
|
50
|
+
const state = useOptionalRouterState();
|
|
51
|
+
const focus = useFocus({ id, autoFocus, isActive: focusable });
|
|
52
|
+
const latest = useRef({ href, replace, onNavigate, state });
|
|
53
|
+
latest.current = { href, replace, onNavigate, state };
|
|
54
|
+
const prefetched = useRef(false);
|
|
55
|
+
|
|
56
|
+
if (prefetch && state && !prefetched.current) {
|
|
57
|
+
prefetched.current = true;
|
|
58
|
+
queueMicrotask(() => state.router.prefetch(href));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const navigate = (): void => {
|
|
62
|
+
const { href: to, replace: rep, onNavigate: cb, state: s } = latest.current;
|
|
63
|
+
cb?.();
|
|
64
|
+
if (s) {
|
|
65
|
+
if (rep) s.router.replace(to);
|
|
66
|
+
else s.router.push(to);
|
|
67
|
+
} else if (typeof window !== "undefined") window.location.assign(to);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
useInput(
|
|
71
|
+
(e) => {
|
|
72
|
+
if (e.key === "return" || e.key === "space") {
|
|
73
|
+
e.stopPropagation();
|
|
74
|
+
navigate();
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{ isActive: focus.isFocused },
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
useInput(
|
|
81
|
+
(e) => {
|
|
82
|
+
if (shortcut && e.input === shortcut && !e.ctrl && !e.meta && !e.alt) {
|
|
83
|
+
e.stopPropagation();
|
|
84
|
+
navigate();
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{ isActive: shortcut !== undefined },
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const active = state !== null && parseHref(href, state.location.pathname).pathname === state.location.pathname;
|
|
91
|
+
const cls = [className, active ? activeClassName : undefined].filter(Boolean).join(" ") || undefined;
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<Text
|
|
95
|
+
{...textProps}
|
|
96
|
+
underline={textProps.underline ?? (focus.isFocused || /\bno-underline\b/.test(cls ?? "") ? undefined : true)}
|
|
97
|
+
color={textProps.color ?? (cls ? undefined : "accent")}
|
|
98
|
+
className={cls}
|
|
99
|
+
bg={focus.isFocused ? theme.accent : textProps.bg}
|
|
100
|
+
fg={focus.isFocused ? theme.accentFg : textProps.fg}
|
|
101
|
+
allowLink
|
|
102
|
+
>
|
|
103
|
+
{children ?? href}
|
|
104
|
+
</Text>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export default Link;
|