@connextar/house 0.1.1 → 0.2.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.
Files changed (112) hide show
  1. package/README.md +205 -28
  2. package/dist/changelog/changelog.d.ts +60 -0
  3. package/dist/changelog/changelog.d.ts.map +1 -0
  4. package/dist/changelog/changelog.js +61 -0
  5. package/dist/changelog/changelog.js.map +1 -0
  6. package/dist/changelog/index.d.ts +2 -0
  7. package/dist/changelog/index.d.ts.map +1 -0
  8. package/dist/changelog/index.js +2 -0
  9. package/dist/changelog/index.js.map +1 -0
  10. package/dist/editor/block-handle.d.ts +15 -0
  11. package/dist/editor/block-handle.d.ts.map +1 -0
  12. package/dist/editor/block-handle.js +106 -0
  13. package/dist/editor/block-handle.js.map +1 -0
  14. package/dist/editor/blocks.d.ts +35 -0
  15. package/dist/editor/blocks.d.ts.map +1 -0
  16. package/dist/editor/blocks.js +114 -0
  17. package/dist/editor/blocks.js.map +1 -0
  18. package/dist/editor/bubble-toolbar.d.ts +11 -0
  19. package/dist/editor/bubble-toolbar.d.ts.map +1 -0
  20. package/dist/editor/bubble-toolbar.js +87 -0
  21. package/dist/editor/bubble-toolbar.js.map +1 -0
  22. package/dist/editor/content-index.d.ts +2 -0
  23. package/dist/editor/content-index.d.ts.map +1 -0
  24. package/dist/editor/content-index.js +2 -0
  25. package/dist/editor/content-index.js.map +1 -0
  26. package/dist/editor/content.d.ts +30 -0
  27. package/dist/editor/content.d.ts.map +1 -0
  28. package/dist/editor/content.js +256 -0
  29. package/dist/editor/content.js.map +1 -0
  30. package/dist/editor/editor.d.ts +52 -0
  31. package/dist/editor/editor.d.ts.map +1 -0
  32. package/dist/editor/editor.js +160 -0
  33. package/dist/editor/editor.js.map +1 -0
  34. package/dist/editor/emoji-index.d.ts +2 -0
  35. package/dist/editor/emoji-index.d.ts.map +1 -0
  36. package/dist/editor/emoji-index.js +2 -0
  37. package/dist/editor/emoji-index.js.map +1 -0
  38. package/dist/editor/emoji-picker.d.ts +16 -0
  39. package/dist/editor/emoji-picker.d.ts.map +1 -0
  40. package/dist/editor/emoji-picker.js +37 -0
  41. package/dist/editor/emoji-picker.js.map +1 -0
  42. package/dist/editor/emoji.d.ts +18 -0
  43. package/dist/editor/emoji.d.ts.map +1 -0
  44. package/dist/editor/emoji.js +418 -0
  45. package/dist/editor/emoji.js.map +1 -0
  46. package/dist/editor/index.d.ts +5 -0
  47. package/dist/editor/index.d.ts.map +1 -0
  48. package/dist/editor/index.js +6 -0
  49. package/dist/editor/index.js.map +1 -0
  50. package/dist/editor/suggestion-popup.d.ts +17 -0
  51. package/dist/editor/suggestion-popup.d.ts.map +1 -0
  52. package/dist/editor/suggestion-popup.js +57 -0
  53. package/dist/editor/suggestion-popup.js.map +1 -0
  54. package/dist/editor/suggestions.d.ts +93 -0
  55. package/dist/editor/suggestions.d.ts.map +1 -0
  56. package/dist/editor/suggestions.js +179 -0
  57. package/dist/editor/suggestions.js.map +1 -0
  58. package/dist/editor/ui.d.ts +54 -0
  59. package/dist/editor/ui.d.ts.map +1 -0
  60. package/dist/editor/ui.js +58 -0
  61. package/dist/editor/ui.js.map +1 -0
  62. package/dist/house.css +267 -0
  63. package/dist/internal/cx.d.ts +3 -0
  64. package/dist/internal/cx.d.ts.map +1 -0
  65. package/dist/internal/cx.js +5 -0
  66. package/dist/internal/cx.js.map +1 -0
  67. package/dist/internal/next-link.d.ts +23 -0
  68. package/dist/internal/next-link.d.ts.map +1 -0
  69. package/dist/internal/next-link.js +3 -0
  70. package/dist/internal/next-link.js.map +1 -0
  71. package/dist/markdown/guide.d.ts +98 -0
  72. package/dist/markdown/guide.d.ts.map +1 -0
  73. package/dist/markdown/guide.js +328 -0
  74. package/dist/markdown/guide.js.map +1 -0
  75. package/dist/markdown/index.d.ts +4 -0
  76. package/dist/markdown/index.d.ts.map +1 -0
  77. package/dist/markdown/index.js +4 -0
  78. package/dist/markdown/index.js.map +1 -0
  79. package/dist/markdown/table-view.d.ts +28 -0
  80. package/dist/markdown/table-view.d.ts.map +1 -0
  81. package/dist/markdown/table-view.js +25 -0
  82. package/dist/markdown/table-view.js.map +1 -0
  83. package/dist/markdown/table.d.ts +35 -0
  84. package/dist/markdown/table.d.ts.map +1 -0
  85. package/dist/markdown/table.js +79 -0
  86. package/dist/markdown/table.js.map +1 -0
  87. package/dist/release/changelog.d.ts +72 -0
  88. package/dist/release/changelog.d.ts.map +1 -0
  89. package/dist/release/changelog.js +92 -0
  90. package/dist/release/changelog.js.map +1 -0
  91. package/dist/release/index.d.ts +3 -0
  92. package/dist/release/index.d.ts.map +1 -0
  93. package/dist/release/index.js +3 -0
  94. package/dist/release/index.js.map +1 -0
  95. package/dist/release/version.d.ts +33 -0
  96. package/dist/release/version.d.ts.map +1 -0
  97. package/dist/release/version.js +57 -0
  98. package/dist/release/version.js.map +1 -0
  99. package/dist/trail/index.d.ts +3 -0
  100. package/dist/trail/index.d.ts.map +1 -0
  101. package/dist/trail/index.js +3 -0
  102. package/dist/trail/index.js.map +1 -0
  103. package/dist/trail/rules.d.ts +44 -0
  104. package/dist/trail/rules.d.ts.map +1 -0
  105. package/dist/trail/rules.js +74 -0
  106. package/dist/trail/rules.js.map +1 -0
  107. package/dist/trail/trail.d.ts +49 -0
  108. package/dist/trail/trail.d.ts.map +1 -0
  109. package/dist/trail/trail.js +188 -0
  110. package/dist/trail/trail.js.map +1 -0
  111. package/docs/ReleaseProcess.md +315 -0
  112. package/package.json +75 -11
