@mvarble/mesearch-ui 0.0.0-stage → 0.1.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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -2
  3. package/dist/ambient.d.ts +2 -0
  4. package/dist/components/Article.svelte +226 -0
  5. package/dist/components/Article.svelte.d.ts +16 -0
  6. package/dist/components/ArticleHeader.svelte +134 -0
  7. package/dist/components/ArticleHeader.svelte.d.ts +19 -0
  8. package/dist/components/Chips.svelte +22 -0
  9. package/dist/components/Chips.svelte.d.ts +8 -0
  10. package/dist/components/Graph.svelte +727 -0
  11. package/dist/components/Graph.svelte.d.ts +16 -0
  12. package/dist/components/GraphPanel.svelte +328 -0
  13. package/dist/components/GraphPanel.svelte.d.ts +17 -0
  14. package/dist/components/Icon.svelte +67 -0
  15. package/dist/components/Icon.svelte.d.ts +8 -0
  16. package/dist/components/Index.svelte +283 -0
  17. package/dist/components/Index.svelte.d.ts +9 -0
  18. package/dist/components/Pager.svelte +71 -0
  19. package/dist/components/Pager.svelte.d.ts +12 -0
  20. package/dist/components/Palette.svelte +333 -0
  21. package/dist/components/Palette.svelte.d.ts +9 -0
  22. package/dist/components/Parts.svelte +62 -0
  23. package/dist/components/Parts.svelte.d.ts +11 -0
  24. package/dist/components/Proof.svelte +39 -0
  25. package/dist/components/Proof.svelte.d.ts +8 -0
  26. package/dist/components/Rail.svelte +186 -0
  27. package/dist/components/Rail.svelte.d.ts +13 -0
  28. package/dist/components/References.svelte +94 -0
  29. package/dist/components/References.svelte.d.ts +8 -0
  30. package/dist/components/SequenceTrack.svelte +186 -0
  31. package/dist/components/SequenceTrack.svelte.d.ts +11 -0
  32. package/dist/components/Shell.svelte +82 -0
  33. package/dist/components/Shell.svelte.d.ts +14 -0
  34. package/dist/components/Statement.svelte +105 -0
  35. package/dist/components/Statement.svelte.d.ts +11 -0
  36. package/dist/components/ThemeToggle.svelte +19 -0
  37. package/dist/components/ThemeToggle.svelte.d.ts +3 -0
  38. package/dist/components/Toc.svelte +145 -0
  39. package/dist/components/Toc.svelte.d.ts +8 -0
  40. package/dist/fonts.d.ts +4 -0
  41. package/dist/fonts.js +6 -0
  42. package/dist/fuzzy.d.ts +12 -0
  43. package/dist/fuzzy.js +57 -0
  44. package/dist/graph-layout.d.ts +35 -0
  45. package/dist/graph-layout.js +98 -0
  46. package/dist/index.d.ts +24 -0
  47. package/dist/index.js +30 -0
  48. package/dist/reading.svelte.d.ts +3 -0
  49. package/dist/reading.svelte.js +4 -0
  50. package/dist/server.d.ts +26 -0
  51. package/dist/server.js +115 -0
  52. package/dist/settings.d.ts +10 -0
  53. package/dist/settings.js +28 -0
  54. package/dist/styles/base.css +136 -0
  55. package/dist/styles/index.css +6 -0
  56. package/dist/styles/prose.css +203 -0
  57. package/dist/styles/tokens.css +111 -0
  58. package/dist/theme.svelte.d.ts +12 -0
  59. package/dist/theme.svelte.js +66 -0
  60. package/dist/types.d.ts +84 -0
  61. package/dist/types.js +3 -0
  62. package/package.json +66 -3
