@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
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type KatexMacros } from '@mvarble/mesearch-markdown/katex';
|
|
2
|
+
import { type GraphTuning } from './graph-layout.js';
|
|
3
|
+
import type { DocumentRef, GraphLink, GraphNode } from './types.js';
|
|
4
|
+
export declare function inlineHtml(text: string, macros?: KatexMacros): string;
|
|
5
|
+
export declare function layoutGraph(documents: Array<DocumentRef & {
|
|
6
|
+
sequenced?: boolean;
|
|
7
|
+
}>, links: GraphLink[], tuning?: GraphTuning): GraphNode[];
|
|
8
|
+
export interface ReferenceSource {
|
|
9
|
+
kind: string;
|
|
10
|
+
title: string;
|
|
11
|
+
year: string;
|
|
12
|
+
authors: Array<{
|
|
13
|
+
fullname: string;
|
|
14
|
+
}>;
|
|
15
|
+
journal?: string;
|
|
16
|
+
volume?: string;
|
|
17
|
+
number?: string;
|
|
18
|
+
pages?: string;
|
|
19
|
+
series?: string;
|
|
20
|
+
edition?: string;
|
|
21
|
+
publisher?: string;
|
|
22
|
+
institution?: string;
|
|
23
|
+
doi?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function referenceHtml(source: ReferenceSource, macros?: KatexMacros): string;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// For a site's server code: what is best done once, at build time, so that a
|
|
2
|
+
// page ships finished HTML rather than the libraries that made it.
|
|
3
|
+
import katex from 'katex';
|
|
4
|
+
import { baseMacros } from '@mvarble/mesearch-markdown/katex';
|
|
5
|
+
import { DEFAULT_TUNING, depths, estimateWidth, layout, NODE_HEIGHT, } from './graph-layout.js';
|
|
6
|
+
const escapeHtml = (text) => text.replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]);
|
|
7
|
+
// A short piece of text --- a title, a heading, a summary --- as HTML, with its
|
|
8
|
+
// inline `$...$` math rendered. `macros` are layered over the base table.
|
|
9
|
+
export function inlineHtml(text, macros = {}) {
|
|
10
|
+
const all = { ...baseMacros, ...macros };
|
|
11
|
+
return text
|
|
12
|
+
.split(/(\$[^$]+\$)/g)
|
|
13
|
+
.map((part) => part.length > 2 && part.startsWith('$') && part.endsWith('$')
|
|
14
|
+
? katex.renderToString(part.slice(1, -1), {
|
|
15
|
+
macros: { ...all },
|
|
16
|
+
throwOnError: false,
|
|
17
|
+
output: 'html',
|
|
18
|
+
})
|
|
19
|
+
: escapeHtml(part))
|
|
20
|
+
.join('');
|
|
21
|
+
}
|
|
22
|
+
// The graph's nodes, laid out by running the force simulation to rest, for
|
|
23
|
+
// `<Graph>` to draw as it is --- with or without JavaScript.
|
|
24
|
+
export function layoutGraph(documents, links, tuning = DEFAULT_TUNING) {
|
|
25
|
+
const keys = documents.map((doc) => doc.key);
|
|
26
|
+
const depth = depths(keys, links);
|
|
27
|
+
const nodes = documents.map((doc) => ({
|
|
28
|
+
id: doc.key,
|
|
29
|
+
width: estimateWidth(doc.title),
|
|
30
|
+
height: NODE_HEIGHT,
|
|
31
|
+
depth: depth.get(doc.key) ?? 0,
|
|
32
|
+
}));
|
|
33
|
+
const known = new Set(keys);
|
|
34
|
+
const positions = layout(nodes, links
|
|
35
|
+
.filter((link) => known.has(link.from) && known.has(link.to))
|
|
36
|
+
.map((link) => ({ source: link.from, target: link.to, style: link.style })), tuning);
|
|
37
|
+
return documents.map((doc, i) => ({
|
|
38
|
+
key: doc.key,
|
|
39
|
+
kind: doc.kind,
|
|
40
|
+
url: doc.url,
|
|
41
|
+
title: doc.title,
|
|
42
|
+
titleHtml: doc.titleHtml,
|
|
43
|
+
x: positions.get(doc.key).x,
|
|
44
|
+
y: positions.get(doc.key).y,
|
|
45
|
+
width: nodes[i].width,
|
|
46
|
+
depth: nodes[i].depth,
|
|
47
|
+
sequenced: doc.sequenced ?? false,
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
const BOOKISH = /^(book|thesis|phdthesis|mastersthesis|manual|booklet|proceedings)$/i;
|
|
51
|
+
// A reference as one short paragraph of HTML: who, what, where, when, and a
|
|
52
|
+
// link when there is one. Braces protecting capitals in BibTeX are dropped,
|
|
53
|
+
// and inline math in a title is rendered.
|
|
54
|
+
export function referenceHtml(source, macros = {}) {
|
|
55
|
+
// Without the braces that protect capitals, except within math.
|
|
56
|
+
const clean = (text) => (text ?? '')
|
|
57
|
+
.split(/(\$[^$]*\$)/g)
|
|
58
|
+
.map((part, i) => (i % 2 ? part : part.replace(/[{}]/g, '')))
|
|
59
|
+
.join('')
|
|
60
|
+
.trim();
|
|
61
|
+
const names = source.authors.map((author) => escapeHtml(clean(author.fullname)));
|
|
62
|
+
const who = names.length <= 2
|
|
63
|
+
? names.join(' and ')
|
|
64
|
+
: `${names.slice(0, -1).join(', ')} and ${names.at(-1)}`;
|
|
65
|
+
const title = inlineHtml(clean(source.title), macros);
|
|
66
|
+
const journal = clean(source.journal);
|
|
67
|
+
const parts = [];
|
|
68
|
+
if (who)
|
|
69
|
+
parts.push(who);
|
|
70
|
+
if (journal) {
|
|
71
|
+
parts.push(title);
|
|
72
|
+
let venue = `<i>${escapeHtml(journal)}</i>`;
|
|
73
|
+
if (source.volume)
|
|
74
|
+
venue += ` ${escapeHtml(clean(source.volume))}`;
|
|
75
|
+
if (source.number)
|
|
76
|
+
venue += `(${escapeHtml(clean(source.number))})`;
|
|
77
|
+
if (source.pages)
|
|
78
|
+
venue += `, ${escapeHtml(clean(source.pages).replace(/-+/g, '–'))}`;
|
|
79
|
+
parts.push(venue);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
parts.push(BOOKISH.test(source.kind) || !source.kind ? `<i>${title}</i>` : title);
|
|
83
|
+
const series = clean(source.series);
|
|
84
|
+
if (series) {
|
|
85
|
+
const volume = clean(source.volume);
|
|
86
|
+
parts.push(escapeHtml(volume ? `${series} ${volume}` : series));
|
|
87
|
+
}
|
|
88
|
+
const edition = clean(source.edition);
|
|
89
|
+
if (edition)
|
|
90
|
+
parts.push(escapeHtml(/edition/i.test(edition) ? edition : `${ordinal(edition)} edition`));
|
|
91
|
+
}
|
|
92
|
+
const house = clean(source.publisher) || clean(source.institution);
|
|
93
|
+
const year = clean(source.year);
|
|
94
|
+
if (house || year)
|
|
95
|
+
parts.push(escapeHtml([house, year].filter(Boolean).join(', ')));
|
|
96
|
+
let html = parts.join('. ') + '.';
|
|
97
|
+
const doi = clean(source.doi);
|
|
98
|
+
const url = clean(source.url);
|
|
99
|
+
if (doi) {
|
|
100
|
+
const href = `https://doi.org/${encodeURI(doi)}`;
|
|
101
|
+
html += ` <a href="${escapeHtml(href)}">doi:${escapeHtml(doi)}</a>`;
|
|
102
|
+
}
|
|
103
|
+
else if (/^https?:\/\//.test(url)) {
|
|
104
|
+
html += ` <a href="${escapeHtml(url)}">${escapeHtml(new URL(url).host)}</a>`;
|
|
105
|
+
}
|
|
106
|
+
return html;
|
|
107
|
+
}
|
|
108
|
+
// `2` as `2nd`; anything already in words is left as it is.
|
|
109
|
+
function ordinal(text) {
|
|
110
|
+
if (!/^\d+$/.test(text))
|
|
111
|
+
return text;
|
|
112
|
+
const n = Number(text);
|
|
113
|
+
const suffix = n % 100 >= 11 && n % 100 <= 13 ? 'th' : (['th', 'st', 'nd', 'rd'][n % 10] ?? 'th');
|
|
114
|
+
return `${n}${suffix}`;
|
|
115
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface Settings {
|
|
2
|
+
lang: string;
|
|
3
|
+
kinds: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
export declare const settings: Settings;
|
|
6
|
+
export declare function configure(given: Partial<Settings>): void;
|
|
7
|
+
export declare function formatDate(date: Date): string;
|
|
8
|
+
export declare const isoDate: (date: Date) => string;
|
|
9
|
+
export declare const kindLabel: (kind: string) => string;
|
|
10
|
+
export declare const plain: (text: string) => string;
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// What the components need to know about the site that is not data: the
|
|
2
|
+
// language dates are written in, and what each kind of document is called.
|
|
3
|
+
// A site sets these once, before rendering, with `configure`.
|
|
4
|
+
export const settings = {
|
|
5
|
+
lang: 'en',
|
|
6
|
+
kinds: { concept: 'Concept', writeup: 'Writeup', sequence: 'Sequence', post: 'Post' },
|
|
7
|
+
};
|
|
8
|
+
export function configure(given) {
|
|
9
|
+
if (given.lang)
|
|
10
|
+
settings.lang = given.lang;
|
|
11
|
+
if (given.kinds)
|
|
12
|
+
settings.kinds = { ...settings.kinds, ...given.kinds };
|
|
13
|
+
formatter = undefined;
|
|
14
|
+
}
|
|
15
|
+
let formatter;
|
|
16
|
+
export function formatDate(date) {
|
|
17
|
+
formatter ??= new Intl.DateTimeFormat(settings.lang, {
|
|
18
|
+
year: 'numeric',
|
|
19
|
+
month: 'short',
|
|
20
|
+
day: 'numeric',
|
|
21
|
+
timeZone: 'UTC',
|
|
22
|
+
});
|
|
23
|
+
return formatter.format(date);
|
|
24
|
+
}
|
|
25
|
+
export const isoDate = (date) => date.toISOString().slice(0, 10);
|
|
26
|
+
export const kindLabel = (kind) => settings.kinds[kind] ?? kind.slice(0, 1).toUpperCase() + kind.slice(1);
|
|
27
|
+
// A title as plain text: inline math loses its dollar signs.
|
|
28
|
+
export const plain = (text) => text.replace(/\$([^$]+)\$/g, '$1');
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
*,
|
|
2
|
+
*::before,
|
|
3
|
+
*::after {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
html {
|
|
8
|
+
background: var(--paper);
|
|
9
|
+
-webkit-text-size-adjust: 100%;
|
|
10
|
+
scroll-padding-top: 1.5rem;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
body {
|
|
14
|
+
margin: 0;
|
|
15
|
+
color: var(--ink);
|
|
16
|
+
background: var(--paper);
|
|
17
|
+
font: 400 var(--font-size-body) / var(--line-height-body) var(--font-body);
|
|
18
|
+
font-optical-sizing: auto;
|
|
19
|
+
-webkit-font-smoothing: antialiased;
|
|
20
|
+
text-rendering: optimizeLegibility;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
::selection {
|
|
24
|
+
background: var(--selection);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
a {
|
|
28
|
+
color: inherit;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
:focus-visible {
|
|
32
|
+
outline: 2px solid var(--accent);
|
|
33
|
+
outline-offset: 2px;
|
|
34
|
+
border-radius: 2px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
button {
|
|
38
|
+
font: inherit;
|
|
39
|
+
color: inherit;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
img,
|
|
43
|
+
svg,
|
|
44
|
+
video {
|
|
45
|
+
max-width: 100%;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Controls that only work with JavaScript stay out of a page read without
|
|
49
|
+
* it; `app.html` adds `.js` before anything paints. */
|
|
50
|
+
html:not(.js) .needs-js {
|
|
51
|
+
display: none !important;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.visually-hidden {
|
|
55
|
+
position: absolute;
|
|
56
|
+
width: 1px;
|
|
57
|
+
height: 1px;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
clip: rect(0 0 0 0);
|
|
60
|
+
white-space: nowrap;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* The small capitals that label sections and kinds throughout. */
|
|
64
|
+
.eyebrow {
|
|
65
|
+
font: 600 var(--font-size-eyebrow) / 1.2 var(--font-ui);
|
|
66
|
+
letter-spacing: 0.12em;
|
|
67
|
+
text-transform: uppercase;
|
|
68
|
+
color: var(--muted);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Every kind of document has a colour; one without its own takes the accent. */
|
|
72
|
+
[class*='kind-'] {
|
|
73
|
+
--kind: var(--accent);
|
|
74
|
+
--kind-soft: var(--accent-soft);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.kind-concept {
|
|
78
|
+
--kind: var(--concept);
|
|
79
|
+
--kind-soft: var(--concept-soft);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.kind-writeup {
|
|
83
|
+
--kind: var(--writeup);
|
|
84
|
+
--kind-soft: var(--writeup-soft);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.kind-sequence {
|
|
88
|
+
--kind: var(--sequence);
|
|
89
|
+
--kind-soft: var(--sequence-soft);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* A small labelled link to a document, used wherever documents are listed in
|
|
93
|
+
* passing. */
|
|
94
|
+
.chip {
|
|
95
|
+
display: inline-flex;
|
|
96
|
+
align-items: baseline;
|
|
97
|
+
gap: 0.4em;
|
|
98
|
+
padding: 0.18em 0.65em 0.22em;
|
|
99
|
+
border: 1px solid var(--rule);
|
|
100
|
+
border-radius: 999px;
|
|
101
|
+
background: var(--paper-raised);
|
|
102
|
+
font: 500 var(--font-size-ui) / 1.4 var(--font-ui);
|
|
103
|
+
color: var(--ink-soft);
|
|
104
|
+
text-decoration: none;
|
|
105
|
+
transition:
|
|
106
|
+
border-color 120ms,
|
|
107
|
+
background 120ms;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.chip::before {
|
|
111
|
+
content: '';
|
|
112
|
+
width: 0.5em;
|
|
113
|
+
height: 0.5em;
|
|
114
|
+
border-radius: 50%;
|
|
115
|
+
background: var(--kind, var(--accent));
|
|
116
|
+
flex: none;
|
|
117
|
+
transform: translateY(-0.05em);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.chip:hover {
|
|
121
|
+
border-color: var(--kind, var(--accent));
|
|
122
|
+
background: var(--kind-soft, var(--accent-soft));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
button.chip {
|
|
126
|
+
cursor: pointer;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.chips {
|
|
130
|
+
display: flex;
|
|
131
|
+
flex-wrap: wrap;
|
|
132
|
+
gap: 0.4rem;
|
|
133
|
+
margin: 0;
|
|
134
|
+
padding: 0;
|
|
135
|
+
list-style: none;
|
|
136
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/* Everything a page needs to look like mesearch: the variables, the base
|
|
2
|
+
* styles, and the styles of rendered documents. Fonts and KaTeX's own
|
|
3
|
+
* stylesheet are imported separately, so a site can choose its own. */
|
|
4
|
+
@import './tokens.css';
|
|
5
|
+
@import './base.css';
|
|
6
|
+
@import './prose.css';
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/* A document's rendered content. Its headings arrive one level down --- a `#`
|
|
2
|
+
* is an <h2> --- since the page's <h1> is its title. Body text is justified, with hyphenation to
|
|
3
|
+
* keep the gaps even, and inline math never breaks across a line (see the
|
|
4
|
+
* shared KaTeX stylesheet). */
|
|
5
|
+
|
|
6
|
+
.prose {
|
|
7
|
+
text-align: justify;
|
|
8
|
+
hyphens: auto;
|
|
9
|
+
-webkit-hyphens: auto;
|
|
10
|
+
hanging-punctuation: first;
|
|
11
|
+
overflow-wrap: break-word;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.prose > :first-child {
|
|
15
|
+
margin-top: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.prose p,
|
|
19
|
+
.prose ul,
|
|
20
|
+
.prose ol,
|
|
21
|
+
.prose dl {
|
|
22
|
+
margin: 0 0 1em;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.prose li {
|
|
26
|
+
margin-bottom: 0.35em;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.prose li > p {
|
|
30
|
+
margin-bottom: 0.4em;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.prose :is(h1, h2, h3, h4, h5, h6) {
|
|
34
|
+
font-family: var(--font-heading);
|
|
35
|
+
color: var(--ink);
|
|
36
|
+
text-align: left;
|
|
37
|
+
hyphens: manual;
|
|
38
|
+
line-height: 1.25;
|
|
39
|
+
margin: 2.1em 0 0.6em;
|
|
40
|
+
scroll-margin-top: 1.5rem;
|
|
41
|
+
text-wrap: balance;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.prose h2 {
|
|
45
|
+
font-size: var(--font-size-h1);
|
|
46
|
+
font-weight: 600;
|
|
47
|
+
letter-spacing: -0.01em;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.prose h3 {
|
|
51
|
+
font-size: var(--font-size-h2);
|
|
52
|
+
font-weight: 600;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.prose h4 {
|
|
56
|
+
font-size: var(--font-size-h3);
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.prose :is(h5, h6) {
|
|
61
|
+
font-size: 1em;
|
|
62
|
+
font-weight: 700;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.prose a {
|
|
66
|
+
color: var(--accent);
|
|
67
|
+
text-decoration-line: underline;
|
|
68
|
+
text-decoration-color: color-mix(in srgb, var(--accent) 35%, transparent);
|
|
69
|
+
text-decoration-thickness: 1px;
|
|
70
|
+
text-underline-offset: 0.18em;
|
|
71
|
+
transition: text-decoration-color 120ms;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.prose a:hover {
|
|
75
|
+
text-decoration-color: var(--accent);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* A citation is a quiet aside in the sentence, not a link to follow away: no
|
|
79
|
+
* underline until it is pointed at, and its reference is in the tooltip. */
|
|
80
|
+
.prose a.citation {
|
|
81
|
+
text-decoration-color: transparent;
|
|
82
|
+
font-variant-numeric: lining-nums;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.prose a.citation:hover {
|
|
86
|
+
text-decoration-color: currentColor;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.prose strong {
|
|
90
|
+
font-weight: 650;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.prose blockquote {
|
|
94
|
+
margin: 1.6em 0;
|
|
95
|
+
padding: 0.1em 0 0.1em 1.25em;
|
|
96
|
+
border-left: 2px solid var(--rule-strong);
|
|
97
|
+
color: var(--ink-soft);
|
|
98
|
+
font-style: italic;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.prose hr {
|
|
102
|
+
border: none;
|
|
103
|
+
height: 1px;
|
|
104
|
+
background: var(--rule);
|
|
105
|
+
margin: 2.8em auto;
|
|
106
|
+
width: 40%;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.prose img,
|
|
110
|
+
.prose video {
|
|
111
|
+
display: block;
|
|
112
|
+
max-width: 100%;
|
|
113
|
+
height: auto;
|
|
114
|
+
margin: 1.8em auto;
|
|
115
|
+
border-radius: var(--radius);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.prose figure {
|
|
119
|
+
margin: 2em 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.prose figcaption {
|
|
123
|
+
font: var(--font-size-small) / 1.5 var(--font-ui);
|
|
124
|
+
color: var(--muted);
|
|
125
|
+
text-align: center;
|
|
126
|
+
margin-top: 0.6em;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.prose table {
|
|
130
|
+
display: block;
|
|
131
|
+
overflow-x: auto;
|
|
132
|
+
border-collapse: collapse;
|
|
133
|
+
margin: 1.8em 0;
|
|
134
|
+
font-size: var(--font-size-small);
|
|
135
|
+
text-align: left;
|
|
136
|
+
hyphens: manual;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.prose th,
|
|
140
|
+
.prose td {
|
|
141
|
+
padding: 0.5em 0.9em;
|
|
142
|
+
border-bottom: 1px solid var(--rule);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.prose th {
|
|
146
|
+
font: 600 var(--font-size-ui) / 1.4 var(--font-ui);
|
|
147
|
+
color: var(--muted);
|
|
148
|
+
border-bottom-color: var(--rule-strong);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.prose code {
|
|
152
|
+
font-family: var(--font-mono);
|
|
153
|
+
font-size: var(--font-size-mono);
|
|
154
|
+
background: var(--code-background);
|
|
155
|
+
border-radius: 4px;
|
|
156
|
+
padding: 0.08em 0.32em;
|
|
157
|
+
hyphens: none;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.prose pre.shiki {
|
|
161
|
+
font-family: var(--font-mono);
|
|
162
|
+
font-size: 0.85rem;
|
|
163
|
+
line-height: 1.65;
|
|
164
|
+
text-align: left;
|
|
165
|
+
hyphens: none;
|
|
166
|
+
margin: 1.6em 0;
|
|
167
|
+
padding: 1em 1.2em;
|
|
168
|
+
overflow-x: auto;
|
|
169
|
+
border: 1px solid var(--rule);
|
|
170
|
+
border-radius: var(--radius);
|
|
171
|
+
background: var(--code-background) !important;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.prose pre.shiki code {
|
|
175
|
+
background: none;
|
|
176
|
+
padding: 0;
|
|
177
|
+
font-size: inherit;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* Shiki emits both themes at once, as `--shiki-dark` custom properties on
|
|
181
|
+
* every token; the dark theme switches to them. */
|
|
182
|
+
:root[data-theme='dark'] .prose .shiki,
|
|
183
|
+
:root[data-theme='dark'] .prose .shiki span {
|
|
184
|
+
color: var(--shiki-dark) !important;
|
|
185
|
+
font-style: var(--shiki-dark-font-style) !important;
|
|
186
|
+
font-weight: var(--shiki-dark-font-weight) !important;
|
|
187
|
+
text-decoration: var(--shiki-dark-text-decoration) !important;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.prose .math-scroll-container {
|
|
191
|
+
margin: 0.4em 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.prose .katex-display {
|
|
195
|
+
text-align: center;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* Footnotes, as mdsvex renders them. */
|
|
199
|
+
.prose .footnotes {
|
|
200
|
+
margin-top: 3em;
|
|
201
|
+
font-size: var(--font-size-small);
|
|
202
|
+
color: var(--ink-soft);
|
|
203
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* Every site-wide decision is a variable here, so that a project's
|
|
2
|
+
* `mesearch.css` --- loaded after this --- can change any of them. The light
|
|
3
|
+
* theme is the default, and the one a page read without JavaScript gets; the
|
|
4
|
+
* dark theme applies when `<html data-theme="dark">`. */
|
|
5
|
+
|
|
6
|
+
:root {
|
|
7
|
+
color-scheme: light;
|
|
8
|
+
|
|
9
|
+
/* Type. Body text is a serif built for long reading; the interface around
|
|
10
|
+
* it is a quiet sans. */
|
|
11
|
+
--font-serif: 'Source Serif 4 Variable', 'Source Serif Pro', Georgia, 'Times New Roman', serif;
|
|
12
|
+
--font-sans:
|
|
13
|
+
'Inter Variable', Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui,
|
|
14
|
+
sans-serif;
|
|
15
|
+
--font-mono: 'Fira Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
|
|
16
|
+
--font-body: var(--font-serif);
|
|
17
|
+
--font-heading: var(--font-serif);
|
|
18
|
+
--font-ui: var(--font-sans);
|
|
19
|
+
|
|
20
|
+
--font-size-body: 1.0625rem;
|
|
21
|
+
--line-height-body: 1.72;
|
|
22
|
+
--font-size-small: 0.9rem;
|
|
23
|
+
--font-size-ui: 0.875rem;
|
|
24
|
+
--font-size-eyebrow: 0.72rem;
|
|
25
|
+
--font-size-title: clamp(2rem, 1.4rem + 2.4vw, 2.85rem);
|
|
26
|
+
--font-size-h1: 1.75rem;
|
|
27
|
+
--font-size-h2: 1.4rem;
|
|
28
|
+
--font-size-h3: 1.15rem;
|
|
29
|
+
--font-size-mono: 0.86em;
|
|
30
|
+
--katex-font-size: 1.04em;
|
|
31
|
+
--katex-font-size-hidpi: 1.04em;
|
|
32
|
+
|
|
33
|
+
/* Paper and ink. */
|
|
34
|
+
--paper: #f9f8f3;
|
|
35
|
+
--paper-raised: #fefdf9;
|
|
36
|
+
--paper-sunken: #f0efe6;
|
|
37
|
+
--ink: #1a1d1c;
|
|
38
|
+
--ink-soft: #343a38;
|
|
39
|
+
--muted: #626a63;
|
|
40
|
+
--faint: #9aa096;
|
|
41
|
+
--rule: #e2e2d6;
|
|
42
|
+
--rule-strong: #cbcdbd;
|
|
43
|
+
--selection: rgba(61, 58, 140, 0.16);
|
|
44
|
+
|
|
45
|
+
/* One accent, and a colour for each kind of document. */
|
|
46
|
+
--accent: #3d3a8c;
|
|
47
|
+
--accent-ink: #f8f7ff;
|
|
48
|
+
--accent-soft: rgba(61, 58, 140, 0.09);
|
|
49
|
+
--concept: #24668f;
|
|
50
|
+
--concept-soft: rgba(36, 102, 143, 0.1);
|
|
51
|
+
--writeup: #9a3a63;
|
|
52
|
+
--writeup-soft: rgba(154, 58, 99, 0.1);
|
|
53
|
+
--sequence: #3d7046;
|
|
54
|
+
--sequence-soft: rgba(61, 112, 70, 0.12);
|
|
55
|
+
|
|
56
|
+
/* The graph. */
|
|
57
|
+
--graph-background: var(--paper-raised);
|
|
58
|
+
--graph-grid: rgba(90, 104, 90, 0.07);
|
|
59
|
+
--graph-edge: #b0b4a4;
|
|
60
|
+
--graph-edge-strong: #626a63;
|
|
61
|
+
--graph-node: var(--paper-raised);
|
|
62
|
+
--graph-label: var(--ink);
|
|
63
|
+
|
|
64
|
+
--code-background: #f1f0e7;
|
|
65
|
+
|
|
66
|
+
/* Shape and space. */
|
|
67
|
+
--radius: 6px;
|
|
68
|
+
--radius-large: 14px;
|
|
69
|
+
--shadow: 0 1px 2px rgba(20, 28, 30, 0.06), 0 8px 24px rgba(20, 28, 30, 0.08);
|
|
70
|
+
--shadow-strong: 0 2px 6px rgba(20, 28, 30, 0.08), 0 24px 60px rgba(20, 28, 30, 0.18);
|
|
71
|
+
--measure: 54rem;
|
|
72
|
+
--rail-width: 4rem;
|
|
73
|
+
--gutter: clamp(1.1rem, 4vw, 2.5rem);
|
|
74
|
+
--sidebar-left: 15rem;
|
|
75
|
+
--sidebar-right: 14rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
:root[data-theme='dark'] {
|
|
79
|
+
color-scheme: dark;
|
|
80
|
+
|
|
81
|
+
--paper: #12141c;
|
|
82
|
+
--paper-raised: #181b25;
|
|
83
|
+
--paper-sunken: #0d0f15;
|
|
84
|
+
--ink: #e7e6df;
|
|
85
|
+
--ink-soft: #c9c9c0;
|
|
86
|
+
--muted: #969a95;
|
|
87
|
+
--faint: #666a6b;
|
|
88
|
+
--rule: #262a38;
|
|
89
|
+
--rule-strong: #383e54;
|
|
90
|
+
--selection: rgba(169, 168, 240, 0.25);
|
|
91
|
+
|
|
92
|
+
--accent: #a9a8f0;
|
|
93
|
+
--accent-ink: #14132b;
|
|
94
|
+
--accent-soft: rgba(169, 168, 240, 0.12);
|
|
95
|
+
--concept: #86bfe6;
|
|
96
|
+
--concept-soft: rgba(134, 191, 230, 0.13);
|
|
97
|
+
--writeup: #eb9bb8;
|
|
98
|
+
--writeup-soft: rgba(235, 155, 184, 0.13);
|
|
99
|
+
--sequence: #98cc9a;
|
|
100
|
+
--sequence-soft: rgba(152, 204, 154, 0.13);
|
|
101
|
+
|
|
102
|
+
--graph-background: #151822;
|
|
103
|
+
--graph-grid: rgba(200, 210, 230, 0.05);
|
|
104
|
+
--graph-edge: #4a5168;
|
|
105
|
+
--graph-edge-strong: #a3a69f;
|
|
106
|
+
|
|
107
|
+
--code-background: #1a1e2a;
|
|
108
|
+
|
|
109
|
+
--shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.3);
|
|
110
|
+
--shadow-strong: 0 2px 6px rgba(0, 0, 0, 0.4), 0 24px 60px rgba(0, 0, 0, 0.5);
|
|
111
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ThemeChoice = 'system' | 'light' | 'dark';
|
|
2
|
+
export declare const THEME_SCRIPT = "(function () {\n var root = document.documentElement;\n var theme;\n root.classList.add('js');\n try {\n theme = localStorage.getItem('mesearch:theme');\n } catch (e) {}\n if (theme !== 'light' && theme !== 'dark') {\n theme = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n }\n root.dataset.theme = theme;\n})();";
|
|
3
|
+
declare class Theme {
|
|
4
|
+
#private;
|
|
5
|
+
choice: ThemeChoice;
|
|
6
|
+
systemDark: boolean;
|
|
7
|
+
resolved: "light" | "dark";
|
|
8
|
+
start(): void;
|
|
9
|
+
toggle(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare const theme: Theme;
|
|
12
|
+
export {};
|