mdorigin 0.1.1 → 0.1.2
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/README.md +47 -4
- package/dist/adapters/cloudflare.d.ts +2 -0
- package/dist/adapters/cloudflare.js +9 -0
- package/dist/adapters/node.d.ts +2 -0
- package/dist/adapters/node.js +50 -11
- package/dist/cli/build-cloudflare.js +7 -1
- package/dist/cli/build-search.d.ts +1 -0
- package/dist/cli/build-search.js +44 -0
- package/dist/cli/dev.js +10 -1
- package/dist/cli/main.js +14 -2
- package/dist/cli/search.d.ts +1 -0
- package/dist/cli/search.js +36 -0
- package/dist/cloudflare.d.ts +2 -0
- package/dist/cloudflare.js +41 -5
- package/dist/core/api.d.ts +13 -0
- package/dist/core/api.js +160 -0
- package/dist/core/content-store.js +5 -0
- package/dist/core/content-type.d.ts +1 -0
- package/dist/core/content-type.js +3 -0
- package/dist/core/directory-index.d.ts +1 -1
- package/dist/core/directory-index.js +5 -1
- package/dist/core/markdown.d.ts +4 -0
- package/dist/core/markdown.js +53 -0
- package/dist/core/request-handler.d.ts +4 -0
- package/dist/core/request-handler.js +90 -14
- package/dist/core/site-config.d.ts +4 -0
- package/dist/core/site-config.js +14 -0
- package/dist/html/template.d.ts +5 -0
- package/dist/html/template.js +203 -11
- package/dist/html/theme.js +254 -9
- package/dist/index-builder.js +54 -29
- package/dist/search.d.ts +59 -0
- package/dist/search.js +370 -0
- package/package.json +10 -1
package/dist/html/template.js
CHANGED
|
@@ -23,6 +23,24 @@ export function renderDocument(options) {
|
|
|
23
23
|
.map((item) => `<li><a href="${escapeHtml(item.href)}">${escapeHtml(item.label)}</a></li>`)
|
|
24
24
|
.join('')}</ul></nav>`
|
|
25
25
|
: '';
|
|
26
|
+
const searchToggleBlock = options.searchEnabled
|
|
27
|
+
? [
|
|
28
|
+
'<div class="site-search" data-site-search>',
|
|
29
|
+
'<button type="button" class="site-search__toggle" aria-expanded="false" aria-controls="site-search-panel">Search</button>',
|
|
30
|
+
'<div id="site-search-panel" class="site-search__panel" hidden>',
|
|
31
|
+
'<form class="site-search__form" role="search" action="/api/search" method="get">',
|
|
32
|
+
'<label class="site-search__label" for="site-search-input">Search site</label>',
|
|
33
|
+
'<div class="site-search__controls">',
|
|
34
|
+
'<input id="site-search-input" class="site-search__input" type="search" name="q" placeholder="Search docs and skills" autocomplete="off">',
|
|
35
|
+
'<button type="submit" class="site-search__submit">Go</button>',
|
|
36
|
+
'</div>',
|
|
37
|
+
'<p class="site-search__hint">Search is powered by <code>/api/search</code>.</p>',
|
|
38
|
+
'</form>',
|
|
39
|
+
'<div class="site-search__results" data-site-search-results></div>',
|
|
40
|
+
'</div>',
|
|
41
|
+
'</div>',
|
|
42
|
+
].join('')
|
|
43
|
+
: '';
|
|
26
44
|
const footerNavBlock = options.footerNav && options.footerNav.length > 0
|
|
27
45
|
? `<nav class="site-footer__nav"><ul>${options.footerNav
|
|
28
46
|
.map((item) => `<li><a href="${escapeHtml(item.href)}">${escapeHtml(item.label)}</a></li>`)
|
|
@@ -46,12 +64,20 @@ export function renderDocument(options) {
|
|
|
46
64
|
const footerTextBlock = options.footerText
|
|
47
65
|
? `<p class="site-footer__text">${escapeHtml(options.footerText)}</p>`
|
|
48
66
|
: '';
|
|
49
|
-
const
|
|
50
|
-
? `<
|
|
67
|
+
const footerMetaBlock = socialLinksBlock || editLinkBlock
|
|
68
|
+
? `<div class="site-footer__meta">${socialLinksBlock}${editLinkBlock}</div>`
|
|
69
|
+
: '';
|
|
70
|
+
const footerBlock = footerNavBlock || footerTextBlock || footerMetaBlock
|
|
71
|
+
? `<footer class="site-footer"><div class="site-footer__inner">${footerNavBlock}${footerTextBlock}${footerMetaBlock}</div></footer>`
|
|
51
72
|
: '';
|
|
52
73
|
const articleBody = options.template === 'catalog'
|
|
53
|
-
? renderCatalogArticle(options.body, options.catalogEntries ?? []
|
|
74
|
+
? renderCatalogArticle(options.body, options.catalogEntries ?? [], {
|
|
75
|
+
requestPath: options.catalogRequestPath ?? '/',
|
|
76
|
+
initialPostCount: options.catalogInitialPostCount ?? 10,
|
|
77
|
+
loadMoreStep: options.catalogLoadMoreStep ?? 10,
|
|
78
|
+
})
|
|
54
79
|
: options.body;
|
|
80
|
+
const searchScript = options.searchEnabled ? renderSearchScript() : '';
|
|
55
81
|
return [
|
|
56
82
|
'<!doctype html>',
|
|
57
83
|
'<html lang="en">',
|
|
@@ -66,11 +92,12 @@ export function renderDocument(options) {
|
|
|
66
92
|
stylesheetBlock,
|
|
67
93
|
'</head>',
|
|
68
94
|
`<body data-theme="${options.theme}" data-template="${options.template}">`,
|
|
69
|
-
`<header class="site-header"><div class="site-header__inner"><div class="site-header__brand"><p class="site-header__title"><a href="${brandHref}">${logoBlock}<span>${siteTitle}</span></a></p>${siteDescriptionBlock}</div>${navBlock}</div></header>`,
|
|
95
|
+
`<header class="site-header"><div class="site-header__inner"><div class="site-header__brand"><p class="site-header__title"><a href="${brandHref}">${logoBlock}<span>${siteTitle}</span></a></p>${siteDescriptionBlock}</div><div class="site-header__actions">${navBlock}${searchToggleBlock}</div></div></header>`,
|
|
70
96
|
'<main>',
|
|
71
97
|
`<article>${articleBody}</article>`,
|
|
72
98
|
'</main>',
|
|
73
99
|
footerBlock,
|
|
100
|
+
searchScript,
|
|
74
101
|
'</body>',
|
|
75
102
|
'</html>',
|
|
76
103
|
].join('');
|
|
@@ -102,18 +129,29 @@ function renderSocialIcon(icon) {
|
|
|
102
129
|
function iconSvg(pathData) {
|
|
103
130
|
return `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="${pathData}"></path></svg>`;
|
|
104
131
|
}
|
|
105
|
-
function renderCatalogArticle(body, entries) {
|
|
132
|
+
function renderCatalogArticle(body, entries, options) {
|
|
106
133
|
if (entries.length === 0) {
|
|
107
134
|
return body;
|
|
108
135
|
}
|
|
109
136
|
const directories = entries.filter((entry) => entry.kind === 'directory');
|
|
110
137
|
const articles = entries.filter((entry) => entry.kind === 'article');
|
|
138
|
+
const initialPostCount = Math.max(1, options.initialPostCount);
|
|
139
|
+
const visibleArticles = articles.slice(0, initialPostCount);
|
|
140
|
+
const shouldLoadMore = articles.length > visibleArticles.length;
|
|
111
141
|
return [
|
|
112
142
|
`<div class="catalog-page__body">${body}</div>`,
|
|
113
143
|
'<section class="catalog-page" aria-label="Catalog">',
|
|
114
144
|
directories.length > 0 ? renderCatalogDirectories(directories) : '',
|
|
115
|
-
articles.length > 0
|
|
145
|
+
articles.length > 0
|
|
146
|
+
? renderCatalogArticles(visibleArticles, {
|
|
147
|
+
requestPath: options.requestPath,
|
|
148
|
+
nextOffset: visibleArticles.length,
|
|
149
|
+
loadMoreStep: options.loadMoreStep,
|
|
150
|
+
hasMore: shouldLoadMore,
|
|
151
|
+
})
|
|
152
|
+
: '',
|
|
116
153
|
'</section>',
|
|
154
|
+
shouldLoadMore ? renderCatalogLoadMoreScript() : '',
|
|
117
155
|
].join('');
|
|
118
156
|
}
|
|
119
157
|
function renderCatalogDirectories(entries) {
|
|
@@ -125,12 +163,166 @@ function renderCatalogDirectories(entries) {
|
|
|
125
163
|
'</div>',
|
|
126
164
|
].join('');
|
|
127
165
|
}
|
|
128
|
-
function
|
|
166
|
+
export function renderCatalogArticleItems(entries) {
|
|
167
|
+
return entries
|
|
168
|
+
.map((entry) => `<a class="catalog-item" href="${escapeHtml(entry.href)}"><strong class="catalog-item__title">${escapeHtml(entry.title)}</strong>${entry.detail
|
|
169
|
+
? `<span class="catalog-item__detail">${escapeHtml(entry.detail)}</span>`
|
|
170
|
+
: ''}</a>`)
|
|
171
|
+
.join('');
|
|
172
|
+
}
|
|
173
|
+
function renderCatalogArticles(entries, options) {
|
|
129
174
|
return [
|
|
130
|
-
'<div class="catalog-list">',
|
|
131
|
-
|
|
132
|
-
? `<span class="catalog-item__detail">${escapeHtml(entry.detail)}</span>`
|
|
133
|
-
: ''}</a>`),
|
|
175
|
+
'<div class="catalog-list" data-catalog-articles>',
|
|
176
|
+
renderCatalogArticleItems(entries),
|
|
134
177
|
'</div>',
|
|
178
|
+
options.hasMore
|
|
179
|
+
? `<div class="catalog-load-more"><button type="button" class="catalog-load-more__button" data-catalog-load-more data-request-path="${escapeHtml(options.requestPath)}" data-next-offset="${escapeHtml(String(options.nextOffset))}" data-load-more-step="${escapeHtml(String(options.loadMoreStep))}">Load more</button></div>`
|
|
180
|
+
: '',
|
|
181
|
+
].join('');
|
|
182
|
+
}
|
|
183
|
+
function renderCatalogLoadMoreScript() {
|
|
184
|
+
return `<script>
|
|
185
|
+
(() => {
|
|
186
|
+
const button = document.querySelector('[data-catalog-load-more]');
|
|
187
|
+
const list = document.querySelector('[data-catalog-articles]');
|
|
188
|
+
if (!(button instanceof HTMLButtonElement) || !(list instanceof HTMLElement)) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const loadMore = async () => {
|
|
193
|
+
const requestPath = button.dataset.requestPath;
|
|
194
|
+
const nextOffset = button.dataset.nextOffset;
|
|
195
|
+
const loadMoreStep = button.dataset.loadMoreStep;
|
|
196
|
+
if (!requestPath || !nextOffset || !loadMoreStep) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
button.disabled = true;
|
|
201
|
+
const previousLabel = button.textContent;
|
|
202
|
+
button.textContent = 'Loading...';
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const url = new URL(requestPath, window.location.origin);
|
|
206
|
+
url.searchParams.set('catalog-format', 'posts');
|
|
207
|
+
url.searchParams.set('catalog-offset', nextOffset);
|
|
208
|
+
url.searchParams.set('catalog-limit', loadMoreStep);
|
|
209
|
+
|
|
210
|
+
const response = await fetch(url.toString(), {
|
|
211
|
+
headers: { Accept: 'application/json' },
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok) {
|
|
214
|
+
throw new Error('Failed to load more posts');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const payload = await response.json();
|
|
218
|
+
if (typeof payload.itemsHtml === 'string' && payload.itemsHtml !== '') {
|
|
219
|
+
list.insertAdjacentHTML('beforeend', payload.itemsHtml);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (payload.hasMore === true && typeof payload.nextOffset === 'number') {
|
|
223
|
+
button.dataset.nextOffset = String(payload.nextOffset);
|
|
224
|
+
button.disabled = false;
|
|
225
|
+
button.textContent = previousLabel ?? 'Load more';
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
button.remove();
|
|
230
|
+
} catch {
|
|
231
|
+
button.disabled = false;
|
|
232
|
+
button.textContent = previousLabel ?? 'Load more';
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
button.addEventListener('click', () => {
|
|
237
|
+
void loadMore();
|
|
238
|
+
});
|
|
239
|
+
})();
|
|
240
|
+
</script>`;
|
|
241
|
+
}
|
|
242
|
+
function renderSearchScript() {
|
|
243
|
+
return [
|
|
244
|
+
'<script>',
|
|
245
|
+
'(function () {',
|
|
246
|
+
' const root = document.querySelector("[data-site-search]");',
|
|
247
|
+
' if (!root) return;',
|
|
248
|
+
' const toggle = root.querySelector(".site-search__toggle");',
|
|
249
|
+
' const panel = root.querySelector(".site-search__panel");',
|
|
250
|
+
' const form = root.querySelector(".site-search__form");',
|
|
251
|
+
' const input = root.querySelector(".site-search__input");',
|
|
252
|
+
' const results = root.querySelector("[data-site-search-results]");',
|
|
253
|
+
' if (!toggle || !panel || !form || !input || !results) return;',
|
|
254
|
+
' let controller = null;',
|
|
255
|
+
' function renderMessage(message) {',
|
|
256
|
+
' results.innerHTML = `<p class="site-search__message">${escapeHtmlForScript(message)}</p>`;',
|
|
257
|
+
' }',
|
|
258
|
+
' function renderHits(hits) {',
|
|
259
|
+
' if (!Array.isArray(hits) || hits.length === 0) {',
|
|
260
|
+
' renderMessage("No results.");',
|
|
261
|
+
' return;',
|
|
262
|
+
' }',
|
|
263
|
+
' results.innerHTML = hits.map((hit) => {',
|
|
264
|
+
' const href = escapeHtmlForScript(hit.canonicalUrl || hit.docId || "#");',
|
|
265
|
+
' const title = escapeHtmlForScript(hit.title || hit.relativePath || "Untitled");',
|
|
266
|
+
' const summary = typeof hit.summary === "string" ? `<span class="site-search__item-summary">${escapeHtmlForScript(hit.summary)}</span>` : "";',
|
|
267
|
+
' const excerpt = hit.bestMatch && typeof hit.bestMatch.excerpt === "string" ? `<span class="site-search__item-excerpt">${escapeHtmlForScript(hit.bestMatch.excerpt)}</span>` : "";',
|
|
268
|
+
' return `<a class="site-search__item" href="${href}"><strong class="site-search__item-title">${title}</strong>${summary}${excerpt}</a>`;',
|
|
269
|
+
' }).join("");',
|
|
270
|
+
' }',
|
|
271
|
+
' async function runSearch(query) {',
|
|
272
|
+
' if (controller) controller.abort();',
|
|
273
|
+
' controller = new AbortController();',
|
|
274
|
+
' renderMessage("Searching...");',
|
|
275
|
+
' try {',
|
|
276
|
+
' const url = new URL("/api/search", window.location.origin);',
|
|
277
|
+
' url.searchParams.set("q", query);',
|
|
278
|
+
' url.searchParams.set("topK", "8");',
|
|
279
|
+
' const response = await fetch(url, { signal: controller.signal });',
|
|
280
|
+
' if (!response.ok) {',
|
|
281
|
+
' renderMessage(`Search failed (${response.status}).`);',
|
|
282
|
+
' return;',
|
|
283
|
+
' }',
|
|
284
|
+
' const payload = await response.json();',
|
|
285
|
+
' renderHits(payload.hits);',
|
|
286
|
+
' } catch (error) {',
|
|
287
|
+
' if (error && typeof error === "object" && "name" in error && error.name === "AbortError") return;',
|
|
288
|
+
' renderMessage("Search failed.");',
|
|
289
|
+
' }',
|
|
290
|
+
' }',
|
|
291
|
+
' function setOpen(open) {',
|
|
292
|
+
' toggle.setAttribute("aria-expanded", open ? "true" : "false");',
|
|
293
|
+
' panel.hidden = !open;',
|
|
294
|
+
' if (open) {',
|
|
295
|
+
' input.focus();',
|
|
296
|
+
' if (!results.innerHTML) renderMessage("Search docs, guides, and skills.");',
|
|
297
|
+
' }',
|
|
298
|
+
' }',
|
|
299
|
+
' toggle.addEventListener("click", () => setOpen(panel.hidden));',
|
|
300
|
+
' form.addEventListener("submit", (event) => {',
|
|
301
|
+
' event.preventDefault();',
|
|
302
|
+
' const query = input.value.trim();',
|
|
303
|
+
' if (!query) {',
|
|
304
|
+
' renderMessage("Enter a search query.");',
|
|
305
|
+
' return;',
|
|
306
|
+
' }',
|
|
307
|
+
' void runSearch(query);',
|
|
308
|
+
' });',
|
|
309
|
+
' document.addEventListener("keydown", (event) => {',
|
|
310
|
+
' if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {',
|
|
311
|
+
' event.preventDefault();',
|
|
312
|
+
' setOpen(true);',
|
|
313
|
+
' }',
|
|
314
|
+
' if (event.key === "Escape" && !panel.hidden) setOpen(false);',
|
|
315
|
+
' });',
|
|
316
|
+
'})();',
|
|
317
|
+
'',
|
|
318
|
+
'function escapeHtmlForScript(value) {',
|
|
319
|
+
' return String(value)',
|
|
320
|
+
' .replaceAll("&", "&")',
|
|
321
|
+
' .replaceAll("<", "<")',
|
|
322
|
+
' .replaceAll(">", ">")',
|
|
323
|
+
' .replaceAll(\'"\', """)',
|
|
324
|
+
' .replaceAll("\\\'", "'");',
|
|
325
|
+
'}',
|
|
326
|
+
'</script>',
|
|
135
327
|
].join('');
|
|
136
328
|
}
|
package/dist/html/theme.js
CHANGED
|
@@ -1,13 +1,135 @@
|
|
|
1
1
|
export function getBuiltInThemeStyles(theme) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
const themeStyles = (() => {
|
|
3
|
+
switch (theme) {
|
|
4
|
+
case 'atlas':
|
|
5
|
+
return buildAtlasThemeStyles();
|
|
6
|
+
case 'gazette':
|
|
7
|
+
return buildGazetteThemeStyles();
|
|
8
|
+
case 'paper':
|
|
9
|
+
default:
|
|
10
|
+
return buildPaperThemeStyles();
|
|
11
|
+
}
|
|
12
|
+
})();
|
|
13
|
+
return `${themeStyles}\n${buildSharedSearchStyles()}`;
|
|
14
|
+
}
|
|
15
|
+
function buildSharedSearchStyles() {
|
|
16
|
+
return `
|
|
17
|
+
.site-header__actions {
|
|
18
|
+
min-width: 0;
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: flex-end;
|
|
21
|
+
justify-content: flex-end;
|
|
22
|
+
gap: 1rem;
|
|
23
|
+
}
|
|
24
|
+
.site-search {
|
|
25
|
+
position: relative;
|
|
26
|
+
}
|
|
27
|
+
.site-search__toggle {
|
|
28
|
+
appearance: none;
|
|
29
|
+
border: 1px solid var(--border);
|
|
30
|
+
background: color-mix(in srgb, var(--surface) 92%, transparent);
|
|
31
|
+
color: var(--muted);
|
|
32
|
+
border-radius: 999px;
|
|
33
|
+
padding: 0.45rem 0.82rem;
|
|
34
|
+
font: inherit;
|
|
35
|
+
font-size: 0.88rem;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
}
|
|
38
|
+
.site-search__toggle:hover {
|
|
39
|
+
color: var(--text);
|
|
40
|
+
}
|
|
41
|
+
.site-search__panel {
|
|
42
|
+
position: absolute;
|
|
43
|
+
top: calc(100% + 0.7rem);
|
|
44
|
+
right: 0;
|
|
45
|
+
width: min(32rem, calc(100vw - 2rem));
|
|
46
|
+
padding: 1rem;
|
|
47
|
+
border: 1px solid var(--border);
|
|
48
|
+
border-radius: 18px;
|
|
49
|
+
background: color-mix(in srgb, var(--surface) 95%, white 5%);
|
|
50
|
+
box-shadow: 0 18px 40px rgba(24, 18, 11, 0.14);
|
|
51
|
+
z-index: 2;
|
|
52
|
+
}
|
|
53
|
+
.site-search__label {
|
|
54
|
+
display: block;
|
|
55
|
+
font-size: 0.8rem;
|
|
56
|
+
text-transform: uppercase;
|
|
57
|
+
letter-spacing: 0.08em;
|
|
58
|
+
color: var(--muted);
|
|
59
|
+
margin-bottom: 0.55rem;
|
|
60
|
+
}
|
|
61
|
+
.site-search__controls {
|
|
62
|
+
display: grid;
|
|
63
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
64
|
+
gap: 0.6rem;
|
|
65
|
+
}
|
|
66
|
+
.site-search__input,
|
|
67
|
+
.site-search__submit {
|
|
68
|
+
font: inherit;
|
|
69
|
+
}
|
|
70
|
+
.site-search__input {
|
|
71
|
+
width: 100%;
|
|
72
|
+
border: 1px solid var(--border);
|
|
73
|
+
border-radius: 12px;
|
|
74
|
+
background: var(--surface);
|
|
75
|
+
color: var(--text);
|
|
76
|
+
padding: 0.7rem 0.85rem;
|
|
77
|
+
}
|
|
78
|
+
.site-search__submit {
|
|
79
|
+
appearance: none;
|
|
80
|
+
border: 1px solid var(--text);
|
|
81
|
+
border-radius: 12px;
|
|
82
|
+
background: var(--text);
|
|
83
|
+
color: var(--surface);
|
|
84
|
+
padding: 0.7rem 0.95rem;
|
|
85
|
+
cursor: pointer;
|
|
86
|
+
}
|
|
87
|
+
.site-search__hint,
|
|
88
|
+
.site-search__message {
|
|
89
|
+
color: var(--muted);
|
|
90
|
+
font-size: 0.84rem;
|
|
91
|
+
}
|
|
92
|
+
.site-search__results {
|
|
93
|
+
margin-top: 0.9rem;
|
|
94
|
+
display: grid;
|
|
95
|
+
gap: 0.7rem;
|
|
96
|
+
}
|
|
97
|
+
.site-search__item {
|
|
98
|
+
display: block;
|
|
99
|
+
text-decoration: none;
|
|
100
|
+
border-top: 1px solid var(--border);
|
|
101
|
+
padding-top: 0.7rem;
|
|
102
|
+
}
|
|
103
|
+
.site-search__item:first-child {
|
|
104
|
+
border-top: 0;
|
|
105
|
+
padding-top: 0;
|
|
106
|
+
}
|
|
107
|
+
.site-search__item-title {
|
|
108
|
+
display: block;
|
|
109
|
+
color: var(--text);
|
|
110
|
+
}
|
|
111
|
+
.site-search__item-summary,
|
|
112
|
+
.site-search__item-excerpt {
|
|
113
|
+
display: block;
|
|
114
|
+
margin-top: 0.25rem;
|
|
115
|
+
color: var(--muted);
|
|
116
|
+
font-size: 0.88rem;
|
|
117
|
+
}
|
|
118
|
+
.site-search__item-excerpt {
|
|
119
|
+
font-size: 0.82rem;
|
|
120
|
+
}
|
|
121
|
+
@media (max-width: 720px) {
|
|
122
|
+
.site-header__actions {
|
|
123
|
+
flex-direction: column;
|
|
124
|
+
align-items: stretch;
|
|
125
|
+
}
|
|
126
|
+
.site-search__panel {
|
|
127
|
+
left: 0;
|
|
128
|
+
right: auto;
|
|
129
|
+
width: min(100%, 34rem);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
`.trim();
|
|
11
133
|
}
|
|
12
134
|
function buildPaperThemeStyles() {
|
|
13
135
|
return `
|
|
@@ -125,6 +247,13 @@ main {
|
|
|
125
247
|
padding-top: 1rem;
|
|
126
248
|
color: var(--muted);
|
|
127
249
|
}
|
|
250
|
+
.site-footer__meta {
|
|
251
|
+
margin-top: 0.9rem;
|
|
252
|
+
display: flex;
|
|
253
|
+
align-items: center;
|
|
254
|
+
justify-content: space-between;
|
|
255
|
+
gap: 1rem;
|
|
256
|
+
}
|
|
128
257
|
.site-footer__nav ul,
|
|
129
258
|
.site-footer__social {
|
|
130
259
|
list-style: none;
|
|
@@ -163,6 +292,19 @@ main {
|
|
|
163
292
|
display: block;
|
|
164
293
|
margin-top: 0.9rem;
|
|
165
294
|
}
|
|
295
|
+
.site-footer__edit-link {
|
|
296
|
+
display: inline-block;
|
|
297
|
+
margin-top: 0;
|
|
298
|
+
font-size: 0.78rem;
|
|
299
|
+
color: var(--muted);
|
|
300
|
+
opacity: 0.72;
|
|
301
|
+
}
|
|
302
|
+
@media (max-width: 720px) {
|
|
303
|
+
.site-footer__meta {
|
|
304
|
+
flex-direction: column;
|
|
305
|
+
align-items: flex-start;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
166
308
|
article {
|
|
167
309
|
background: color-mix(in srgb, var(--surface) 92%, white 8%);
|
|
168
310
|
border: 1px solid var(--border);
|
|
@@ -253,6 +395,27 @@ hr { border: none; border-top: 1px solid var(--border); margin: 2rem 0; }
|
|
|
253
395
|
.catalog-item:hover {
|
|
254
396
|
text-decoration: none;
|
|
255
397
|
}
|
|
398
|
+
.catalog-load-more {
|
|
399
|
+
margin-top: 1.2rem;
|
|
400
|
+
}
|
|
401
|
+
.catalog-load-more__button {
|
|
402
|
+
appearance: none;
|
|
403
|
+
border: 1px solid var(--border);
|
|
404
|
+
background: var(--surface);
|
|
405
|
+
color: var(--text);
|
|
406
|
+
border-radius: 999px;
|
|
407
|
+
padding: 0.65rem 1rem;
|
|
408
|
+
font: inherit;
|
|
409
|
+
font-size: 0.95rem;
|
|
410
|
+
cursor: pointer;
|
|
411
|
+
}
|
|
412
|
+
.catalog-load-more__button:hover {
|
|
413
|
+
background: color-mix(in srgb, var(--surface) 86%, var(--code-bg) 14%);
|
|
414
|
+
}
|
|
415
|
+
.catalog-load-more__button:disabled {
|
|
416
|
+
cursor: wait;
|
|
417
|
+
opacity: 0.7;
|
|
418
|
+
}
|
|
256
419
|
@media (max-width: 720px) {
|
|
257
420
|
html { font-size: 17px; }
|
|
258
421
|
.site-header, main, .site-footer { padding-left: 1rem; padding-right: 1rem; }
|
|
@@ -386,6 +549,13 @@ main {
|
|
|
386
549
|
padding-top: 1rem;
|
|
387
550
|
color: var(--muted);
|
|
388
551
|
}
|
|
552
|
+
.site-footer__meta {
|
|
553
|
+
margin-top: 1rem;
|
|
554
|
+
display: flex;
|
|
555
|
+
align-items: center;
|
|
556
|
+
justify-content: space-between;
|
|
557
|
+
gap: 1rem;
|
|
558
|
+
}
|
|
389
559
|
.site-footer__nav ul,
|
|
390
560
|
.site-footer__social {
|
|
391
561
|
list-style: none;
|
|
@@ -424,6 +594,19 @@ main {
|
|
|
424
594
|
display: block;
|
|
425
595
|
margin-top: 0.95rem;
|
|
426
596
|
}
|
|
597
|
+
.site-footer__edit-link {
|
|
598
|
+
display: inline-block;
|
|
599
|
+
margin-top: 0;
|
|
600
|
+
font-size: 0.78rem;
|
|
601
|
+
color: var(--muted);
|
|
602
|
+
opacity: 0.72;
|
|
603
|
+
}
|
|
604
|
+
@media (max-width: 720px) {
|
|
605
|
+
.site-footer__meta {
|
|
606
|
+
flex-direction: column;
|
|
607
|
+
align-items: flex-start;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
427
610
|
article {
|
|
428
611
|
background: linear-gradient(180deg, rgba(255,255,255,0.96), rgba(247,250,252,0.98));
|
|
429
612
|
border: 1px solid var(--border);
|
|
@@ -518,6 +701,27 @@ hr { border: none; border-top: 1px solid var(--border); margin: 2rem 0; }
|
|
|
518
701
|
.catalog-item:hover {
|
|
519
702
|
text-decoration: none;
|
|
520
703
|
}
|
|
704
|
+
.catalog-load-more {
|
|
705
|
+
margin-top: 1.2rem;
|
|
706
|
+
}
|
|
707
|
+
.catalog-load-more__button {
|
|
708
|
+
appearance: none;
|
|
709
|
+
border: 1px solid var(--border);
|
|
710
|
+
background: var(--surface);
|
|
711
|
+
color: var(--text);
|
|
712
|
+
border-radius: 999px;
|
|
713
|
+
padding: 0.65rem 1rem;
|
|
714
|
+
font: inherit;
|
|
715
|
+
font-size: 0.95rem;
|
|
716
|
+
cursor: pointer;
|
|
717
|
+
}
|
|
718
|
+
.catalog-load-more__button:hover {
|
|
719
|
+
background: color-mix(in srgb, var(--surface) 75%, var(--accent) 25%);
|
|
720
|
+
}
|
|
721
|
+
.catalog-load-more__button:disabled {
|
|
722
|
+
cursor: wait;
|
|
723
|
+
opacity: 0.7;
|
|
724
|
+
}
|
|
521
725
|
@media (max-width: 720px) {
|
|
522
726
|
.site-header__inner, main, .site-footer { padding-left: 1rem; padding-right: 1rem; }
|
|
523
727
|
.site-header__inner {
|
|
@@ -648,6 +852,13 @@ main {
|
|
|
648
852
|
padding-top: 1rem;
|
|
649
853
|
color: var(--muted);
|
|
650
854
|
}
|
|
855
|
+
.site-footer__meta {
|
|
856
|
+
margin-top: 0.95rem;
|
|
857
|
+
display: flex;
|
|
858
|
+
align-items: center;
|
|
859
|
+
justify-content: space-between;
|
|
860
|
+
gap: 1rem;
|
|
861
|
+
}
|
|
651
862
|
.site-footer__nav ul,
|
|
652
863
|
.site-footer__social {
|
|
653
864
|
list-style: none;
|
|
@@ -686,6 +897,19 @@ main {
|
|
|
686
897
|
display: block;
|
|
687
898
|
margin-top: 0.9rem;
|
|
688
899
|
}
|
|
900
|
+
.site-footer__edit-link {
|
|
901
|
+
display: inline-block;
|
|
902
|
+
margin-top: 0;
|
|
903
|
+
font-size: 0.78rem;
|
|
904
|
+
color: var(--muted);
|
|
905
|
+
opacity: 0.72;
|
|
906
|
+
}
|
|
907
|
+
@media (max-width: 720px) {
|
|
908
|
+
.site-footer__meta {
|
|
909
|
+
flex-direction: column;
|
|
910
|
+
align-items: flex-start;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
689
913
|
article {
|
|
690
914
|
background:
|
|
691
915
|
linear-gradient(180deg, rgba(255,255,255,0.94), rgba(255,253,248,0.98)),
|
|
@@ -785,6 +1009,27 @@ hr { border: none; border-top: 1px solid var(--border); margin: 2rem 0; }
|
|
|
785
1009
|
.catalog-item:hover {
|
|
786
1010
|
text-decoration: none;
|
|
787
1011
|
}
|
|
1012
|
+
.catalog-load-more {
|
|
1013
|
+
margin-top: 1.2rem;
|
|
1014
|
+
}
|
|
1015
|
+
.catalog-load-more__button {
|
|
1016
|
+
appearance: none;
|
|
1017
|
+
border: 1px solid var(--border);
|
|
1018
|
+
background: var(--surface);
|
|
1019
|
+
color: var(--text);
|
|
1020
|
+
border-radius: 999px;
|
|
1021
|
+
padding: 0.65rem 1rem;
|
|
1022
|
+
font: inherit;
|
|
1023
|
+
font-size: 0.95rem;
|
|
1024
|
+
cursor: pointer;
|
|
1025
|
+
}
|
|
1026
|
+
.catalog-load-more__button:hover {
|
|
1027
|
+
background: color-mix(in srgb, var(--surface) 84%, #fff 16%);
|
|
1028
|
+
}
|
|
1029
|
+
.catalog-load-more__button:disabled {
|
|
1030
|
+
cursor: wait;
|
|
1031
|
+
opacity: 0.7;
|
|
1032
|
+
}
|
|
788
1033
|
@media (max-width: 720px) {
|
|
789
1034
|
html { font-size: 17px; }
|
|
790
1035
|
.site-header, main, .site-footer { padding-left: 1rem; padding-right: 1rem; }
|