chore: bump site-kit, use new components

- use search components
- use shell component
pull/8453/head
Simon Holthausen 1 year ago
parent 339ea85d55
commit bad1780d48

@ -22,7 +22,7 @@
"@resvg/resvg-js": "^2.4.1",
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.12.0",
"@sveltejs/site-kit": "^3.2.2",
"@sveltejs/site-kit": "^3.3.6",
"@sveltejs/vite-plugin-svelte": "^2.0.3",
"@types/marked": "^4.0.8",
"@types/prismjs": "^1.26.0",
@ -1274,11 +1274,21 @@
"yootils": "^0.3.1"
}
},
"node_modules/@sveltejs/site-kit": {
"node_modules/@sveltejs/repl/node_modules/@sveltejs/site-kit": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-3.2.2.tgz",
"integrity": "sha512-HBLtfNdLr5Ykl8i8CvJcYjib7zMIJupg4T/omplp3ccpgpiUh26tk71vRG1+a6yMkfbfy0ShoPb9uwNril5cnw=="
},
"node_modules/@sveltejs/site-kit": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-3.3.6.tgz",
"integrity": "sha512-MUqHseHHFJOT6AcJYTbUqK7hMy1OkBa231tS4TXkmbgQ8EaFVI2unjs9+WY8m3CXASc4p31givOiKVSqtorbbg==",
"dev": true,
"peerDependencies": {
"@sveltejs/kit": "^1.0.0",
"svelte": "^3.54.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.3.tgz",
@ -4925,12 +4935,21 @@
"resolve.exports": "^2.0.0",
"svelte-json-tree": "^1.0.0",
"yootils": "^0.3.1"
},
"dependencies": {
"@sveltejs/site-kit": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-3.2.2.tgz",
"integrity": "sha512-HBLtfNdLr5Ykl8i8CvJcYjib7zMIJupg4T/omplp3ccpgpiUh26tk71vRG1+a6yMkfbfy0ShoPb9uwNril5cnw=="
}
}
},
"@sveltejs/site-kit": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-3.2.2.tgz",
"integrity": "sha512-HBLtfNdLr5Ykl8i8CvJcYjib7zMIJupg4T/omplp3ccpgpiUh26tk71vRG1+a6yMkfbfy0ShoPb9uwNril5cnw=="
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-3.3.6.tgz",
"integrity": "sha512-MUqHseHHFJOT6AcJYTbUqK7hMy1OkBa231tS4TXkmbgQ8EaFVI2unjs9+WY8m3CXASc4p31givOiKVSqtorbbg==",
"dev": true,
"requires": {}
},
"@sveltejs/vite-plugin-svelte": {
"version": "2.0.3",

@ -30,7 +30,7 @@
"@resvg/resvg-js": "^2.4.1",
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.12.0",
"@sveltejs/site-kit": "^3.2.2",
"@sveltejs/site-kit": "^3.3.6",
"@sveltejs/vite-plugin-svelte": "^2.0.3",
"@types/marked": "^4.0.8",
"@types/prismjs": "^1.26.0",

@ -1,68 +0,0 @@
/** @param {HTMLElement} node */
export function focusable_children(node) {
const nodes = Array.from(
node.querySelectorAll(
'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
)
);
const index = nodes.indexOf(document.activeElement);
const update = (d) => {
let i = index + d;
i += nodes.length;
i %= nodes.length;
// @ts-expect-error
nodes[i].focus();
};
return {
/** @param {string} [selector] */
next: (selector) => {
const reordered = [...nodes.slice(index + 1), ...nodes.slice(0, index + 1)];
for (let i = 0; i < reordered.length; i += 1) {
if (!selector || reordered[i].matches(selector)) {
reordered[i].focus();
return;
}
}
},
/** @param {string} [selector] */
prev: (selector) => {
const reordered = [...nodes.slice(index + 1), ...nodes.slice(0, index + 1)];
for (let i = reordered.length - 2; i >= 0; i -= 1) {
if (!selector || reordered[i].matches(selector)) {
reordered[i].focus();
return;
}
}
},
update
};
}
export function trap(node) {
const handle_keydown = (e) => {
if (e.key === 'Tab') {
e.preventDefault();
const group = focusable_children(node);
if (e.shiftKey) {
group.prev();
} else {
group.next();
}
}
};
node.addEventListener('keydown', handle_keydown);
return {
destroy: () => {
node.removeEventListener('keydown', handle_keydown);
}
};
}

@ -1,130 +0,0 @@
<script>
import { browser } from '$app/environment';
import { searching, query } from './stores.js';
export let q = '';
</script>
<form class="search-container" action="/search">
<input
value={q}
on:input={(e) => {
$searching = true;
$query = e.currentTarget.value;
e.currentTarget.value = '';
}}
on:mousedown|preventDefault={() => ($searching = true)}
on:touchend|preventDefault={() => ($searching = true)}
type="search"
name="q"
placeholder="Search"
aria-label="Search"
spellcheck="false"
/>
{#if browser}
<div class="shortcut">
<kbd>{navigator.platform === 'MacIntel' ? '⌘' : 'Ctrl'}</kbd> <kbd>K</kbd>
</div>
{/if}
</form>
<style>
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.search-container {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: 100%;
}
input {
padding: 0.5em 0.5em 0.4em 2em;
border: 1px solid var(--sk-back-translucent);
font-family: inherit;
font-size: 1.4rem;
/* text-align: center; */
appearance: none;
-webkit-appearance: none;
width: 100%;
height: 3.2rem;
border-radius: var(--sk-border-radius);
background: no-repeat 1rem 50% / 1em 1em url(/icons/search.svg);
color: var(--sk-text-3);
}
input:focus + .shortcut {
display: none;
}
input::placeholder {
font-size: 1.2rem;
text-transform: uppercase;
}
.shortcut {
color: var(--sk-text-3);
position: absolute;
top: calc(50% - 0.9rem);
right: 0;
width: 100%;
text-align: right;
pointer-events: none;
font-size: 1.2rem;
text-transform: uppercase;
animation: fade-in 0.2s;
}
kbd {
display: none;
background: var(--sk-back-2);
border: 1px solid var(--sk-back-translucent);
padding: 0.2rem 0.2rem 0rem 0.2rem;
color: var(--sk-text-3);
font-size: inherit;
font-family: inherit;
border-radius: 2px;
}
@media (min-width: 800px) {
.search-container {
width: 11rem;
}
.shortcut {
padding: 0 1.6rem 0 0;
}
input {
border-radius: 1.6rem;
}
input::placeholder {
opacity: 0;
}
/* we're using media query as an imperfect proxy for mobile/desktop */
kbd {
display: inline;
}
}
@media (min-width: 960px) {
.search-container {
width: 19rem;
}
input::placeholder {
opacity: 1;
}
}
</style>

@ -1,420 +0,0 @@
<script>
import { onMount } from 'svelte';
import Icon from '@sveltejs/site-kit/components/Icon.svelte';
import { afterNavigate } from '$app/navigation';
import { searching, query, recent } from './stores.js';
import { focusable_children, trap } from '../actions/focus.js';
import SearchResults from './SearchResults.svelte';
import SearchWorker from '$lib/workers/search.js?worker';
let modal;
let search = null;
let recent_searches = [];
let worker;
let ready = false;
let uid = 1;
const pending = new Set();
onMount(async () => {
worker = new SearchWorker();
worker.addEventListener('message', (event) => {
const { type, payload } = event.data;
if (type === 'ready') {
ready = true;
}
if (type === 'results') {
search = payload;
}
if (type === 'recents') {
recent_searches = payload;
}
});
worker.postMessage({
type: 'init',
payload: {
origin: location.origin,
},
});
});
afterNavigate(() => {
// TODO this also needs to apply when only the hash changes
// (should before/afterNavigate fire at that time? unclear)
close();
});
function close() {
if ($searching) {
$searching = false;
const scroll = -parseInt(document.body.style.top || '0');
document.body.style.position = '';
document.body.style.top = '';
document.body.tabIndex = -1;
document.body.focus();
document.body.removeAttribute('tabindex');
window.scrollTo(0, scroll);
}
search = null;
}
/** @param {string} href */
function navigate(href) {
$recent = [href, ...$recent.filter((x) => x !== href)];
close();
}
$: if (ready) {
const id = uid++;
pending.add(id);
worker.postMessage({ type: 'query', id, payload: $query });
}
$: if (ready) {
worker.postMessage({ type: 'recents', payload: $recent });
}
$: if ($searching) {
document.body.style.top = `-${window.scrollY}px`;
document.body.style.position = 'fixed';
}
</script>
<svelte:window
on:keydown={(e) => {
if (e.key === 'k' && (navigator.platform === 'MacIntel' ? e.metaKey : e.ctrlKey)) {
e.preventDefault();
$query = '';
if ($searching) {
close();
} else {
$searching = true;
}
}
if (e.code === 'Escape') {
close();
}
}}
/>
{#if $searching && ready}
<div class="modal-background" on:click={close} on:keyup={(e) => e.key === ' ' && close()} />
<div
bind:this={modal}
class="modal"
on:keydown={(e) => {
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
e.preventDefault();
const group = focusable_children(e.currentTarget);
// when using arrow keys (as opposed to tab), don't focus buttons
const selector = 'a, input';
if (e.key === 'ArrowDown') {
group.next(selector);
} else {
group.prev(selector);
}
}
}}
use:trap
>
<div class="search-box">
<!-- svelte-ignore a11y-autofocus -->
<input
autofocus
on:keydown={(e) => {
if (e.key === 'Enter') {
modal.querySelector('a[data-has-node]')?.click();
}
}}
on:input={(e) => ($query = e.currentTarget.value)}
value={$query}
placeholder="Search"
aria-describedby="search-description"
aria-label="Search"
spellcheck="false"
/>
<button aria-label="Close" on:click={close}>
<Icon name="close" />
</button>
<span id="search-description" class="visually-hidden">Results will update as you type</span>
<div class="results">
{#if search?.query}
<div
class="results-container"
on:click={() => ($searching = false)}
on:keydown={(e) => e.key === ' ' && ($searching = false)}
>
<SearchResults
results={search.results}
query={search.query}
on:select={(e) => {
navigate(e.detail.href);
}}
/>
</div>
{:else}
<h2 class="info" class:empty={recent_searches.length === 0}>
{recent_searches.length ? 'Recent searches' : 'No recent searches'}
</h2>
{#if recent_searches.length}
<div class="results-container">
<ul>
{#each recent_searches as search, i}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<li class="recent">
<a on:click={() => navigate(search.href)} href={search.href}>
<small>{search.breadcrumbs.join('/')}</small>
<strong>{search.breadcrumbs.at(-1)}</strong>
</a>
<button
aria-label="Delete"
on:click={(e) => {
$recent = $recent.filter((href) => href !== search.href);
e.stopPropagation();
e.preventDefault();
}}
>
<Icon name="delete" />
</button>
</li>
{/each}
</ul>
</div>
{/if}
{/if}
</div>
</div>
</div>
{/if}
<div aria-live="assertive" class="visually-hidden">
{#if $searching && search?.results.length === 0}
<p>No results</p>
{/if}
</div>
<style>
input {
font-family: inherit;
font-size: 1.6rem;
width: 100%;
padding: 1rem 6rem 0.5rem 1rem;
height: 5rem;
border: none;
border-bottom: 1px solid var(--sk-back-3);
font-weight: 600;
flex-shrink: 0;
background: var(--sk-back-2);
color: var(--sk-text-1);
}
input::selection {
background-color: var(--sk-back-translucent);
}
input::placeholder {
color: var(--sk-text-3);
opacity: 0.3;
}
input:focus-visible {
background: var(--sk-theme-2);
color: white;
outline: none;
}
input:focus-visible::placeholder {
color: rgba(255, 255, 255, 0.5);
}
button[aria-label='Close'] {
--size: 2rem;
position: absolute;
top: 0;
right: 0;
width: 5rem;
height: 5rem;
background: none;
color: var(--sk-text-2);
}
button[aria-label='Close']:focus-visible {
background: var(--sk-theme-2);
color: var(--sk-back-1);
outline: none;
}
input:focus-visible + button[aria-label='Close'] {
color: var(--sk-back-1);
}
ul {
margin: 0;
}
.modal-background,
.modal {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 9999;
}
.modal-background {
background: var(--sk-back-1);
opacity: 0.7;
}
.modal {
display: flex;
justify-content: center;
align-items: center;
pointer-events: none;
}
.search-box {
position: relative;
height: calc(100% - 2rem);
width: calc(100vw - 2rem);
max-width: 50rem;
max-height: 50rem;
filter: drop-shadow(2px 4px 16px rgba(0, 0, 0, 0.2));
border-radius: var(--sk-border-radius);
display: flex;
flex-direction: column;
overflow: hidden;
}
.search-box > * {
pointer-events: all;
}
.results {
overflow: auto;
overscroll-behavior-y: none;
}
.results-container {
background: var(--sk-back-2);
border-radius: 0 0 var(--sk-border-radius) var(--sk-border-radius);
pointer-events: all;
}
.info {
padding: 1rem;
font-size: 1.2rem;
font-weight: normal;
text-transform: uppercase;
background-color: var(--sk-back-2);
pointer-events: all;
}
.info.empty {
border-radius: 0 0 var(--sk-border-radius) var(--sk-border-radius);
}
a {
display: block;
text-decoration: none;
line-height: 1;
padding: 1rem;
}
a:hover {
background: rgba(0, 0, 0, 0.05);
}
a:focus {
background: var(--sk-theme-2);
color: var(--sk-back-1);
outline: none;
}
a small,
a strong {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1;
}
a small {
font-size: 1rem;
text-transform: uppercase;
font-weight: 600;
color: var(--sk-text-3);
}
a strong {
font-size: 1.6rem;
color: var(--sk-text-2);
margin: 0.4rem 0;
}
a:focus small {
color: white;
opacity: 0.6;
}
a:focus strong {
color: white;
}
a strong :global(mark) {
background: var(--sk-theme-2);
color: var(--sk-text-3);
text-decoration: none;
border-radius: 1px;
}
li {
position: relative;
}
button[aria-label='Delete'] {
position: absolute;
top: 0;
right: 0;
width: 5rem;
height: 100%;
color: var(--sk-text-2);
opacity: 0.1;
}
a:focus + [aria-label='Delete'] {
color: var(--sk-back-1);
}
button[aria-label='Delete']:hover {
opacity: 1;
outline: none;
}
button[aria-label='Delete']:focus-visible {
background: var(--sk-theme-2);
color: var(--sk-text-1);
opacity: 1;
outline: none;
}
</style>

@ -1,163 +0,0 @@
<script>
import { createEventDispatcher } from 'svelte';
/** @type {import('./types').Tree[]} */
export let results;
/** @type {string} */
export let query;
const dispatch = createEventDispatcher();
/** @param {string} text */
function escape(text) {
return text.replace(/</g, '&lt;').replace(/>/g, '&gt;').replaceAll('`', '');
}
/**
* @param {string} content
* @param {string} query
*/
function excerpt(content, query) {
if (content === null) return '';
const index = content.toLowerCase().indexOf(query.toLowerCase());
if (index === -1) {
return escape(content.slice(0, 100));
}
const prefix = index > 20 ? `…${content.slice(index - 15, index)}` : content.slice(0, index);
const suffix = content.slice(
index + query.length,
index + query.length + (80 - (prefix.length + query.length))
);
return (
escape(prefix) +
`<mark>${escape(content.slice(index, index + query.length))}</mark>` +
escape(suffix)
);
}
</script>
<ul>
{#each results as result (result.href)}
<li>
<a
data-sveltekit-preload-data
href={result.href}
on:click={() => dispatch('select', { href: result.href })}
data-has-node={result.node ? true : undefined}
>
<strong>{@html excerpt(result.breadcrumbs[result.breadcrumbs.length - 1], query)}</strong>
{#if result.node?.content}
<span>{@html excerpt(result.node.content, query)}</span>
{/if}
</a>
{#if result.children.length > 0}
<svelte:self results={result.children} {query} on:select />
{/if}
</li>
{/each}
</ul>
<style>
ul {
position: relative;
margin: 0;
}
ul :global(ul) {
margin-left: 0.8em !important;
padding-left: 0em;
border-left: 1px solid var(--sk-back-5);
}
li {
list-style: none;
margin-bottom: 1em;
}
li:last-child {
margin-bottom: 0;
}
ul ul li {
margin: 0;
}
a {
display: block;
text-decoration: none;
line-height: 1;
padding: 1rem;
}
a:hover {
background: rgba(0, 0, 0, 0.05);
}
a:focus {
background: var(--sk-theme-2);
color: white;
outline: none;
}
a strong,
a span {
display: block;
white-space: nowrap;
line-height: 1;
overflow: hidden;
text-overflow: ellipsis;
}
a strong {
font-size: 1.6rem;
color: var(--sk-text-2);
}
a span {
font-size: 1.2rem;
color: #737373;
margin: 0.4rem 0 0 0;
}
a :global(mark) {
--highlight-color: rgba(255, 255, 0, 0.2);
}
a span :global(mark) {
background: none;
color: var(--sk-text-1);
background: var(--highlight-color);
outline: 2px solid var(--highlight-color);
border-top: 2px solid var(--highlight-color);
/* mix-blend-mode: darken; */
}
a:focus span {
color: rgba(255, 255, 255, 0.6);
}
a:focus strong {
color: white;
}
a:focus span :global(mark),
a:focus strong :global(mark) {
--highlight-color: hsl(240, 8%, 54%);
mix-blend-mode: lighten;
color: white;
}
a strong :global(mark) {
color: var(--sk-text-1);
background: var(--highlight-color);
outline: 2px solid var(--highlight-color);
/* border-top: 2px solid var(--highlight-color); */
border-radius: 1px;
}
</style>

@ -1,28 +0,0 @@
<script>
import SearchResultList from './SearchResultList.svelte';
/** @type {import('./types').Tree[]} */
export let results;
/** @type {string} */
export let query;
</script>
{#if results.length > 0}
<SearchResultList {results} {query} on:select />
{:else if query}
<p class="info">No results</p>
{/if}
<style>
.info {
padding: 1rem;
font-size: 1.2rem;
font-weight: normal;
text-transform: uppercase;
background-color: var(--sk-back-2);
border-radius: 0 0 var(--sk-border-radius) var(--sk-border-radius);
pointer-events: all;
margin: 0;
}
</style>

@ -1,107 +0,0 @@
import flexsearch from 'flexsearch';
// @ts-expect-error
const Index = /** @type {import('flexsearch').Index} */ (flexsearch.Index ?? flexsearch);
export let inited = false;
/** @type {import('flexsearch').Index[]} */
let indexes;
/** @type {Map<string, import('./types').Block>} */
const map = new Map();
/** @type {Map<string, string>} */
const hrefs = new Map();
/** @param {import('./types').Block[]} blocks */
export function init(blocks) {
if (inited) return;
// we have multiple indexes, so we can rank sections (migration guide comes last)
const max_rank = Math.max(...blocks.map((block) => block.rank ?? 0));
indexes = Array.from({ length: max_rank + 1 }, () => new Index({ tokenize: 'forward' }));
for (const block of blocks) {
const title = block.breadcrumbs.at(-1);
map.set(block.href, block);
// NOTE: we're not using a number as the ID here, but it is recommended:
// https://github.com/nextapps-de/flexsearch#use-numeric-ids
// If we were to switch to a number we would need a second map from ID to block
// We need to keep the existing one to allow looking up recent searches by URL even if docs change
// It's unclear how much browsers do string interning and how this might affect memory
// We'd probably want to test both implementations across browsers if memory usage becomes an issue
// TODO: fix the type by updating flexsearch after
// https://github.com/nextapps-de/flexsearch/pull/364 is merged and released
indexes[block.rank ?? 0].add(block.href, `${title} ${block.content}`);
hrefs.set(block.breadcrumbs.join('::'), block.href);
}
inited = true;
}
/**
* @param {string} query
* @returns {import('./types').Block[]}
*/
export function search(query) {
const escaped = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
const regex = new RegExp(`(^|\\b)${escaped}`, 'i');
const blocks = indexes
.map((index) => index.search(query))
.flat()
.map(lookup)
.map((block, rank) => ({ block, rank }))
.sort((a, b) => {
const a_title_matches = regex.test(a.block.breadcrumbs.at(-1));
const b_title_matches = regex.test(b.block.breadcrumbs.at(-1));
// massage the order a bit, so that title matches
// are given higher priority
if (a_title_matches !== b_title_matches) {
return a_title_matches ? -1 : 1;
}
return a.block.breadcrumbs.length - b.block.breadcrumbs.length || a.rank - b.rank;
})
.map(({ block }) => block);
const results = tree([], blocks).children;
return results;
}
/** @param {string} href */
export function lookup(href) {
return map.get(href);
}
/**
* @param {string[]} breadcrumbs
* @param {import('./types').Block[]} blocks
*/
function tree(breadcrumbs, blocks) {
const depth = breadcrumbs.length;
const node = blocks.find((block) => {
if (block.breadcrumbs.length !== depth) return false;
return breadcrumbs.every((part, i) => block.breadcrumbs[i] === part);
});
const descendants = blocks.filter((block) => {
if (block.breadcrumbs.length <= depth) return false;
return breadcrumbs.every((part, i) => block.breadcrumbs[i] === part);
});
const child_parts = Array.from(new Set(descendants.map((block) => block.breadcrumbs[depth])));
return {
breadcrumbs,
href: hrefs.get(breadcrumbs.join('::')),
node,
children: child_parts.map((part) => tree([...breadcrumbs, part], descendants)),
};
}

@ -1,8 +0,0 @@
import { writable } from 'svelte/store';
import { persisted } from 'svelte-local-storage-store';
export const searching = writable(false);
export const query = writable('');
/** @type {import('svelte/store').Writable<any[]>} */
export const recent = persisted('recent_searches', []);

@ -1,13 +0,0 @@
export interface Block {
breadcrumbs: string[];
href: string;
content: string;
rank: number;
}
export interface Tree {
breadcrumbs: string[];
href: string;
node: Block;
children: Tree[];
}

@ -1,26 +0,0 @@
import { init, search, lookup } from '../search/search.js';
addEventListener('message', async (event) => {
const { type, payload } = event.data;
if (type === 'init') {
const res = await fetch(`${payload.origin}/content.json`);
const { blocks } = await res.json();
init(blocks);
postMessage({ type: 'ready' });
}
if (type === 'query') {
const query = payload;
const results = search(query);
postMessage({ type: 'results', payload: { results, query } });
}
if (type === 'recents') {
const results = payload.map(lookup).filter(Boolean);
postMessage({ type: 'recents', payload: results });
}
});

@ -1,6 +1,6 @@
<script>
import { getContext } from 'svelte';
import Icon from '@sveltejs/site-kit/components/Icon.svelte';
import { Icon } from '@sveltejs/site-kit/components';
import { ago } from '$lib/time';
import { goto, invalidate } from '$app/navigation';

@ -1,7 +1,7 @@
<script>
import { createEventDispatcher, getContext } from 'svelte';
import UserMenu from './UserMenu.svelte';
import Icon from '@sveltejs/site-kit/components/Icon.svelte';
import { Icon } from '@sveltejs/site-kit/components';
import * as doNotZip from 'do-not-zip';
import downloadBlob from './downloadBlob.js';
import { enter } from '$lib/utils/events.js';

@ -1,27 +1,23 @@
<script>
import { browser } from '$app/environment';
import { navigating, page } from '$app/stores';
import PreloadingIndicator from '$lib/components/PreloadingIndicator.svelte';
import Search from '$lib/search/Search.svelte';
import SearchBox from '$lib/search/SearchBox.svelte';
import { Icon, Icons, Nav, NavItem, SkipLink } from '@sveltejs/site-kit';
import { page } from '$app/stores';
import { Icon, Nav, NavItem, Separator, Shell } from '@sveltejs/site-kit/components';
import { Search, SearchBox } from '@sveltejs/site-kit/search';
import '@sveltejs/site-kit/styles/index.css';
</script>
<Icons />
{#if $navigating && $navigating.to}
<PreloadingIndicator />
{/if}
<svelte:head>
{#if $page.route.id !== '/blog/[slug]'}
<meta name="twitter:card" content="summary" />
<meta name="twitter:image" content="https://svelte.dev/images/twitter-thumbnail.jpg" />
<meta name="og:image" content="https://svelte.dev/images/twitter-thumbnail.jpg" />
{/if}
</svelte:head>
{#if $page.url.pathname !== '/repl/embed'}
<SkipLink href="#main" />
<Nav {page} logo="/svelte-logo.svg">
<Shell nav_visible={$page.url.pathname !== '/repl/embed'}>
<Nav logo="/svelte-logo.svg">
<svelte:fragment slot="nav-center">
{#if $page.url.pathname !== '/search'}
<!-- the <Nav> component renders this content inside a <ul>, so
we need to wrap it in an <li>. TODO if we adopt this design
on other sites, change <Nav> so we don't need to do this -->
<li><Search /></li>
{/if}
</svelte:fragment>
@ -34,34 +30,24 @@
<NavItem href="/blog">Blog</NavItem>
<NavItem href="/faq">FAQ</NavItem>
<li aria-hidden="true"><span class="separator" /></li>
<Separator />
<NavItem external="https://kit.svelte.dev">SvelteKit</NavItem>
<NavItem external="/chat" title="Discord Chat">
<span class="small">Discord</span>
<span class="large"><Icon name="message-square" /></span>
<span slot="small">Discord</span>
<Icon name="message-square" />
</NavItem>
<NavItem external="https://github.com/sveltejs/svelte" title="GitHub Repo">
<span class="small">GitHub</span>
<span class="large"><Icon name="github" /></span>
<span slot="small">GitHub</span>
<Icon name="github" />
</NavItem>
</svelte:fragment>
</Nav>
{/if}
<svelte:head>
{#if $page.route.id !== '/blog/[slug]'}
<meta name="twitter:card" content="summary" />
<meta name="twitter:image" content="https://svelte.dev/images/twitter-thumbnail.jpg" />
<meta name="og:image" content="https://svelte.dev/images/twitter-thumbnail.jpg" />
{/if}
</svelte:head>
<main id="main">
<slot />
</main>
</Shell>
{#if browser}
<SearchBox />
@ -78,74 +64,11 @@
}
}
main {
position: relative;
margin: 0 auto;
/* padding: var(--nav-h) var(--side-nav) 0 var(--side-nav); */
padding: var(--nav-h) 0 0 0;
overflow: auto;
}
.small {
display: inline;
}
.large {
display: none;
}
.separator {
display: block;
position: relative;
height: 1px;
margin: 0.5rem 0;
background: radial-gradient(circle at center, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.05));
}
@media (min-width: 800px) {
.small {
display: none;
}
.large {
display: inline;
}
.separator {
display: flex;
align-items: center;
justify-content: center;
background: none;
height: 100%;
margin: 0;
border: none;
text-align: center;
}
.separator::before {
content: '•';
margin: 0 0.3rem;
color: #ccc;
}
}
:global(html, body) {
height: 100%;
width: 100%;
}
/* :global(.examples-container, .repl-outer, .tutorial-outer) {
height: calc(100vh - var(--nav-h) - var(--ukr-footer-height)) !important;
} */
:global(.toggle) {
bottom: var(--ukr-footer-height) !important;
}
/* :global(.zen-mode) {
height: calc(100vh - var(--ukr-footer-height)) !important;
} */
@media (max-width: 830px) {
:global(aside) {
z-index: 9999 !important;

@ -1,5 +1,5 @@
<script>
import Blurb from '@sveltejs/site-kit/components/Blurb.svelte';
import { Blurb } from '@sveltejs/site-kit/components';
import Supporters from './_components/Supporters/index.svelte';
import Hero from './_components/Hero.svelte';
import Image from './_components/Image.svelte';

@ -1,4 +1,4 @@
import { content } from '$lib/search/content';
import { content } from './content.server';
import { json } from '@sveltejs/kit';
export const prerender = true;

@ -23,7 +23,7 @@ const categories = [
];
export function content() {
/** @type {import('./types').Block[]} */
/** @type {import('@sveltejs/site-kit/search').Block[]} */
const blocks = [];
for (const category of categories) {

@ -1,6 +1,6 @@
<script>
import { goto } from '$app/navigation';
import Icon from '@sveltejs/site-kit/components/Icon.svelte';
import { Icon } from '@sveltejs/site-kit/components';
export let sections;
export let slug;

@ -43,8 +43,9 @@ const config = {
'codemirror/addon/fold/markdown-fold.js',
'codemirror/addon/fold/comment-fold.js',
],
exclude: ['@sveltejs/repl'],
exclude: ['@sveltejs/repl', '@sveltejs/site-kit'],
},
ssr: {noExternal: ['@sveltejs/site-kit']},
server: {
fs: {
strict: false,

Loading…
Cancel
Save