@@ -0,0 +1,92 @@
1
+ import { isReleaseVersion, releaseKind } from "./version.js";
2
+ export const CHANGE_TYPES = ["new", "improved", "changed", "fixed"];
3
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
4
+ /**
5
+ * Everything wrong with a release history, as sentences — an empty list means
6
+ * it is sound. An app's guard test is one assertion:
7
+ *
8
+ * ```ts
9
+ * expect(releaseHistoryProblems(CHANGELOG, { pkg, lock })).toEqual([]);
10
+ * ```
11
+ *
12
+ * A version lives in three places — package.json, its lockfile and the newest
13
+ * changelog release — and a bump that touches one and forgets another is the
14
+ * drift this exists to catch. Returning problems rather than throwing on the
15
+ * first means a broken history is reported whole.
16
+ */
17
+ export function releaseHistoryProblems(changelog, manifest) {
18
+ const problems = [];
19
+ const newest = changelog[0];
20
+ if (!newest)
21
+ return ["the changelog has no releases"];
22
+ if (manifest) {
23
+ const declared = manifest.pkg.version;
24
+ if (declared !== newest.version) {
25
+ problems.push(`package.json declares ${declared ?? "no version"} but the newest release is ${newest.version}`);
26
+ }
27
+ if (manifest.lock) {
28
+ if (manifest.lock.version !== declared) {
29
+ problems.push(`package-lock.json declares ${manifest.lock.version ?? "no version"}, package.json ${declared}`);
30
+ }
31
+ const root = manifest.lock.packages?.[""]?.version;
32
+ if (manifest.lock.packages && root !== declared) {
33
+ problems.push(`package-lock.json's root package declares ${root ?? "no version"}, package.json ${declared}`);
34
+ }
35
+ }
36
+ }
37
+ changelog.forEach((release, i) => {
38
+ const label = release.version || `release #${i + 1}`;
39
+ if (!isReleaseVersion(release.version)) {
40
+ problems.push(`${label} is not MAJOR.MINOR.PATCH`);
41
+ }
42
+ if (!ISO_DATE.test(release.date) || Number.isNaN(new Date(`${release.date}T00:00:00Z`).getTime())) {
43
+ problems.push(`${label} has date "${release.date}", not YYYY-MM-DD`);
44
+ }
45
+ const older = changelog[i + 1];
46
+ if (older && isReleaseVersion(release.version) && isReleaseVersion(older.version)) {
47
+ if (releaseKind(older.version, release.version) === null) {
48
+ problems.push(`${older.version} → ${release.version} is not one step (newest first, no skipped numbers)`);
49
+ }
50
+ if (ISO_DATE.test(release.date) && ISO_DATE.test(older.date) && release.date < older.date) {
51
+ problems.push(`${release.version} is dated ${release.date}, before ${older.version} (${older.date})`);
52
+ }
53
+ }
54
+ if (!release.title.trim())
55
+ problems.push(`${label} has no title`);
56
+ if (!release.summary.trim())
57
+ problems.push(`${label} has no summary`);
58
+ if (release.sections.length === 0)
59
+ problems.push(`${label} has no sections`);
60
+ const headings = release.sections.map((section) => section.heading);
61
+ if (new Set(headings).size !== headings.length)
62
+ problems.push(`${label} repeats a section heading`);
63
+ for (const section of release.sections) {
64
+ if (!section.heading.trim())
65
+ problems.push(`${label} has a section with no heading`);
66
+ if (section.changes.length === 0)
67
+ problems.push(`${label} · ${section.heading} has no changes`);
68
+ for (const change of section.changes) {
69
+ if (!CHANGE_TYPES.includes(change.type)) {
70
+ problems.push(`${label} · ${section.heading} has a change of unknown type "${String(change.type)}"`);
71
+ }
72
+ if (!change.title.trim())
73
+ problems.push(`${label} · ${section.heading} has a change with no title`);
74
+ if (!change.text.trim())
75
+ problems.push(`${label} · ${section.heading} · "${change.title}" has no text`);
76
+ }
77
+ }
78
+ });
79
+ return problems;
80
+ }
81
+ /** A fragment id without dots, so it never needs escaping as a selector: `v0-25-0`. */
82
+ export function releaseAnchor(version) {
83
+ return `v${version.replaceAll(".", "-")}`;
84
+ }
85
+ /** Format an ISO date as "29 August 2026" (or the locale's equivalent), in UTC so it never shifts a day. */
86
+ export function formatReleaseDate(iso, locale = "en-GB") {
87
+ return new Intl.DateTimeFormat(locale, { day: "numeric", month: "long", year: "numeric", timeZone: "UTC" }).format(new Date(`${iso}T00:00:00Z`));
88
+ }
89
+ export function countChanges(release) {
90
+ return release.sections.reduce((n, section) => n + section.changes.length, 0);
91
+ }
92
+ //# sourceMappingURL=changelog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/release/changelog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { compareVersions, isReleaseVersion, nextVersion, parseVersion, releaseKind, type ReleaseKind, type Version, } from "./version.js";
2
+ export { CHANGE_TYPES, countChanges, formatReleaseDate, releaseAnchor, releaseHistoryProblems, type ChangeType, type ChangelogChange, type ChangelogRelease, type ChangelogSection, type ReleaseManifest, } from "./changelog.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,OAAO,GACb,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { compareVersions, isReleaseVersion, nextVersion, parseVersion, releaseKind, } from "./version.js";
2
+ export { CHANGE_TYPES, countChanges, formatReleaseDate, releaseAnchor, releaseHistoryProblems, } from "./changelog.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,WAAW,GAGZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,sBAAsB,GAMvB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Semantic versions for an app's releases.
3
+ *
4
+ * The rules for choosing a version live in docs/ReleaseProcess.md; this file
5
+ * only knows the arithmetic, so a changelog page and a release guard test read
6
+ * versions the same way.
7
+ */
8
+ export type ReleaseKind = "major" | "minor" | "patch";
9
+ export interface Version {
10
+ major: number;
11
+ minor: number;
12
+ patch: number;
13
+ }
14
+ export declare function parseVersion(value: string): Version;
15
+ export declare function isReleaseVersion(value: string): boolean;
16
+ /** Negative when `a` is older than `b`, positive when newer, 0 when equal. */
17
+ export declare function compareVersions(a: string, b: string): number;
18
+ /**
19
+ * What kind of release `next` is after `previous` — or null when it is not
20
+ * exactly one step on (a skipped number, a reset that isn't to zero, or going
21
+ * backwards). One step at a time is what makes every version findable.
22
+ */
23
+ export declare function releaseKind(previous: string, next: string): ReleaseKind | null;
24
+ /**
25
+ * The version after `current` for a change of the given class.
26
+ *
27
+ * Before 1.0.0 a MAJOR-class change bumps MINOR — semver treats 0.x as a period
28
+ * when anything may change, and leaving it is the owner's call, never a side
29
+ * effect of a change set. The release must then carry a `changed` entry saying
30
+ * what people need to do.
31
+ */
32
+ export declare function nextVersion(current: string, change: ReleaseKind): string;
33
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/release/version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAKD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAInD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAO9E;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAKxE"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Semantic versions for an app's releases.
3
+ *
4
+ * The rules for choosing a version live in docs/ReleaseProcess.md; this file
5
+ * only knows the arithmetic, so a changelog page and a release guard test read
6
+ * versions the same way.
7
+ */
8
+ /** MAJOR.MINOR.PATCH with no leading zeros, no "v" and no pre-release suffix. */
9
+ const RELEASE_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
10
+ export function parseVersion(value) {
11
+ const match = RELEASE_VERSION.exec(value);
12
+ if (!match)
13
+ throw new Error(`Not a release version (MAJOR.MINOR.PATCH): "${value}"`);
14
+ return { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]) };
15
+ }
16
+ export function isReleaseVersion(value) {
17
+ return RELEASE_VERSION.test(value);
18
+ }
19
+ /** Negative when `a` is older than `b`, positive when newer, 0 when equal. */
20
+ export function compareVersions(a, b) {
21
+ const x = parseVersion(a);
22
+ const y = parseVersion(b);
23
+ return x.major - y.major || x.minor - y.minor || x.patch - y.patch;
24
+ }
25
+ /**
26
+ * What kind of release `next` is after `previous` — or null when it is not
27
+ * exactly one step on (a skipped number, a reset that isn't to zero, or going
28
+ * backwards). One step at a time is what makes every version findable.
29
+ */
30
+ export function releaseKind(previous, next) {
31
+ const p = parseVersion(previous);
32
+ const n = parseVersion(next);
33
+ if (n.major === p.major + 1 && n.minor === 0 && n.patch === 0)
34
+ return "major";
35
+ if (n.major === p.major && n.minor === p.minor + 1 && n.patch === 0)
36
+ return "minor";
37
+ if (n.major === p.major && n.minor === p.minor && n.patch === p.patch + 1)
38
+ return "patch";
39
+ return null;
40
+ }
41
+ /**
42
+ * The version after `current` for a change of the given class.
43
+ *
44
+ * Before 1.0.0 a MAJOR-class change bumps MINOR — semver treats 0.x as a period
45
+ * when anything may change, and leaving it is the owner's call, never a side
46
+ * effect of a change set. The release must then carry a `changed` entry saying
47
+ * what people need to do.
48
+ */
49
+ export function nextVersion(current, change) {
50
+ const { major, minor, patch } = parseVersion(current);
51
+ if (change === "patch")
52
+ return `${major}.${minor}.${patch + 1}`;
53
+ if (change === "minor" || major === 0)
54
+ return `${major}.${minor + 1}.0`;
55
+ return `${major + 1}.0.0`;
56
+ }
57
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/release/version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,iFAAiF;AACjF,MAAM,eAAe,GAAG,4CAA4C,CAAC;AAErE,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,KAAK,GAAG,CAAC,CAAC;IACrF,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS;IAClD,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAC1B,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,IAAY;IACxD,MAAM,CAAC,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACjC,MAAM,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9E,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACpF,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IAC1F,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,MAAmB;IAC9D,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACtD,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,GAAG,KAAK,IAAI,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;IAChE,IAAI,MAAM,KAAK,OAAO,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC;IACxE,OAAO,GAAG,KAAK,GAAG,CAAC,MAAM,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { codeFor, isCrumb, nextTrail, TRAIL_LIMIT, type Crumb } from "./rules.js";
2
+ export { carryTrail, ScrollToHash, TrailBar, TrailHere, TrailProvider, TrailRail, UnlessTrail, useActiveTrail, useTrail, } from "./trail.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/trail/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EACL,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,aAAa,EACb,SAAS,EACT,WAAW,EACX,cAAc,EACd,QAAQ,GACT,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { codeFor, isCrumb, nextTrail, TRAIL_LIMIT } from "./rules.js";
2
+ export { carryTrail, ScrollToHash, TrailBar, TrailHere, TrailProvider, TrailRail, UnlessTrail, useActiveTrail, useTrail, } from "./trail.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/trail/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAc,MAAM,YAAY,CAAC;AAClF,OAAO,EACL,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,aAAa,EACb,SAAS,EACT,WAAW,EACX,cAAc,EACd,QAAQ,GACT,MAAM,YAAY,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The navigation trail: the path someone took to where they are, kept so they
3
+ * can step back to any point of it in one click.
4
+ *
5
+ * Every page that sits in a hierarchy says where it is — its own chain, root
6
+ * first (Home › Amara › Progress). Most of the time that chain *is* the trail.
7
+ * The exception is following a reference: from a note, `@Amara` opens Amara's
8
+ * page, and the way back to the note must survive the jump even though the note
9
+ * is nowhere in Amara's hierarchy. So a reference link "carries" the trail
10
+ * across, and the destination's chain is added to the end of it.
11
+ *
12
+ * Pure, so the rules are tested without a browser; `./trail.tsx` keeps the
13
+ * state and draws it.
14
+ */
15
+ export interface Crumb {
16
+ /** Identity across pages: "note:abc", "learner:xyz", "section:progress". */
17
+ key: string;
18
+ label: string;
19
+ href: string;
20
+ /** A short code for the pill's chip — "NO", "LE" — derived when absent. */
21
+ code?: string;
22
+ }
23
+ /** The most crumbs kept: past this, the oldest fall off the front. */
24
+ export declare const TRAIL_LIMIT = 8;
25
+ /**
26
+ * The next trail, from the one before, the page's own chain, and whether the
27
+ * page was reached by following a reference.
28
+ *
29
+ * - **Carried** (a reference was followed): the page's chain is appended to the
30
+ * trail — minus anything already in it. Following a reference back to
31
+ * something already on the trail returns to that point instead of looping.
32
+ * - **Not carried**: the page's chain *is* the trail — unless the page is
33
+ * already on the trail (a pill was clicked, or Back pressed), in which case
34
+ * the trail is cut back to it.
35
+ */
36
+ export declare function nextTrail(previous: readonly Crumb[], chain: readonly Crumb[], carried: boolean): Crumb[];
37
+ /** "Maths — Schedule" → "MS"; "Notes" → "NO"; always two characters or fewer, upper-case. */
38
+ export declare function codeFor(crumb: Crumb): string;
39
+ /**
40
+ * Only a same-origin path is a crumb. The trail is kept in `sessionStorage`,
41
+ * which is ours — but it is still input, and a crumb is a link.
42
+ */
43
+ export declare function isCrumb(value: unknown): value is Crumb;
44
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/trail/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,KAAK;IACpB,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,sEAAsE;AACtE,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,SAAS,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,GAAG,KAAK,EAAE,CAcxG;AAED,6FAA6F;AAC7F,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAU5C;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAYtD"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The navigation trail: the path someone took to where they are, kept so they
3
+ * can step back to any point of it in one click.
4
+ *
5
+ * Every page that sits in a hierarchy says where it is — its own chain, root
6
+ * first (Home › Amara › Progress). Most of the time that chain *is* the trail.
7
+ * The exception is following a reference: from a note, `@Amara` opens Amara's
8
+ * page, and the way back to the note must survive the jump even though the note
9
+ * is nowhere in Amara's hierarchy. So a reference link "carries" the trail
10
+ * across, and the destination's chain is added to the end of it.
11
+ *
12
+ * Pure, so the rules are tested without a browser; `./trail.tsx` keeps the
13
+ * state and draws it.
14
+ */
15
+ /** The most crumbs kept: past this, the oldest fall off the front. */
16
+ export const TRAIL_LIMIT = 8;
17
+ /**
18
+ * The next trail, from the one before, the page's own chain, and whether the
19
+ * page was reached by following a reference.
20
+ *
21
+ * - **Carried** (a reference was followed): the page's chain is appended to the
22
+ * trail — minus anything already in it. Following a reference back to
23
+ * something already on the trail returns to that point instead of looping.
24
+ * - **Not carried**: the page's chain *is* the trail — unless the page is
25
+ * already on the trail (a pill was clicked, or Back pressed), in which case
26
+ * the trail is cut back to it.
27
+ */
28
+ export function nextTrail(previous, chain, carried) {
29
+ const here = chain.at(-1);
30
+ if (!here)
31
+ return [];
32
+ const at = previous.findIndex((crumb) => crumb.key === here.key);
33
+ if (at !== -1) {
34
+ // Back to a point already on the trail: keep what led there, refresh the
35
+ // crumb itself (its label may have changed — a renamed note).
36
+ return [...previous.slice(0, at), here].slice(-TRAIL_LIMIT);
37
+ }
38
+ if (!carried)
39
+ return chain.slice(-TRAIL_LIMIT);
40
+ const seen = new Set(previous.map((crumb) => crumb.key));
41
+ return [...previous, ...chain.filter((crumb) => !seen.has(crumb.key))].slice(-TRAIL_LIMIT);
42
+ }
43
+ /** "Maths — Schedule" → "MS"; "Notes" → "NO"; always two characters or fewer, upper-case. */
44
+ export function codeFor(crumb) {
45
+ if (crumb.code)
46
+ return crumb.code.slice(0, 3).toUpperCase();
47
+ const words = crumb.label
48
+ .replace(/[^\p{L}\p{N}\s]/gu, " ")
49
+ .split(/\s+/)
50
+ .filter(Boolean);
51
+ const [first, second] = words;
52
+ if (!first)
53
+ return "·";
54
+ if (!second)
55
+ return first.slice(0, 2).toUpperCase();
56
+ return ((first[0] ?? "") + (second[0] ?? "")).toUpperCase();
57
+ }
58
+ /**
59
+ * Only a same-origin path is a crumb. The trail is kept in `sessionStorage`,
60
+ * which is ours — but it is still input, and a crumb is a link.
61
+ */
62
+ export function isCrumb(value) {
63
+ if (typeof value !== "object" || value === null)
64
+ return false;
65
+ const { key, label, href, code } = value;
66
+ return (typeof key === "string" &&
67
+ typeof label === "string" &&
68
+ typeof href === "string" &&
69
+ href.startsWith("/") &&
70
+ !href.startsWith("//") &&
71
+ !href.startsWith("/\\") &&
72
+ (code === undefined || typeof code === "string"));
73
+ }
74
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/trail/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAWH,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAE7B;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS,CAAC,QAA0B,EAAE,KAAuB,EAAE,OAAgB;IAC7F,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC;IAEjE,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QACd,yEAAyE;QACzE,8DAA8D;QAC9D,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;IAE/C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,QAAQ,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;AAC7F,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,OAAO,CAAC,KAAY;IAClC,IAAI,KAAK,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK;SACtB,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC;SACjC,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,GAAG,CAAC;IACvB,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACpD,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AAC9D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAgC,CAAC;IACpE,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QACpB,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QACtB,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;QACvB,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CACjD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { type ReactNode } from "react";
2
+ import { type Crumb } from "./rules.js";
3
+ /**
4
+ * `scope` is what the trail is made in — typically `${user.id}:${view}`.
5
+ * Mount it once in the layout that frames the pages which register crumbs.
6
+ */
7
+ export declare function TrailProvider({ scope, children }: {
8
+ scope?: string;
9
+ children: ReactNode;
10
+ }): import("react").JSX.Element;
11
+ /**
12
+ * Say where this page sits: its chain, root first, ending with itself. Pass null
13
+ * while the page doesn't know yet (nothing selected). Re-registers when the
14
+ * chain's keys, labels or links change — not on every render.
15
+ */
16
+ export declare function useTrail(chain: Crumb[] | null): void;
17
+ /** For a server page: say where it sits in the trail. Renders nothing. */
18
+ export declare function TrailHere({ chain }: {
19
+ chain: Crumb[];
20
+ }): null;
21
+ /** Before following a reference: the next page keeps the trail that led to it. */
22
+ export declare function carryTrail(): void;
23
+ /** The trail for the page on screen, or null when there is nothing to go back to. */
24
+ export declare function useActiveTrail(): Crumb[] | null;
25
+ /**
26
+ * Renders its children only while no trail is showing. A page's static
27
+ * breadcrumb and the trail answer the same question — how do I get back? — so
28
+ * when the trail is up, it answers alone.
29
+ */
30
+ export declare function UnlessTrail({ children }: {
31
+ children: ReactNode;
32
+ }): import("react").JSX.Element | null;
33
+ /** Stacked vertical pills pinned to the left of the page (wide screens). */
34
+ export declare function TrailRail({ className, top }: {
35
+ className?: string;
36
+ top?: string;
37
+ }): import("react").JSX.Element | null;
38
+ /** The same trail as a pinned bar across the top, with Back (narrow screens). */
39
+ export declare function TrailBar({ className, top }: {
40
+ className?: string;
41
+ top?: string;
42
+ }): import("react").JSX.Element | null;
43
+ /**
44
+ * Scroll to the element the URL's `#hash` names, once it exists. The router
45
+ * tries on arrival, but a streamed page can arrive before the element does — a
46
+ * crumb pointing at `/home#item-…` otherwise lands at the top of the page.
47
+ */
48
+ export declare function ScrollToHash(): null;
49
+ //# sourceMappingURL=trail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trail.d.ts","sourceRoot":"","sources":["../../src/trail/trail.tsx"],"names":[],"mappings":"AAKA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,EAA+B,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAyDrE;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,KAAU,EAAE,QAAQ,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAwC9F;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,GAAG,IAAI,CAapD;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,KAAK,EAAE,CAAA;CAAE,QAGtD;AAED,kFAAkF;AAClF,wBAAgB,UAAU,IAAI,IAAI,CAMjC;AAED,qFAAqF;AACrF,wBAAgB,cAAc,IAAI,KAAK,EAAE,GAAG,IAAI,CAK/C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,sCAEhE;AAED,4EAA4E;AAC5E,wBAAgB,SAAS,CAAC,EAAE,SAAS,EAAE,GAAY,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,sCAoE1F;AAED,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAY,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,sCA2DzF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,SAW3B"}
@@ -0,0 +1,188 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ChevronLeft } from "lucide-react";
4
+ import { Link } from "../internal/next-link.js";
5
+ import { usePathname } from "next/navigation.js";
6
+ import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useRef, useState, } from "react";
7
+ import { cx } from "../internal/cx.js";
8
+ import { codeFor, isCrumb, nextTrail } from "./rules.js";
9
+ /**
10
+ * The navigation trail on screen — see `./rules.ts` for the rules.
11
+ *
12
+ * Pages say where they are with `useTrail(chain)` (or `<TrailHere>` from a
13
+ * server page); the provider works out the trail and keeps it in
14
+ * `sessionStorage`, so it belongs to one tab and survives a reload but not a
15
+ * new window. A reference link calls `carryTrail()` before navigating — or
16
+ * carries `data-trail-carry` — which is what lets the destination keep the way
17
+ * back to the page it came from.
18
+ *
19
+ * Two renderings of the same crumbs: stacked vertical pills pinned to the left
20
+ * of the page on wide screens, and a pinned bar with Back across the top on
21
+ * narrow ones. Both appear only when there is somewhere to go back to — two
22
+ * crumbs or more — and only on the page that registered them.
23
+ *
24
+ * A trail belongs to one scope. Switching who is signed in, or which view of
25
+ * the product they are in, starts a new one: the old crumbs may lead to pages
26
+ * the new scope cannot see, so the stored trail carries the `scope` it was made
27
+ * in, and one from another scope is dropped.
28
+ *
29
+ * Storage keys need no per-app prefix: sessionStorage is already per origin.
30
+ */
31
+ const STORE = "house:trail";
32
+ const CARRY = "house:trail:carry";
33
+ /** A carry older than this was a navigation that never landed on a trail page. */
34
+ const CARRY_MS = 15_000;
35
+ // Two contexts: the trail itself, which changes, and the function that sets it,
36
+ // which changes only with the scope. Pages depend only on the second —
37
+ // depending on a context that changes whenever they register would register
38
+ // again, forever.
39
+ const TrailStateContext = createContext(null);
40
+ const TrailRegisterContext = createContext(null);
41
+ function readStored(scope) {
42
+ try {
43
+ const parsed = JSON.parse(sessionStorage.getItem(STORE) ?? "null");
44
+ const stored = typeof parsed === "object" && parsed !== null ? parsed : {};
45
+ const previous = stored.scope === scope && Array.isArray(stored.crumbs) ? stored.crumbs.filter(isCrumb) : [];
46
+ const stamp = Number(sessionStorage.getItem(CARRY) ?? 0);
47
+ return { previous, carried: stamp > 0 && Date.now() - stamp < CARRY_MS };
48
+ }
49
+ catch {
50
+ return { previous: [], carried: false };
51
+ }
52
+ }
53
+ /**
54
+ * `scope` is what the trail is made in — typically `${user.id}:${view}`.
55
+ * Mount it once in the layout that frames the pages which register crumbs.
56
+ */
57
+ export function TrailProvider({ scope = "", children }) {
58
+ const [state, setState] = useState(null);
59
+ const [shownScope, setShownScope] = useState(scope);
60
+ // Switched scope on this page: the trail on screen goes with the old one.
61
+ if (shownScope !== scope) {
62
+ setShownScope(scope);
63
+ setState(null);
64
+ }
65
+ const register = useCallback((path, chain) => {
66
+ const { previous, carried } = readStored(scope);
67
+ const crumbs = nextTrail(previous, chain, carried);
68
+ try {
69
+ sessionStorage.setItem(STORE, JSON.stringify({ scope, crumbs }));
70
+ sessionStorage.removeItem(CARRY);
71
+ }
72
+ catch {
73
+ // Storage can be unavailable (private windows); the trail just won't persist.
74
+ }
75
+ setState({ path, crumbs });
76
+ }, [scope]);
77
+ // A link marked `data-trail-carry` — a reference in read-only text — carries
78
+ // the trail, as the editor's references do.
79
+ useEffect(() => {
80
+ const onClick = (event) => {
81
+ if (event.target instanceof Element && event.target.closest("a[data-trail-carry]"))
82
+ carryTrail();
83
+ };
84
+ document.addEventListener("click", onClick, true);
85
+ return () => document.removeEventListener("click", onClick, true);
86
+ }, []);
87
+ return (_jsx(TrailRegisterContext.Provider, { value: register, children: _jsx(TrailStateContext.Provider, { value: state, children: children }) }));
88
+ }
89
+ /**
90
+ * Say where this page sits: its chain, root first, ending with itself. Pass null
91
+ * while the page doesn't know yet (nothing selected). Re-registers when the
92
+ * chain's keys, labels or links change — not on every render.
93
+ */
94
+ export function useTrail(chain) {
95
+ const register = useContext(TrailRegisterContext);
96
+ const pathname = usePathname();
97
+ const signature = chain ? JSON.stringify(chain.map((c) => [c.key, c.label, c.href])) : "";
98
+ const latest = useRef(chain);
99
+ useLayoutEffect(() => {
100
+ latest.current = chain;
101
+ });
102
+ useEffect(() => {
103
+ const current = latest.current;
104
+ if (register && current && current.length > 0)
105
+ register(pathname, current);
106
+ }, [register, pathname, signature]);
107
+ }
108
+ /** For a server page: say where it sits in the trail. Renders nothing. */
109
+ export function TrailHere({ chain }) {
110
+ useTrail(chain);
111
+ return null;
112
+ }
113
+ /** Before following a reference: the next page keeps the trail that led to it. */
114
+ export function carryTrail() {
115
+ try {
116
+ sessionStorage.setItem(CARRY, String(Date.now()));
117
+ }
118
+ catch {
119
+ // No storage, no trail — the link still works.
120
+ }
121
+ }
122
+ /** The trail for the page on screen, or null when there is nothing to go back to. */
123
+ export function useActiveTrail() {
124
+ const state = useContext(TrailStateContext);
125
+ const pathname = usePathname();
126
+ if (!state || state.path !== pathname || state.crumbs.length < 2)
127
+ return null;
128
+ return state.crumbs;
129
+ }
130
+ /**
131
+ * Renders its children only while no trail is showing. A page's static
132
+ * breadcrumb and the trail answer the same question — how do I get back? — so
133
+ * when the trail is up, it answers alone.
134
+ */
135
+ export function UnlessTrail({ children }) {
136
+ return useActiveTrail() ? null : _jsx(_Fragment, { children: children });
137
+ }
138
+ /** Stacked vertical pills pinned to the left of the page (wide screens). */
139
+ export function TrailRail({ className, top = "4rem" }) {
140
+ const crumbs = useActiveTrail();
141
+ if (!crumbs)
142
+ return null;
143
+ const last = crumbs.length - 1;
144
+ return (_jsx("nav", { "aria-label": "Where you've been", className: cx("house-trail-rail sticky hidden shrink-0 self-start border-r border-border lg:flex print:hidden", className), style: { top, height: `calc(100dvh - ${top})` }, children: _jsx("ol", { className: "flex", children: crumbs.map((crumb, index) => {
145
+ const current = index === last;
146
+ const body = (_jsxs(_Fragment, { children: [_jsx("span", { className: cx("rounded px-1 py-0.5 font-mono text-[9px] leading-none font-bold", current ? "bg-primary text-primary-foreground" : "bg-muted-foreground/70 text-background"), "aria-hidden": true, children: codeFor(crumb) }), _jsx("span", { className: cx("max-h-[calc(100dvh-10rem)] overflow-hidden text-[12.5px] font-semibold text-ellipsis whitespace-nowrap [writing-mode:vertical-rl] [transform:rotate(180deg)]", current ? "text-foreground" : "text-muted-foreground"), children: crumb.label })] }));
147
+ const shared = "house-trail-pill flex h-full w-11 flex-col items-center gap-2.5 border-r border-border py-3.5";
148
+ // Each step deeper is a shade further from the card; the stylesheet
149
+ // turns the step into a tint, so HSL-triplet themes can map the colours.
150
+ const style = { "--house-trail-step": Math.min(index, 5) };
151
+ return (_jsx("li", { className: "flex", children: current ? (_jsx("span", { "aria-current": "page", title: crumb.label, className: shared, style: style, "data-current": "", children: body })) : (_jsx(Link, { href: crumb.href, title: `Back to ${crumb.label}`, className: cx(shared, "transition-colors hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-inset"), style: style, children: body })) }, crumb.key));
152
+ }) }) }));
153
+ }
154
+ /** The same trail as a pinned bar across the top, with Back (narrow screens). */
155
+ export function TrailBar({ className, top = "4rem" }) {
156
+ const crumbs = useActiveTrail();
157
+ const listRef = useRef(null);
158
+ // The end of the trail is where you are; keep it in view on a narrow bar.
159
+ useEffect(() => {
160
+ const list = listRef.current;
161
+ if (list)
162
+ list.scrollLeft = list.scrollWidth;
163
+ }, [crumbs]);
164
+ const previous = crumbs?.at(-2);
165
+ if (!crumbs || !previous)
166
+ return null;
167
+ const last = crumbs.length - 1;
168
+ return (_jsxs("nav", { "aria-label": "Where you've been", "data-trail-bar": "", className: cx("sticky z-20 -mx-4 -mt-6 mb-4 flex items-center gap-1 border-b border-border bg-background/95 px-2 py-1.5 backdrop-blur sm:-mx-6 lg:hidden print:hidden", className), style: { top }, children: [_jsxs(Link, { href: previous.href, className: "flex min-h-8 shrink-0 items-center gap-0.5 rounded-md px-1.5 text-sm font-medium text-primary hover:bg-accent", "aria-label": `Back to ${previous.label}`, children: [_jsx(ChevronLeft, { className: "size-4", "aria-hidden": true }), "Back"] }), _jsx("ol", { ref: listRef, className: "flex min-w-0 flex-1 items-center overflow-x-auto text-sm whitespace-nowrap", children: crumbs.map((crumb, index) => (_jsxs("li", { className: "flex items-center", children: [index > 0 ? (_jsx("span", { className: "px-1 text-muted-foreground/60", "aria-hidden": true, children: "/" })) : null, index === last ? (_jsx("span", { "aria-current": "page", className: "max-w-[12rem] truncate font-medium", children: crumb.label })) : (_jsx(Link, { href: crumb.href, className: "inline-flex min-h-8 max-w-[9rem] items-center truncate rounded px-1 text-muted-foreground hover:text-foreground", children: crumb.label }))] }, crumb.key))) })] }));
169
+ }
170
+ /**
171
+ * Scroll to the element the URL's `#hash` names, once it exists. The router
172
+ * tries on arrival, but a streamed page can arrive before the element does — a
173
+ * crumb pointing at `/home#item-…` otherwise lands at the top of the page.
174
+ */
175
+ export function ScrollToHash() {
176
+ useEffect(() => {
177
+ const go = () => {
178
+ const id = decodeURIComponent(window.location.hash.slice(1));
179
+ if (id)
180
+ document.getElementById(id)?.scrollIntoView({ block: "start" });
181
+ };
182
+ go();
183
+ window.addEventListener("hashchange", go);
184
+ return () => window.removeEventListener("hashchange", go);
185
+ }, []);
186
+ return null;
187
+ }
188
+ //# sourceMappingURL=trail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trail.js","sourceRoot":"","sources":["../../src/trail/trail.tsx"],"names":[],"mappings":"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"}