streetui 1.8.0 → 2.0.0
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/dist/bin.cjs +1 -1
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/create-bin.cjs +1 -1
- package/dist/create-bin.cjs.map +1 -1
- package/dist/create-bin.js +1 -1
- package/dist/create-bin.js.map +1 -1
- package/dist/{compile-CJJQYi71.d.cts → hydration-diagnostics-CFYDXBlm.d.cts} +471 -5
- package/dist/{compile-CJJQYi71.d.ts → hydration-diagnostics-CFYDXBlm.d.ts} +471 -5
- package/dist/index.cjs +1401 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +598 -147
- package/dist/index.d.ts +598 -147
- package/dist/index.js +1379 -47
- package/dist/index.js.map +1 -1
- package/dist/renderer-interface-BUhHV7ja.d.cts +26 -0
- package/dist/renderer-interface-B_-hWnKk.d.ts +26 -0
- package/dist/{server-CnLEnlYU.d.cts → server-C0PvFBzD.d.ts} +181 -2
- package/dist/{server-DNlg7ou2.d.ts → server-DnAOFpMP.d.cts} +181 -2
- package/dist/server.cjs +482 -32
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +481 -32
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +754 -41
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +42 -4
- package/dist/testing.d.ts +42 -4
- package/dist/testing.js +746 -40
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/dist/hydration-diagnostics-BQeehq3i.d.cts +0 -89
- package/dist/hydration-diagnostics-DNAJumYF.d.ts +0 -89
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/version.ts
|
|
2
|
-
var VERSION = "
|
|
2
|
+
var VERSION = "2.0.0";
|
|
3
3
|
|
|
4
4
|
// ../state/src/signal.ts
|
|
5
5
|
var _activeConsumer = null;
|
|
@@ -339,6 +339,198 @@ function resource(loader, options = {}) {
|
|
|
339
339
|
};
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
+
// ../state/src/mutation.ts
|
|
343
|
+
function mutation(mutator, options = {}) {
|
|
344
|
+
const status = signal("idle");
|
|
345
|
+
const data = signal(void 0);
|
|
346
|
+
const error = signal(void 0);
|
|
347
|
+
const pending = derived(() => status.get() === "loading");
|
|
348
|
+
let disposed = false;
|
|
349
|
+
let runId = 0;
|
|
350
|
+
const mutate = async (args) => {
|
|
351
|
+
if (disposed) {
|
|
352
|
+
return await mutator(args);
|
|
353
|
+
}
|
|
354
|
+
const myRun = ++runId;
|
|
355
|
+
batch(() => {
|
|
356
|
+
error.set(void 0);
|
|
357
|
+
status.set("loading");
|
|
358
|
+
});
|
|
359
|
+
try {
|
|
360
|
+
const result = await mutator(args);
|
|
361
|
+
if (!disposed && myRun === runId) {
|
|
362
|
+
batch(() => {
|
|
363
|
+
data.set(result);
|
|
364
|
+
error.set(void 0);
|
|
365
|
+
status.set("success");
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
if (!disposed && myRun === runId) {
|
|
369
|
+
await options.onSuccess?.(result, args);
|
|
370
|
+
await options.onSettled?.(args);
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
} catch (err) {
|
|
374
|
+
if (!disposed && myRun === runId) {
|
|
375
|
+
batch(() => {
|
|
376
|
+
error.set(err);
|
|
377
|
+
status.set("error");
|
|
378
|
+
});
|
|
379
|
+
await options.onError?.(err, args);
|
|
380
|
+
await options.onSettled?.(args);
|
|
381
|
+
}
|
|
382
|
+
throw err;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
const reset = () => {
|
|
386
|
+
batch(() => {
|
|
387
|
+
status.set("idle");
|
|
388
|
+
data.set(void 0);
|
|
389
|
+
error.set(void 0);
|
|
390
|
+
});
|
|
391
|
+
};
|
|
392
|
+
const dispose = () => {
|
|
393
|
+
disposed = true;
|
|
394
|
+
};
|
|
395
|
+
if (options.onCleanup !== void 0) options.onCleanup(dispose);
|
|
396
|
+
return { status, data, error, pending, mutate, reset, dispose };
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ../state/src/client.ts
|
|
400
|
+
var HttpError = class extends Error {
|
|
401
|
+
status;
|
|
402
|
+
statusText;
|
|
403
|
+
url;
|
|
404
|
+
body;
|
|
405
|
+
constructor(status, statusText, url, body) {
|
|
406
|
+
super(`HTTP ${status} ${statusText} for ${url}`);
|
|
407
|
+
this.name = "HttpError";
|
|
408
|
+
this.status = status;
|
|
409
|
+
this.statusText = statusText;
|
|
410
|
+
this.url = url;
|
|
411
|
+
this.body = body;
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
function joinUrl(baseUrl, path) {
|
|
415
|
+
if (baseUrl === void 0 || baseUrl === "") return path;
|
|
416
|
+
const b = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
417
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
418
|
+
return `${b}${p}`;
|
|
419
|
+
}
|
|
420
|
+
function withQuery(url, query) {
|
|
421
|
+
if (query === void 0) return url;
|
|
422
|
+
const params = new URLSearchParams();
|
|
423
|
+
for (const [k, v] of Object.entries(query)) params.set(k, String(v));
|
|
424
|
+
const qs = params.toString();
|
|
425
|
+
if (qs === "") return url;
|
|
426
|
+
return url.includes("?") ? `${url}&${qs}` : `${url}?${qs}`;
|
|
427
|
+
}
|
|
428
|
+
function createClient(config = {}) {
|
|
429
|
+
const doFetch = config.fetch ?? ((input, init) => {
|
|
430
|
+
if (typeof fetch === "undefined") {
|
|
431
|
+
throw new Error("createClient: no global fetch; pass { fetch } explicitly");
|
|
432
|
+
}
|
|
433
|
+
return fetch(input, init);
|
|
434
|
+
});
|
|
435
|
+
async function request(method, path, body, reqConfig = {}) {
|
|
436
|
+
const url = withQuery(joinUrl(config.baseUrl, path), reqConfig.query);
|
|
437
|
+
const headers = { ...config.headers, ...reqConfig.headers };
|
|
438
|
+
const init = { method, headers };
|
|
439
|
+
if (reqConfig.signal !== void 0) init.signal = reqConfig.signal;
|
|
440
|
+
if (body !== void 0) {
|
|
441
|
+
if (headers["Content-Type"] === void 0) headers["Content-Type"] = "application/json";
|
|
442
|
+
init.body = JSON.stringify(body);
|
|
443
|
+
}
|
|
444
|
+
const response = await doFetch(url, init);
|
|
445
|
+
const parsed = await parseBody(response);
|
|
446
|
+
if (!response.ok) {
|
|
447
|
+
throw new HttpError(response.status, response.statusText, url, parsed);
|
|
448
|
+
}
|
|
449
|
+
return parsed;
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
request,
|
|
453
|
+
get: (path, c) => request("GET", path, void 0, c),
|
|
454
|
+
post: (path, b, c) => request("POST", path, b, c),
|
|
455
|
+
put: (path, b, c) => request("PUT", path, b, c),
|
|
456
|
+
patch: (path, b, c) => request("PATCH", path, b, c),
|
|
457
|
+
del: (path, c) => request("DELETE", path, void 0, c),
|
|
458
|
+
resource: (path, options = {}) => {
|
|
459
|
+
const { query, ...resourceOptions } = options;
|
|
460
|
+
return resource(
|
|
461
|
+
(ctx) => request("GET", path, void 0, { signal: ctx.signal, ...query !== void 0 ? { query } : {} }),
|
|
462
|
+
resourceOptions
|
|
463
|
+
);
|
|
464
|
+
},
|
|
465
|
+
mutation: (method, path, options) => mutation((args) => request(method, path, args), options ?? {})
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
async function parseBody(response) {
|
|
469
|
+
const text = await response.text();
|
|
470
|
+
if (text === "") return void 0;
|
|
471
|
+
const type = response.headers.get("content-type") ?? "";
|
|
472
|
+
if (type.includes("application/json")) {
|
|
473
|
+
try {
|
|
474
|
+
return JSON.parse(text);
|
|
475
|
+
} catch {
|
|
476
|
+
return text;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return text;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// ../state/src/auth.ts
|
|
483
|
+
function createAuthSession(config) {
|
|
484
|
+
const session = resource(
|
|
485
|
+
(ctx) => config.loadUser(ctx),
|
|
486
|
+
{
|
|
487
|
+
...config.immediate !== void 0 ? { immediate: config.immediate } : {},
|
|
488
|
+
...config.onCleanup !== void 0 ? { onCleanup: config.onCleanup } : {}
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
const status = derived(() => {
|
|
492
|
+
const s = session.status.get();
|
|
493
|
+
const d = session.data.get();
|
|
494
|
+
if (s === "error") return "error";
|
|
495
|
+
if (s === "idle" || s === "loading" && d === void 0) return "loading";
|
|
496
|
+
return d === null || d === void 0 ? "unauthenticated" : "authenticated";
|
|
497
|
+
});
|
|
498
|
+
const user = derived(() => {
|
|
499
|
+
const d = session.data.get();
|
|
500
|
+
return d === null ? void 0 : d;
|
|
501
|
+
});
|
|
502
|
+
const authenticated = derived(() => status.get() === "authenticated");
|
|
503
|
+
const unauthenticated = derived(() => status.get() === "unauthenticated");
|
|
504
|
+
const loading = derived(() => status.get() === "loading");
|
|
505
|
+
const logoutMutation = mutation(
|
|
506
|
+
async () => {
|
|
507
|
+
await config.logout?.();
|
|
508
|
+
},
|
|
509
|
+
{ onSuccess: () => session.refetch() }
|
|
510
|
+
);
|
|
511
|
+
const dispose = () => {
|
|
512
|
+
session.dispose();
|
|
513
|
+
status.dispose();
|
|
514
|
+
user.dispose();
|
|
515
|
+
authenticated.dispose();
|
|
516
|
+
unauthenticated.dispose();
|
|
517
|
+
loading.dispose();
|
|
518
|
+
logoutMutation.dispose();
|
|
519
|
+
};
|
|
520
|
+
return {
|
|
521
|
+
status,
|
|
522
|
+
user,
|
|
523
|
+
error: session.error,
|
|
524
|
+
authenticated,
|
|
525
|
+
unauthenticated,
|
|
526
|
+
loading,
|
|
527
|
+
loggingOut: logoutMutation.pending,
|
|
528
|
+
refresh: () => session.refetch(),
|
|
529
|
+
logout: () => logoutMutation.mutate(void 0),
|
|
530
|
+
dispose
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
342
534
|
// ../core/src/a11y-ids.ts
|
|
343
535
|
var UNSAFE = /[^A-Za-z0-9_-]+/g;
|
|
344
536
|
function toIdToken(base) {
|
|
@@ -354,6 +546,9 @@ function a11yIds(base) {
|
|
|
354
546
|
description: `${token}-description`,
|
|
355
547
|
error: `${token}-error`,
|
|
356
548
|
title: `${token}-title`,
|
|
549
|
+
trigger: `${token}-trigger`,
|
|
550
|
+
controls: `${token}-controls`,
|
|
551
|
+
owns: `${token}-owns`,
|
|
357
552
|
id: (suffix) => `${token}-${toIdToken(suffix)}`
|
|
358
553
|
};
|
|
359
554
|
}
|
|
@@ -602,9 +797,13 @@ function formatDiagnosticContext(context) {
|
|
|
602
797
|
const parts = [];
|
|
603
798
|
if (context.package !== void 0) parts.push(`package=${context.package}`);
|
|
604
799
|
if (context.operation !== void 0) parts.push(`operation=${context.operation}`);
|
|
800
|
+
if (context.component !== void 0) parts.push(`component=${context.component}`);
|
|
801
|
+
if (context.phase !== void 0) parts.push(`phase=${context.phase}`);
|
|
605
802
|
if (context.nodeId !== void 0) parts.push(`node=${context.nodeId}`);
|
|
803
|
+
if (context.element !== void 0) parts.push(`element=${context.element}`);
|
|
606
804
|
if (context.route !== void 0) parts.push(`route=${context.route}`);
|
|
607
805
|
if (context.resource !== void 0) parts.push(`resource=${context.resource}`);
|
|
806
|
+
if (context.signal !== void 0) parts.push(`signal=${context.signal}`);
|
|
608
807
|
return parts.length > 0 ? ` [${parts.join(", ")}]` : "";
|
|
609
808
|
}
|
|
610
809
|
var StreetFrameworkError = class extends Error {
|
|
@@ -635,6 +834,42 @@ function consoleDiagnosticSink(logger = console) {
|
|
|
635
834
|
error: (m, c) => logger.error?.(`${m}${formatDiagnosticContext(c)}`)
|
|
636
835
|
};
|
|
637
836
|
}
|
|
837
|
+
function describeErrorAt(error, context, options, depth) {
|
|
838
|
+
const isError = error instanceof Error;
|
|
839
|
+
const name = isError ? error.name : "Error";
|
|
840
|
+
const message = isError ? error.message : safeStringify(error);
|
|
841
|
+
const report = { name, message, isError };
|
|
842
|
+
if (context !== void 0) report.context = context;
|
|
843
|
+
if (options.includeStack === true && isError && typeof error.stack === "string") {
|
|
844
|
+
report.stack = error.stack;
|
|
845
|
+
}
|
|
846
|
+
if (options.includeCause === true && isError && depth < 4) {
|
|
847
|
+
const cause = error.cause;
|
|
848
|
+
if (cause !== void 0 && cause !== null) {
|
|
849
|
+
report.cause = describeErrorAt(cause, void 0, options, depth + 1);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return report;
|
|
853
|
+
}
|
|
854
|
+
function safeStringify(value) {
|
|
855
|
+
if (typeof value === "string") return value;
|
|
856
|
+
if (value === null) return "null";
|
|
857
|
+
if (value === void 0) return "undefined";
|
|
858
|
+
const t = typeof value;
|
|
859
|
+
if (t === "number" || t === "boolean" || t === "bigint" || t === "symbol") {
|
|
860
|
+
return String(value);
|
|
861
|
+
}
|
|
862
|
+
const ctor = t === "object" && value !== null ? value.constructor?.name ?? "Object" : t;
|
|
863
|
+
return `[non-Error ${ctor}]`;
|
|
864
|
+
}
|
|
865
|
+
function describeError(error, context, options = {}) {
|
|
866
|
+
return describeErrorAt(error, context, options, 0);
|
|
867
|
+
}
|
|
868
|
+
function reportError(sink, error, context, options = {}) {
|
|
869
|
+
const report = describeError(error, context, options);
|
|
870
|
+
reportDiagnostic(sink, "error", report.message, report.context);
|
|
871
|
+
return report;
|
|
872
|
+
}
|
|
638
873
|
|
|
639
874
|
// ../graph/src/graph-node.ts
|
|
640
875
|
var GraphNode = class _GraphNode {
|
|
@@ -804,6 +1039,8 @@ var ApplicationGraph = class {
|
|
|
804
1039
|
this.handlers.delete(`__listplan__${node.id}`);
|
|
805
1040
|
this.handlers.delete(`__overlay__${node.id}`);
|
|
806
1041
|
this.handlers.delete(`__component__${node.id}`);
|
|
1042
|
+
this.handlers.delete(`__transition__${node.id}`);
|
|
1043
|
+
this.handlers.delete(`__head__${node.id}`);
|
|
807
1044
|
}
|
|
808
1045
|
// ── Handler registry ──────────────────────────────────────────────────────
|
|
809
1046
|
registerHandler(key, fn) {
|
|
@@ -892,6 +1129,162 @@ var ApplicationGraph = class {
|
|
|
892
1129
|
}
|
|
893
1130
|
};
|
|
894
1131
|
|
|
1132
|
+
// ../dsl/src/transition.ts
|
|
1133
|
+
function classes(value) {
|
|
1134
|
+
if (value === void 0) return [];
|
|
1135
|
+
const out = [];
|
|
1136
|
+
for (const token of value.split(/\s+/)) {
|
|
1137
|
+
if (token.length > 0 && !out.includes(token)) out.push(token);
|
|
1138
|
+
}
|
|
1139
|
+
return out;
|
|
1140
|
+
}
|
|
1141
|
+
function merge(a, b) {
|
|
1142
|
+
const out = [...a];
|
|
1143
|
+
for (const token of b) if (!out.includes(token)) out.push(token);
|
|
1144
|
+
return out;
|
|
1145
|
+
}
|
|
1146
|
+
function resolveTransition(config) {
|
|
1147
|
+
const n = config.name;
|
|
1148
|
+
const enterActive = merge(
|
|
1149
|
+
classes(config.enter),
|
|
1150
|
+
classes(config.enterActive ?? (n !== void 0 ? `${n}-enter-active` : void 0))
|
|
1151
|
+
);
|
|
1152
|
+
const leaveActive = merge(
|
|
1153
|
+
classes(config.leave),
|
|
1154
|
+
classes(config.leaveActive ?? (n !== void 0 ? `${n}-leave-active` : void 0))
|
|
1155
|
+
);
|
|
1156
|
+
return {
|
|
1157
|
+
enterActive,
|
|
1158
|
+
enterFrom: classes(config.enterFrom ?? (n !== void 0 ? `${n}-enter-from` : void 0)),
|
|
1159
|
+
enterTo: classes(config.enterTo ?? (n !== void 0 ? `${n}-enter-to` : void 0)),
|
|
1160
|
+
leaveActive,
|
|
1161
|
+
leaveFrom: classes(config.leaveFrom ?? (n !== void 0 ? `${n}-leave-from` : void 0)),
|
|
1162
|
+
leaveTo: classes(config.leaveTo ?? (n !== void 0 ? `${n}-leave-to` : void 0)),
|
|
1163
|
+
appear: config.appear ?? false,
|
|
1164
|
+
duration: config.duration ?? 1e3
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
function isTransitionConfig(value) {
|
|
1168
|
+
if (value === null || typeof value !== "object") return false;
|
|
1169
|
+
const o = value;
|
|
1170
|
+
return typeof o["name"] === "string" || typeof o["enter"] === "string" || typeof o["enterActive"] === "string" || typeof o["enterFrom"] === "string" || typeof o["leave"] === "string" || typeof o["leaveActive"] === "string" || typeof o["leaveFrom"] === "string";
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// ../dsl/src/head.ts
|
|
1174
|
+
function metaDedupKey(m) {
|
|
1175
|
+
if (m.charset !== void 0) return "meta:charset";
|
|
1176
|
+
if (m.name !== void 0) return `meta:name=${m.name}`;
|
|
1177
|
+
if (m.property !== void 0) return `meta:property=${m.property}`;
|
|
1178
|
+
if (m.httpEquiv !== void 0) return `meta:http-equiv=${m.httpEquiv}`;
|
|
1179
|
+
return void 0;
|
|
1180
|
+
}
|
|
1181
|
+
function metaAttrs(m) {
|
|
1182
|
+
const attrs = {};
|
|
1183
|
+
if (m.charset !== void 0) attrs["charset"] = m.charset;
|
|
1184
|
+
if (m.name !== void 0) attrs["name"] = m.name;
|
|
1185
|
+
if (m.property !== void 0) attrs["property"] = m.property;
|
|
1186
|
+
if (m.httpEquiv !== void 0) attrs["http-equiv"] = m.httpEquiv;
|
|
1187
|
+
if (m.content !== void 0) attrs["content"] = m.content;
|
|
1188
|
+
return attrs;
|
|
1189
|
+
}
|
|
1190
|
+
function linkAttrs(l) {
|
|
1191
|
+
const attrs = { rel: l.rel, href: l.href };
|
|
1192
|
+
if (l.sizes !== void 0) attrs["sizes"] = l.sizes;
|
|
1193
|
+
if (l.type !== void 0) attrs["type"] = l.type;
|
|
1194
|
+
if (l.media !== void 0) attrs["media"] = l.media;
|
|
1195
|
+
if (l.as !== void 0) attrs["as"] = l.as;
|
|
1196
|
+
if (l.crossorigin !== void 0) attrs["crossorigin"] = l.crossorigin;
|
|
1197
|
+
if (l.hreflang !== void 0) attrs["hreflang"] = l.hreflang;
|
|
1198
|
+
return attrs;
|
|
1199
|
+
}
|
|
1200
|
+
function resolveHead(config) {
|
|
1201
|
+
const entries = [];
|
|
1202
|
+
if (config.charset !== void 0) {
|
|
1203
|
+
entries.push({ tag: "meta", dedupKey: "meta:charset", attrs: { charset: config.charset } });
|
|
1204
|
+
}
|
|
1205
|
+
if (config.base !== void 0) {
|
|
1206
|
+
entries.push({ tag: "base", dedupKey: "base", attrs: { href: config.base } });
|
|
1207
|
+
}
|
|
1208
|
+
if (config.title !== void 0) {
|
|
1209
|
+
entries.push({ tag: "title", dedupKey: "title", attrs: {}, text: config.title });
|
|
1210
|
+
}
|
|
1211
|
+
if (config.description !== void 0) {
|
|
1212
|
+
entries.push({
|
|
1213
|
+
tag: "meta",
|
|
1214
|
+
dedupKey: "meta:name=description",
|
|
1215
|
+
attrs: { name: "description", content: config.description }
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
if (config.canonical !== void 0) {
|
|
1219
|
+
entries.push({
|
|
1220
|
+
tag: "link",
|
|
1221
|
+
dedupKey: "link:rel=canonical",
|
|
1222
|
+
attrs: { rel: "canonical", href: config.canonical }
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
if (config.robots !== void 0) {
|
|
1226
|
+
entries.push({
|
|
1227
|
+
tag: "meta",
|
|
1228
|
+
dedupKey: "meta:name=robots",
|
|
1229
|
+
attrs: { name: "robots", content: config.robots }
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
if (config.themeColor !== void 0) {
|
|
1233
|
+
entries.push({
|
|
1234
|
+
tag: "meta",
|
|
1235
|
+
dedupKey: "meta:name=theme-color",
|
|
1236
|
+
attrs: { name: "theme-color", content: config.themeColor }
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
if (config.viewport !== void 0) {
|
|
1240
|
+
entries.push({
|
|
1241
|
+
tag: "meta",
|
|
1242
|
+
dedupKey: "meta:name=viewport",
|
|
1243
|
+
attrs: { name: "viewport", content: config.viewport }
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
if (config.favicon !== void 0) {
|
|
1247
|
+
const l = typeof config.favicon === "string" ? { rel: "icon", href: config.favicon } : config.favicon;
|
|
1248
|
+
entries.push({ tag: "link", dedupKey: `link:rel=${l.rel}`, attrs: linkAttrs(l) });
|
|
1249
|
+
}
|
|
1250
|
+
if (config.openGraph !== void 0) {
|
|
1251
|
+
for (const key of Object.keys(config.openGraph)) {
|
|
1252
|
+
const property = `og:${key}`;
|
|
1253
|
+
entries.push({
|
|
1254
|
+
tag: "meta",
|
|
1255
|
+
dedupKey: `meta:property=${property}`,
|
|
1256
|
+
attrs: { property, content: config.openGraph[key] }
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
if (config.twitter !== void 0) {
|
|
1261
|
+
for (const key of Object.keys(config.twitter)) {
|
|
1262
|
+
const name = `twitter:${key}`;
|
|
1263
|
+
entries.push({
|
|
1264
|
+
tag: "meta",
|
|
1265
|
+
dedupKey: `meta:name=${name}`,
|
|
1266
|
+
attrs: { name, content: config.twitter[key] }
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
if (config.meta !== void 0) {
|
|
1271
|
+
for (const m of config.meta) {
|
|
1272
|
+
const key = metaDedupKey(m);
|
|
1273
|
+
if (key === void 0) continue;
|
|
1274
|
+
entries.push({ tag: "meta", dedupKey: key, attrs: metaAttrs(m) });
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
if (config.link !== void 0) {
|
|
1278
|
+
for (const l of config.link) {
|
|
1279
|
+
entries.push({ tag: "link", dedupKey: `link:rel=${l.rel}:href=${l.href}`, attrs: linkAttrs(l) });
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return { entries };
|
|
1283
|
+
}
|
|
1284
|
+
function isHeadContribution(value) {
|
|
1285
|
+
return value !== null && typeof value === "object" && Array.isArray(value["entries"]);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
895
1288
|
// ../dsl/src/builders.ts
|
|
896
1289
|
function isSignal(v) {
|
|
897
1290
|
return v !== null && typeof v === "object" && typeof v["get"] === "function" && typeof v["subscribe"] === "function";
|
|
@@ -919,6 +1312,18 @@ function applyA11yProps(props, options) {
|
|
|
919
1312
|
if (options.ariaInvalid !== void 0) props["aria-invalid"] = String(options.ariaInvalid);
|
|
920
1313
|
if (options.ariaRequired !== void 0) props["aria-required"] = String(options.ariaRequired);
|
|
921
1314
|
if (options.ariaModal !== void 0) props["aria-modal"] = String(options.ariaModal);
|
|
1315
|
+
if (options.ariaOwns !== void 0) props["aria-owns"] = options.ariaOwns;
|
|
1316
|
+
if (options.ariaActiveDescendant !== void 0) props["aria-activedescendant"] = options.ariaActiveDescendant;
|
|
1317
|
+
if (options.ariaHasPopup !== void 0) props["aria-haspopup"] = String(options.ariaHasPopup);
|
|
1318
|
+
if (options.ariaSelected !== void 0) props["aria-selected"] = String(options.ariaSelected);
|
|
1319
|
+
}
|
|
1320
|
+
function registerTransition(graph, node, config) {
|
|
1321
|
+
if (config === void 0) return;
|
|
1322
|
+
const resolved = resolveTransition(config);
|
|
1323
|
+
graph.registerHandler(
|
|
1324
|
+
`__transition__${node.id}`,
|
|
1325
|
+
() => resolved
|
|
1326
|
+
);
|
|
922
1327
|
}
|
|
923
1328
|
function containerProps(options) {
|
|
924
1329
|
const props = {};
|
|
@@ -979,6 +1384,7 @@ var OVERLAY_KINDS = {
|
|
|
979
1384
|
role: "menu",
|
|
980
1385
|
modal: false,
|
|
981
1386
|
takesFocus: true,
|
|
1387
|
+
menu: true,
|
|
982
1388
|
ariaModal: false,
|
|
983
1389
|
defaultCloseOnEscape: true,
|
|
984
1390
|
defaultRestoreFocus: true
|
|
@@ -1111,6 +1517,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1111
1517
|
parent: this._node,
|
|
1112
1518
|
props: containerProps(options)
|
|
1113
1519
|
});
|
|
1520
|
+
registerTransition(this._graph, node, options.transition);
|
|
1114
1521
|
builder(new SectionBuilderImpl(node, this._graph));
|
|
1115
1522
|
}
|
|
1116
1523
|
container(key, builder, options = {}) {
|
|
@@ -1119,6 +1526,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1119
1526
|
parent: this._node,
|
|
1120
1527
|
props: containerProps(options)
|
|
1121
1528
|
});
|
|
1529
|
+
registerTransition(this._graph, node, options.transition);
|
|
1122
1530
|
builder(new ContainerBuilderImpl(node, this._graph));
|
|
1123
1531
|
}
|
|
1124
1532
|
list(key, builder, options = {}) {
|
|
@@ -1127,6 +1535,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1127
1535
|
parent: this._node,
|
|
1128
1536
|
props: containerProps(options)
|
|
1129
1537
|
});
|
|
1538
|
+
registerTransition(this._graph, node, options.transition);
|
|
1130
1539
|
builder(new ListBuilderImpl(node, this._graph));
|
|
1131
1540
|
}
|
|
1132
1541
|
listOf(key, items, renderItem, options = {}) {
|
|
@@ -1136,6 +1545,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1136
1545
|
parent: this._node,
|
|
1137
1546
|
props: containerProps(options)
|
|
1138
1547
|
});
|
|
1548
|
+
registerTransition(graph, node, options.transition);
|
|
1139
1549
|
const signalId = `${node.id}:items`;
|
|
1140
1550
|
node.stateRefs.push({ signalId, propKey: "items" });
|
|
1141
1551
|
graph.registerHandler(`__signal__${signalId}`, items);
|
|
@@ -1157,6 +1567,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1157
1567
|
}
|
|
1158
1568
|
});
|
|
1159
1569
|
renderItem(item, index, new ContainerBuilderImpl(itemNode, graph));
|
|
1570
|
+
registerTransition(graph, itemNode, options.itemTransition);
|
|
1160
1571
|
return itemNode;
|
|
1161
1572
|
};
|
|
1162
1573
|
const buildPlan = (raw) => {
|
|
@@ -1188,6 +1599,7 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1188
1599
|
parent: this._node,
|
|
1189
1600
|
props
|
|
1190
1601
|
});
|
|
1602
|
+
registerTransition(this._graph, node, options.transition);
|
|
1191
1603
|
if (options.onSubmit !== void 0) {
|
|
1192
1604
|
const handlerKey = `submit:${node.id}`;
|
|
1193
1605
|
this._graph.registerHandler(handlerKey, options.onSubmit);
|
|
@@ -1195,18 +1607,20 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1195
1607
|
}
|
|
1196
1608
|
builder(new FormBuilderImpl(node, this._graph));
|
|
1197
1609
|
}
|
|
1198
|
-
when(condition, builder, elseBuilder) {
|
|
1610
|
+
when(condition, builder, elseBuilder, options = {}) {
|
|
1199
1611
|
const graph = this._graph;
|
|
1200
1612
|
const node = graph.createNode("conditional", {
|
|
1201
1613
|
parent: this._node,
|
|
1202
1614
|
props: containerProps({})
|
|
1203
1615
|
});
|
|
1616
|
+
const branchTransition = options.transition !== void 0 && options.appear === true ? { ...options.transition, appear: true } : options.transition;
|
|
1204
1617
|
const buildBranch = (build, tag) => {
|
|
1205
1618
|
const branchKey = `when-${tag}:${node.id}`;
|
|
1206
1619
|
const branch = graph.createNode("container", {
|
|
1207
1620
|
key: branchKey,
|
|
1208
1621
|
props: { key: branchKey }
|
|
1209
1622
|
});
|
|
1623
|
+
registerTransition(graph, branch, branchTransition);
|
|
1210
1624
|
build(new ContainerBuilderImpl(branch, graph));
|
|
1211
1625
|
return branch;
|
|
1212
1626
|
};
|
|
@@ -1252,8 +1666,14 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1252
1666
|
this.container(id, (c) => {
|
|
1253
1667
|
c.when(
|
|
1254
1668
|
hasError,
|
|
1255
|
-
// Error state → fallback.
|
|
1256
|
-
|
|
1669
|
+
// Error state → fallback. The fallback branch mounts exactly when the
|
|
1670
|
+
// boundary enters its error state, so this is also where the optional
|
|
1671
|
+
// `onError` reporting hook fires (observe-only; §6/§7).
|
|
1672
|
+
(fb) => {
|
|
1673
|
+
const currentError = readError();
|
|
1674
|
+
options.onError?.(currentError);
|
|
1675
|
+
options.fallback(fb, currentError, retry);
|
|
1676
|
+
},
|
|
1257
1677
|
// Healthy state → body, guarded against synchronous build throws.
|
|
1258
1678
|
(body) => {
|
|
1259
1679
|
try {
|
|
@@ -1265,6 +1685,24 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1265
1685
|
);
|
|
1266
1686
|
}, { id });
|
|
1267
1687
|
}
|
|
1688
|
+
asyncBoundary(key, res, branches) {
|
|
1689
|
+
const isError = derived(() => res.status.get() === "error");
|
|
1690
|
+
const showSuccess = derived(
|
|
1691
|
+
() => res.status.get() !== "error" && res.data.get() !== void 0
|
|
1692
|
+
);
|
|
1693
|
+
const showLoading = derived(
|
|
1694
|
+
() => res.status.get() !== "error" && res.data.get() === void 0
|
|
1695
|
+
);
|
|
1696
|
+
const dataSignal = derived(() => res.data.get());
|
|
1697
|
+
const retry = () => {
|
|
1698
|
+
void res.refetch();
|
|
1699
|
+
};
|
|
1700
|
+
this.container(key, (c) => {
|
|
1701
|
+
c.when(isError, (fb) => branches.error?.(fb, res.error.peek(), retry));
|
|
1702
|
+
c.when(showSuccess, (sb) => branches.success(sb, dataSignal));
|
|
1703
|
+
c.when(showLoading, (lb) => branches.loading?.(lb));
|
|
1704
|
+
}, { key });
|
|
1705
|
+
}
|
|
1268
1706
|
// ── Portals & overlays ──────────────────────────────────────────────────────
|
|
1269
1707
|
portal(key, builder, options = {}) {
|
|
1270
1708
|
const node = this._graph.createNode("portal", {
|
|
@@ -1274,6 +1712,31 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1274
1712
|
});
|
|
1275
1713
|
builder(new ContainerBuilderImpl(node, this._graph));
|
|
1276
1714
|
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Declare document metadata (2.0 §1–§3). Creates a `'head'` node — a neutral,
|
|
1717
|
+
* empty inline anchor at this position (one node / one element, so positional
|
|
1718
|
+
* hydration is preserved) — and registers a `__head__<id>` descriptor holding
|
|
1719
|
+
* this call's normalized, dedup-keyed {@link resolveHead} contribution. The
|
|
1720
|
+
* renderer applies it to `document.head` on the browser (adopting server tags
|
|
1721
|
+
* on hydration, cleaning up on unmount / route change) and emits the active
|
|
1722
|
+
* graph's merged metadata as a string on the server (`renderHead`).
|
|
1723
|
+
*
|
|
1724
|
+
* Multiple `head()` nodes may be live at once (app default + route + component)
|
|
1725
|
+
* — the renderer merges them and, per dedup key, the last in document order
|
|
1726
|
+
* wins (see head.ts). No new render path: this reuses the same graph-node +
|
|
1727
|
+
* handler-registry convention as overlays/transitions/components.
|
|
1728
|
+
*/
|
|
1729
|
+
head(metadata) {
|
|
1730
|
+
const node = this._graph.createNode("head", {
|
|
1731
|
+
parent: this._node,
|
|
1732
|
+
props: { "data-streetui-head-anchor": "" }
|
|
1733
|
+
});
|
|
1734
|
+
const contribution = resolveHead(metadata);
|
|
1735
|
+
this._graph.registerHandler(
|
|
1736
|
+
`__head__${node.id}`,
|
|
1737
|
+
() => contribution
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1277
1740
|
/**
|
|
1278
1741
|
* Shared assembly for every overlay kind: a `portal` node whose single child
|
|
1279
1742
|
* is a `when(open, panel)` conditional. The panel container carries the
|
|
@@ -1301,13 +1764,24 @@ var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
|
1301
1764
|
...options.ariaDescribedBy !== void 0 ? { ariaDescribedBy: options.ariaDescribedBy } : {}
|
|
1302
1765
|
};
|
|
1303
1766
|
const portalBuilder = new ContainerBuilderImpl(portalNode, graph);
|
|
1304
|
-
portalBuilder.when(
|
|
1305
|
-
|
|
1306
|
-
|
|
1767
|
+
portalBuilder.when(
|
|
1768
|
+
openSignal,
|
|
1769
|
+
(panelHost) => {
|
|
1770
|
+
panelHost.container(`${key}__panel`, builder, panelOptions);
|
|
1771
|
+
},
|
|
1772
|
+
void 0,
|
|
1773
|
+
// Overlay open/close rides the panel's `when`; a transition animates the
|
|
1774
|
+
// panel in on open and — via the reconciler's deferred-leave — plays the
|
|
1775
|
+
// leave before the panel is removed (§10). Focus is restored at close-
|
|
1776
|
+
// request time (see wireOverlayBehavior), so it never stays trapped inside
|
|
1777
|
+
// a panel that is animating away.
|
|
1778
|
+
options.transition !== void 0 ? { transition: options.transition } : {}
|
|
1779
|
+
);
|
|
1307
1780
|
const descriptor = {
|
|
1308
1781
|
open: openSignal,
|
|
1309
1782
|
modal: kind.modal,
|
|
1310
1783
|
takesFocus: kind.takesFocus,
|
|
1784
|
+
menu: kind.menu ?? false,
|
|
1311
1785
|
closeOnEscape: options.closeOnEscape ?? kind.defaultCloseOnEscape,
|
|
1312
1786
|
restoreFocus: options.restoreFocus ?? kind.defaultRestoreFocus,
|
|
1313
1787
|
...options.initialFocusId !== void 0 ? { initialFocusId: options.initialFocusId } : {},
|
|
@@ -1404,6 +1878,7 @@ var ListBuilderImpl = class extends ContentBuilderBase {
|
|
|
1404
1878
|
parent: this._node,
|
|
1405
1879
|
props: containerProps(options)
|
|
1406
1880
|
});
|
|
1881
|
+
registerTransition(this._graph, node, options.transition);
|
|
1407
1882
|
builder(new ContainerBuilderImpl(node, this._graph));
|
|
1408
1883
|
}
|
|
1409
1884
|
};
|
|
@@ -1633,8 +2108,8 @@ var RuntimeNodeInstance = class {
|
|
|
1633
2108
|
this.children.push(instance);
|
|
1634
2109
|
}
|
|
1635
2110
|
/** Subscribe to a signal and register the unsubscribe for cleanup. */
|
|
1636
|
-
trackSignal(
|
|
1637
|
-
const unsub =
|
|
2111
|
+
trackSignal(signal3, handler) {
|
|
2112
|
+
const unsub = signal3.subscribe(handler);
|
|
1638
2113
|
this.cleanup.add(unsub);
|
|
1639
2114
|
}
|
|
1640
2115
|
/** Register an arbitrary cleanup function (e.g. DOM event removal). */
|
|
@@ -2005,6 +2480,9 @@ var BrowserDOMAdapter = class {
|
|
|
2005
2480
|
body() {
|
|
2006
2481
|
return document.body ?? null;
|
|
2007
2482
|
}
|
|
2483
|
+
head() {
|
|
2484
|
+
return document.head ?? null;
|
|
2485
|
+
}
|
|
2008
2486
|
activeElement() {
|
|
2009
2487
|
return document.activeElement ?? null;
|
|
2010
2488
|
}
|
|
@@ -2384,6 +2862,9 @@ var ServerDOMAdapter = class {
|
|
|
2384
2862
|
body() {
|
|
2385
2863
|
return null;
|
|
2386
2864
|
}
|
|
2865
|
+
head() {
|
|
2866
|
+
return null;
|
|
2867
|
+
}
|
|
2387
2868
|
activeElement() {
|
|
2388
2869
|
return null;
|
|
2389
2870
|
}
|
|
@@ -2498,18 +2979,25 @@ function trapFocus(dom, container) {
|
|
|
2498
2979
|
dom.addEventListener(container, "keydown", onKeydown);
|
|
2499
2980
|
return () => dom.removeEventListener(container, "keydown", onKeydown);
|
|
2500
2981
|
}
|
|
2982
|
+
var containmentStack = [];
|
|
2501
2983
|
function containFocus(dom, container) {
|
|
2502
2984
|
const body = dom.body();
|
|
2503
2985
|
if (body === null) return () => {
|
|
2504
2986
|
};
|
|
2987
|
+
containmentStack.push(container);
|
|
2505
2988
|
const onFocusIn = (event) => {
|
|
2989
|
+
if (containmentStack[containmentStack.length - 1] !== container) return;
|
|
2506
2990
|
const target = event.target;
|
|
2507
2991
|
if (target !== null && !dom.contains(container, target)) {
|
|
2508
2992
|
focusFirst(dom, container);
|
|
2509
2993
|
}
|
|
2510
2994
|
};
|
|
2511
2995
|
dom.addEventListener(body, "focusin", onFocusIn);
|
|
2512
|
-
return () =>
|
|
2996
|
+
return () => {
|
|
2997
|
+
dom.removeEventListener(body, "focusin", onFocusIn);
|
|
2998
|
+
const index = containmentStack.lastIndexOf(container);
|
|
2999
|
+
if (index !== -1) containmentStack.splice(index, 1);
|
|
3000
|
+
};
|
|
2513
3001
|
}
|
|
2514
3002
|
function onEscape(dom, target, handler) {
|
|
2515
3003
|
const onKeydown = (event) => {
|
|
@@ -2518,6 +3006,84 @@ function onEscape(dom, target, handler) {
|
|
|
2518
3006
|
dom.addEventListener(target, "keydown", onKeydown);
|
|
2519
3007
|
return () => dom.removeEventListener(target, "keydown", onKeydown);
|
|
2520
3008
|
}
|
|
3009
|
+
function rovingMenu(dom, container, selector = FOCUSABLE_SELECTOR) {
|
|
3010
|
+
const onKeydown = (event) => {
|
|
3011
|
+
const key = event.key;
|
|
3012
|
+
const isActivate = key === "Enter" || key === " " || key === "Spacebar";
|
|
3013
|
+
const isMove = key === "ArrowDown" || key === "ArrowUp" || key === "Home" || key === "End";
|
|
3014
|
+
if (!isActivate && !isMove) return;
|
|
3015
|
+
const items = getFocusable(dom, container, selector);
|
|
3016
|
+
if (items.length === 0) return;
|
|
3017
|
+
const active = dom.activeElement();
|
|
3018
|
+
const index = active === null ? -1 : items.indexOf(active);
|
|
3019
|
+
if (isActivate) {
|
|
3020
|
+
if (index < 0) return;
|
|
3021
|
+
event.preventDefault();
|
|
3022
|
+
items[index].click?.();
|
|
3023
|
+
return;
|
|
3024
|
+
}
|
|
3025
|
+
event.preventDefault();
|
|
3026
|
+
let next;
|
|
3027
|
+
if (key === "Home") next = 0;
|
|
3028
|
+
else if (key === "End") next = items.length - 1;
|
|
3029
|
+
else if (key === "ArrowDown") next = index < 0 ? 0 : (index + 1) % items.length;
|
|
3030
|
+
else next = index <= 0 ? items.length - 1 : index - 1;
|
|
3031
|
+
dom.focus(items[next]);
|
|
3032
|
+
};
|
|
3033
|
+
dom.addEventListener(container, "keydown", onKeydown);
|
|
3034
|
+
return () => dom.removeEventListener(container, "keydown", onKeydown);
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3037
|
+
// ../dom/src/live-region.ts
|
|
3038
|
+
var VISUALLY_HIDDEN = "position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;";
|
|
3039
|
+
function makeRegion(dom, politeness) {
|
|
3040
|
+
const el = dom.createElement("div");
|
|
3041
|
+
dom.setAttribute(el, "aria-live", politeness);
|
|
3042
|
+
dom.setAttribute(el, "aria-atomic", "true");
|
|
3043
|
+
dom.setAttribute(el, "role", politeness === "assertive" ? "alert" : "status");
|
|
3044
|
+
dom.setAttribute(el, "data-streetui-live", politeness);
|
|
3045
|
+
dom.setAttribute(el, "style", VISUALLY_HIDDEN);
|
|
3046
|
+
return el;
|
|
3047
|
+
}
|
|
3048
|
+
function createAnnouncer(dom) {
|
|
3049
|
+
const body = dom.body();
|
|
3050
|
+
if (body === null) {
|
|
3051
|
+
return { announce() {
|
|
3052
|
+
}, clear() {
|
|
3053
|
+
}, destroy() {
|
|
3054
|
+
} };
|
|
3055
|
+
}
|
|
3056
|
+
const polite = makeRegion(dom, "polite");
|
|
3057
|
+
const assertive = makeRegion(dom, "assertive");
|
|
3058
|
+
dom.appendChild(body, polite);
|
|
3059
|
+
dom.appendChild(body, assertive);
|
|
3060
|
+
let destroyed = false;
|
|
3061
|
+
const write = (region, message) => {
|
|
3062
|
+
dom.setTextContent(region, "");
|
|
3063
|
+
void Promise.resolve().then(() => {
|
|
3064
|
+
if (!destroyed) dom.setTextContent(region, message);
|
|
3065
|
+
});
|
|
3066
|
+
};
|
|
3067
|
+
return {
|
|
3068
|
+
announce(message, options) {
|
|
3069
|
+
if (destroyed) return;
|
|
3070
|
+
write(options?.assertive === true ? assertive : polite, message);
|
|
3071
|
+
},
|
|
3072
|
+
clear() {
|
|
3073
|
+
if (destroyed) return;
|
|
3074
|
+
dom.setTextContent(polite, "");
|
|
3075
|
+
dom.setTextContent(assertive, "");
|
|
3076
|
+
},
|
|
3077
|
+
destroy() {
|
|
3078
|
+
if (destroyed) return;
|
|
3079
|
+
destroyed = true;
|
|
3080
|
+
for (const region of [polite, assertive]) {
|
|
3081
|
+
const parent = dom.parentNode(region);
|
|
3082
|
+
if (parent !== null) dom.removeChild(parent, region);
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
2521
3087
|
|
|
2522
3088
|
// ../renderer/src/render-context.ts
|
|
2523
3089
|
function createRenderContext(dom, graph, container, hydrationDiagnostics, staticHTML) {
|
|
@@ -2681,7 +3247,13 @@ var TAG_MAP = {
|
|
|
2681
3247
|
// its children are relocated to a document.body container on the browser
|
|
2682
3248
|
// (see the portal branch in mount.ts). On the server (no body) it renders
|
|
2683
3249
|
// inline, so the anchor tag is what SSR/hydration positionally match on.
|
|
2684
|
-
portal: "div"
|
|
3250
|
+
portal: "div",
|
|
3251
|
+
// A `head()` node renders as a neutral, empty inline anchor <div> at its
|
|
3252
|
+
// declaration site (like a portal anchor). Its actual contribution — title/
|
|
3253
|
+
// meta/link/etc. — is applied to `document.head` by `wireHeadBehavior` on the
|
|
3254
|
+
// browser, and emitted separately by `renderHead()` on the server. Keeping a
|
|
3255
|
+
// one-node/one-element anchor preserves positional hydration.
|
|
3256
|
+
head: "div"
|
|
2685
3257
|
};
|
|
2686
3258
|
function resolveTag(type) {
|
|
2687
3259
|
return TAG_MAP[type] ?? "div";
|
|
@@ -2729,7 +3301,7 @@ function patchNode(ctx, graphNode, propKey, newValue) {
|
|
|
2729
3301
|
}
|
|
2730
3302
|
|
|
2731
3303
|
// ../renderer/src/reconciliation.ts
|
|
2732
|
-
function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
|
|
3304
|
+
function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn, hooks) {
|
|
2733
3305
|
const oldByKey = /* @__PURE__ */ new Map();
|
|
2734
3306
|
for (const inst of oldInstances) {
|
|
2735
3307
|
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
@@ -2750,28 +3322,33 @@ function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
|
|
|
2750
3322
|
}
|
|
2751
3323
|
newInstances.push(existing);
|
|
2752
3324
|
} else {
|
|
2753
|
-
const
|
|
2754
|
-
|
|
3325
|
+
const reclaimed = hooks?.takeLeaving(key);
|
|
3326
|
+
if (reclaimed !== void 0) {
|
|
3327
|
+
patchExistingInstance(ctx, reclaimed, newNode);
|
|
3328
|
+
reconcileItemChildren(ctx, reclaimed, newNode, mountFn);
|
|
3329
|
+
hooks?.onEnter(reclaimed);
|
|
3330
|
+
newInstances.push(reclaimed);
|
|
3331
|
+
} else {
|
|
3332
|
+
const inst = mountFn(newNode, parentDom);
|
|
3333
|
+
hooks?.onEnter(inst);
|
|
3334
|
+
newInstances.push(inst);
|
|
3335
|
+
}
|
|
2755
3336
|
}
|
|
2756
3337
|
}
|
|
2757
3338
|
const removed = [];
|
|
2758
3339
|
for (const inst of oldInstances) {
|
|
2759
3340
|
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
2760
|
-
if (
|
|
2761
|
-
|
|
2762
|
-
}
|
|
2763
|
-
}
|
|
2764
|
-
for (const inst of removed) {
|
|
3341
|
+
if (usedKeys.has(key)) continue;
|
|
3342
|
+
if (hooks !== void 0 && hooks.beginLeave(inst)) continue;
|
|
2765
3343
|
const parent = ctx.dom.parentNode(inst.domNode);
|
|
2766
|
-
if (parent !== null)
|
|
2767
|
-
ctx.dom.removeChild(parent, inst.domNode);
|
|
2768
|
-
}
|
|
3344
|
+
if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
|
|
2769
3345
|
inst.dispose();
|
|
3346
|
+
removed.push(inst);
|
|
2770
3347
|
}
|
|
2771
3348
|
reorderDom(ctx, parentDom, newInstances);
|
|
2772
3349
|
return { instances: newInstances, removed };
|
|
2773
3350
|
}
|
|
2774
|
-
function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
|
|
3351
|
+
function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn, hooks) {
|
|
2775
3352
|
const oldByKey = /* @__PURE__ */ new Map();
|
|
2776
3353
|
for (const inst of oldInstances) {
|
|
2777
3354
|
oldByKey.set(inst.graphNode.key ?? inst.graphNode.id, inst);
|
|
@@ -2798,21 +3375,34 @@ function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
|
|
|
2798
3375
|
}
|
|
2799
3376
|
newInstances.push(existing);
|
|
2800
3377
|
} else {
|
|
2801
|
-
const
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
3378
|
+
const reclaimed = hooks?.takeLeaving(entry.key);
|
|
3379
|
+
if (reclaimed !== void 0) {
|
|
3380
|
+
const freshNode = entry.build();
|
|
3381
|
+
built.push(freshNode);
|
|
3382
|
+
patchExistingInstance(ctx, reclaimed, freshNode);
|
|
3383
|
+
reconcileItemChildren(ctx, reclaimed, freshNode, mountFn);
|
|
3384
|
+
reclaimed.graphNode.setProp("_sig", freshNode.getProp("_sig"));
|
|
3385
|
+
reclaimed.graphNode.setProp("_item", entry.item);
|
|
3386
|
+
hooks?.onEnter(reclaimed);
|
|
3387
|
+
newInstances.push(reclaimed);
|
|
3388
|
+
} else {
|
|
3389
|
+
const freshNode = entry.build();
|
|
3390
|
+
built.push(freshNode);
|
|
3391
|
+
const inst = mountFn(freshNode, parentDom);
|
|
3392
|
+
hooks?.onEnter(inst);
|
|
3393
|
+
newInstances.push(inst);
|
|
3394
|
+
}
|
|
2805
3395
|
}
|
|
2806
3396
|
}
|
|
2807
3397
|
const removed = [];
|
|
2808
3398
|
for (const inst of oldInstances) {
|
|
2809
3399
|
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
2810
|
-
if (
|
|
2811
|
-
|
|
2812
|
-
for (const inst of removed) {
|
|
3400
|
+
if (usedKeys.has(key)) continue;
|
|
3401
|
+
if (hooks !== void 0 && hooks.beginLeave(inst)) continue;
|
|
2813
3402
|
const parent = ctx.dom.parentNode(inst.domNode);
|
|
2814
3403
|
if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
|
|
2815
3404
|
inst.dispose();
|
|
3405
|
+
removed.push(inst);
|
|
2816
3406
|
}
|
|
2817
3407
|
reorderDomMinimal(ctx, parentDom, oldInstances, newInstances);
|
|
2818
3408
|
return { instances: newInstances, removed, built };
|
|
@@ -2938,6 +3528,365 @@ function patchExistingInstance(ctx, instance, newNode) {
|
|
|
2938
3528
|
}
|
|
2939
3529
|
}
|
|
2940
3530
|
|
|
3531
|
+
// ../renderer/src/transition.ts
|
|
3532
|
+
function getResolvedTransition(graph, nodeId) {
|
|
3533
|
+
const fn = graph.getHandler(`__transition__${nodeId}`);
|
|
3534
|
+
return fn === void 0 ? void 0 : fn();
|
|
3535
|
+
}
|
|
3536
|
+
function host() {
|
|
3537
|
+
return globalThis;
|
|
3538
|
+
}
|
|
3539
|
+
function nextFrame(cb) {
|
|
3540
|
+
const h = host();
|
|
3541
|
+
const raf = h.requestAnimationFrame;
|
|
3542
|
+
if (typeof raf === "function") {
|
|
3543
|
+
raf(() => raf(cb));
|
|
3544
|
+
} else {
|
|
3545
|
+
h.setTimeout(cb, 0);
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
function splitClass(value) {
|
|
3549
|
+
if (value === null) return [];
|
|
3550
|
+
const out = [];
|
|
3551
|
+
for (const t of value.split(/\s+/)) if (t.length > 0) out.push(t);
|
|
3552
|
+
return out;
|
|
3553
|
+
}
|
|
3554
|
+
function addClasses(dom, el, classes2) {
|
|
3555
|
+
if (classes2.length === 0) return;
|
|
3556
|
+
const current = splitClass(dom.getAttribute(el, "class"));
|
|
3557
|
+
let changed = false;
|
|
3558
|
+
for (const c of classes2) {
|
|
3559
|
+
if (!current.includes(c)) {
|
|
3560
|
+
current.push(c);
|
|
3561
|
+
changed = true;
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
if (changed) dom.setAttribute(el, "class", current.join(" "));
|
|
3565
|
+
}
|
|
3566
|
+
function removeClasses(dom, el, classes2) {
|
|
3567
|
+
if (classes2.length === 0) return;
|
|
3568
|
+
const current = splitClass(dom.getAttribute(el, "class"));
|
|
3569
|
+
const next = current.filter((c) => !classes2.includes(c));
|
|
3570
|
+
if (next.length !== current.length) {
|
|
3571
|
+
if (next.length === 0) dom.removeAttribute(el, "class");
|
|
3572
|
+
else dom.setAttribute(el, "class", next.join(" "));
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
function startRun(dom, el, active, from, to, duration, onDone) {
|
|
3576
|
+
const h = host();
|
|
3577
|
+
let finished = false;
|
|
3578
|
+
let timer = null;
|
|
3579
|
+
const onEvent = (e) => {
|
|
3580
|
+
if (e.target !== el) return;
|
|
3581
|
+
finish();
|
|
3582
|
+
};
|
|
3583
|
+
const detach = () => {
|
|
3584
|
+
dom.removeEventListener(el, "transitionend", onEvent);
|
|
3585
|
+
dom.removeEventListener(el, "animationend", onEvent);
|
|
3586
|
+
if (timer !== null) {
|
|
3587
|
+
h.clearTimeout(timer);
|
|
3588
|
+
timer = null;
|
|
3589
|
+
}
|
|
3590
|
+
};
|
|
3591
|
+
const finish = () => {
|
|
3592
|
+
if (finished) return;
|
|
3593
|
+
finished = true;
|
|
3594
|
+
detach();
|
|
3595
|
+
removeClasses(dom, el, active);
|
|
3596
|
+
removeClasses(dom, el, to);
|
|
3597
|
+
removeClasses(dom, el, from);
|
|
3598
|
+
onDone();
|
|
3599
|
+
};
|
|
3600
|
+
addClasses(dom, el, from);
|
|
3601
|
+
addClasses(dom, el, active);
|
|
3602
|
+
dom.addEventListener(el, "transitionend", onEvent);
|
|
3603
|
+
dom.addEventListener(el, "animationend", onEvent);
|
|
3604
|
+
timer = h.setTimeout(finish, duration);
|
|
3605
|
+
nextFrame(() => {
|
|
3606
|
+
if (finished) return;
|
|
3607
|
+
removeClasses(dom, el, from);
|
|
3608
|
+
addClasses(dom, el, to);
|
|
3609
|
+
});
|
|
3610
|
+
return {
|
|
3611
|
+
cancel: () => {
|
|
3612
|
+
if (finished) return;
|
|
3613
|
+
finished = true;
|
|
3614
|
+
detach();
|
|
3615
|
+
removeClasses(dom, el, active);
|
|
3616
|
+
removeClasses(dom, el, to);
|
|
3617
|
+
removeClasses(dom, el, from);
|
|
3618
|
+
}
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
function runElementTransition(dom, el, rt, phase, onDone) {
|
|
3622
|
+
if (dom.body() === null || !dom.isElement(el)) {
|
|
3623
|
+
onDone();
|
|
3624
|
+
return { cancel: () => {
|
|
3625
|
+
} };
|
|
3626
|
+
}
|
|
3627
|
+
const active = phase === "enter" ? rt.enterActive : rt.leaveActive;
|
|
3628
|
+
const from = phase === "enter" ? rt.enterFrom : rt.leaveFrom;
|
|
3629
|
+
const to = phase === "enter" ? rt.enterTo : rt.leaveTo;
|
|
3630
|
+
return startRun(dom, el, active, from, to, rt.duration, onDone);
|
|
3631
|
+
}
|
|
3632
|
+
var TransitionController = class {
|
|
3633
|
+
constructor(dom, graph, finalize) {
|
|
3634
|
+
this.dom = dom;
|
|
3635
|
+
this.graph = graph;
|
|
3636
|
+
this.finalize = finalize;
|
|
3637
|
+
}
|
|
3638
|
+
leaving = /* @__PURE__ */ new Map();
|
|
3639
|
+
/** True only in a real DOM environment (browser). */
|
|
3640
|
+
get browser() {
|
|
3641
|
+
return this.dom.body() !== null;
|
|
3642
|
+
}
|
|
3643
|
+
keyOf(inst) {
|
|
3644
|
+
return inst.graphNode.key ?? inst.graphNode.id;
|
|
3645
|
+
}
|
|
3646
|
+
resolved(node) {
|
|
3647
|
+
return getResolvedTransition(this.graph, node.id);
|
|
3648
|
+
}
|
|
3649
|
+
/** Run the enter animation for `inst` if it carries a transition (browser only). */
|
|
3650
|
+
enter(inst) {
|
|
3651
|
+
if (!this.browser) return;
|
|
3652
|
+
const rt = this.resolved(inst.graphNode);
|
|
3653
|
+
if (rt === void 0) return;
|
|
3654
|
+
const el = inst.domNode;
|
|
3655
|
+
if (!this.dom.isElement(el)) return;
|
|
3656
|
+
startRun(this.dom, el, rt.enterActive, rt.enterFrom, rt.enterTo, rt.duration, () => {
|
|
3657
|
+
});
|
|
3658
|
+
}
|
|
3659
|
+
/**
|
|
3660
|
+
* Play `appear` for any initial child that opted into it (fresh browser mount
|
|
3661
|
+
* only — hydration must never animate appear, §22, and this is called only on
|
|
3662
|
+
* the mount path).
|
|
3663
|
+
*/
|
|
3664
|
+
appear(children) {
|
|
3665
|
+
if (!this.browser) return;
|
|
3666
|
+
for (const child of children) {
|
|
3667
|
+
const rt = this.resolved(child.graphNode);
|
|
3668
|
+
if (rt !== void 0 && rt.appear) this.enter(child);
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
hooks() {
|
|
3672
|
+
return {
|
|
3673
|
+
takeLeaving: (key) => {
|
|
3674
|
+
const entry = this.leaving.get(key);
|
|
3675
|
+
if (entry === void 0) return void 0;
|
|
3676
|
+
entry.run.cancel();
|
|
3677
|
+
this.leaving.delete(key);
|
|
3678
|
+
return entry.inst;
|
|
3679
|
+
},
|
|
3680
|
+
beginLeave: (inst) => {
|
|
3681
|
+
if (!this.browser) return false;
|
|
3682
|
+
const rt = this.resolved(inst.graphNode);
|
|
3683
|
+
if (rt === void 0) return false;
|
|
3684
|
+
const el = inst.domNode;
|
|
3685
|
+
if (!this.dom.isElement(el)) return false;
|
|
3686
|
+
const key = this.keyOf(inst);
|
|
3687
|
+
const prior = this.leaving.get(key);
|
|
3688
|
+
if (prior !== void 0) prior.run.cancel();
|
|
3689
|
+
const run = startRun(
|
|
3690
|
+
this.dom,
|
|
3691
|
+
el,
|
|
3692
|
+
rt.leaveActive,
|
|
3693
|
+
rt.leaveFrom,
|
|
3694
|
+
rt.leaveTo,
|
|
3695
|
+
rt.duration,
|
|
3696
|
+
() => {
|
|
3697
|
+
const current = this.leaving.get(key);
|
|
3698
|
+
if (current !== void 0 && current.run === run) {
|
|
3699
|
+
this.leaving.delete(key);
|
|
3700
|
+
this.finalize(inst);
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
);
|
|
3704
|
+
this.leaving.set(key, { inst, run });
|
|
3705
|
+
return true;
|
|
3706
|
+
},
|
|
3707
|
+
onEnter: (inst) => this.enter(inst)
|
|
3708
|
+
};
|
|
3709
|
+
}
|
|
3710
|
+
};
|
|
3711
|
+
|
|
3712
|
+
// ../renderer/src/head.ts
|
|
3713
|
+
var HEAD_MARKER = "data-streetui-head";
|
|
3714
|
+
var HEAD_KEY = "data-streetui-head-key";
|
|
3715
|
+
function isSignalLike(v) {
|
|
3716
|
+
return v !== null && typeof v === "object" && typeof v["subscribe"] === "function" && typeof v["peek"] === "function";
|
|
3717
|
+
}
|
|
3718
|
+
function readValue(v) {
|
|
3719
|
+
if (isSignalLike(v)) return String(v.peek() ?? "");
|
|
3720
|
+
return String(v ?? "");
|
|
3721
|
+
}
|
|
3722
|
+
function resolveEntry(entry) {
|
|
3723
|
+
const attrs = {};
|
|
3724
|
+
for (const key of Object.keys(entry.attrs)) {
|
|
3725
|
+
attrs[key] = readValue(entry.attrs[key]);
|
|
3726
|
+
}
|
|
3727
|
+
const resolved = { tag: entry.tag, attrs };
|
|
3728
|
+
if (entry.tag === "title") {
|
|
3729
|
+
return { ...resolved, text: readValue(entry.text) };
|
|
3730
|
+
}
|
|
3731
|
+
return resolved;
|
|
3732
|
+
}
|
|
3733
|
+
var HeadManager = class {
|
|
3734
|
+
_dom;
|
|
3735
|
+
_head;
|
|
3736
|
+
_contributions = /* @__PURE__ */ new Map();
|
|
3737
|
+
_applied = /* @__PURE__ */ new Map();
|
|
3738
|
+
_order = 0;
|
|
3739
|
+
_adopted = false;
|
|
3740
|
+
constructor(dom, head) {
|
|
3741
|
+
this._dom = dom;
|
|
3742
|
+
this._head = head;
|
|
3743
|
+
}
|
|
3744
|
+
/** Register (or replace) a node's contribution and re-apply the merged result. */
|
|
3745
|
+
register(nodeId, entries) {
|
|
3746
|
+
this._contributions.set(nodeId, { order: this._order++, entries });
|
|
3747
|
+
this.apply();
|
|
3748
|
+
}
|
|
3749
|
+
/** Withdraw a node's contribution (unmount / route change) and re-apply. */
|
|
3750
|
+
unregister(nodeId) {
|
|
3751
|
+
if (this._contributions.delete(nodeId)) this.apply();
|
|
3752
|
+
}
|
|
3753
|
+
/** Recompute the merged head and patch `document.head` to match. */
|
|
3754
|
+
apply() {
|
|
3755
|
+
if (!this._adopted) {
|
|
3756
|
+
this._adoptServerTags();
|
|
3757
|
+
this._adopted = true;
|
|
3758
|
+
}
|
|
3759
|
+
const ordered = [...this._contributions.values()].sort((a, b) => a.order - b.order);
|
|
3760
|
+
const merged = /* @__PURE__ */ new Map();
|
|
3761
|
+
for (const contribution of ordered) {
|
|
3762
|
+
for (const entry of contribution.entries) {
|
|
3763
|
+
merged.set(entry.dedupKey, resolveEntry(entry));
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
for (const [key, desired] of merged) {
|
|
3767
|
+
const existing = this._applied.get(key);
|
|
3768
|
+
if (existing !== void 0 && existing.tag === desired.tag) {
|
|
3769
|
+
this._reconcileAttrs(existing, desired);
|
|
3770
|
+
} else {
|
|
3771
|
+
if (existing !== void 0) {
|
|
3772
|
+
this._dom.removeChild(this._head, existing.el);
|
|
3773
|
+
this._applied.delete(key);
|
|
3774
|
+
}
|
|
3775
|
+
const el = this._createTag(key, desired);
|
|
3776
|
+
this._dom.appendChild(this._head, el);
|
|
3777
|
+
this._applied.set(key, { el, attrKeys: new Set(Object.keys(desired.attrs)), tag: desired.tag });
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
for (const [key, record] of [...this._applied]) {
|
|
3781
|
+
if (!merged.has(key)) {
|
|
3782
|
+
this._dom.removeChild(this._head, record.el);
|
|
3783
|
+
this._applied.delete(key);
|
|
3784
|
+
}
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
_createTag(key, desired) {
|
|
3788
|
+
const el = this._dom.createElement(desired.tag);
|
|
3789
|
+
this._dom.setAttribute(el, HEAD_MARKER, "");
|
|
3790
|
+
this._dom.setAttribute(el, HEAD_KEY, key);
|
|
3791
|
+
for (const attr of Object.keys(desired.attrs)) {
|
|
3792
|
+
this._dom.setAttribute(el, attr, desired.attrs[attr]);
|
|
3793
|
+
}
|
|
3794
|
+
if (desired.tag === "title") this._dom.setTextContent(el, desired.text ?? "");
|
|
3795
|
+
return el;
|
|
3796
|
+
}
|
|
3797
|
+
_reconcileAttrs(record, desired) {
|
|
3798
|
+
const nextKeys = new Set(Object.keys(desired.attrs));
|
|
3799
|
+
for (const attr of record.attrKeys) {
|
|
3800
|
+
if (!nextKeys.has(attr)) this._dom.removeAttribute(record.el, attr);
|
|
3801
|
+
}
|
|
3802
|
+
for (const attr of nextKeys) {
|
|
3803
|
+
this._dom.setAttribute(record.el, attr, desired.attrs[attr]);
|
|
3804
|
+
}
|
|
3805
|
+
if (desired.tag === "title") this._dom.setTextContent(record.el, desired.text ?? "");
|
|
3806
|
+
record.attrKeys = nextKeys;
|
|
3807
|
+
}
|
|
3808
|
+
/**
|
|
3809
|
+
* Seed `_applied` from server-emitted `[data-streetui-head-key]` tags already
|
|
3810
|
+
* in `document.head`. The subsequent diff reuses these elements when the
|
|
3811
|
+
* client desires the same key (no duplicate), rewrites them if the value
|
|
3812
|
+
* changed, or removes them if the client graph no longer wants them.
|
|
3813
|
+
*/
|
|
3814
|
+
_adoptServerTags() {
|
|
3815
|
+
for (const child of this._dom.childNodes(this._head)) {
|
|
3816
|
+
if (!this._dom.isElement(child)) continue;
|
|
3817
|
+
const el = child;
|
|
3818
|
+
const key = this._dom.getAttribute(el, HEAD_KEY);
|
|
3819
|
+
if (key === null) continue;
|
|
3820
|
+
this._applied.set(key, {
|
|
3821
|
+
el,
|
|
3822
|
+
attrKeys: new Set(this._attrNames(el)),
|
|
3823
|
+
tag: this._dom.tagName(el)
|
|
3824
|
+
});
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
/** The framework-managed attribute names currently on a server tag. */
|
|
3828
|
+
_attrNames(el) {
|
|
3829
|
+
const names = [];
|
|
3830
|
+
if (this._dom.getAttribute(el, HEAD_MARKER) !== null) names.push(HEAD_MARKER);
|
|
3831
|
+
if (this._dom.getAttribute(el, HEAD_KEY) !== null) names.push(HEAD_KEY);
|
|
3832
|
+
return names;
|
|
3833
|
+
}
|
|
3834
|
+
};
|
|
3835
|
+
function getHeadManager(ctx) {
|
|
3836
|
+
const head = ctx.dom.head();
|
|
3837
|
+
if (head === null) return null;
|
|
3838
|
+
const mutable = ctx;
|
|
3839
|
+
if (mutable.head === void 0) mutable.head = new HeadManager(ctx.dom, head);
|
|
3840
|
+
return mutable.head;
|
|
3841
|
+
}
|
|
3842
|
+
function wireHeadBehavior(ctx, graphNode, instance) {
|
|
3843
|
+
const manager = getHeadManager(ctx);
|
|
3844
|
+
if (manager === null) return;
|
|
3845
|
+
const descFn = ctx.graph.getHandler(`__head__${graphNode.id}`);
|
|
3846
|
+
if (descFn === void 0) return;
|
|
3847
|
+
const contribution = descFn();
|
|
3848
|
+
const nodeId = graphNode.id;
|
|
3849
|
+
manager.register(nodeId, contribution.entries);
|
|
3850
|
+
for (const entry of contribution.entries) {
|
|
3851
|
+
for (const attrKey of Object.keys(entry.attrs)) {
|
|
3852
|
+
const v = entry.attrs[attrKey];
|
|
3853
|
+
if (isSignalLike(v)) {
|
|
3854
|
+
instance.trackCleanup(v.subscribe(() => manager.apply()));
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
if (isSignalLike(entry.text)) {
|
|
3858
|
+
instance.trackCleanup(entry.text.subscribe(() => manager.apply()));
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
instance.trackCleanup(() => manager.unregister(nodeId));
|
|
3862
|
+
}
|
|
3863
|
+
function renderHead(compiled) {
|
|
3864
|
+
const graph = compiled.graph;
|
|
3865
|
+
const merged = /* @__PURE__ */ new Map();
|
|
3866
|
+
graph.walk((node) => {
|
|
3867
|
+
if (node.type !== "head") return;
|
|
3868
|
+
const descFn = graph.getHandler(`__head__${node.id}`);
|
|
3869
|
+
if (descFn === void 0) return;
|
|
3870
|
+
for (const entry of descFn().entries) {
|
|
3871
|
+
merged.set(entry.dedupKey, resolveEntry(entry));
|
|
3872
|
+
}
|
|
3873
|
+
});
|
|
3874
|
+
if (merged.size === 0) return "";
|
|
3875
|
+
const dom = new ServerDOMAdapter();
|
|
3876
|
+
let out = "";
|
|
3877
|
+
for (const [key, desired] of merged) {
|
|
3878
|
+
const el = dom.createElement(desired.tag);
|
|
3879
|
+
dom.setAttribute(el, HEAD_MARKER, "");
|
|
3880
|
+
dom.setAttribute(el, HEAD_KEY, key);
|
|
3881
|
+
for (const attr of Object.keys(desired.attrs)) {
|
|
3882
|
+
dom.setAttribute(el, attr, desired.attrs[attr]);
|
|
3883
|
+
}
|
|
3884
|
+
if (desired.tag === "title") dom.setTextContent(el, desired.text ?? "");
|
|
3885
|
+
out += dom.serializeOuter(el);
|
|
3886
|
+
}
|
|
3887
|
+
return out;
|
|
3888
|
+
}
|
|
3889
|
+
|
|
2941
3890
|
// ../renderer/src/mount.ts
|
|
2942
3891
|
var SKIP_PROP_KEYS = /* @__PURE__ */ new Set([
|
|
2943
3892
|
"text",
|
|
@@ -3060,6 +4009,9 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
3060
4009
|
const instance2 = new NodeInstance(graphNode, el2);
|
|
3061
4010
|
ctx.instances.set(graphNode.id, instance2);
|
|
3062
4011
|
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
4012
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
4013
|
+
wireSignalBindings(ctx, graphNode, instance2, linkUpdate(dom, el2));
|
|
4014
|
+
}
|
|
3063
4015
|
dom.appendChild(parentDom, el2);
|
|
3064
4016
|
return instance2;
|
|
3065
4017
|
}
|
|
@@ -3090,7 +4042,7 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
3090
4042
|
instance2.addChild(childInstance);
|
|
3091
4043
|
}
|
|
3092
4044
|
dom.appendChild(parentDom, el2);
|
|
3093
|
-
wireReactiveList(ctx, graphNode, instance2, el2);
|
|
4045
|
+
wireReactiveList(ctx, graphNode, instance2, el2, true);
|
|
3094
4046
|
return instance2;
|
|
3095
4047
|
}
|
|
3096
4048
|
if (graphNode.type === "portal") {
|
|
@@ -3115,6 +4067,16 @@ function mountNode(ctx, graphNode, parentDom) {
|
|
|
3115
4067
|
wireOverlayBehavior(ctx, graphNode, instance2, target);
|
|
3116
4068
|
return instance2;
|
|
3117
4069
|
}
|
|
4070
|
+
if (graphNode.type === "head") {
|
|
4071
|
+
const anchor = dom.createElement(resolveTag("head"));
|
|
4072
|
+
dom.setAttribute(anchor, "data-streetui-head-anchor", "");
|
|
4073
|
+
applyNodeProps(ctx, graphNode, anchor);
|
|
4074
|
+
const instance2 = new NodeInstance(graphNode, anchor);
|
|
4075
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
4076
|
+
dom.appendChild(parentDom, anchor);
|
|
4077
|
+
wireHeadBehavior(ctx, graphNode, instance2);
|
|
4078
|
+
return instance2;
|
|
4079
|
+
}
|
|
3118
4080
|
const tag = resolveTag(graphNode.type);
|
|
3119
4081
|
const el = dom.createElement(tag);
|
|
3120
4082
|
applyNodeProps(ctx, graphNode, el);
|
|
@@ -3179,6 +4141,17 @@ function buttonUpdate(dom, el) {
|
|
|
3179
4141
|
}
|
|
3180
4142
|
};
|
|
3181
4143
|
}
|
|
4144
|
+
function linkUpdate(dom, el) {
|
|
4145
|
+
return (propKey, value) => {
|
|
4146
|
+
if (propKey === "label") {
|
|
4147
|
+
dom.setTextContent(el, String(value ?? ""));
|
|
4148
|
+
} else if (propKey === "href") {
|
|
4149
|
+
dom.setAttribute(el, "href", String(value ?? ""));
|
|
4150
|
+
} else {
|
|
4151
|
+
applyProp(dom, el, propKey, value);
|
|
4152
|
+
}
|
|
4153
|
+
};
|
|
4154
|
+
}
|
|
3182
4155
|
function applyNodeProps(ctx, graphNode, el) {
|
|
3183
4156
|
const props = graphNode.props;
|
|
3184
4157
|
for (const key in props) {
|
|
@@ -3199,32 +4172,42 @@ function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
|
|
|
3199
4172
|
instance.trackCleanup(unsub);
|
|
3200
4173
|
}
|
|
3201
4174
|
}
|
|
3202
|
-
function wireReactiveList(ctx, graphNode, instance, el) {
|
|
4175
|
+
function wireReactiveList(ctx, graphNode, instance, el, runAppear = false) {
|
|
3203
4176
|
const plan = ctx.graph.getHandler(`__listplan__${graphNode.id}`);
|
|
3204
4177
|
const build = ctx.graph.getHandler(`__listbuild__${graphNode.id}`);
|
|
3205
4178
|
if (plan === void 0 && build === void 0) return;
|
|
4179
|
+
const controller = new TransitionController(ctx.dom, ctx.graph, (leaving) => {
|
|
4180
|
+
const parent = ctx.dom.parentNode(leaving.domNode);
|
|
4181
|
+
if (parent !== null) ctx.dom.removeChild(parent, leaving.domNode);
|
|
4182
|
+
leaving.dispose();
|
|
4183
|
+
forgetInstance(ctx, leaving);
|
|
4184
|
+
ctx.graph.detachNode(leaving.graphNode);
|
|
4185
|
+
});
|
|
4186
|
+
const hooks = controller.hooks();
|
|
4187
|
+
if (runAppear) controller.appear(instance.children);
|
|
3206
4188
|
for (const stateRef of graphNode.stateRefs) {
|
|
3207
4189
|
if (stateRef.propKey !== "items") continue;
|
|
3208
4190
|
const sig = ctx.graph.getHandler(`__signal__${stateRef.signalId}`);
|
|
3209
4191
|
if (sig === void 0 || typeof sig.subscribe !== "function") continue;
|
|
3210
4192
|
const unsub = sig.subscribe((value) => {
|
|
3211
4193
|
if (plan !== void 0) {
|
|
3212
|
-
reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value));
|
|
4194
|
+
reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value), hooks);
|
|
3213
4195
|
} else {
|
|
3214
|
-
reconcileReactiveList(ctx, graphNode, instance, el, build(value));
|
|
4196
|
+
reconcileReactiveList(ctx, graphNode, instance, el, build(value), hooks);
|
|
3215
4197
|
}
|
|
3216
4198
|
});
|
|
3217
4199
|
instance.trackCleanup(unsub);
|
|
3218
4200
|
}
|
|
3219
4201
|
}
|
|
3220
|
-
function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan) {
|
|
4202
|
+
function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan, hooks) {
|
|
3221
4203
|
const oldInstances = [...listInstance.children];
|
|
3222
4204
|
const result = reconcileChildrenByPlan(
|
|
3223
4205
|
ctx,
|
|
3224
4206
|
listEl,
|
|
3225
4207
|
oldInstances,
|
|
3226
4208
|
plan,
|
|
3227
|
-
(node, parent) => mountNode(ctx, node, parent)
|
|
4209
|
+
(node, parent) => mountNode(ctx, node, parent),
|
|
4210
|
+
hooks
|
|
3228
4211
|
);
|
|
3229
4212
|
listInstance.children.length = 0;
|
|
3230
4213
|
for (const inst of result.instances) listInstance.children.push(inst);
|
|
@@ -3239,14 +4222,15 @@ function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan)
|
|
|
3239
4222
|
for (const child of [...listNode.children]) listNode.removeChild(child);
|
|
3240
4223
|
for (const inst of result.instances) listNode.appendChild(inst.graphNode);
|
|
3241
4224
|
}
|
|
3242
|
-
function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes) {
|
|
4225
|
+
function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes, hooks) {
|
|
3243
4226
|
const oldInstances = [...listInstance.children];
|
|
3244
4227
|
const result = reconcileChildren(
|
|
3245
4228
|
ctx,
|
|
3246
4229
|
listEl,
|
|
3247
4230
|
oldInstances,
|
|
3248
4231
|
newNodes,
|
|
3249
|
-
(node, parent) => mountNode(ctx, node, parent)
|
|
4232
|
+
(node, parent) => mountNode(ctx, node, parent),
|
|
4233
|
+
hooks
|
|
3250
4234
|
);
|
|
3251
4235
|
listInstance.children.length = 0;
|
|
3252
4236
|
for (const inst of result.instances) listInstance.children.push(inst);
|
|
@@ -3282,11 +4266,14 @@ function wireOverlayBehavior(ctx, graphNode, instance, target) {
|
|
|
3282
4266
|
const onOpenChange = (isOpen) => {
|
|
3283
4267
|
if (isOpen) {
|
|
3284
4268
|
if (desc.restoreFocus) saved = saveFocus(dom);
|
|
3285
|
-
if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
|
|
3286
4269
|
if (desc.modal) {
|
|
3287
4270
|
active.push(trapFocus(dom, target));
|
|
3288
4271
|
active.push(containFocus(dom, target));
|
|
3289
4272
|
}
|
|
4273
|
+
if (desc.menu) {
|
|
4274
|
+
active.push(rovingMenu(dom, target));
|
|
4275
|
+
}
|
|
4276
|
+
if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
|
|
3290
4277
|
if (desc.closeOnEscape && desc.onClose !== void 0) {
|
|
3291
4278
|
active.push(onEscape(dom, target, desc.onClose));
|
|
3292
4279
|
}
|
|
@@ -3399,7 +4386,12 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
3399
4386
|
case "link": {
|
|
3400
4387
|
const instance = new NodeInstance(graphNode, domNode);
|
|
3401
4388
|
ctx.instances.set(graphNode.id, instance);
|
|
3402
|
-
if (graphNode.type === "link")
|
|
4389
|
+
if (graphNode.type === "link") {
|
|
4390
|
+
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
4391
|
+
if (graphNode.stateRefs.length !== 0) {
|
|
4392
|
+
wireSignalBindings(ctx, graphNode, instance, linkUpdate(dom, domNode));
|
|
4393
|
+
}
|
|
4394
|
+
}
|
|
3403
4395
|
return instance;
|
|
3404
4396
|
}
|
|
3405
4397
|
case "reactive-list":
|
|
@@ -3429,6 +4421,12 @@ function hydrateNode(ctx, graphNode, domNode, path) {
|
|
|
3429
4421
|
wireOverlayBehavior(ctx, graphNode, instance, target);
|
|
3430
4422
|
return instance;
|
|
3431
4423
|
}
|
|
4424
|
+
case "head": {
|
|
4425
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
4426
|
+
ctx.instances.set(graphNode.id, instance);
|
|
4427
|
+
wireHeadBehavior(ctx, graphNode, instance);
|
|
4428
|
+
return instance;
|
|
4429
|
+
}
|
|
3432
4430
|
default: {
|
|
3433
4431
|
const instance = new NodeInstance(graphNode, domNode);
|
|
3434
4432
|
ctx.instances.set(graphNode.id, instance);
|
|
@@ -3675,7 +4673,8 @@ function analyzeGraph(graph) {
|
|
|
3675
4673
|
const isConditional = node.type === "conditional";
|
|
3676
4674
|
const isPortal = node.type === "portal";
|
|
3677
4675
|
const isComponent = node.type === "component";
|
|
3678
|
-
const
|
|
4676
|
+
const isHead = node.type === "head";
|
|
4677
|
+
const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal && !isComponent && !isHead;
|
|
3679
4678
|
const isStaticSubtree = isStatic && allChildrenStatic;
|
|
3680
4679
|
nodes.set(node.id, {
|
|
3681
4680
|
isStatic,
|
|
@@ -4021,16 +5020,38 @@ function mountRouter(router, options) {
|
|
|
4021
5020
|
} else {
|
|
4022
5021
|
outlet = container;
|
|
4023
5022
|
}
|
|
5023
|
+
const routeTransition = options.transition !== void 0 ? resolveTransition(options.transition) : void 0;
|
|
5024
|
+
const txDom = routeTransition !== void 0 ? new BrowserDOMAdapter() : void 0;
|
|
4024
5025
|
let active = null;
|
|
5026
|
+
const pendingLeaves = /* @__PURE__ */ new Map();
|
|
4025
5027
|
let firstRender = hydrateMode;
|
|
5028
|
+
const teardownRoute = (route) => {
|
|
5029
|
+
route.registry.run();
|
|
5030
|
+
route.mounted.unmount();
|
|
5031
|
+
if (route.host !== null && txDom !== void 0) {
|
|
5032
|
+
const parent = txDom.parentNode(route.host);
|
|
5033
|
+
if (parent !== null) txDom.removeChild(parent, route.host);
|
|
5034
|
+
}
|
|
5035
|
+
};
|
|
4026
5036
|
const disposeActive = () => {
|
|
4027
5037
|
if (active === null) return;
|
|
4028
|
-
active
|
|
4029
|
-
active.mounted.unmount();
|
|
5038
|
+
teardownRoute(active);
|
|
4030
5039
|
active = null;
|
|
4031
5040
|
};
|
|
4032
|
-
const
|
|
4033
|
-
|
|
5041
|
+
const wrapOutletChildren = (dom) => {
|
|
5042
|
+
const host2 = dom.createElement("div");
|
|
5043
|
+
dom.setAttribute(host2, "data-streetui-route", "");
|
|
5044
|
+
const moved = [];
|
|
5045
|
+
let child = dom.firstChild(outlet);
|
|
5046
|
+
while (child !== null) {
|
|
5047
|
+
moved.push(child);
|
|
5048
|
+
child = dom.nextSibling(child);
|
|
5049
|
+
}
|
|
5050
|
+
for (const n of moved) dom.appendChild(host2, n);
|
|
5051
|
+
dom.appendChild(outlet, host2);
|
|
5052
|
+
return host2;
|
|
5053
|
+
};
|
|
5054
|
+
const buildRoute = (match, target, hydrate) => {
|
|
4034
5055
|
const registry = new CleanupRegistry();
|
|
4035
5056
|
const ctx = {
|
|
4036
5057
|
path: match.path,
|
|
@@ -4043,9 +5064,53 @@ function mountRouter(router, options) {
|
|
|
4043
5064
|
routeApp.page("route", (page) => match.route.builder(page, ctx));
|
|
4044
5065
|
const runtime = createRuntime({ renderer });
|
|
4045
5066
|
const routeCompiled = compile(routeApp);
|
|
4046
|
-
const mounted =
|
|
5067
|
+
const mounted = hydrate ? runtime.hydrate(routeCompiled, target) : runtime.mount(routeCompiled, target);
|
|
5068
|
+
return { registry, mounted, host: null, enterRun: null };
|
|
5069
|
+
};
|
|
5070
|
+
const renderRoute = (match) => {
|
|
5071
|
+
if (routeTransition === void 0 || txDom === void 0) {
|
|
5072
|
+
disposeActive();
|
|
5073
|
+
active = buildRoute(match, outlet, firstRender);
|
|
5074
|
+
firstRender = false;
|
|
5075
|
+
return;
|
|
5076
|
+
}
|
|
5077
|
+
if (active === null) {
|
|
5078
|
+
if (firstRender) {
|
|
5079
|
+
active = buildRoute(match, outlet, true);
|
|
5080
|
+
active.host = wrapOutletChildren(txDom);
|
|
5081
|
+
} else {
|
|
5082
|
+
const host2 = txDom.createElement("div");
|
|
5083
|
+
txDom.setAttribute(host2, "data-streetui-route", "");
|
|
5084
|
+
txDom.appendChild(outlet, host2);
|
|
5085
|
+
active = buildRoute(match, host2, false);
|
|
5086
|
+
active.host = host2;
|
|
5087
|
+
}
|
|
5088
|
+
firstRender = false;
|
|
5089
|
+
return;
|
|
5090
|
+
}
|
|
4047
5091
|
firstRender = false;
|
|
4048
|
-
|
|
5092
|
+
const leaving = active;
|
|
5093
|
+
leaving.enterRun?.cancel();
|
|
5094
|
+
leaving.enterRun = null;
|
|
5095
|
+
const leaveHost = leaving.host;
|
|
5096
|
+
const enterHost = txDom.createElement("div");
|
|
5097
|
+
txDom.setAttribute(enterHost, "data-streetui-route", "");
|
|
5098
|
+
txDom.appendChild(outlet, enterHost);
|
|
5099
|
+
const next = buildRoute(match, enterHost, false);
|
|
5100
|
+
next.host = enterHost;
|
|
5101
|
+
next.enterRun = runElementTransition(txDom, enterHost, routeTransition, "enter", () => {
|
|
5102
|
+
next.enterRun = null;
|
|
5103
|
+
});
|
|
5104
|
+
active = next;
|
|
5105
|
+
if (leaveHost !== null) {
|
|
5106
|
+
const run = runElementTransition(txDom, leaveHost, routeTransition, "leave", () => {
|
|
5107
|
+
pendingLeaves.delete(leaving);
|
|
5108
|
+
teardownRoute(leaving);
|
|
5109
|
+
});
|
|
5110
|
+
pendingLeaves.set(leaving, run);
|
|
5111
|
+
} else {
|
|
5112
|
+
teardownRoute(leaving);
|
|
5113
|
+
}
|
|
4049
5114
|
};
|
|
4050
5115
|
renderRoute(router.currentRoute.peek());
|
|
4051
5116
|
const stopRouteSub = router.currentRoute.subscribe((match) => renderRoute(match));
|
|
@@ -4073,6 +5138,11 @@ function mountRouter(router, options) {
|
|
|
4073
5138
|
unmount() {
|
|
4074
5139
|
if (interceptLinks) container.removeEventListener("click", onClick);
|
|
4075
5140
|
stopRouteSub();
|
|
5141
|
+
for (const [route, run] of pendingLeaves) {
|
|
5142
|
+
run.cancel();
|
|
5143
|
+
teardownRoute(route);
|
|
5144
|
+
}
|
|
5145
|
+
pendingLeaves.clear();
|
|
4076
5146
|
disposeActive();
|
|
4077
5147
|
shellMounted?.unmount();
|
|
4078
5148
|
router.destroy();
|
|
@@ -4397,6 +5467,40 @@ function inspectComponents(graph) {
|
|
|
4397
5467
|
});
|
|
4398
5468
|
return out;
|
|
4399
5469
|
}
|
|
5470
|
+
function inspectInteractions(graph) {
|
|
5471
|
+
const overlays = [];
|
|
5472
|
+
const transitions = [];
|
|
5473
|
+
graph.walk((node, depth) => {
|
|
5474
|
+
const overlayFn = graph.getHandler(`__overlay__${node.id}`);
|
|
5475
|
+
if (overlayFn !== void 0) {
|
|
5476
|
+
const d = overlayFn();
|
|
5477
|
+
overlays.push({
|
|
5478
|
+
id: node.id,
|
|
5479
|
+
key: node.key,
|
|
5480
|
+
open: d.open.peek(),
|
|
5481
|
+
modal: d.modal,
|
|
5482
|
+
takesFocus: d.takesFocus,
|
|
5483
|
+
menu: d.menu,
|
|
5484
|
+
closeOnEscape: d.closeOnEscape,
|
|
5485
|
+
restoreFocus: d.restoreFocus,
|
|
5486
|
+
depth
|
|
5487
|
+
});
|
|
5488
|
+
}
|
|
5489
|
+
const transitionFn = graph.getHandler(`__transition__${node.id}`);
|
|
5490
|
+
if (transitionFn !== void 0) {
|
|
5491
|
+
const t = transitionFn();
|
|
5492
|
+
transitions.push({
|
|
5493
|
+
id: node.id,
|
|
5494
|
+
key: node.key,
|
|
5495
|
+
nodeType: node.type,
|
|
5496
|
+
duration: t.duration,
|
|
5497
|
+
appear: t.appear,
|
|
5498
|
+
depth
|
|
5499
|
+
});
|
|
5500
|
+
}
|
|
5501
|
+
});
|
|
5502
|
+
return { overlays, transitions };
|
|
5503
|
+
}
|
|
4400
5504
|
|
|
4401
5505
|
// ../devtools/src/application.ts
|
|
4402
5506
|
function collectPerf(node, distinctSignals, acc) {
|
|
@@ -4632,11 +5736,22 @@ function capture(compiled, sources, options) {
|
|
|
4632
5736
|
snapshot: app.perf,
|
|
4633
5737
|
diagnostics: diagnosePerformance(compiled, options.perfThresholds)
|
|
4634
5738
|
};
|
|
5739
|
+
const components = inspectComponents(compiled.graph);
|
|
5740
|
+
const interactions = inspectInteractions(compiled.graph);
|
|
5741
|
+
const diagnostics = {
|
|
5742
|
+
errors: app.diagnostics.errors,
|
|
5743
|
+
warnings: app.diagnostics.warnings,
|
|
5744
|
+
messages: app.diagnostics.messages
|
|
5745
|
+
};
|
|
4635
5746
|
const snapshot = {
|
|
4636
5747
|
application,
|
|
4637
5748
|
graph: app.graph,
|
|
4638
5749
|
signals,
|
|
4639
5750
|
performance,
|
|
5751
|
+
components,
|
|
5752
|
+
overlays: interactions.overlays,
|
|
5753
|
+
transitions: interactions.transitions,
|
|
5754
|
+
diagnostics,
|
|
4640
5755
|
...sources.router !== void 0 ? { router: inspectRouter(sources.router) } : {},
|
|
4641
5756
|
...sources.resources !== void 0 ? { resources: mapInspect(sources.resources, (r) => inspectResource(r)) } : {},
|
|
4642
5757
|
...sources.forms !== void 0 ? { forms: mapInspect(sources.forms, (f) => inspectForm(f)) } : {},
|
|
@@ -4677,6 +5792,25 @@ function formatSnapshot(s) {
|
|
|
4677
5792
|
for (const [label, sig] of Object.entries(s.signals.live)) {
|
|
4678
5793
|
lines.push(` ${label} [${sig.kind}] = ${format(sig.value)} \xB7 observers ${sig.observerCount ?? "?"}`);
|
|
4679
5794
|
}
|
|
5795
|
+
if (s.components.length > 0) {
|
|
5796
|
+
lines.push(`Components: ${s.components.length}`);
|
|
5797
|
+
for (const c of s.components) {
|
|
5798
|
+
lines.push(` ${" ".repeat(c.depth)}${c.name}${c.key !== void 0 ? ` (#${c.key})` : ""} \xB7 children ${c.childCount}`);
|
|
5799
|
+
}
|
|
5800
|
+
}
|
|
5801
|
+
if (s.overlays.length > 0) {
|
|
5802
|
+
lines.push(`Overlays: ${s.overlays.length}`);
|
|
5803
|
+
for (const o of s.overlays) {
|
|
5804
|
+
const kind = o.modal ? "modal" : o.menu ? "menu" : o.takesFocus ? "focusable" : "non-modal";
|
|
5805
|
+
lines.push(` ${o.key ?? o.id} [${kind}] ${o.open ? "open" : "closed"}`);
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
if (s.transitions.length > 0) {
|
|
5809
|
+
lines.push(`Transitions: ${s.transitions.length}`);
|
|
5810
|
+
for (const t of s.transitions) {
|
|
5811
|
+
lines.push(` ${t.key ?? t.id} on <${t.nodeType}> \xB7 ${t.duration}ms${t.appear ? " \xB7 appear" : ""}`);
|
|
5812
|
+
}
|
|
5813
|
+
}
|
|
4680
5814
|
if (s.router !== void 0) {
|
|
4681
5815
|
lines.push(`Router: ${s.router.path} (${s.router.pattern})${s.router.isFallback ? " [fallback]" : ""}`);
|
|
4682
5816
|
}
|
|
@@ -4710,6 +5844,12 @@ function formatSnapshot(s) {
|
|
|
4710
5844
|
for (const d of s.performance.diagnostics) {
|
|
4711
5845
|
lines.push(` ${d.code}: ${d.message}`);
|
|
4712
5846
|
}
|
|
5847
|
+
lines.push(
|
|
5848
|
+
`Diagnostics: ${s.diagnostics.errors} error(s), ${s.diagnostics.warnings} warning(s)`
|
|
5849
|
+
);
|
|
5850
|
+
for (const m of s.diagnostics.messages) {
|
|
5851
|
+
lines.push(` ${m}`);
|
|
5852
|
+
}
|
|
4713
5853
|
return lines.join("\n");
|
|
4714
5854
|
}
|
|
4715
5855
|
function format(value) {
|
|
@@ -4725,6 +5865,176 @@ function format(value) {
|
|
|
4725
5865
|
return String(value);
|
|
4726
5866
|
}
|
|
4727
5867
|
|
|
5868
|
+
// ../devtools/src/view.ts
|
|
5869
|
+
function escapeHtml(value) {
|
|
5870
|
+
const s = typeof value === "string" ? value : stringifyValue(value);
|
|
5871
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5872
|
+
}
|
|
5873
|
+
function stringifyValue(value) {
|
|
5874
|
+
if (value === null || value === void 0) return String(value);
|
|
5875
|
+
if (typeof value === "object") {
|
|
5876
|
+
try {
|
|
5877
|
+
return JSON.stringify(value);
|
|
5878
|
+
} catch {
|
|
5879
|
+
return "[object]";
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
return String(value);
|
|
5883
|
+
}
|
|
5884
|
+
function section(title, count, body) {
|
|
5885
|
+
const badge = count === void 0 ? "" : ` <span class="st-count">${count}</span>`;
|
|
5886
|
+
return `<section class="st-panel"><h2>${escapeHtml(title)}${badge}</h2>${body}</section>`;
|
|
5887
|
+
}
|
|
5888
|
+
function ul(items) {
|
|
5889
|
+
if (items.length === 0) return '<p class="st-empty">(none)</p>';
|
|
5890
|
+
return `<ul>${items.map((i) => `<li>${i}</li>`).join("")}</ul>`;
|
|
5891
|
+
}
|
|
5892
|
+
function renderDevToolsHTML(s) {
|
|
5893
|
+
const app = s.application;
|
|
5894
|
+
const parts = [];
|
|
5895
|
+
parts.push(
|
|
5896
|
+
section(
|
|
5897
|
+
"Application",
|
|
5898
|
+
void 0,
|
|
5899
|
+
`<p>${escapeHtml(app.identity.name)} <span class="st-dim">v${escapeHtml(app.identity.version)}</span></p><p class="st-dim">${app.nodeCount} nodes \xB7 depth ${app.maxDepth} \xB7 ${app.pages.length} page(s) \xB7 ${app.signalCount} signals \xB7 ${app.eventHandlers} handlers \xB7 ${app.stateBindings} bindings</p>`
|
|
5900
|
+
)
|
|
5901
|
+
);
|
|
5902
|
+
parts.push(
|
|
5903
|
+
section(
|
|
5904
|
+
"Components",
|
|
5905
|
+
s.components.length,
|
|
5906
|
+
ul(
|
|
5907
|
+
s.components.map(
|
|
5908
|
+
(c) => `<span class="st-depth" style="--d:${c.depth}"></span><code>${escapeHtml(c.name)}</code>` + (c.key !== void 0 ? ` <span class="st-key">#${escapeHtml(c.key)}</span>` : "") + ` <span class="st-dim">${c.childCount} child(ren)</span>`
|
|
5909
|
+
)
|
|
5910
|
+
)
|
|
5911
|
+
)
|
|
5912
|
+
);
|
|
5913
|
+
parts.push(section("Graph", void 0, `<pre class="st-tree">${escapeHtml(renderNodeTree(s.graph))}</pre>`));
|
|
5914
|
+
parts.push(
|
|
5915
|
+
section(
|
|
5916
|
+
"Signals",
|
|
5917
|
+
s.signals.boundSignalIds.length,
|
|
5918
|
+
`<p class="st-dim">${s.signals.boundSignalIds.length} bound in graph \xB7 effects shown as observer counts (no global effect registry)</p>` + ul(
|
|
5919
|
+
Object.entries(s.signals.live).map(
|
|
5920
|
+
([label, sig]) => `<code>${escapeHtml(label)}</code> <span class="st-key">[${escapeHtml(sig.kind)}]</span> = <code>${escapeHtml(sig.value)}</code> <span class="st-dim">observers ${escapeHtml(sig.observerCount ?? "?")}</span>`
|
|
5921
|
+
)
|
|
5922
|
+
)
|
|
5923
|
+
)
|
|
5924
|
+
);
|
|
5925
|
+
if (s.router !== void 0) {
|
|
5926
|
+
parts.push(
|
|
5927
|
+
section(
|
|
5928
|
+
"Router",
|
|
5929
|
+
void 0,
|
|
5930
|
+
`<p><code>${escapeHtml(s.router.path)}</code> <span class="st-dim">(${escapeHtml(s.router.pattern)})${s.router.isFallback ? " \xB7 fallback" : ""}</span></p>`
|
|
5931
|
+
)
|
|
5932
|
+
);
|
|
5933
|
+
}
|
|
5934
|
+
if (s.resources !== void 0) {
|
|
5935
|
+
parts.push(
|
|
5936
|
+
section(
|
|
5937
|
+
"Resources",
|
|
5938
|
+
Object.keys(s.resources).length,
|
|
5939
|
+
ul(
|
|
5940
|
+
Object.entries(s.resources).map(
|
|
5941
|
+
([label, r]) => `<code>${escapeHtml(label)}</code>: ${escapeHtml(r.status)}` + (r.loading ? ' <span class="st-dim">(loading)</span>' : "") + (r.hasError ? ` <span class="st-err">!${escapeHtml(r.errorName)}</span>` : "")
|
|
5942
|
+
)
|
|
5943
|
+
)
|
|
5944
|
+
)
|
|
5945
|
+
);
|
|
5946
|
+
}
|
|
5947
|
+
parts.push(
|
|
5948
|
+
section(
|
|
5949
|
+
"Overlays",
|
|
5950
|
+
s.overlays.length,
|
|
5951
|
+
ul(
|
|
5952
|
+
s.overlays.map((o) => {
|
|
5953
|
+
const kind = o.modal ? "modal" : o.menu ? "menu" : o.takesFocus ? "focusable" : "non-modal";
|
|
5954
|
+
return `<code>${escapeHtml(o.key ?? o.id)}</code> <span class="st-key">[${kind}]</span> <span class="st-dim">${o.open ? "open" : "closed"}${o.closeOnEscape ? " \xB7 esc" : ""}${o.restoreFocus ? " \xB7 restore" : ""}</span>`;
|
|
5955
|
+
})
|
|
5956
|
+
)
|
|
5957
|
+
)
|
|
5958
|
+
);
|
|
5959
|
+
parts.push(
|
|
5960
|
+
section(
|
|
5961
|
+
"Transitions",
|
|
5962
|
+
s.transitions.length,
|
|
5963
|
+
ul(
|
|
5964
|
+
s.transitions.map(
|
|
5965
|
+
(t) => `<code>${escapeHtml(t.key ?? t.id)}</code> on <code><${escapeHtml(t.nodeType)}></code> <span class="st-dim">${t.duration}ms${t.appear ? " \xB7 appear" : ""}</span>`
|
|
5966
|
+
)
|
|
5967
|
+
)
|
|
5968
|
+
)
|
|
5969
|
+
);
|
|
5970
|
+
if (s.forms !== void 0) {
|
|
5971
|
+
parts.push(
|
|
5972
|
+
section(
|
|
5973
|
+
"Forms",
|
|
5974
|
+
Object.keys(s.forms).length,
|
|
5975
|
+
ul(
|
|
5976
|
+
Object.entries(s.forms).map(
|
|
5977
|
+
([label, f]) => `<code>${escapeHtml(label)}</code>: ${f.valid ? "valid" : "invalid"} \xB7 ${escapeHtml(f.status)} \xB7 ${f.fields.length} field(s)`
|
|
5978
|
+
)
|
|
5979
|
+
)
|
|
5980
|
+
)
|
|
5981
|
+
);
|
|
5982
|
+
}
|
|
5983
|
+
if (s.i18n !== void 0) {
|
|
5984
|
+
const missing = s.i18n.missingKeys;
|
|
5985
|
+
parts.push(
|
|
5986
|
+
section(
|
|
5987
|
+
"i18n",
|
|
5988
|
+
void 0,
|
|
5989
|
+
`<p><code>${escapeHtml(s.i18n.locale)}</code> of [${s.i18n.locales.map(escapeHtml).join(", ")}]${missing !== void 0 ? ` <span class="st-dim">\xB7 missing ${missing.length}</span>` : ""}</p>`
|
|
5990
|
+
)
|
|
5991
|
+
);
|
|
5992
|
+
}
|
|
5993
|
+
parts.push(
|
|
5994
|
+
section(
|
|
5995
|
+
"Performance",
|
|
5996
|
+
s.performance.diagnostics.length,
|
|
5997
|
+
`<p class="st-dim">Structural counts, not runtime timings \u2014 not a production profiler.</p>` + ul(s.performance.diagnostics.map((d) => `<code>${escapeHtml(d.code)}</code>: ${escapeHtml(d.message)}`))
|
|
5998
|
+
)
|
|
5999
|
+
);
|
|
6000
|
+
parts.push(
|
|
6001
|
+
section(
|
|
6002
|
+
"Diagnostics",
|
|
6003
|
+
s.diagnostics.errors + s.diagnostics.warnings,
|
|
6004
|
+
`<p class="st-dim">${s.diagnostics.errors} error(s) \xB7 ${s.diagnostics.warnings} warning(s)</p>` + ul(s.diagnostics.messages.map((m) => escapeHtml(m)))
|
|
6005
|
+
)
|
|
6006
|
+
);
|
|
6007
|
+
const title = `StreetUI DevTools \u2014 ${escapeHtml(app.identity.name)}`;
|
|
6008
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${title}</title><style>${DEVTOOLS_CSS}</style></head><body class="st-devtools"><header class="st-header"><h1>${title} <span class="st-dim">v${escapeHtml(app.identity.version)}</span></h1></header><main>${parts.join("")}</main></body></html>`;
|
|
6009
|
+
}
|
|
6010
|
+
function renderNodeTree(node) {
|
|
6011
|
+
const lines = [];
|
|
6012
|
+
const walk = (n) => {
|
|
6013
|
+
const indent = " ".repeat(n.depth);
|
|
6014
|
+
const key = n.key !== void 0 ? ` #${n.key}` : "";
|
|
6015
|
+
lines.push(`${indent}<${n.type}${key}> ${n.id}`);
|
|
6016
|
+
for (const c of n.children) walk(c);
|
|
6017
|
+
};
|
|
6018
|
+
walk(node);
|
|
6019
|
+
return lines.join("\n");
|
|
6020
|
+
}
|
|
6021
|
+
var DEVTOOLS_CSS = [
|
|
6022
|
+
".st-devtools{font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;margin:0;color:#e6e6e6;background:#1e1e28}",
|
|
6023
|
+
".st-header{padding:12px 16px;border-bottom:1px solid #333;background:#15151c}",
|
|
6024
|
+
".st-header h1{font-size:14px;margin:0}",
|
|
6025
|
+
"main{padding:8px 16px}",
|
|
6026
|
+
".st-panel{margin:12px 0;border:1px solid #2c2c38;border-radius:6px;overflow:hidden}",
|
|
6027
|
+
".st-panel h2{font-size:12px;text-transform:uppercase;letter-spacing:.04em;margin:0;padding:6px 10px;background:#23232e}",
|
|
6028
|
+
".st-panel ul{list-style:none;margin:0;padding:6px 10px}",
|
|
6029
|
+
".st-panel li{padding:1px 0}",
|
|
6030
|
+
".st-panel p{margin:6px 10px}",
|
|
6031
|
+
".st-count{background:#3a3a4a;border-radius:10px;padding:0 7px;font-size:11px;float:right}",
|
|
6032
|
+
".st-dim{color:#8a8a9a}.st-key{color:#7db4ff}.st-err{color:#ff8a8a}.st-empty{color:#6a6a7a}",
|
|
6033
|
+
".st-tree{margin:6px 10px;white-space:pre;overflow:auto;color:#c8c8d4}",
|
|
6034
|
+
".st-depth{display:inline-block}.st-depth{width:calc(var(--d,0)*12px)}",
|
|
6035
|
+
"code{color:#d7d7e0}"
|
|
6036
|
+
].join("");
|
|
6037
|
+
|
|
4728
6038
|
// src/config.ts
|
|
4729
6039
|
function defineConfig(config) {
|
|
4730
6040
|
return config;
|
|
@@ -4747,6 +6057,8 @@ export {
|
|
|
4747
6057
|
FOCUSABLE_SELECTOR,
|
|
4748
6058
|
FormBuilderImpl,
|
|
4749
6059
|
GraphNode,
|
|
6060
|
+
HeadManager,
|
|
6061
|
+
HttpError,
|
|
4750
6062
|
Lifecycle,
|
|
4751
6063
|
ListBuilderImpl,
|
|
4752
6064
|
NodeInstance,
|
|
@@ -4770,6 +6082,7 @@ export {
|
|
|
4770
6082
|
StreetFrameworkError,
|
|
4771
6083
|
StreetRenderHandle,
|
|
4772
6084
|
StreetRendererImpl,
|
|
6085
|
+
TransitionController,
|
|
4773
6086
|
VERSION,
|
|
4774
6087
|
a11yIds,
|
|
4775
6088
|
applyNodeProps,
|
|
@@ -4784,8 +6097,11 @@ export {
|
|
|
4784
6097
|
consoleDiagnosticSink,
|
|
4785
6098
|
consoleHydrationDiagnosticSink,
|
|
4786
6099
|
containFocus,
|
|
6100
|
+
createAnnouncer,
|
|
4787
6101
|
createApplication,
|
|
6102
|
+
createAuthSession,
|
|
4788
6103
|
createBrowserHistory,
|
|
6104
|
+
createClient,
|
|
4789
6105
|
createContext,
|
|
4790
6106
|
createDevTools,
|
|
4791
6107
|
createForm,
|
|
@@ -4801,10 +6117,12 @@ export {
|
|
|
4801
6117
|
createStreetEvent,
|
|
4802
6118
|
defineConfig,
|
|
4803
6119
|
derived,
|
|
6120
|
+
describeError,
|
|
4804
6121
|
diagnosePerformance,
|
|
4805
6122
|
effect,
|
|
4806
6123
|
email,
|
|
4807
6124
|
environment,
|
|
6125
|
+
escapeHtml,
|
|
4808
6126
|
escapeHtmlAttr,
|
|
4809
6127
|
escapeHtmlText,
|
|
4810
6128
|
flushSync,
|
|
@@ -4818,6 +6136,7 @@ export {
|
|
|
4818
6136
|
generateApplicationId,
|
|
4819
6137
|
generateNodeId,
|
|
4820
6138
|
getFocusable,
|
|
6139
|
+
getResolvedTransition,
|
|
4821
6140
|
globalEventBus,
|
|
4822
6141
|
headingUpdate,
|
|
4823
6142
|
hydrateGraph,
|
|
@@ -4828,12 +6147,16 @@ export {
|
|
|
4828
6147
|
inspectForm,
|
|
4829
6148
|
inspectGraph,
|
|
4830
6149
|
inspectI18n,
|
|
6150
|
+
inspectInteractions,
|
|
4831
6151
|
inspectResource,
|
|
4832
6152
|
inspectRouter,
|
|
4833
6153
|
inspectSignal,
|
|
4834
6154
|
interpolate,
|
|
4835
6155
|
isBatching,
|
|
4836
6156
|
isComponentDefinition,
|
|
6157
|
+
isHeadContribution,
|
|
6158
|
+
isTransitionConfig,
|
|
6159
|
+
linkUpdate,
|
|
4837
6160
|
matchPattern,
|
|
4838
6161
|
matchRoutes,
|
|
4839
6162
|
maxLength,
|
|
@@ -4841,6 +6164,7 @@ export {
|
|
|
4841
6164
|
mountGraph,
|
|
4842
6165
|
mountNode,
|
|
4843
6166
|
mountRouter,
|
|
6167
|
+
mutation,
|
|
4844
6168
|
nextId,
|
|
4845
6169
|
nodeIdPrefix,
|
|
4846
6170
|
nodeTypeStats,
|
|
@@ -4857,14 +6181,21 @@ export {
|
|
|
4857
6181
|
readState,
|
|
4858
6182
|
reconcileChildren,
|
|
4859
6183
|
reconcileChildrenByPlan,
|
|
6184
|
+
renderDevToolsHTML,
|
|
6185
|
+
renderHead,
|
|
4860
6186
|
renderToString,
|
|
4861
6187
|
reportDiagnostic,
|
|
6188
|
+
reportError,
|
|
4862
6189
|
required,
|
|
4863
6190
|
resetIdCounter,
|
|
6191
|
+
resolveHead,
|
|
4864
6192
|
resolveTag,
|
|
6193
|
+
resolveTransition,
|
|
4865
6194
|
resource,
|
|
4866
6195
|
restoreFocus,
|
|
4867
6196
|
routerOutlet,
|
|
6197
|
+
rovingMenu,
|
|
6198
|
+
runElementTransition,
|
|
4868
6199
|
runValidators,
|
|
4869
6200
|
saveFocus,
|
|
4870
6201
|
scheduleImmediate,
|
|
@@ -4885,6 +6216,7 @@ export {
|
|
|
4885
6216
|
validateGraph,
|
|
4886
6217
|
wireComponentBehavior,
|
|
4887
6218
|
wireEvents,
|
|
6219
|
+
wireHeadBehavior,
|
|
4888
6220
|
wireOverlayBehavior,
|
|
4889
6221
|
wireReactiveList,
|
|
4890
6222
|
wireSignalBindings
|