@@ -0,0 +1,9 @@
1
+ import type { SearchEntry } from '../types.js';
2
+ type $$ComponentProps = {
3
+ index: string | (() => Promise<SearchEntry[]>);
4
+ navigate?: (url: string) => unknown;
5
+ open?: boolean;
6
+ };
7
+ declare const Palette: import("svelte").Component<$$ComponentProps, {}, "open">;
8
+ type Palette = ReturnType<typeof Palette>;
9
+ export default Palette;
@@ -0,0 +1,62 @@
1
+ <script lang="ts">
2
+ import type { DocumentRef } from '../types.js';
3
+
4
+ // The parts of a sequence on its own page: numbered, titled, summarised.
5
+ type Part = DocumentRef & { summaryHtml?: string; label?: string };
6
+ let { parts }: { parts: Part[] } = $props();
7
+ </script>
8
+
9
+ {#if parts.length}
10
+ <ol class="parts">
11
+ {#each parts as part, i (part.key)}
12
+ <li class="kind-{part.kind}">
13
+ <span class="number">{part.label ?? i + 1}</span>
14
+ <div>
15
+ <a href={part.url}>{@html part.titleHtml}</a>
16
+ {#if part.summaryHtml}<p>{@html part.summaryHtml}</p>{/if}
17
+ </div>
18
+ </li>
19
+ {/each}
20
+ </ol>
21
+ {/if}
22
+
23
+ <style>
24
+ .parts {
25
+ list-style: none;
26
+ margin: 2.5rem 0 0;
27
+ padding: 0;
28
+ border-top: 1px solid var(--rule);
29
+ }
30
+
31
+ li {
32
+ display: grid;
33
+ grid-template-columns: 2.5rem 1fr;
34
+ gap: 0.75rem;
35
+ padding: 1.1rem 0;
36
+ border-bottom: 1px solid var(--rule);
37
+ }
38
+
39
+ .number {
40
+ font: 600 1.6rem / 1.1 var(--font-serif);
41
+ color: var(--kind);
42
+ font-variant-numeric: oldstyle-nums;
43
+ }
44
+
45
+ a {
46
+ font: 600 1.15rem / 1.3 var(--font-serif);
47
+ color: var(--ink);
48
+ text-decoration: none;
49
+ }
50
+
51
+ a:hover {
52
+ color: var(--kind);
53
+ }
54
+
55
+ p {
56
+ margin: 0.3rem 0 0;
57
+ color: var(--ink-soft);
58
+ font-size: 0.97rem;
59
+ line-height: 1.6;
60
+ text-align: left;
61
+ }
62
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { DocumentRef } from '../types.js';
2
+ type Part = DocumentRef & {
3
+ summaryHtml?: string;
4
+ label?: string;
5
+ };
6
+ type $$ComponentProps = {
7
+ parts: Part[];
8
+ };
9
+ declare const Parts: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type Parts = ReturnType<typeof Parts>;
11
+ export default Parts;
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ // A proof: "Proof." running into its first line, and a square at its end.
5
+ let { title = 'Proof', children }: { title?: string; children: Snippet } = $props();
6
+ </script>
7
+
8
+ <div class="proof">
9
+ <span class="heading">{title}.</span>{@render children()}
10
+ </div>
11
+
12
+ <style>
13
+ .proof {
14
+ margin: 1.25em 0 1.75em;
15
+ }
16
+
17
+ .heading {
18
+ margin-right: 0.3em;
19
+ font-style: italic;
20
+ color: var(--ink);
21
+ }
22
+
23
+ /* The first paragraph runs on from "Proof." */
24
+ .heading + :global(p) {
25
+ display: inline;
26
+ }
27
+
28
+ .proof > :global(:last-child) {
29
+ margin-bottom: 0;
30
+ }
31
+
32
+ .proof::after {
33
+ content: '∎';
34
+ display: block;
35
+ margin-top: -1.72em;
36
+ text-align: right;
37
+ color: var(--muted);
38
+ }
39
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ title?: string;
4
+ children: Snippet;
5
+ };
6
+ declare const Proof: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Proof = ReturnType<typeof Proof>;
8
+ export default Proof;
@@ -0,0 +1,186 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { RailLink } from '../types.js';
4
+ import Icon from './Icon.svelte';
5
+ import ThemeToggle from './ThemeToggle.svelte';
6
+
7
+ // Instead of a header across the top every page has this: a slim spine
8
+ // down the left edge on wide screens, and a floating dock at the bottom on
9
+ // narrow ones. The site's monogram, search, a few links, and the theme ---
10
+ // and on a document, how far along the reader is.
11
+ let {
12
+ title,
13
+ home,
14
+ links = [],
15
+ onsearch,
16
+ progress = null,
17
+ logo,
18
+ }: {
19
+ // The site's name; its initials make the monogram.
20
+ title: string;
21
+ home: string;
22
+ links?: RailLink[];
23
+ // Left out, there is no search button.
24
+ onsearch?: () => void;
25
+ progress?: number | null;
26
+ // Drawn in place of the initials.
27
+ logo?: Snippet;
28
+ } = $props();
29
+
30
+ let monogram = $derived(
31
+ title
32
+ .split(/\s+/)
33
+ .filter(Boolean)
34
+ .slice(0, 2)
35
+ .map((word) => word[0]!.toUpperCase())
36
+ .join(''),
37
+ );
38
+ </script>
39
+
40
+ <nav class="rail" aria-label="Site">
41
+ <a class="monogram" href={home} {title} aria-label={`${title}, home`}>
42
+ {#if logo}{@render logo()}{:else}{monogram}{/if}
43
+ </a>
44
+ <div class="actions">
45
+ {#if onsearch}
46
+ <button
47
+ class="rail-button needs-js"
48
+ type="button"
49
+ onclick={onsearch}
50
+ aria-label="Search"
51
+ title="Search ( / )"
52
+ >
53
+ <Icon name="search" />
54
+ </button>
55
+ {/if}
56
+ {#each links as link (link.url)}
57
+ <a class="rail-button" href={link.url} aria-label={link.label} title={link.label}
58
+ ><Icon name={link.icon} /></a
59
+ >
60
+ {/each}
61
+ </div>
62
+ <div class="end">
63
+ <ThemeToggle />
64
+ </div>
65
+ {#if progress !== null}
66
+ <div class="progress" aria-hidden="true">
67
+ <div class="progress-fill" style:transform="scaleY({progress})"></div>
68
+ </div>
69
+ {/if}
70
+ </nav>
71
+
72
+ <style>
73
+ .rail {
74
+ position: fixed;
75
+ z-index: 40;
76
+ display: flex;
77
+ align-items: center;
78
+ background: color-mix(in srgb, var(--paper-sunken) 88%, transparent);
79
+ backdrop-filter: blur(10px);
80
+ -webkit-backdrop-filter: blur(10px);
81
+ font-family: var(--font-ui);
82
+ }
83
+
84
+ .monogram {
85
+ display: grid;
86
+ place-items: center;
87
+ width: 2.5rem;
88
+ height: 2.5rem;
89
+ border-radius: 10px;
90
+ background: var(--accent);
91
+ color: var(--accent-ink);
92
+ font: 600 1.05rem / 1 var(--font-serif);
93
+ letter-spacing: 0.02em;
94
+ text-decoration: none;
95
+ flex: none;
96
+ }
97
+
98
+ .actions,
99
+ .end {
100
+ display: flex;
101
+ align-items: center;
102
+ gap: 0.25rem;
103
+ }
104
+
105
+ .rail :global(.rail-button) {
106
+ display: grid;
107
+ place-items: center;
108
+ width: 2.5rem;
109
+ height: 2.5rem;
110
+ border: none;
111
+ border-radius: 10px;
112
+ background: transparent;
113
+ color: var(--muted);
114
+ cursor: pointer;
115
+ text-decoration: none;
116
+ transition:
117
+ background 120ms,
118
+ color 120ms;
119
+ }
120
+
121
+ .rail :global(.rail-button:hover) {
122
+ background: var(--accent-soft);
123
+ color: var(--ink);
124
+ }
125
+
126
+ /* Narrow screens: a floating dock, centred at the bottom. */
127
+ .rail {
128
+ left: 50%;
129
+ bottom: max(0.9rem, env(safe-area-inset-bottom));
130
+ transform: translateX(-50%);
131
+ flex-direction: row;
132
+ gap: 0.35rem;
133
+ padding: 0.35rem;
134
+ border: 1px solid var(--rule);
135
+ border-radius: 16px;
136
+ box-shadow: var(--shadow);
137
+ }
138
+
139
+ .progress {
140
+ display: none;
141
+ }
142
+
143
+ /* Wide screens: a spine down the left edge. */
144
+ @media (min-width: 960px) {
145
+ .rail {
146
+ left: 0;
147
+ top: 0;
148
+ bottom: 0;
149
+ transform: none;
150
+ width: var(--rail-width);
151
+ flex-direction: column;
152
+ gap: 0;
153
+ padding: 0.9rem 0;
154
+ border: none;
155
+ border-right: 1px solid var(--rule);
156
+ border-radius: 0;
157
+ box-shadow: none;
158
+ }
159
+
160
+ .actions {
161
+ flex-direction: column;
162
+ margin-top: 1.5rem;
163
+ }
164
+
165
+ .end {
166
+ margin-top: auto;
167
+ flex-direction: column;
168
+ }
169
+
170
+ .progress {
171
+ display: block;
172
+ position: absolute;
173
+ top: 0;
174
+ right: -1px;
175
+ bottom: 0;
176
+ width: 2px;
177
+ }
178
+
179
+ .progress-fill {
180
+ height: 100%;
181
+ background: var(--accent);
182
+ transform-origin: top;
183
+ transition: transform 80ms linear;
184
+ }
185
+ }
186
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { RailLink } from '../types.js';
3
+ type $$ComponentProps = {
4
+ title: string;
5
+ home: string;
6
+ links?: RailLink[];
7
+ onsearch?: () => void;
8
+ progress?: number | null;
9
+ logo?: Snippet;
10
+ };
11
+ declare const Rail: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type Rail = ReturnType<typeof Rail>;
13
+ export default Rail;
@@ -0,0 +1,94 @@
1
+ <script lang="ts">
2
+ import type { Reference } from '../types.js';
3
+
4
+ // What a page cites, at its end: small, in label order, each entry the
5
+ // target of the citations to it. Following one highlights it; the
6
+ // browser's back button returns to the text.
7
+ let { entries, heading = 'References' }: { entries: Reference[]; heading?: string } = $props();
8
+ </script>
9
+
10
+ {#if entries.length}
11
+ <section class="references" aria-label={heading}>
12
+ <p class="eyebrow">{heading}</p>
13
+ <ol>
14
+ {#each entries as entry (entry.id)}
15
+ <li id={entry.id}>
16
+ <span class="label">[{entry.label}]</span>
17
+ <span class="entry">{@html entry.html}</span>
18
+ </li>
19
+ {/each}
20
+ </ol>
21
+ </section>
22
+ {/if}
23
+
24
+ <style>
25
+ .references {
26
+ margin-top: 3rem;
27
+ padding-top: 1.1rem;
28
+ border-top: 1px solid var(--rule);
29
+ }
30
+
31
+ .eyebrow {
32
+ margin: 0 0 0.8rem;
33
+ color: var(--muted);
34
+ }
35
+
36
+ ol {
37
+ display: grid;
38
+ grid-template-columns: max-content 1fr;
39
+ gap: 0.45rem 0.9rem;
40
+ margin: 0;
41
+ padding: 0;
42
+ list-style: none;
43
+ }
44
+
45
+ li {
46
+ display: grid;
47
+ grid-column: 1 / -1;
48
+ grid-template-columns: subgrid;
49
+ scroll-margin-top: 30vh;
50
+ border-radius: var(--radius);
51
+ }
52
+
53
+ .label {
54
+ padding-top: 0.12em;
55
+ font: 500 0.75rem / 1.5 var(--font-ui);
56
+ font-variant-numeric: tabular-nums;
57
+ color: var(--muted);
58
+ }
59
+
60
+ .entry {
61
+ font: 400 0.86rem / 1.55 var(--font-body);
62
+ color: var(--ink-soft);
63
+ text-wrap: pretty;
64
+ }
65
+
66
+ .entry :global(a) {
67
+ color: var(--muted);
68
+ text-decoration-color: var(--rule-strong);
69
+ text-underline-offset: 0.15em;
70
+ overflow-wrap: anywhere;
71
+ }
72
+
73
+ .entry :global(a:hover) {
74
+ color: var(--accent);
75
+ }
76
+
77
+ li:target {
78
+ animation: arrive 2.4s ease-out;
79
+ }
80
+
81
+ li:target .entry {
82
+ color: var(--ink);
83
+ }
84
+
85
+ li:target .label {
86
+ color: var(--accent);
87
+ }
88
+
89
+ @keyframes arrive {
90
+ from {
91
+ background: var(--accent-soft);
92
+ }
93
+ }
94
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Reference } from '../types.js';
2
+ type $$ComponentProps = {
3
+ entries: Reference[];
4
+ heading?: string;
5
+ };
6
+ declare const References: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type References = ReturnType<typeof References>;
8
+ export default References;
@@ -0,0 +1,186 @@
1
+ <script lang="ts">
2
+ import type { TrackItem } from '../types.js';
3
+
4
+ // A sequence drawn as a line of stations: the ones behind the reader are
5
+ // filled in, the current one is marked, the ones ahead are open circles.
6
+ // A station's sections hang beneath it as smaller stops.
7
+ let {
8
+ title,
9
+ url,
10
+ items,
11
+ current,
12
+ eyebrow,
13
+ }: {
14
+ // The sequence itself, as HTML, and where its own page is.
15
+ title: string;
16
+ url: string;
17
+ items: TrackItem[];
18
+ // The key of the stop the reader is on, if any.
19
+ current?: string;
20
+ // Above the sequence's name; `Sequence · 2 of 3` by default.
21
+ eyebrow?: string;
22
+ } = $props();
23
+
24
+ const flatten = (stops: TrackItem[]): TrackItem[] =>
25
+ stops.flatMap((stop) => [stop, ...flatten(stop.children ?? [])]);
26
+ let order = $derived(flatten(items).map((stop) => stop.key));
27
+ let position = $derived(current ? order.indexOf(current) : -1);
28
+ const state = (stop: TrackItem) => {
29
+ const at = order.indexOf(stop.key);
30
+ return { done: position >= 0 && at < position, here: at == position };
31
+ };
32
+ let top = $derived(items.findIndex((stop) => flatten([stop]).some((s) => s.key == current)));
33
+ </script>
34
+
35
+ {#snippet stops(list: TrackItem[], nested: boolean)}
36
+ <ol class:nested>
37
+ {#each list as stop, i (stop.key)}
38
+ {@const { done, here } = state(stop)}
39
+ <li class:done class:current={here}>
40
+ <span class="station" aria-hidden="true">{stop.label ?? i + 1}</span>
41
+ {#if here}
42
+ <span class="title" aria-current="page">{@html stop.titleHtml}</span>
43
+ {:else}
44
+ <a class="title" href={stop.url}>{@html stop.titleHtml}</a>
45
+ {/if}
46
+ {#if stop.children?.length}{@render stops(stop.children, true)}{/if}
47
+ </li>
48
+ {/each}
49
+ </ol>
50
+ {/snippet}
51
+
52
+ <nav class="track kind-sequence" aria-label="Sequence">
53
+ <p class="eyebrow">
54
+ {eyebrow ?? (top >= 0 ? `Sequence · ${top + 1} of ${items.length}` : 'Sequence')}
55
+ </p>
56
+ <a class="name" href={url} aria-current={position < 0 && current ? 'page' : undefined}
57
+ >{@html title}</a
58
+ >
59
+ {@render stops(items, false)}
60
+ </nav>
61
+
62
+ <style>
63
+ .track {
64
+ font-family: var(--font-ui);
65
+ }
66
+
67
+ .eyebrow {
68
+ margin: 0 0 0.35rem;
69
+ color: var(--kind);
70
+ }
71
+
72
+ .name {
73
+ display: block;
74
+ margin-bottom: 1rem;
75
+ font: 600 1rem / 1.3 var(--font-serif);
76
+ color: var(--ink);
77
+ text-decoration: none;
78
+ }
79
+
80
+ .name:hover {
81
+ color: var(--kind);
82
+ }
83
+
84
+ ol {
85
+ list-style: none;
86
+ margin: 0;
87
+ padding: 0;
88
+ }
89
+
90
+ ol.nested {
91
+ grid-column: 2;
92
+ margin-top: 0.6rem;
93
+ }
94
+
95
+ ol.nested li {
96
+ grid-template-columns: 1.1rem 1fr;
97
+ padding-bottom: 0.5rem;
98
+ }
99
+
100
+ ol.nested .station {
101
+ width: 1.1rem;
102
+ height: 1.1rem;
103
+ border-width: 1.5px;
104
+ font-size: 0;
105
+ }
106
+
107
+ ol.nested li:not(:last-child)::before {
108
+ left: calc(0.55rem - 1px);
109
+ top: 1.1rem;
110
+ }
111
+
112
+ ol.nested .title {
113
+ padding-top: 0;
114
+ font-size: 0.8rem;
115
+ }
116
+
117
+ li {
118
+ position: relative;
119
+ display: grid;
120
+ grid-template-columns: 1.6rem 1fr;
121
+ gap: 0.6rem;
122
+ padding-bottom: 0.9rem;
123
+ }
124
+
125
+ /* The line joining one station to the next. */
126
+ li:not(:last-child)::before {
127
+ content: '';
128
+ position: absolute;
129
+ left: calc(0.8rem - 1px);
130
+ top: 1.6rem;
131
+ bottom: 0;
132
+ width: 2px;
133
+ background: var(--rule-strong);
134
+ }
135
+
136
+ /* Filled once the stop it leads to is reached, so a chapter the reader is
137
+ * partway through does not look finished. */
138
+ li:has(+ li.done)::before,
139
+ li:has(+ li.current)::before {
140
+ background: var(--kind);
141
+ }
142
+
143
+ .station {
144
+ display: grid;
145
+ place-items: center;
146
+ width: 1.6rem;
147
+ height: 1.6rem;
148
+ border: 2px solid var(--rule-strong);
149
+ border-radius: 50%;
150
+ background: var(--paper);
151
+ font: 600 0.68rem / 1 var(--font-ui);
152
+ color: var(--muted);
153
+ font-variant-numeric: tabular-nums;
154
+ }
155
+
156
+ /* A stop's own station and title only: its sections are stops of their
157
+ * own, behind or ahead of the reader independently. */
158
+ li.done > .station {
159
+ border-color: var(--kind);
160
+ background: var(--kind);
161
+ color: var(--paper);
162
+ }
163
+
164
+ li.current > .station {
165
+ border-color: var(--kind);
166
+ color: var(--kind);
167
+ box-shadow: 0 0 0 4px var(--kind-soft);
168
+ }
169
+
170
+ .title {
171
+ font-size: 0.84rem;
172
+ line-height: 1.4;
173
+ padding-top: 0.2rem;
174
+ color: var(--muted);
175
+ text-decoration: none;
176
+ }
177
+
178
+ a.title:hover {
179
+ color: var(--ink);
180
+ }
181
+
182
+ li.current > .title {
183
+ color: var(--ink);
184
+ font-weight: 600;
185
+ }
186
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { TrackItem } from '../types.js';
2
+ type $$ComponentProps = {
3
+ title: string;
4
+ url: string;
5
+ items: TrackItem[];
6
+ current?: string;
7
+ eyebrow?: string;
8
+ };
9
+ declare const SequenceTrack: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type SequenceTrack = ReturnType<typeof SequenceTrack>;
11
+ export default SequenceTrack;