@alchemy.run/sigil 0.0.0-alpha.1 → 0.0.0-alpha.11

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.
Files changed (223) hide show
  1. package/README.md +499 -313
  2. package/THIRD_PARTY_NOTICES.md +98 -23
  3. package/dist/Text-DV9CuzAT.d.ts +452 -0
  4. package/dist/ansi.d.ts +217 -0
  5. package/dist/ansi.js +87 -0
  6. package/dist/capabilities.d.ts +5 -0
  7. package/dist/capabilities.js +3 -0
  8. package/dist/cell-_ZVhbfl0.js +44 -0
  9. package/dist/color-CkbalRqK.js +2 -0
  10. package/dist/color-policy-BAC9-TZX.js +592 -0
  11. package/dist/color-policy-CCxuHIdD.d.ts +22 -0
  12. package/dist/color-profile-CyeHnG1T.d.ts +97 -0
  13. package/dist/color-profile-DHhQHY55.js +36 -0
  14. package/dist/color.d.ts +21 -0
  15. package/dist/color.js +3 -0
  16. package/dist/cursor-position-D2LAkRG0.d.ts +7 -0
  17. package/dist/detect-B3dL4Q11.js +374 -0
  18. package/dist/detect-Db6GbKOm.d.ts +195 -0
  19. package/dist/{devtools-QpCMm9JH.mjs → devtools-DbthxoD1.js} +23 -24
  20. package/dist/env-YVw64yZS.js +9 -0
  21. package/dist/escapes-CB_6CWOE.d.ts +72 -0
  22. package/dist/geometry-BxXOzJgo.d.ts +11 -0
  23. package/dist/index-D48vQhhe.d.ts +21 -0
  24. package/dist/index-DDVME65c.d.ts +919 -0
  25. package/dist/index.d.ts +1310 -0
  26. package/dist/index.js +3211 -0
  27. package/dist/jsx-dev-runtime.d.ts +2 -0
  28. package/dist/jsx-dev-runtime.js +227 -0
  29. package/dist/jsx-runtime-XtwIVFsg.js +255 -0
  30. package/dist/jsx-runtime.d.ts +2 -0
  31. package/dist/jsx-runtime.js +8 -0
  32. package/dist/osc-BFKKSqpg.js +71 -0
  33. package/dist/osc-Cn0fw77g.d.ts +23 -0
  34. package/dist/paint-Cx-zC_sX.d.ts +81 -0
  35. package/dist/query-BNc2B8GD.d.ts +152 -0
  36. package/dist/react-CTZ_8dwh.js +1197 -0
  37. package/dist/react.d.ts +2 -0
  38. package/dist/react.js +49 -0
  39. package/dist/router.d.ts +392 -0
  40. package/dist/router.js +711 -0
  41. package/dist/sample-Cqw1bjUL.js +445 -0
  42. package/dist/screen-CgC2WlVM.d.ts +49 -0
  43. package/dist/screen-CiPytswf.js +342 -0
  44. package/dist/screen.d.ts +5 -0
  45. package/dist/screen.js +5 -0
  46. package/dist/semantic-text-style-DIMzC7xt.js +91 -0
  47. package/dist/serialize-BTkAZgw1.js +79 -0
  48. package/dist/session-DDQ5V300.js +723 -0
  49. package/dist/sgr-BhwaWAJB.js +246 -0
  50. package/dist/store-C1P5fOUi.d.ts +72 -0
  51. package/dist/string-width-CijQwpIk.js +69 -0
  52. package/dist/strip-BvU4toXG.js +6 -0
  53. package/dist/terminal.d.ts +121 -0
  54. package/dist/terminal.js +2 -0
  55. package/dist/tokenize-AjqbvtiT.js +1242 -0
  56. package/dist/tokenize-Dx1y_l5H.d.ts +57 -0
  57. package/dist/truncate-D31fhU6i.js +562 -0
  58. package/dist/use-focus-B5npZrJU.js +18736 -0
  59. package/dist/yoga-5jKhYCJC.js +3465 -0
  60. package/dist/yoga.d.ts +2 -0
  61. package/dist/yoga.js +2 -0
  62. package/package.json +89 -29
  63. package/src/ansi/chalk.ts +138 -0
  64. package/src/ansi/cursor.ts +46 -0
  65. package/src/ansi/east-asian-width.ts +259 -0
  66. package/src/ansi/escapes.ts +120 -0
  67. package/src/ansi/graphemes.ts +8 -0
  68. package/src/ansi/hyperlink.ts +44 -0
  69. package/src/ansi/index.ts +27 -0
  70. package/src/ansi/osc.ts +77 -0
  71. package/src/ansi/sgr.ts +234 -0
  72. package/src/ansi/slice.ts +43 -0
  73. package/src/ansi/string-width.ts +121 -0
  74. package/src/ansi/strip.ts +13 -0
  75. package/src/ansi/tokenize.ts +380 -0
  76. package/src/ansi/truncate.ts +196 -0
  77. package/src/ansi/wrap.ts +765 -0
  78. package/src/ansi-tokenizer.ts +510 -0
  79. package/src/capabilities/color-policy.ts +34 -0
  80. package/src/capabilities/detect.ts +608 -0
  81. package/src/capabilities/index.ts +37 -0
  82. package/src/capabilities/query.ts +679 -0
  83. package/src/capabilities/store.ts +394 -0
  84. package/src/code-excerpt.ts +39 -0
  85. package/src/color/index.ts +3 -0
  86. package/src/color/paint.ts +169 -0
  87. package/src/color/palette.ts +48 -0
  88. package/src/color/sample.ts +323 -0
  89. package/src/color.ts +1 -0
  90. package/src/components/AccessibilityContext.ts +5 -0
  91. package/src/components/AnimationContext.ts +24 -0
  92. package/src/components/AnsiText.tsx +42 -0
  93. package/src/components/App.tsx +878 -0
  94. package/src/components/AppContext.ts +111 -0
  95. package/src/components/BackgroundContext.ts +7 -0
  96. package/src/components/Box.tsx +100 -0
  97. package/src/components/CursorContext.ts +19 -0
  98. package/src/components/ErrorBoundary.tsx +39 -0
  99. package/src/components/ErrorOverview.tsx +135 -0
  100. package/src/components/FocusContext.ts +30 -0
  101. package/src/components/Hyperlink.tsx +56 -0
  102. package/src/components/Newline.tsx +16 -0
  103. package/src/components/Spacer.tsx +11 -0
  104. package/src/components/Static.tsx +60 -0
  105. package/src/components/StderrContext.ts +24 -0
  106. package/src/components/StdinContext.ts +48 -0
  107. package/src/components/StdoutContext.ts +26 -0
  108. package/src/components/TerminalOscContext.ts +25 -0
  109. package/src/components/Text.tsx +122 -0
  110. package/src/components/Transform.tsx +38 -0
  111. package/src/components/VirtualList.tsx +128 -0
  112. package/src/cursor-position.ts +103 -0
  113. package/src/devtools.ts +103 -0
  114. package/src/dom.ts +301 -0
  115. package/src/env.ts +12 -0
  116. package/src/get-max-width.ts +11 -0
  117. package/src/global.d.ts +38 -0
  118. package/src/glyphs.ts +99 -0
  119. package/src/hooks/use-animation.ts +142 -0
  120. package/src/hooks/use-app.ts +8 -0
  121. package/src/hooks/use-box-metrics.ts +134 -0
  122. package/src/hooks/use-capabilities.ts +73 -0
  123. package/src/hooks/use-cursor.ts +33 -0
  124. package/src/hooks/use-focus-manager.ts +62 -0
  125. package/src/hooks/use-focus.ts +82 -0
  126. package/src/hooks/use-input.ts +267 -0
  127. package/src/hooks/use-is-screen-reader-enabled.ts +12 -0
  128. package/src/hooks/use-paste.ts +78 -0
  129. package/src/hooks/use-stderr.ts +8 -0
  130. package/src/hooks/use-stdin.ts +10 -0
  131. package/src/hooks/use-stdout.ts +8 -0
  132. package/src/hooks/use-terminal-osc.ts +59 -0
  133. package/src/hooks/use-virtual-scroll.ts +84 -0
  134. package/src/hooks/use-window-size.ts +37 -0
  135. package/src/index.ts +96 -0
  136. package/src/ink.tsx +1452 -0
  137. package/src/input-parser.ts +303 -0
  138. package/src/instances.ts +9 -0
  139. package/src/jsx-dev-runtime.ts +3 -0
  140. package/src/jsx-runtime.ts +6 -0
  141. package/src/kitty-keyboard.ts +185 -0
  142. package/src/measure-element.ts +62 -0
  143. package/src/measure-text.ts +31 -0
  144. package/src/paint-tree.ts +220 -0
  145. package/src/parse-keypress.ts +515 -0
  146. package/src/parse-stack-line.ts +138 -0
  147. package/src/patch-console.ts +106 -0
  148. package/src/quick-lru.ts +85 -0
  149. package/src/react.ts +53 -0
  150. package/src/reconciler.ts +476 -0
  151. package/src/render-background.ts +59 -0
  152. package/src/render-border.ts +167 -0
  153. package/src/render-frame.ts +83 -0
  154. package/src/render-to-string.ts +146 -0
  155. package/src/render.ts +284 -0
  156. package/src/router/components.tsx +343 -0
  157. package/src/router/context.ts +41 -0
  158. package/src/router/history.ts +194 -0
  159. package/src/router/hooks.tsx +391 -0
  160. package/src/router/index.ts +34 -0
  161. package/src/router/matcher.ts +571 -0
  162. package/src/sanitize-ansi.ts +33 -0
  163. package/src/screen/ansi.ts +184 -0
  164. package/src/screen/canvas.ts +160 -0
  165. package/src/screen/cell.ts +138 -0
  166. package/src/screen/color-profile.ts +47 -0
  167. package/src/screen/geometry.ts +9 -0
  168. package/src/screen/index.ts +6 -0
  169. package/src/screen/screen.ts +305 -0
  170. package/src/screen/serialize.ts +129 -0
  171. package/src/screen.ts +1 -0
  172. package/src/semantic-text-style.ts +118 -0
  173. package/src/signal-exit.ts +106 -0
  174. package/src/squash-text-nodes.ts +37 -0
  175. package/src/stream.ts +28 -0
  176. package/src/structured-text.ts +325 -0
  177. package/src/styles.ts +753 -0
  178. package/src/terminal/index.ts +2 -0
  179. package/src/terminal/inline-presenter.ts +120 -0
  180. package/src/terminal/input.ts +92 -0
  181. package/src/terminal/render-scheduler.ts +37 -0
  182. package/src/terminal/screen-presenter.ts +242 -0
  183. package/src/terminal/session.ts +408 -0
  184. package/src/terminal-size.ts +58 -0
  185. package/src/terminal.ts +1 -0
  186. package/src/testing/browser.ts +588 -0
  187. package/src/testing/emulators.ts +205 -0
  188. package/src/testing/explorer-app/index.html +12 -0
  189. package/src/testing/explorer-app/main.ts +381 -0
  190. package/src/testing/explorer-app/style.css +194 -0
  191. package/src/testing/explorer-app/tsconfig.json +15 -0
  192. package/src/testing/explorer-app/vite-env.d.ts +1 -0
  193. package/src/testing/index.ts +26 -0
  194. package/src/testing/keys.ts +56 -0
  195. package/src/testing/live.ts +85 -0
  196. package/src/testing/matchers.ts +70 -0
  197. package/src/testing/public.ts +94 -0
  198. package/src/testing/terminal.ts +361 -0
  199. package/src/testing/vitest.ts +157 -0
  200. package/src/throttle.ts +73 -0
  201. package/src/transform-adapter.ts +14 -0
  202. package/src/types.ts +10 -0
  203. package/src/virtual-scroll.ts +133 -0
  204. package/src/wrap-text.ts +54 -0
  205. package/src/yoga/config.ts +57 -0
  206. package/src/yoga/core/absoluteLayout.ts +626 -0
  207. package/src/yoga/core/baseline.ts +66 -0
  208. package/src/yoga/core/cache.ts +136 -0
  209. package/src/yoga/core/calculateLayout.ts +2926 -0
  210. package/src/yoga/core/config.ts +104 -0
  211. package/src/yoga/core/flexLine.ts +177 -0
  212. package/src/yoga/core/helpers.ts +293 -0
  213. package/src/yoga/core/layoutResults.ts +167 -0
  214. package/src/yoga/core/node.ts +611 -0
  215. package/src/yoga/core/numeric.ts +44 -0
  216. package/src/yoga/core/pixelGrid.ts +151 -0
  217. package/src/yoga/core/style.ts +887 -0
  218. package/src/yoga/core/types.ts +224 -0
  219. package/src/yoga/generated/YGEnums.ts +263 -0
  220. package/src/yoga/index.ts +19 -0
  221. package/src/yoga/node.ts +1140 -0
  222. package/dist/index.d.mts +0 -2379
  223. package/dist/index.mjs +0 -10072
