@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.
- package/LICENSE +21 -0
- package/README.md +84 -2
- package/dist/ambient.d.ts +2 -0
- package/dist/components/Article.svelte +226 -0
- package/dist/components/Article.svelte.d.ts +16 -0
- package/dist/components/ArticleHeader.svelte +134 -0
- package/dist/components/ArticleHeader.svelte.d.ts +19 -0
- package/dist/components/Chips.svelte +22 -0
- package/dist/components/Chips.svelte.d.ts +8 -0
- package/dist/components/Graph.svelte +727 -0
- package/dist/components/Graph.svelte.d.ts +16 -0
- package/dist/components/GraphPanel.svelte +328 -0
- package/dist/components/GraphPanel.svelte.d.ts +17 -0
- package/dist/components/Icon.svelte +67 -0
- package/dist/components/Icon.svelte.d.ts +8 -0
- package/dist/components/Index.svelte +283 -0
- package/dist/components/Index.svelte.d.ts +9 -0
- package/dist/components/Pager.svelte +71 -0
- package/dist/components/Pager.svelte.d.ts +12 -0
- package/dist/components/Palette.svelte +333 -0
- package/dist/components/Palette.svelte.d.ts +9 -0
- package/dist/components/Parts.svelte +62 -0
- package/dist/components/Parts.svelte.d.ts +11 -0
- package/dist/components/Proof.svelte +39 -0
- package/dist/components/Proof.svelte.d.ts +8 -0
- package/dist/components/Rail.svelte +186 -0
- package/dist/components/Rail.svelte.d.ts +13 -0
- package/dist/components/References.svelte +94 -0
- package/dist/components/References.svelte.d.ts +8 -0
- package/dist/components/SequenceTrack.svelte +186 -0
- package/dist/components/SequenceTrack.svelte.d.ts +11 -0
- package/dist/components/Shell.svelte +82 -0
- package/dist/components/Shell.svelte.d.ts +14 -0
- package/dist/components/Statement.svelte +105 -0
- package/dist/components/Statement.svelte.d.ts +11 -0
- package/dist/components/ThemeToggle.svelte +19 -0
- package/dist/components/ThemeToggle.svelte.d.ts +3 -0
- package/dist/components/Toc.svelte +145 -0
- package/dist/components/Toc.svelte.d.ts +8 -0
- package/dist/fonts.d.ts +4 -0
- package/dist/fonts.js +6 -0
- package/dist/fuzzy.d.ts +12 -0
- package/dist/fuzzy.js +57 -0
- package/dist/graph-layout.d.ts +35 -0
- package/dist/graph-layout.js +98 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +30 -0
- package/dist/reading.svelte.d.ts +3 -0
- package/dist/reading.svelte.js +4 -0
- package/dist/server.d.ts +26 -0
- package/dist/server.js +115 -0
- package/dist/settings.d.ts +10 -0
- package/dist/settings.js +28 -0
- package/dist/styles/base.css +136 -0
- package/dist/styles/index.css +6 -0
- package/dist/styles/prose.css +203 -0
- package/dist/styles/tokens.css +111 -0
- package/dist/theme.svelte.d.ts +12 -0
- package/dist/theme.svelte.js +66 -0
- package/dist/types.d.ts +84 -0
- package/dist/types.js +3 -0
- package/package.json +66 -3
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GraphLegend as Legend } from '../types.js';
|
|
2
|
+
export declare const MESEARCH_LEGEND: Legend;
|
|
3
|
+
import { type Component } from 'svelte';
|
|
4
|
+
import { type GraphTuning } from '../graph-layout.js';
|
|
5
|
+
import type { GraphLegend, GraphLink, GraphNode, IndexEntry } from '../types.js';
|
|
6
|
+
type $$ComponentProps = {
|
|
7
|
+
nodes: GraphNode[];
|
|
8
|
+
links: GraphLink[];
|
|
9
|
+
entries: Record<string, IndexEntry>;
|
|
10
|
+
descriptions?: Record<string, Component>;
|
|
11
|
+
tuning?: GraphTuning;
|
|
12
|
+
legend?: GraphLegend;
|
|
13
|
+
};
|
|
14
|
+
declare const Graph: Component<$$ComponentProps, {}, "">;
|
|
15
|
+
type Graph = ReturnType<typeof Graph>;
|
|
16
|
+
export default Graph;
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Component } from 'svelte';
|
|
3
|
+
import { formatDate, isoDate, kindLabel } from '../settings.js';
|
|
4
|
+
import type { GraphLink, IndexEntry } from '../types.js';
|
|
5
|
+
import Icon from './Icon.svelte';
|
|
6
|
+
|
|
7
|
+
// A selected node's preview. Beside a wide graph it is a drawer that slides
|
|
8
|
+
// out of the graph's right edge, leaving the graph usable; under a narrow
|
|
9
|
+
// one it is a sheet that rises from the bottom of the screen, which a thumb
|
|
10
|
+
// can reach and dismiss with a downward swipe. Either way the reader stays
|
|
11
|
+
// on the map, can hop to neighbouring nodes, and opens the document only
|
|
12
|
+
// when they choose to.
|
|
13
|
+
let {
|
|
14
|
+
entry,
|
|
15
|
+
Description,
|
|
16
|
+
links,
|
|
17
|
+
entries,
|
|
18
|
+
onselect,
|
|
19
|
+
headings = { from: 'Builds on', to: 'Leads to', related: 'Related' },
|
|
20
|
+
}: {
|
|
21
|
+
entry: IndexEntry | undefined;
|
|
22
|
+
Description: Component | undefined;
|
|
23
|
+
links: GraphLink[];
|
|
24
|
+
entries: Record<string, IndexEntry>;
|
|
25
|
+
onselect: (key: string | null) => void;
|
|
26
|
+
headings?: { from: string; to: string; related: string };
|
|
27
|
+
} = $props();
|
|
28
|
+
|
|
29
|
+
let panel: HTMLElement | undefined = $state();
|
|
30
|
+
let dragFrom: number | null = null;
|
|
31
|
+
let dragBy = $state(0);
|
|
32
|
+
|
|
33
|
+
const neighbours = (pick: (link: GraphLink) => string | undefined) =>
|
|
34
|
+
links
|
|
35
|
+
.map(pick)
|
|
36
|
+
.filter((key): key is string => !!key && !!entries[key])
|
|
37
|
+
.map((key) => entries[key]!);
|
|
38
|
+
|
|
39
|
+
let buildsOn = $derived(
|
|
40
|
+
entry
|
|
41
|
+
? neighbours((l) => (l.style == 'solid' && l.to == entry!.key ? l.from : undefined))
|
|
42
|
+
: [],
|
|
43
|
+
);
|
|
44
|
+
let leadsTo = $derived(
|
|
45
|
+
entry
|
|
46
|
+
? neighbours((l) => (l.style == 'solid' && l.from == entry!.key ? l.to : undefined))
|
|
47
|
+
: [],
|
|
48
|
+
);
|
|
49
|
+
let related = $derived(
|
|
50
|
+
entry
|
|
51
|
+
? neighbours((l) =>
|
|
52
|
+
l.style == 'dashed'
|
|
53
|
+
? l.from == entry!.key
|
|
54
|
+
? l.to
|
|
55
|
+
: l.to == entry!.key
|
|
56
|
+
? l.from
|
|
57
|
+
: undefined
|
|
58
|
+
: undefined,
|
|
59
|
+
)
|
|
60
|
+
: [],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
$effect(() => {
|
|
64
|
+
if (entry) panel?.focus({ preventScroll: true });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
function onkeydown(event: KeyboardEvent) {
|
|
68
|
+
if (event.key == 'Escape') {
|
|
69
|
+
onselect(null);
|
|
70
|
+
event.stopPropagation();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Swiping the sheet's handle down far enough dismisses it.
|
|
75
|
+
function onhandledown(event: PointerEvent) {
|
|
76
|
+
dragFrom = event.clientY;
|
|
77
|
+
(event.currentTarget as Element).setPointerCapture(event.pointerId);
|
|
78
|
+
}
|
|
79
|
+
function onhandlemove(event: PointerEvent) {
|
|
80
|
+
if (dragFrom !== null) dragBy = Math.max(0, event.clientY - dragFrom);
|
|
81
|
+
}
|
|
82
|
+
function onhandleup() {
|
|
83
|
+
if (dragBy > 90) onselect(null);
|
|
84
|
+
dragFrom = null;
|
|
85
|
+
dragBy = 0;
|
|
86
|
+
}
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
{#if entry}
|
|
90
|
+
<button
|
|
91
|
+
class="backdrop"
|
|
92
|
+
type="button"
|
|
93
|
+
aria-label="Close the preview"
|
|
94
|
+
onclick={() => onselect(null)}
|
|
95
|
+
></button>
|
|
96
|
+
{/if}
|
|
97
|
+
|
|
98
|
+
<aside
|
|
99
|
+
bind:this={panel}
|
|
100
|
+
class="panel kind-{entry?.kind ?? 'concept'}"
|
|
101
|
+
class:open={entry}
|
|
102
|
+
style:--drag="{dragBy}px"
|
|
103
|
+
aria-label={entry ? `${kindLabel(entry.kind)}: ${entry.title}` : undefined}
|
|
104
|
+
aria-hidden={!entry}
|
|
105
|
+
tabindex="-1"
|
|
106
|
+
{onkeydown}
|
|
107
|
+
inert={!entry}
|
|
108
|
+
>
|
|
109
|
+
{#if entry}
|
|
110
|
+
<div
|
|
111
|
+
class="handle"
|
|
112
|
+
onpointerdown={onhandledown}
|
|
113
|
+
onpointermove={onhandlemove}
|
|
114
|
+
onpointerup={onhandleup}
|
|
115
|
+
onpointercancel={onhandleup}
|
|
116
|
+
aria-hidden="true"
|
|
117
|
+
></div>
|
|
118
|
+
<header>
|
|
119
|
+
<p class="eyebrow kind">{kindLabel(entry.kind)}</p>
|
|
120
|
+
<button class="close" type="button" onclick={() => onselect(null)} aria-label="Close">
|
|
121
|
+
<Icon name="close" size={18} />
|
|
122
|
+
</button>
|
|
123
|
+
</header>
|
|
124
|
+
<h3 class="title">{@html entry.titleHtml}</h3>
|
|
125
|
+
<p class="meta">
|
|
126
|
+
Updated <time datetime={isoDate(entry.updated)}>{formatDate(entry.updated)}</time>
|
|
127
|
+
{#if entry.readingMinutes}· {entry.readingMinutes} min read{/if}
|
|
128
|
+
</p>
|
|
129
|
+
<div class="body prose">
|
|
130
|
+
{#if Description}
|
|
131
|
+
<Description />
|
|
132
|
+
{:else if entry.summaryHtml}
|
|
133
|
+
<p>{@html entry.summaryHtml}</p>
|
|
134
|
+
{/if}
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
{#each [[headings.from, buildsOn], [headings.to, leadsTo], [headings.related, related]] as const as [heading, list] (heading)}
|
|
138
|
+
{#if list.length}
|
|
139
|
+
<section>
|
|
140
|
+
<h4 class="eyebrow">{heading}</h4>
|
|
141
|
+
<ul class="chips">
|
|
142
|
+
{#each list as other (other.key)}
|
|
143
|
+
<li>
|
|
144
|
+
<button
|
|
145
|
+
type="button"
|
|
146
|
+
class="chip kind-{other.kind}"
|
|
147
|
+
onclick={() => onselect(other.key)}
|
|
148
|
+
>
|
|
149
|
+
{other.title}
|
|
150
|
+
</button>
|
|
151
|
+
</li>
|
|
152
|
+
{/each}
|
|
153
|
+
</ul>
|
|
154
|
+
</section>
|
|
155
|
+
{/if}
|
|
156
|
+
{/each}
|
|
157
|
+
|
|
158
|
+
<a class="read" href={entry.url}>
|
|
159
|
+
Read the {kindLabel(entry.kind).toLowerCase()}
|
|
160
|
+
<Icon name="arrow-right" size={18} />
|
|
161
|
+
</a>
|
|
162
|
+
{/if}
|
|
163
|
+
</aside>
|
|
164
|
+
|
|
165
|
+
<style>
|
|
166
|
+
.panel {
|
|
167
|
+
position: absolute;
|
|
168
|
+
z-index: 5;
|
|
169
|
+
top: 0;
|
|
170
|
+
right: 0;
|
|
171
|
+
bottom: 0;
|
|
172
|
+
width: min(23rem, 45%);
|
|
173
|
+
display: flex;
|
|
174
|
+
flex-direction: column;
|
|
175
|
+
gap: 0.9rem;
|
|
176
|
+
padding: 1.1rem 1.25rem 1.25rem;
|
|
177
|
+
background: var(--paper-raised);
|
|
178
|
+
border-left: 1px solid var(--rule);
|
|
179
|
+
box-shadow: var(--shadow);
|
|
180
|
+
overflow-y: auto;
|
|
181
|
+
outline: none;
|
|
182
|
+
transform: translateX(105%);
|
|
183
|
+
transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
184
|
+
font-family: var(--font-ui);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.panel.open {
|
|
188
|
+
transform: none;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.backdrop,
|
|
192
|
+
.handle {
|
|
193
|
+
display: none;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
header {
|
|
197
|
+
display: flex;
|
|
198
|
+
align-items: center;
|
|
199
|
+
justify-content: space-between;
|
|
200
|
+
border-top: 3px solid var(--kind);
|
|
201
|
+
margin: -1.1rem -1.25rem 0;
|
|
202
|
+
padding: 0.8rem 1.25rem 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.kind {
|
|
206
|
+
color: var(--kind);
|
|
207
|
+
margin: 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.close {
|
|
211
|
+
display: grid;
|
|
212
|
+
place-items: center;
|
|
213
|
+
width: 2rem;
|
|
214
|
+
height: 2rem;
|
|
215
|
+
border: none;
|
|
216
|
+
border-radius: 8px;
|
|
217
|
+
background: transparent;
|
|
218
|
+
color: var(--muted);
|
|
219
|
+
cursor: pointer;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.close:hover {
|
|
223
|
+
background: var(--accent-soft);
|
|
224
|
+
color: var(--ink);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.title {
|
|
228
|
+
margin: 0;
|
|
229
|
+
font: 600 1.45rem / 1.2 var(--font-serif);
|
|
230
|
+
color: var(--ink);
|
|
231
|
+
text-wrap: balance;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.meta {
|
|
235
|
+
margin: -0.4rem 0 0;
|
|
236
|
+
font-size: 0.8rem;
|
|
237
|
+
color: var(--muted);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.body {
|
|
241
|
+
font: 400 0.98rem / 1.65 var(--font-serif);
|
|
242
|
+
color: var(--ink-soft);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.body :global(p:last-child) {
|
|
246
|
+
margin-bottom: 0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
section h4 {
|
|
250
|
+
margin: 0 0 0.45rem;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.chip {
|
|
254
|
+
font-size: 0.8rem;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.read {
|
|
258
|
+
margin-top: auto;
|
|
259
|
+
display: inline-flex;
|
|
260
|
+
align-items: center;
|
|
261
|
+
justify-content: center;
|
|
262
|
+
gap: 0.5rem;
|
|
263
|
+
padding: 0.7rem 1rem;
|
|
264
|
+
border-radius: 10px;
|
|
265
|
+
background: var(--kind);
|
|
266
|
+
color: var(--paper-raised);
|
|
267
|
+
font: 600 0.9rem / 1 var(--font-ui);
|
|
268
|
+
text-decoration: none;
|
|
269
|
+
transition: filter 120ms;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.read:hover {
|
|
273
|
+
filter: brightness(1.08);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* A narrow screen: a sheet over the bottom of it. */
|
|
277
|
+
@media (max-width: 759px) {
|
|
278
|
+
.panel {
|
|
279
|
+
position: fixed;
|
|
280
|
+
z-index: 60;
|
|
281
|
+
top: auto;
|
|
282
|
+
left: 0;
|
|
283
|
+
right: 0;
|
|
284
|
+
bottom: 0;
|
|
285
|
+
width: auto;
|
|
286
|
+
max-height: 75vh;
|
|
287
|
+
border-left: none;
|
|
288
|
+
border-top: 1px solid var(--rule);
|
|
289
|
+
border-radius: 18px 18px 0 0;
|
|
290
|
+
box-shadow: var(--shadow-strong);
|
|
291
|
+
padding-bottom: max(1.25rem, env(safe-area-inset-bottom));
|
|
292
|
+
transform: translateY(105%);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.panel.open {
|
|
296
|
+
transform: translateY(var(--drag));
|
|
297
|
+
transition: transform 200ms ease;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
header {
|
|
301
|
+
border-top: none;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.handle {
|
|
305
|
+
display: block;
|
|
306
|
+
align-self: center;
|
|
307
|
+
width: 2.75rem;
|
|
308
|
+
height: 5px;
|
|
309
|
+
margin: -0.35rem 0 -0.4rem;
|
|
310
|
+
padding: 0;
|
|
311
|
+
border-radius: 3px;
|
|
312
|
+
background: var(--rule-strong);
|
|
313
|
+
cursor: grab;
|
|
314
|
+
touch-action: none;
|
|
315
|
+
flex: none;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.backdrop {
|
|
319
|
+
display: block;
|
|
320
|
+
position: fixed;
|
|
321
|
+
z-index: 55;
|
|
322
|
+
inset: 0;
|
|
323
|
+
border: none;
|
|
324
|
+
background: rgba(20, 16, 10, 0.3);
|
|
325
|
+
cursor: default;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
import type { GraphLink, IndexEntry } from '../types.js';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
entry: IndexEntry | undefined;
|
|
5
|
+
Description: Component | undefined;
|
|
6
|
+
links: GraphLink[];
|
|
7
|
+
entries: Record<string, IndexEntry>;
|
|
8
|
+
onselect: (key: string | null) => void;
|
|
9
|
+
headings?: {
|
|
10
|
+
from: string;
|
|
11
|
+
to: string;
|
|
12
|
+
related: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
declare const GraphPanel: Component<$$ComponentProps, {}, "">;
|
|
16
|
+
type GraphPanel = ReturnType<typeof GraphPanel>;
|
|
17
|
+
export default GraphPanel;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// A handful of line icons, drawn on a 24-unit grid in the current colour.
|
|
3
|
+
import type { IconName } from '../types.js';
|
|
4
|
+
|
|
5
|
+
let { name, size = 20 }: { name: IconName; size?: number } = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<svg
|
|
9
|
+
width={size}
|
|
10
|
+
height={size}
|
|
11
|
+
viewBox="0 0 24 24"
|
|
12
|
+
fill="none"
|
|
13
|
+
stroke="currentColor"
|
|
14
|
+
stroke-width="1.6"
|
|
15
|
+
stroke-linecap="round"
|
|
16
|
+
stroke-linejoin="round"
|
|
17
|
+
aria-hidden="true"
|
|
18
|
+
>
|
|
19
|
+
{#if name == 'search'}
|
|
20
|
+
<circle cx="11" cy="11" r="6.5" /><path d="m20 20-4.3-4.3" />
|
|
21
|
+
{:else if name == 'graph'}
|
|
22
|
+
<circle cx="6" cy="6" r="2.4" /><circle cx="18" cy="8" r="2.4" /><circle
|
|
23
|
+
cx="11"
|
|
24
|
+
cy="18"
|
|
25
|
+
r="2.4"
|
|
26
|
+
/><path d="M8.2 7 15.7 7.7M7.2 8.1l2.6 7.7M16.8 10l-4.4 6.2" />
|
|
27
|
+
{:else if name == 'list'}
|
|
28
|
+
<path d="M9 6h11M9 12h11M9 18h11" /><circle cx="4.5" cy="6" r="1" /><circle
|
|
29
|
+
cx="4.5"
|
|
30
|
+
cy="12"
|
|
31
|
+
r="1"
|
|
32
|
+
/><circle cx="4.5" cy="18" r="1" />
|
|
33
|
+
{:else if name == 'sun'}
|
|
34
|
+
<circle cx="12" cy="12" r="4" /><path
|
|
35
|
+
d="M12 2.5v2M12 19.5v2M2.5 12h2M19.5 12h2M5.3 5.3l1.4 1.4M17.3 17.3l1.4 1.4M5.3 18.7l1.4-1.4M17.3 6.7l1.4-1.4"
|
|
36
|
+
/>
|
|
37
|
+
{:else if name == 'moon'}
|
|
38
|
+
<path d="M20 14.5A8.5 8.5 0 0 1 9.5 4a8.5 8.5 0 1 0 10.5 10.5Z" />
|
|
39
|
+
{:else if name == 'auto'}
|
|
40
|
+
<circle cx="12" cy="12" r="8.5" /><path
|
|
41
|
+
d="M12 3.5v17A8.5 8.5 0 0 0 12 3.5Z"
|
|
42
|
+
fill="currentColor"
|
|
43
|
+
/>
|
|
44
|
+
{:else if name == 'plus'}
|
|
45
|
+
<path d="M12 5v14M5 12h14" />
|
|
46
|
+
{:else if name == 'minus'}
|
|
47
|
+
<path d="M5 12h14" />
|
|
48
|
+
{:else if name == 'reset'}
|
|
49
|
+
<path d="M4 4v5h5" /><path d="M5.5 15a7.5 7.5 0 1 0 1.3-8.3L4 9" />
|
|
50
|
+
{:else if name == 'close'}
|
|
51
|
+
<path d="M6 6l12 12M18 6 6 18" />
|
|
52
|
+
{:else if name == 'arrow-right'}
|
|
53
|
+
<path d="M5 12h14M13 6l6 6-6 6" />
|
|
54
|
+
{:else if name == 'arrow-left'}
|
|
55
|
+
<path d="M19 12H5M11 6l-6 6 6 6" />
|
|
56
|
+
{:else if name == 'home'}
|
|
57
|
+
<path d="M4 11 12 4l8 7M6 9.5V20h12V9.5" />
|
|
58
|
+
{:else if name == 'pen'}
|
|
59
|
+
<path d="M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3Z" /><path d="m14.5 7.5 3 3" />
|
|
60
|
+
{:else if name == 'book'}
|
|
61
|
+
<path d="M4 5.5A2.5 2.5 0 0 1 6.5 3H20v15H6.5A2.5 2.5 0 0 0 4 20.5Z" /><path
|
|
62
|
+
d="M4 20.5A2.5 2.5 0 0 0 6.5 23H20v-5"
|
|
63
|
+
/>
|
|
64
|
+
{:else if name == 'quote'}
|
|
65
|
+
<path d="M5 11h4v6H4v-5c0-3 1.5-5 4-6M15 11h4v6h-5v-5c0-3 1.5-5 4-6" />
|
|
66
|
+
{/if}
|
|
67
|
+
</svg>
|