package/dist/router.js ADDED
@@ -0,0 +1,711 @@
1
+ import { r as __toESM, t as require_react } from "./react-CTZ_8dwh.js";
2
+ import { f as Text, n as useInput, t as useFocus } from "./use-focus-B5npZrJU.js";
3
+ import { t as require_jsx_runtime } from "./jsx-runtime-XtwIVFsg.js";
4
+ //#region src/router/history.ts
5
+ /**
6
+ Splits a path string into its pathname and search parts.
7
+ */
8
+ const parsePath = (path) => {
9
+ const parsedPath = {};
10
+ if (path) {
11
+ const searchIndex = path.indexOf("?");
12
+ if (searchIndex >= 0) {
13
+ parsedPath.search = path.slice(searchIndex);
14
+ path = path.slice(0, searchIndex);
15
+ }
16
+ if (path) parsedPath.pathname = path;
17
+ }
18
+ return parsedPath;
19
+ };
20
+ /**
21
+ Joins a `Path` back into a single string.
22
+ */
23
+ const createPath = ({ pathname = "/", search = "" }) => pathname + (search && search !== "?" ? search.startsWith("?") ? search : `?${search}` : "");
24
+ const createMemoryHistory = ({ initialEntries = ["/"], initialIndex } = {}) => {
25
+ let keyCounter = 0;
26
+ const createKey = () => `k${keyCounter++}`;
27
+ const createLocation = (to, state = null) => {
28
+ const path = typeof to === "string" ? parsePath(to) : to;
29
+ const location = {
30
+ pathname: path.pathname ?? "/",
31
+ search: path.search ?? "",
32
+ state,
33
+ key: createKey()
34
+ };
35
+ if (!location.pathname.startsWith("/")) throw new Error(`Route pathnames must be absolute, but "${location.pathname}" was used to initialize the navigation stack.`);
36
+ return location;
37
+ };
38
+ const entries = initialEntries.map((entry) => createLocation(entry, typeof entry === "string" ? null : entry.state ?? null));
39
+ let index = Math.min(Math.max(initialIndex ?? entries.length - 1, 0), entries.length - 1);
40
+ let action = "POP";
41
+ let listener = null;
42
+ return {
43
+ get index() {
44
+ return index;
45
+ },
46
+ get action() {
47
+ return action;
48
+ },
49
+ get location() {
50
+ return entries[index];
51
+ },
52
+ get canGoBack() {
53
+ return index > 0;
54
+ },
55
+ get canGoForward() {
56
+ return index < entries.length - 1;
57
+ },
58
+ push(to, state) {
59
+ action = "PUSH";
60
+ const nextLocation = createLocation(to, state);
61
+ index += 1;
62
+ entries.splice(index, entries.length, nextLocation);
63
+ listener?.({
64
+ action,
65
+ location: nextLocation,
66
+ delta: 1
67
+ });
68
+ },
69
+ replace(to, state) {
70
+ action = "REPLACE";
71
+ const nextLocation = createLocation(to, state);
72
+ entries[index] = nextLocation;
73
+ listener?.({
74
+ action,
75
+ location: nextLocation,
76
+ delta: 0
77
+ });
78
+ },
79
+ go(delta) {
80
+ action = "POP";
81
+ const nextIndex = Math.min(Math.max(index + delta, 0), entries.length - 1);
82
+ const actualDelta = nextIndex - index;
83
+ index = nextIndex;
84
+ listener?.({
85
+ action,
86
+ location: entries[index],
87
+ delta: actualDelta
88
+ });
89
+ },
90
+ listen(newListener) {
91
+ if (listener) throw new Error("A memory history only supports one listener at a time.");
92
+ listener = newListener;
93
+ return () => {
94
+ listener = null;
95
+ };
96
+ }
97
+ };
98
+ };
99
+ //#endregion
100
+ //#region src/router/matcher.ts
101
+ var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
102
+ const joinPaths = (paths) => paths.join("/").replace(/\/\/+/g, "/");
103
+ const normalizePathname = (pathname) => pathname.replace(/\/+$/, "").replace(/^\/*/, "/");
104
+ const normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : `?${search}`;
105
+ /**
106
+ Matches a set of (possibly nested) routes against a location and returns the
107
+ chain of matches from the root route down to the leaf, or `null` if nothing
108
+ matches.
109
+ */
110
+ function matchRoutes(routes, locationArg) {
111
+ const pathname = (typeof locationArg === "string" ? parsePath(locationArg) : locationArg).pathname ?? "/";
112
+ const branches = flattenRoutes(routes);
113
+ rankRouteBranches(branches);
114
+ let matches = null;
115
+ for (let index = 0; matches == null && index < branches.length; index++) matches = matchRouteBranch(branches[index], pathname);
116
+ return matches;
117
+ }
118
+ function flattenRoutes(routes, branches = [], parentsMeta = [], parentPath = "", hasParentOptionalSegments = false) {
119
+ const flattenRoute = (route, index, insideOptional, relativePath = route.path ?? "") => {
120
+ const meta = {
121
+ relativePath,
122
+ childrenIndex: index,
123
+ route
124
+ };
125
+ if (meta.relativePath.startsWith("/")) {
126
+ if (!meta.relativePath.startsWith(parentPath)) {
127
+ if (insideOptional) return;
128
+ throw new Error(`Absolute route path "${meta.relativePath}" nested under path "${parentPath}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`);
129
+ }
130
+ meta.relativePath = meta.relativePath.slice(parentPath.length);
131
+ }
132
+ const path = joinPaths([parentPath, meta.relativePath]);
133
+ const routesMeta = parentsMeta.concat(meta);
134
+ if (route.children && route.children.length > 0) {
135
+ if (route.index) throw new Error(`Index routes must not have child routes. Please remove all child routes from route path "${path}".`);
136
+ flattenRoutes(route.children, branches, routesMeta, path, insideOptional);
137
+ }
138
+ if (route.path == null && !route.index) return;
139
+ branches.push({
140
+ path,
141
+ score: computeScore(path, route.index),
142
+ routesMeta
143
+ });
144
+ };
145
+ routes.forEach((route, index) => {
146
+ if (route.path === "" || !route.path?.includes("?")) flattenRoute(route, index, hasParentOptionalSegments);
147
+ else for (const exploded of explodeOptionalSegments(route.path)) flattenRoute(route, index, true, exploded);
148
+ });
149
+ return branches;
150
+ }
151
+ function explodeOptionalSegments(path) {
152
+ const segments = path.split("/");
153
+ if (segments.length === 0) return [];
154
+ const [first = "", ...rest] = segments;
155
+ const isOptional = first.endsWith("?");
156
+ const required = first.replace(/\?$/, "");
157
+ if (rest.length === 0) return isOptional ? [required, ""] : [required];
158
+ const restExploded = explodeOptionalSegments(rest.join("/"));
159
+ const result = [];
160
+ result.push(...restExploded.map((subpath) => subpath === "" ? required : [required, subpath].join("/")));
161
+ if (isOptional) result.push(...restExploded);
162
+ return result.map((exploded) => path.startsWith("/") && exploded === "" ? "/" : exploded);
163
+ }
164
+ const paramRe = /^:[\w-]+$/;
165
+ const dynamicSegmentValue = 3;
166
+ const indexRouteValue = 2;
167
+ const emptySegmentValue = 1;
168
+ const staticSegmentValue = 10;
169
+ const splatPenalty = -2;
170
+ const isSplat = (segment) => segment === "*";
171
+ function computeScore(path, index) {
172
+ const segments = path.split("/");
173
+ let initialScore = segments.length;
174
+ if (segments.some(isSplat)) initialScore += splatPenalty;
175
+ if (index) initialScore += indexRouteValue;
176
+ return segments.filter((segment) => !isSplat(segment)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore);
177
+ }
178
+ function rankRouteBranches(branches) {
179
+ branches.sort((a, b) => a.score !== b.score ? b.score - a.score : compareIndexes(a.routesMeta.map((meta) => meta.childrenIndex), b.routesMeta.map((meta) => meta.childrenIndex)));
180
+ }
181
+ function compareIndexes(a, b) {
182
+ return a.length === b.length && a.slice(0, -1).every((n, index) => n === b[index]) ? a[a.length - 1] - b[b.length - 1] : 0;
183
+ }
184
+ function matchRouteBranch(branch, pathname) {
185
+ const { routesMeta } = branch;
186
+ const matchedParams = {};
187
+ let matchedPathname = "/";
188
+ const matches = [];
189
+ for (let index = 0; index < routesMeta.length; index++) {
190
+ const meta = routesMeta[index];
191
+ const end = index === routesMeta.length - 1;
192
+ const remainingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/";
193
+ const match = matchPath({
194
+ path: meta.relativePath,
195
+ end
196
+ }, remainingPathname);
197
+ if (!match) return null;
198
+ Object.assign(matchedParams, match.params);
199
+ matches.push({
200
+ params: matchedParams,
201
+ pathname: joinPaths([matchedPathname, match.pathname]),
202
+ pathnameBase: normalizePathname(joinPaths([matchedPathname, match.pathnameBase])),
203
+ route: meta.route
204
+ });
205
+ if (match.pathnameBase !== "/") matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
206
+ }
207
+ return matches;
208
+ }
209
+ /**
210
+ Matches a single path pattern against a pathname. Returns the match with
211
+ extracted params, or `null` if the pattern does not match.
212
+ */
213
+ function matchPath(pattern, pathname) {
214
+ if (typeof pattern === "string") pattern = {
215
+ path: pattern,
216
+ end: true
217
+ };
218
+ const [matcher, compiledParams] = compilePath(pattern.path, pattern.end);
219
+ const match = pathname.match(matcher);
220
+ if (!match) return null;
221
+ const matchedPathname = match[0];
222
+ let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
223
+ const captureGroups = match.slice(1);
224
+ return {
225
+ params: compiledParams.reduce((memo, { paramName, isOptional }, index) => {
226
+ const value = captureGroups[index];
227
+ if (paramName === "*") {
228
+ const splatValue = value || "";
229
+ pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
230
+ }
231
+ memo[paramName] = isOptional && !value ? void 0 : value || "";
232
+ return memo;
233
+ }, {}),
234
+ pathname: matchedPathname,
235
+ pathnameBase,
236
+ pattern
237
+ };
238
+ }
239
+ function compilePath(path, end = true) {
240
+ if (path !== "*" && path.endsWith("*") && !path.endsWith("/*")) throw new Error(`Route path "${path}" is invalid because the \`*\` character must always follow a \`/\` in the pattern. Please change the route path to "${path.replace(/\*$/, "/*")}".`);
241
+ const params = [];
242
+ let regexpSource = "^" + path.replace(/\/*\*?$/, "").replace(/^\/*/, "/").replace(/[\\.*+^${}|()[\]]/g, "\\$&").replace(/\/:([\w-]+)(\?)?/g, (_, paramName, isOptional) => {
243
+ params.push({
244
+ paramName,
245
+ isOptional: isOptional != null
246
+ });
247
+ return isOptional ? "/?([^\\/]+)?" : "/([^\\/]+)";
248
+ }).replace(/\/([\w-]+)\?(\/|$)/g, "(/$1)?$2");
249
+ if (path.endsWith("*")) {
250
+ params.push({ paramName: "*" });
251
+ regexpSource += path === "*" || path === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
252
+ } else if (end) regexpSource += "\\/*$";
253
+ else if (path !== "" && path !== "/") regexpSource += "(?:(?=\\/|$))";
254
+ return [new RegExp(regexpSource), params];
255
+ }
256
+ /**
257
+ Interpolates params into a route path pattern.
258
+
259
+ ```ts
260
+ generatePath("/users/:id", { id: "42" }); // "/users/42"
261
+ ```
262
+ */
263
+ function generatePath(originalPath, params = {}) {
264
+ const path = originalPath;
265
+ if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) throw new Error(`Route path "${path}" is invalid because the \`*\` character must always follow a \`/\` in the pattern. Please change the route path to "${path.replace(/\*$/, "/*")}".`);
266
+ return (path.startsWith("/") ? "/" : "") + path.split(/\/+/).map((segment, index, array) => {
267
+ if (index === array.length - 1 && segment === "*") return params["*"] ?? "";
268
+ const keyMatch = segment.match(/^:([\w-]+)(\??)$/);
269
+ if (keyMatch) {
270
+ const key = keyMatch[1];
271
+ const optional = keyMatch[2];
272
+ const param = params[key];
273
+ if (optional !== "?" && param == null) throw new Error(`Missing ":${key}" param`);
274
+ return param ?? "";
275
+ }
276
+ return segment.replace(/\?$/g, "");
277
+ }).filter((segment) => !!segment).join("/");
278
+ }
279
+ /**
280
+ Resolves a `To` value against a starting pathname, handling `.` and `..`
281
+ segments.
282
+ */
283
+ function resolvePath(to, fromPathname = "/") {
284
+ const { pathname: toPathname, search = "" } = typeof to === "string" ? parsePath(to) : to;
285
+ return {
286
+ pathname: toPathname ? toPathname.startsWith("/") ? toPathname : resolvePathname(toPathname, fromPathname) : fromPathname,
287
+ search: normalizeSearch(search)
288
+ };
289
+ }
290
+ function resolvePathname(relativePath, fromPathname) {
291
+ const segments = fromPathname.replace(/\/+$/, "").split("/");
292
+ for (const segment of relativePath.split("/")) if (segment === "..") {
293
+ if (segments.length > 1) segments.pop();
294
+ } else if (segment !== ".") segments.push(segment);
295
+ return segments.length > 1 ? segments.join("/") : "/";
296
+ }
297
+ function getPathContributingMatches(matches) {
298
+ return matches.filter((match, index) => index === 0 || match.route.path && match.route.path.length > 0);
299
+ }
300
+ function getResolveToMatches(matches) {
301
+ const pathMatches = getPathContributingMatches(matches);
302
+ return pathMatches.map((match, index) => index === pathMatches.length - 1 ? match.pathname : match.pathnameBase);
303
+ }
304
+ function resolveTo(toArg, routePathnames, locationPathname) {
305
+ const to = typeof toArg === "string" ? parsePath(toArg) : { ...toArg };
306
+ if (to.pathname?.includes("?")) throw new Error(`Cannot include a '?' character in a manually specified \`to.pathname\` field [${JSON.stringify(to)}]. Please separate it out to the \`to.search\` field.`);
307
+ const isEmptyPath = toArg === "" || to.pathname === "";
308
+ const toPathname = isEmptyPath ? "/" : to.pathname;
309
+ let from;
310
+ if (toPathname == null) from = locationPathname;
311
+ else {
312
+ let routePathnameIndex = routePathnames.length - 1;
313
+ if (toPathname.startsWith("..")) {
314
+ const toSegments = toPathname.split("/");
315
+ while (toSegments[0] === "..") {
316
+ toSegments.shift();
317
+ routePathnameIndex -= 1;
318
+ }
319
+ to.pathname = toSegments.join("/");
320
+ }
321
+ from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";
322
+ }
323
+ const path = resolvePath(to, from);
324
+ const hasExplicitTrailingSlash = toPathname && toPathname !== "/" && toPathname.endsWith("/");
325
+ const hasCurrentTrailingSlash = (isEmptyPath || toPathname === ".") && locationPathname.endsWith("/");
326
+ if (!path.pathname.endsWith("/") && (hasExplicitTrailingSlash || hasCurrentTrailingSlash)) path.pathname += "/";
327
+ return path;
328
+ }
329
+ //#endregion
330
+ //#region src/router/context.ts
331
+ const NavigationContext = (0, import_react.createContext)(null);
332
+ const LocationContext = (0, import_react.createContext)(null);
333
+ const RouteContext = (0, import_react.createContext)({
334
+ outlet: null,
335
+ matches: []
336
+ });
337
+ const OutletContext = (0, import_react.createContext)(null);
338
+ //#endregion
339
+ //#region src/router/hooks.tsx
340
+ /** @jsxImportSource react */
341
+ var import_jsx_runtime = require_jsx_runtime();
342
+ const warned = /* @__PURE__ */ new Set();
343
+ const warnOnce = (key, message) => {
344
+ if (!warned.has(key)) {
345
+ warned.add(key);
346
+ console.warn(message);
347
+ }
348
+ };
349
+ /**
350
+ Returns `true` when rendered inside a `<MemoryRouter>`. Useful for components
351
+ that optionally integrate with routing.
352
+ */
353
+ const useInRouterContext = () => (0, import_react.useContext)(LocationContext) != null;
354
+ /**
355
+ Returns the current `Location`.
356
+ */
357
+ const useLocation = () => {
358
+ const locationContext = (0, import_react.useContext)(LocationContext);
359
+ if (!locationContext) throw new Error("useLocation() may be used only in the context of a <MemoryRouter> component.");
360
+ return locationContext.location;
361
+ };
362
+ /**
363
+ Returns the type of navigation that produced the current location: `"POP"`,
364
+ `"PUSH"`, or `"REPLACE"`.
365
+ */
366
+ const useNavigationType = () => {
367
+ const locationContext = (0, import_react.useContext)(LocationContext);
368
+ if (!locationContext) throw new Error("useNavigationType() may be used only in the context of a <MemoryRouter> component.");
369
+ return locationContext.navigationType;
370
+ };
371
+ const useNavigationContext = (hookName) => {
372
+ const navigationContext = (0, import_react.useContext)(NavigationContext);
373
+ if (!navigationContext) throw new Error(`${hookName} may be used only in the context of a <MemoryRouter> component.`);
374
+ return navigationContext;
375
+ };
376
+ /**
377
+ Returns a stable function for imperative navigation.
378
+ */
379
+ const useNavigate = () => {
380
+ const { navigator } = useNavigationContext("useNavigate()");
381
+ const { matches } = (0, import_react.useContext)(RouteContext);
382
+ const { pathname: locationPathname } = useLocation();
383
+ const routePathnamesJson = JSON.stringify(getResolveToMatches(matches));
384
+ const activeRef = (0, import_react.useRef)(false);
385
+ (0, import_react.useLayoutEffect)(() => {
386
+ activeRef.current = true;
387
+ });
388
+ return (0, import_react.useCallback)((to, options = {}) => {
389
+ if (!activeRef.current) return;
390
+ if (typeof to === "number") {
391
+ navigator.go(to);
392
+ return;
393
+ }
394
+ const path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname);
395
+ (options.replace ? navigator.replace : navigator.push)(path, options.state);
396
+ }, [
397
+ navigator,
398
+ routePathnamesJson,
399
+ locationPathname
400
+ ]);
401
+ };
402
+ /**
403
+ Returns whether the navigation stack has entries behind/ahead of the current
404
+ one — i.e. whether `navigate(-1)` / `navigate(1)` will move anywhere. Handy
405
+ for "Esc goes back, unless at root" bindings.
406
+ */
407
+ const useNavigationStack = () => {
408
+ const { navigator } = useNavigationContext("useNavigationStack()");
409
+ useLocation();
410
+ return {
411
+ canGoBack: navigator.canGoBack(),
412
+ canGoForward: navigator.canGoForward()
413
+ };
414
+ };
415
+ /**
416
+ Returns the params from all dynamic segments matched by the current route and
417
+ its ancestors.
418
+ */
419
+ const useParams = () => {
420
+ const { matches } = (0, import_react.useContext)(RouteContext);
421
+ const routeMatch = matches[matches.length - 1];
422
+ return routeMatch ? routeMatch.params : {};
423
+ };
424
+ /**
425
+ Matches a path pattern against the current location's pathname. Returns the
426
+ match (with params) or `null`.
427
+ */
428
+ const useMatch = (pattern) => {
429
+ const { pathname } = useLocation();
430
+ return matchPath(pattern, pathname);
431
+ };
432
+ /**
433
+ Resolves a `To` value against the current route, exactly as `useNavigate`
434
+ would. Useful for building navigation UI.
435
+ */
436
+ const useResolvedPath = (to) => {
437
+ const { matches } = (0, import_react.useContext)(RouteContext);
438
+ const { pathname: locationPathname } = useLocation();
439
+ return resolveTo(to, getResolveToMatches(matches), locationPathname);
440
+ };
441
+ /**
442
+ Returns the element for the child route at this level of the route hierarchy,
443
+ or `null` if there is none. Used internally by `<Outlet>`.
444
+ */
445
+ const useOutlet = (context) => {
446
+ const { outlet } = (0, import_react.useContext)(RouteContext);
447
+ if (outlet) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OutletContext.Provider, {
448
+ value: context,
449
+ children: outlet
450
+ });
451
+ return outlet;
452
+ };
453
+ /**
454
+ Returns the value passed to the nearest parent `<Outlet context={...}>`.
455
+ */
456
+ const useOutletContext = () => (0, import_react.useContext)(OutletContext);
457
+ /**
458
+ Creates a `URLSearchParams` from common initializer shapes, including
459
+ `{ key: ["a", "b"] }` for repeated keys.
460
+ */
461
+ const createSearchParams = (init = "") => new URLSearchParams(typeof init === "string" || Array.isArray(init) || init instanceof URLSearchParams ? init : Object.keys(init).flatMap((key) => {
462
+ const value = init[key];
463
+ return Array.isArray(value) ? value.map((v) => [key, v]) : [[key, value]];
464
+ }));
465
+ /**
466
+ Returns the current location's search params and a setter that navigates to
467
+ the same pathname with the new params.
468
+ */
469
+ const useSearchParams = (defaultInit) => {
470
+ const defaultSearchParamsRef = (0, import_react.useRef)(createSearchParams(defaultInit));
471
+ const hasSetSearchParamsRef = (0, import_react.useRef)(false);
472
+ const location = useLocation();
473
+ const searchParams = (0, import_react.useMemo)(() => {
474
+ const params = createSearchParams(location.search);
475
+ if (!hasSetSearchParamsRef.current) {
476
+ for (const key of defaultSearchParamsRef.current.keys()) if (!params.has(key)) for (const value of defaultSearchParamsRef.current.getAll(key)) params.append(key, value);
477
+ }
478
+ return params;
479
+ }, [location.search]);
480
+ const navigate = useNavigate();
481
+ return [searchParams, (0, import_react.useCallback)((nextInit, navigateOptions) => {
482
+ const newSearchParams = createSearchParams(typeof nextInit === "function" ? nextInit(new URLSearchParams(searchParams)) : nextInit);
483
+ hasSetSearchParamsRef.current = true;
484
+ navigate(`?${newSearchParams.toString()}`, navigateOptions);
485
+ }, [navigate, searchParams])];
486
+ };
487
+ /**
488
+ Matches a set of route objects against the current location (or an override)
489
+ and returns the rendered element tree. The plain-object alternative to
490
+ `<Routes>`.
491
+ */
492
+ const useRoutes = (routes, locationArg) => {
493
+ if (!useInRouterContext()) throw new Error("useRoutes() may be used only in the context of a <MemoryRouter> component.");
494
+ const { matches: parentMatches } = (0, import_react.useContext)(RouteContext);
495
+ const routeMatch = parentMatches[parentMatches.length - 1];
496
+ const parentParams = routeMatch ? routeMatch.params : {};
497
+ const parentPathnameBase = routeMatch ? routeMatch.pathnameBase : "/";
498
+ const parentRoute = routeMatch?.route;
499
+ const locationFromContext = useLocation();
500
+ let location;
501
+ if (locationArg) {
502
+ const parsedLocationArg = typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
503
+ if (parentPathnameBase !== "/" && !parsedLocationArg.pathname?.startsWith(parentPathnameBase)) throw new Error(`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the pathname that was matched by all parent routes. The current pathname base is "${parentPathnameBase}" but pathname "${parsedLocationArg.pathname}" was given in the \`location\` prop.`);
504
+ location = parsedLocationArg;
505
+ } else location = locationFromContext;
506
+ const pathname = location.pathname ?? "/";
507
+ let remainingPathname = pathname;
508
+ if (parentPathnameBase !== "/") {
509
+ const parentSegments = parentPathnameBase.replace(/^\//, "").split("/");
510
+ remainingPathname = `/${pathname.replace(/^\//, "").split("/").slice(parentSegments.length).join("/")}`;
511
+ }
512
+ const matches = matchRoutes(routes, { pathname: remainingPathname });
513
+ const parentPath = parentRoute?.path ?? "";
514
+ if (parentRoute && !parentPath.endsWith("*")) warnOnce(`descendant-routes:${routeMatch.pathname}`, `You rendered descendant <Routes> (or called useRoutes()) at "${routeMatch.pathname}" (under <Route path="${parentPath}">) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. Please change the parent to <Route path="${parentPath === "/" ? "*" : `${parentPath}/*`}">.`);
515
+ if (matches == null && !parentRoute) warnOnce(`no-match:${pathname}${location.search ?? ""}`, `No routes matched location "${pathname}${location.search ?? ""}".`);
516
+ return renderMatches(matches && matches.map((match) => ({
517
+ ...match,
518
+ params: {
519
+ ...parentParams,
520
+ ...match.params
521
+ },
522
+ pathname: joinPaths([parentPathnameBase, match.pathname]),
523
+ pathnameBase: match.pathnameBase === "/" ? parentPathnameBase : joinPaths([parentPathnameBase, match.pathnameBase])
524
+ })), parentMatches);
525
+ };
526
+ function renderMatches(matches, parentMatches) {
527
+ if (matches == null) return null;
528
+ return matches.reduceRight((outlet, match, index) => {
529
+ const matchesUpToHere = parentMatches.concat(matches.slice(0, index + 1));
530
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouteContext.Provider, {
531
+ value: {
532
+ outlet,
533
+ matches: matchesUpToHere
534
+ },
535
+ children: match.route.element ?? outlet
536
+ });
537
+ }, null);
538
+ }
539
+ //#endregion
540
+ //#region src/router/components.tsx
541
+ /** @jsxImportSource react */
542
+ /**
543
+ The routing container for a Sigil app. Stores the navigation stack in memory —
544
+ routes aren't URLs, they're screen states.
545
+
546
+ ```tsx
547
+ <MemoryRouter>
548
+ <Routes>
549
+ <Route path="/" element={<Home />} />
550
+ <Route path="/settings" element={<Settings />} />
551
+ </Routes>
552
+ </MemoryRouter>
553
+ ```
554
+ */
555
+ function MemoryRouter({ initialEntries, initialIndex, children }) {
556
+ const historyRef = (0, import_react.useRef)(null);
557
+ historyRef.current ??= createMemoryHistory({
558
+ initialEntries,
559
+ initialIndex
560
+ });
561
+ const history = historyRef.current;
562
+ const [state, setState] = (0, import_react.useState)({
563
+ action: history.action,
564
+ location: history.location
565
+ });
566
+ (0, import_react.useLayoutEffect)(() => history.listen(({ action, location }) => {
567
+ (0, import_react.startTransition)(() => {
568
+ setState({
569
+ action,
570
+ location
571
+ });
572
+ });
573
+ }), [history]);
574
+ const navigator = (0, import_react.useMemo)(() => ({
575
+ push: (to, historyState) => history.push(to, historyState),
576
+ replace: (to, historyState) => history.replace(to, historyState),
577
+ go: (delta) => history.go(delta),
578
+ canGoBack: () => history.canGoBack,
579
+ canGoForward: () => history.canGoForward
580
+ }), [history]);
581
+ const navigationContext = (0, import_react.useMemo)(() => ({ navigator }), [navigator]);
582
+ const locationContext = (0, import_react.useMemo)(() => ({
583
+ location: state.location,
584
+ navigationType: state.action
585
+ }), [state]);
586
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavigationContext.Provider, {
587
+ value: navigationContext,
588
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocationContext.Provider, {
589
+ value: locationContext,
590
+ children
591
+ })
592
+ });
593
+ }
594
+ /**
595
+ Declares a route. Only valid as a child of `<Routes>` or another `<Route>`.
596
+ */
597
+ function Route(_props) {
598
+ throw new Error("A <Route> is only ever to be used as the child of a <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.");
599
+ }
600
+ function createRoutesFromChildren(children) {
601
+ const routes = [];
602
+ import_react.Children.forEach(children, (element) => {
603
+ if (!(0, import_react.isValidElement)(element)) return;
604
+ if (element.type === import_react.Fragment) {
605
+ routes.push(...createRoutesFromChildren(element.props.children));
606
+ return;
607
+ }
608
+ if (element.type !== Route) throw new Error(`[${typeof element.type === "string" ? element.type : element.type.name ?? "unknown"}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>.`);
609
+ const props = element.props;
610
+ const route = {
611
+ path: props.path,
612
+ index: props.index,
613
+ element: props.element
614
+ };
615
+ if (props.children) route.children = createRoutesFromChildren(props.children);
616
+ routes.push(route);
617
+ });
618
+ return routes;
619
+ }
620
+ /**
621
+ Renders the branch of child `<Route>` elements that best matches the current
622
+ location.
623
+ */
624
+ function Routes({ children, location }) {
625
+ return useRoutes(createRoutesFromChildren(children), location);
626
+ }
627
+ /**
628
+ Renders the matching child route of a parent route, or nothing if no child
629
+ matches.
630
+ */
631
+ function Outlet(props) {
632
+ return useOutlet(props.context);
633
+ }
634
+ /**
635
+ Navigates as soon as it renders. The component form of `useNavigate`, for
636
+ declarative redirects:
637
+
638
+ ```tsx
639
+ <Route path="/" element={<Navigate to="/home" replace />} />
640
+ ```
641
+ */
642
+ function Navigate({ to, replace, state }) {
643
+ const navigate = useNavigate();
644
+ const { pathname, search } = useResolvedPath(to);
645
+ (0, import_react.useEffect)(() => {
646
+ navigate({
647
+ pathname,
648
+ search
649
+ }, {
650
+ replace,
651
+ state
652
+ });
653
+ }, [
654
+ navigate,
655
+ pathname,
656
+ search,
657
+ replace,
658
+ state
659
+ ]);
660
+ return null;
661
+ }
662
+ /**
663
+ A focusable navigation element — the terminal's `<a>` tag. Focus it with
664
+ <kbd>Tab</kbd> and activate it with <kbd>Enter</kbd>. By default the focused
665
+ link renders inverse; pass a function as `children` (or any `Text` props) to
666
+ customize.
667
+
668
+ ```tsx
669
+ <Link to="/settings">Settings</Link>
670
+ ```
671
+ */
672
+ function Link({ to, replace = false, state, autoFocus = false, id, children, ...textProps }) {
673
+ const navigate = useNavigate();
674
+ const path = useResolvedPath(to);
675
+ const { pathname: locationPathname } = useLocation();
676
+ const { isFocused } = useFocus({
677
+ autoFocus,
678
+ id
679
+ });
680
+ const toPathname = normalizePathname(path.pathname);
681
+ const isActive = locationPathname === toPathname || locationPathname.startsWith(toPathname) && locationPathname.charAt(toPathname.length) === "/";
682
+ const activate = (0, import_react.useCallback)(() => {
683
+ navigate({
684
+ pathname: path.pathname,
685
+ search: path.search
686
+ }, {
687
+ replace,
688
+ state
689
+ });
690
+ }, [
691
+ navigate,
692
+ path.pathname,
693
+ path.search,
694
+ replace,
695
+ state
696
+ ]);
697
+ useInput((_input, key) => {
698
+ if (key.return) activate();
699
+ }, { isActive: isFocused });
700
+ if (typeof children === "function") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: children({
701
+ isFocused,
702
+ isActive
703
+ }) });
704
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
705
+ inverse: isFocused,
706
+ ...textProps,
707
+ children
708
+ });
709
+ }
710
+ //#endregion
711
+ export { Link, MemoryRouter, Navigate, Outlet, Route, Routes, createPath, createSearchParams, generatePath, matchPath, matchRoutes, parsePath, resolvePath, useInRouterContext, useLocation, useMatch, useNavigate, useNavigationStack, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRoutes, useSearchParams };