@twango/twiki 0.0.0-stage → 0.1.1
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/.github/actions/product-cache/action.yml +32 -0
- package/.github/actions/product-cache/index.mjs +450 -0
- package/LICENSE +21 -0
- package/README.md +146 -2
- package/bun.lock +1701 -0
- package/cli/runtime.test.ts +420 -0
- package/cli/runtime.ts +385 -0
- package/cli/twiki.ts +305 -0
- package/package.json +138 -4
- package/site/_headers +7 -0
- package/site/assets/fonts/OFL.txt +93 -0
- package/site/assets/fonts/OverusedGrotesk-SemiBold.ttf +0 -0
- package/site/assets/social-background.svg +38 -0
- package/site/bun.lock +1701 -0
- package/site/database/index.ts +7 -0
- package/site/database/schema.ts +95 -0
- package/site/drizzle.config.ts +7 -0
- package/site/migrations/0001_auth.sql +14 -0
- package/site/migrations/0002_site_access.sql +5 -0
- package/site/migrations/0003_github_username.sql +1 -0
- package/site/migrations/meta/0002_snapshot.json +432 -0
- package/site/migrations/meta/0003_snapshot.json +439 -0
- package/site/migrations/meta/_journal.json +27 -0
- package/site/package.json +120 -0
- package/site/src/app.d.ts +14 -0
- package/site/src/app.html +11 -0
- package/site/src/hooks.server.ts +12 -0
- package/site/src/lib/analytics-config.json +1 -0
- package/site/src/lib/analytics.ts +40 -0
- package/site/src/lib/auth-config.json +3 -0
- package/site/src/lib/components/SEO.svelte +113 -0
- package/site/src/lib/components/doc/AccessLink.svelte +27 -0
- package/site/src/lib/components/doc/Backlinks.svelte +59 -0
- package/site/src/lib/components/doc/Breadcrumbs.svelte +29 -0
- package/site/src/lib/components/doc/ContentMeta.svelte +61 -0
- package/site/src/lib/components/doc/DocPager.svelte +91 -0
- package/site/src/lib/components/doc/DocShell.svelte +115 -0
- package/site/src/lib/components/doc/LicenseNotice.svelte +26 -0
- package/site/src/lib/components/doc/LockedContent.svelte +29 -0
- package/site/src/lib/components/doc/NavTree.svelte +158 -0
- package/site/src/lib/components/doc/NoteActions.svelte +139 -0
- package/site/src/lib/components/doc/PublicationBadge.svelte +49 -0
- package/site/src/lib/components/doc/ReaderModeToggle.svelte +19 -0
- package/site/src/lib/components/doc/Sidebar.svelte +351 -0
- package/site/src/lib/components/doc/Toc.svelte +339 -0
- package/site/src/lib/components/doc/enhancements.ts +146 -0
- package/site/src/lib/components/doc/heading-tracker.svelte.ts +39 -0
- package/site/src/lib/components/doc/image-preview.ts +114 -0
- package/site/src/lib/components/doc/reader-mode.svelte.ts +40 -0
- package/site/src/lib/components/embeds/mermaid.ts +353 -0
- package/site/src/lib/components/files/FileCode.svelte +81 -0
- package/site/src/lib/components/files/FileExplorer.svelte +74 -0
- package/site/src/lib/components/files/FileHistory.svelte +170 -0
- package/site/src/lib/components/files/FileIcon.svelte +28 -0
- package/site/src/lib/components/files/FileRmd.svelte +209 -0
- package/site/src/lib/components/files/FileTabs.svelte +180 -0
- package/site/src/lib/components/files/FileTree.svelte +67 -0
- package/site/src/lib/components/files/blame-gutter.ts +45 -0
- package/site/src/lib/components/files/code-view.ts +98 -0
- package/site/src/lib/components/files/file-workspace.svelte.ts +100 -0
- package/site/src/lib/components/graph/GraphPanel.svelte +275 -0
- package/site/src/lib/components/graph/GraphView.svelte +100 -0
- package/site/src/lib/components/graph/graph-data.ts +137 -0
- package/site/src/lib/components/graph/graph-model.ts +103 -0
- package/site/src/lib/components/graph/index.ts +3 -0
- package/site/src/lib/components/graph/render-graph.ts +626 -0
- package/site/src/lib/components/popover/index.ts +1 -0
- package/site/src/lib/components/popover/link-popovers.ts +233 -0
- package/site/src/lib/components/popover/popover.css +82 -0
- package/site/src/lib/components/search/SearchButton.svelte +57 -0
- package/site/src/lib/components/search/SearchPalette.svelte +189 -0
- package/site/src/lib/components/search/index.ts +3 -0
- package/site/src/lib/components/search/pagefind-client.svelte.ts +129 -0
- package/site/src/lib/components/search/pagefind-query.ts +22 -0
- package/site/src/lib/components/search/pagefind-url.ts +12 -0
- package/site/src/lib/components/search/search-state.svelte.ts +22 -0
- package/site/src/lib/components/ui/IconButton.svelte +30 -0
- package/site/src/lib/components/ui/ThemeToggle.svelte +22 -0
- package/site/src/lib/components/ui/index.ts +1 -0
- package/site/src/lib/config.ts +9 -0
- package/site/src/lib/content-license.ts +7 -0
- package/site/src/lib/file-history.ts +37 -0
- package/site/src/lib/file-icons.ts +27 -0
- package/site/src/lib/file-tabs.ts +72 -0
- package/site/src/lib/files.ts +73 -0
- package/site/src/lib/github-user.ts +70 -0
- package/site/src/lib/metadata.ts +166 -0
- package/site/src/lib/notebook-nav.ts +63 -0
- package/site/src/lib/publication.ts +1 -0
- package/site/src/lib/rmd.ts +156 -0
- package/site/src/lib/server/access-admin.ts +33 -0
- package/site/src/lib/server/content-model.ts +253 -0
- package/site/src/lib/server/content-provider.ts +266 -0
- package/site/src/lib/server/content.ts +277 -0
- package/site/src/lib/server/runtime-content.ts +34 -0
- package/site/src/lib/site-config.json +9 -0
- package/site/src/lib/social-image.ts +13 -0
- package/site/src/lib/styles/article.css +607 -0
- package/site/src/lib/styles/foundation.css +138 -0
- package/site/src/lib/styles/print.css +115 -0
- package/site/src/lib/styles/shell.css +182 -0
- package/site/src/lib/text-exports.ts +5 -0
- package/site/src/lib/types.ts +140 -0
- package/site/src/lib/utils.ts +6 -0
- package/site/src/routes/+error.svelte +16 -0
- package/site/src/routes/+layout.server.ts +10 -0
- package/site/src/routes/+layout.svelte +72 -0
- package/site/src/routes/+layout.ts +3 -0
- package/site/src/routes/404/+page.svelte +13 -0
- package/site/src/routes/[...slug]/+page.server.ts +24 -0
- package/site/src/routes/[...slug]/+page.svelte +180 -0
- package/site/src/routes/[course]/files/[...file]/+page.svelte +373 -0
- package/site/src/routes/[course]/files/[...file]/+page.ts +38 -0
- package/site/src/routes/admin/access/+page.server.ts +53 -0
- package/site/src/routes/admin/access/+page.svelte +118 -0
- package/site/src/routes/graph.json/+server.ts +7 -0
- package/site/src/routes/index.xml/+server.ts +7 -0
- package/site/src/routes/layout.css +10 -0
- package/site/src/routes/robots.txt/+server.ts +9 -0
- package/site/src/routes/sitemap.xml/+server.ts +7 -0
- package/site/src/routes/tags/+page.server.ts +5 -0
- package/site/src/routes/tags/+page.svelte +38 -0
- package/site/src/routes/tags/[...tag]/+page.server.ts +22 -0
- package/site/src/routes/tags/[...tag]/+page.svelte +52 -0
- package/site/static/favicon.png +0 -0
- package/site/static/fonts/JetBrainsMono-Italic-VF.woff2 +0 -0
- package/site/static/fonts/JetBrainsMono-VF.woff2 +0 -0
- package/site/static/fonts/OverusedGrotesk-VF.woff2 +0 -0
- package/site/static/fonts/OverusedGroteskItalic-VF.woff2 +0 -0
- package/site/static/fonts/Virgil.woff2 +0 -0
- package/site/svelte.config.js +71 -0
- package/site/tooling/LICENSE-quartz +25 -0
- package/site/tooling/adapter.js +55 -0
- package/site/tooling/assets.ts +323 -0
- package/site/tooling/benchmark.ts +191 -0
- package/site/tooling/build.ts +407 -0
- package/site/tooling/cache.ts +133 -0
- package/site/tooling/check-diagrams.ts +164 -0
- package/site/tooling/check-links.ts +86 -0
- package/site/tooling/check-targets.ts +139 -0
- package/site/tooling/compiler.ts +1386 -0
- package/site/tooling/deploy.ts +112 -0
- package/site/tooling/fingerprint.ts +81 -0
- package/site/tooling/lib/app-version.js +92 -0
- package/site/tooling/lib/application-asset-urls.ts +27 -0
- package/site/tooling/lib/application-cache.ts +269 -0
- package/site/tooling/lib/asset-hash-cache.ts +116 -0
- package/site/tooling/lib/autotag.ts +99 -0
- package/site/tooling/lib/build-assembly.ts +148 -0
- package/site/tooling/lib/build-workspace.ts +386 -0
- package/site/tooling/lib/cache-selection.ts +70 -0
- package/site/tooling/lib/cloudflare-prewarm.js +40 -0
- package/site/tooling/lib/comments.ts +32 -0
- package/site/tooling/lib/content-cache.ts +244 -0
- package/site/tooling/lib/content-urls.ts +50 -0
- package/site/tooling/lib/course-cache.ts +298 -0
- package/site/tooling/lib/course-files.ts +281 -0
- package/site/tooling/lib/currency.ts +33 -0
- package/site/tooling/lib/deployment-config.ts +222 -0
- package/site/tooling/lib/deployment-manifest.ts +274 -0
- package/site/tooling/lib/deployment-prepare.ts +325 -0
- package/site/tooling/lib/deployment-upload.ts +293 -0
- package/site/tooling/lib/dev-git-state.ts +130 -0
- package/site/tooling/lib/dev-history.ts +86 -0
- package/site/tooling/lib/dev-social-images.ts +38 -0
- package/site/tooling/lib/dev-state.ts +107 -0
- package/site/tooling/lib/edition-builds.ts +184 -0
- package/site/tooling/lib/edition-paths.js +29 -0
- package/site/tooling/lib/empty-tag-route.js +23 -0
- package/site/tooling/lib/file-history.ts +201 -0
- package/site/tooling/lib/file-icons.ts +93 -0
- package/site/tooling/lib/file-policy.ts +38 -0
- package/site/tooling/lib/file-search.js +13 -0
- package/site/tooling/lib/git-revisions.ts +41 -0
- package/site/tooling/lib/history-pool.ts +74 -0
- package/site/tooling/lib/history-worker.ts +22 -0
- package/site/tooling/lib/knit-rmd.R +38 -0
- package/site/tooling/lib/lastmod.ts +201 -0
- package/site/tooling/lib/markdown-files.ts +51 -0
- package/site/tooling/lib/ofm.ts +634 -0
- package/site/tooling/lib/output.ts +60 -0
- package/site/tooling/lib/product-cache.ts +1006 -0
- package/site/tooling/lib/project-config.ts +288 -0
- package/site/tooling/lib/public-assets.ts +38 -0
- package/site/tooling/lib/public-links.ts +56 -0
- package/site/tooling/lib/publishing.ts +156 -0
- package/site/tooling/lib/r-runtime.ts +257 -0
- package/site/tooling/lib/rebuild-queue.ts +25 -0
- package/site/tooling/lib/rmd-preflight.ts +166 -0
- package/site/tooling/lib/rmd-preview-cache.ts +93 -0
- package/site/tooling/lib/rmd-previews.ts +129 -0
- package/site/tooling/lib/runtime-data-modules.ts +30 -0
- package/site/tooling/lib/runtime-packaging.ts +321 -0
- package/site/tooling/lib/search-cache.js +165 -0
- package/site/tooling/lib/search-inputs.js +129 -0
- package/site/tooling/lib/slug.ts +377 -0
- package/site/tooling/lib/social-images.ts +210 -0
- package/site/tooling/lib/text-exports.ts +51 -0
- package/site/tooling/lib/visit-elements.ts +17 -0
- package/site/tooling/live-test.ts +138 -0
- package/site/tooling/pipeline.ts +103 -0
- package/site/tooling/prepare.ts +3 -0
- package/site/tooling/publish.ts +28 -0
- package/site/tooling/r-cache.ts +45 -0
- package/site/tooling/r-render-profile.json +7 -0
- package/site/tooling/search.js +94 -0
- package/site/tooling/verify-deployment.ts +109 -0
- package/site/tooling/vite-content.ts +240 -0
- package/site/tooling/wrangler-paths.js +25 -0
- package/site/tooling/wrangler.ts +19 -0
- package/site/tsconfig.json +16 -0
- package/site/vite.config.ts +40 -0
- package/site/worker/access.ts +57 -0
- package/site/worker/analytics.ts +100 -0
- package/site/worker/auth-routes.ts +78 -0
- package/site/worker/auth.ts +78 -0
- package/site/worker/content-runtime.ts +226 -0
- package/site/worker/framing.ts +4 -0
- package/site/worker/gate.ts +147 -0
- package/site/worker/index.ts +9 -0
- package/site/worker/login.ts +62 -0
- package/site/worker/publication.ts +44 -0
- package/site/worker/runtime.ts +104 -0
- package/site/worker/tsconfig.json +13 -0
- package/site/wrangler.jsonc +37 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { site } from '$lib/config';
|
|
3
|
+
import { enhance } from '$app/forms';
|
|
4
|
+
import type { SubmitFunction } from '@sveltejs/kit';
|
|
5
|
+
import { lookupGithubUser } from '$lib/github-user';
|
|
6
|
+
import ShieldCheck from '@lucide/svelte/icons/shield-check';
|
|
7
|
+
import UserPlus from '@lucide/svelte/icons/user-plus';
|
|
8
|
+
import type { PageProps } from './$types';
|
|
9
|
+
|
|
10
|
+
let { data, form }: PageProps = $props();
|
|
11
|
+
let adding = $state(false);
|
|
12
|
+
let lookupError = $state('');
|
|
13
|
+
|
|
14
|
+
const addAccess: SubmitFunction = async ({ formData, cancel }) => {
|
|
15
|
+
if (adding) return cancel();
|
|
16
|
+
adding = true;
|
|
17
|
+
lookupError = '';
|
|
18
|
+
form = null;
|
|
19
|
+
const result = await lookupGithubUser(formData.get('username'));
|
|
20
|
+
if ('error' in result) {
|
|
21
|
+
lookupError = result.error;
|
|
22
|
+
adding = false;
|
|
23
|
+
return cancel();
|
|
24
|
+
}
|
|
25
|
+
formData.set('githubId', result.user.id);
|
|
26
|
+
formData.set('githubLogin', result.user.login);
|
|
27
|
+
return async ({ update }) => {
|
|
28
|
+
try {
|
|
29
|
+
await update();
|
|
30
|
+
} finally {
|
|
31
|
+
adding = false;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<svelte:head><title>Access · {site.title}</title></svelte:head>
|
|
38
|
+
|
|
39
|
+
<section class="mx-auto w-full max-w-xl">
|
|
40
|
+
<header class="mb-6">
|
|
41
|
+
<h1 class="mb-2 text-3xl font-semibold tracking-tight">Access</h1>
|
|
42
|
+
<p class="text-sm text-muted">
|
|
43
|
+
Approved people can read all notes. Only you can manage access.
|
|
44
|
+
</p>
|
|
45
|
+
</header>
|
|
46
|
+
|
|
47
|
+
<form method="POST" action="?/add" use:enhance={addAccess} class="mb-6">
|
|
48
|
+
<label for="github-username" class="mb-2 block text-sm font-medium">GitHub username</label>
|
|
49
|
+
<div class="flex gap-2">
|
|
50
|
+
<input
|
|
51
|
+
id="github-username"
|
|
52
|
+
name="username"
|
|
53
|
+
disabled={adding}
|
|
54
|
+
required
|
|
55
|
+
maxlength="40"
|
|
56
|
+
autocomplete="off"
|
|
57
|
+
autocapitalize="none"
|
|
58
|
+
spellcheck="false"
|
|
59
|
+
placeholder="username"
|
|
60
|
+
class="min-w-0 flex-1 rounded-lg border border-border bg-bg px-3 py-2 text-sm outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
|
61
|
+
/>
|
|
62
|
+
<button
|
|
63
|
+
disabled={adding}
|
|
64
|
+
class="flex shrink-0 items-center gap-2 rounded-lg bg-text px-3 py-2 text-sm font-medium text-bg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
|
|
65
|
+
><UserPlus size={16} />{adding ? 'Adding…' : 'Add access'}</button
|
|
66
|
+
>
|
|
67
|
+
</div>
|
|
68
|
+
</form>
|
|
69
|
+
|
|
70
|
+
<noscript><p>Enable JavaScript to look up GitHub accounts and add access.</p></noscript>
|
|
71
|
+
{#if lookupError || form?.error}<p role="alert" class="mb-4 text-sm text-accent">
|
|
72
|
+
{lookupError || form?.error}
|
|
73
|
+
</p>{/if}
|
|
74
|
+
{#if form?.message}<p role="status" class="mb-4 text-sm text-muted">{form.message}</p>{/if}
|
|
75
|
+
|
|
76
|
+
<ul
|
|
77
|
+
class="m-0 list-none divide-y divide-border border-y border-border p-0"
|
|
78
|
+
aria-label="People with access"
|
|
79
|
+
>
|
|
80
|
+
<li class="flex items-center justify-between gap-3 py-4">
|
|
81
|
+
<div>
|
|
82
|
+
<p class="font-medium">You</p>
|
|
83
|
+
<p class="text-xs text-muted">GitHub ID {data.ownerId}</p>
|
|
84
|
+
</div>
|
|
85
|
+
<span class="flex items-center gap-1.5 text-xs text-muted"
|
|
86
|
+
><ShieldCheck size={14} />Owner</span
|
|
87
|
+
>
|
|
88
|
+
</li>
|
|
89
|
+
{#each data.grants as grant (grant.githubId)}
|
|
90
|
+
<li class="flex items-center justify-between gap-3 py-4">
|
|
91
|
+
<div class="min-w-0">
|
|
92
|
+
<a
|
|
93
|
+
class="font-medium text-text hover:text-accent"
|
|
94
|
+
href={`https://github.com/${grant.githubLogin}`}
|
|
95
|
+
target="_blank"
|
|
96
|
+
rel="noreferrer">@{grant.githubLogin}</a
|
|
97
|
+
>
|
|
98
|
+
<p class="text-xs text-muted">GitHub ID {grant.githubId}</p>
|
|
99
|
+
</div>
|
|
100
|
+
<form
|
|
101
|
+
method="POST"
|
|
102
|
+
action="?/revoke"
|
|
103
|
+
use:enhance={({ cancel }) => {
|
|
104
|
+
if (!confirm(`Revoke @${grant.githubLogin}'s access and sign out their sessions?`))
|
|
105
|
+
cancel();
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
<input type="hidden" name="githubId" value={grant.githubId} />
|
|
109
|
+
<button
|
|
110
|
+
class="rounded-md px-2 py-1 text-sm text-muted hover:bg-accent-soft hover:text-accent focus-visible:outline-2 focus-visible:outline-accent"
|
|
111
|
+
aria-label={`Revoke @${grant.githubLogin}`}>Revoke</button
|
|
112
|
+
>
|
|
113
|
+
</form>
|
|
114
|
+
</li>
|
|
115
|
+
{/each}
|
|
116
|
+
</ul>
|
|
117
|
+
{#if !data.grants.length}<p class="mt-4 text-sm text-muted">Only you have access.</p>{/if}
|
|
118
|
+
</section>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RequestHandler } from './$types';
|
|
2
|
+
import { requestContent } from '$lib/server/runtime-content';
|
|
3
|
+
export const prerender = import.meta.env.VITE_STATIC_EXPORT === 'true';
|
|
4
|
+
export const GET: RequestHandler = async ({ platform }) =>
|
|
5
|
+
new Response(JSON.stringify((await requestContent(platform).manifest()).graph), {
|
|
6
|
+
headers: { 'Content-Type': 'application/json' }
|
|
7
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RequestHandler } from './$types';
|
|
2
|
+
import { requestContent } from '$lib/server/runtime-content';
|
|
3
|
+
export const prerender = import.meta.env.VITE_STATIC_EXPORT === 'true';
|
|
4
|
+
export const GET: RequestHandler = async ({ platform }) =>
|
|
5
|
+
new Response((await requestContent(platform).model()).rssXml(), {
|
|
6
|
+
headers: { 'Content-Type': 'application/rss+xml; charset=utf-8' }
|
|
7
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
@import 'tailwindcss';
|
|
2
|
+
@source not '../../docs';
|
|
3
|
+
@source not '../../tests';
|
|
4
|
+
@source not '../../benchmarks';
|
|
5
|
+
@import '../lib/styles/foundation.css';
|
|
6
|
+
@import '../lib/styles/shell.css';
|
|
7
|
+
@import '../lib/styles/article.css';
|
|
8
|
+
@import '../lib/styles/print.css';
|
|
9
|
+
@plugin '@tailwindcss/typography';
|
|
10
|
+
@custom-variant dark (&:where(:root.dark, :root.dark *));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { site } from '$lib/config';
|
|
2
|
+
|
|
3
|
+
export const prerender = import.meta.env.VITE_STATIC_EXPORT === 'true';
|
|
4
|
+
|
|
5
|
+
export function GET() {
|
|
6
|
+
return new Response(`User-agent: *\nAllow: /\nSitemap: ${site.url}/sitemap.xml\n`, {
|
|
7
|
+
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RequestHandler } from './$types';
|
|
2
|
+
import { requestContent } from '$lib/server/runtime-content';
|
|
3
|
+
export const prerender = import.meta.env.VITE_STATIC_EXPORT === 'true';
|
|
4
|
+
export const GET: RequestHandler = async ({ platform }) =>
|
|
5
|
+
new Response((await requestContent(platform).model()).sitemapXml(), {
|
|
6
|
+
headers: { 'Content-Type': 'application/xml; charset=utf-8' }
|
|
7
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import SEO from '$lib/components/SEO.svelte';
|
|
3
|
+
import Breadcrumbs from '$lib/components/doc/Breadcrumbs.svelte';
|
|
4
|
+
import type { PageData } from './$types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
data: PageData;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { data }: Props = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<SEO
|
|
14
|
+
title="Tag Index"
|
|
15
|
+
description="Browse topics across notes and reference material."
|
|
16
|
+
canonical="/tags"
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<Breadcrumbs route="tags" current="Tag Index" />
|
|
20
|
+
|
|
21
|
+
<article class="prose dark:prose-invert max-w-none">
|
|
22
|
+
<h1>Tag Index</h1>
|
|
23
|
+
<p class="text-muted">Found {data.tags.length} total tags.</p>
|
|
24
|
+
</article>
|
|
25
|
+
|
|
26
|
+
<ul class="m-0 mt-4 flex list-none flex-wrap gap-2 p-0">
|
|
27
|
+
{#each data.tags as { tag, count } (tag)}
|
|
28
|
+
<li>
|
|
29
|
+
<a
|
|
30
|
+
class="inline-flex items-center gap-1.5 rounded-md border border-border px-2.5 py-1 text-sm text-text no-underline transition-colors hover:border-accent hover:text-accent"
|
|
31
|
+
href={`/tags/${tag}`}
|
|
32
|
+
>
|
|
33
|
+
<span>{tag}</span>
|
|
34
|
+
<span class="text-xs text-muted">{count}</span>
|
|
35
|
+
</a>
|
|
36
|
+
</li>
|
|
37
|
+
{/each}
|
|
38
|
+
</ul>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { error } from '@sveltejs/kit';
|
|
2
|
+
import type { EntryGenerator, PageServerLoad } from './$types';
|
|
3
|
+
import { requestContent } from '$lib/server/runtime-content';
|
|
4
|
+
export const entries: EntryGenerator = async () => {
|
|
5
|
+
if (import.meta.env.VITE_STATIC_EXPORT !== 'true') return [];
|
|
6
|
+
return Object.keys((await requestContent(undefined).manifest()).tags).map((tag) => ({ tag }));
|
|
7
|
+
};
|
|
8
|
+
export const load: PageServerLoad = async ({ params, platform }) => {
|
|
9
|
+
const model = await requestContent(platform).model();
|
|
10
|
+
const pages = model.tagPages(params.tag);
|
|
11
|
+
if (!pages) error(404, 'Tag not found');
|
|
12
|
+
return {
|
|
13
|
+
tag: params.tag,
|
|
14
|
+
pages: pages.map((p) => ({
|
|
15
|
+
route: `/${model.displayRoute(p.slug)}`,
|
|
16
|
+
title: p.title,
|
|
17
|
+
description: p.description,
|
|
18
|
+
modified: p.dates.modified,
|
|
19
|
+
tags: p.tags
|
|
20
|
+
}))
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import SEO from '$lib/components/SEO.svelte';
|
|
3
|
+
import Breadcrumbs from '$lib/components/doc/Breadcrumbs.svelte';
|
|
4
|
+
import type { PageData } from './$types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
data: PageData;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { data }: Props = $props();
|
|
11
|
+
|
|
12
|
+
function shortDate(iso: string): string {
|
|
13
|
+
if (!iso) return '';
|
|
14
|
+
return new Date(iso).toLocaleDateString('en-US', {
|
|
15
|
+
year: 'numeric',
|
|
16
|
+
month: 'short',
|
|
17
|
+
day: 'numeric'
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<SEO
|
|
23
|
+
title={`Tag: ${data.tag}`}
|
|
24
|
+
description={`Browse course notes and reference material tagged ${data.tag}.`}
|
|
25
|
+
canonical={`/tags/${data.tag}`}
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<Breadcrumbs route={`tags/${data.tag}`} current={`Tag: ${data.tag}`} />
|
|
29
|
+
|
|
30
|
+
<article class="prose dark:prose-invert max-w-none">
|
|
31
|
+
<h1>Tag: {data.tag}</h1>
|
|
32
|
+
<p class="text-muted">
|
|
33
|
+
{data.pages.length}
|
|
34
|
+
{data.pages.length === 1 ? 'item' : 'items'} with this tag.
|
|
35
|
+
</p>
|
|
36
|
+
</article>
|
|
37
|
+
|
|
38
|
+
<ul class="m-0 mt-4 list-none space-y-1 p-0">
|
|
39
|
+
{#each data.pages as page (page.route)}
|
|
40
|
+
<li>
|
|
41
|
+
<a class="block rounded-md px-3 py-2 no-underline hover:bg-surface" href={page.route}>
|
|
42
|
+
<span class="flex items-baseline justify-between gap-3">
|
|
43
|
+
<span class="min-w-0 truncate text-sm font-medium text-text">{page.title}</span>
|
|
44
|
+
<span class="shrink-0 text-xs text-muted">{shortDate(page.modified)}</span>
|
|
45
|
+
</span>
|
|
46
|
+
{#if page.description}
|
|
47
|
+
<span class="mt-0.5 line-clamp-1 block text-xs text-muted">{page.description}</span>
|
|
48
|
+
{/if}
|
|
49
|
+
</a>
|
|
50
|
+
</li>
|
|
51
|
+
{/each}
|
|
52
|
+
</ul>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import site from './src/lib/site-config.json' with { type: 'json' };
|
|
2
|
+
import { staticDirectory } from './tooling/lib/edition-paths.js';
|
|
3
|
+
import adapter from './tooling/adapter.js';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { applicationVersion } from './tooling/lib/app-version.js';
|
|
6
|
+
import { emptyTagRoute } from './tooling/lib/empty-tag-route.js';
|
|
7
|
+
|
|
8
|
+
const prerenderConcurrency = Number(process.env.WISCONSIN_PRERENDER_CONCURRENCY ?? 1);
|
|
9
|
+
if (!Number.isInteger(prerenderConcurrency) || prerenderConcurrency < 1)
|
|
10
|
+
throw new Error('WISCONSIN_PRERENDER_CONCURRENCY must be a positive integer');
|
|
11
|
+
|
|
12
|
+
/** @param {string} filename @param {string} value */
|
|
13
|
+
function expectedDiagnostic(filename, value) {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(readFileSync(`build/generated/${filename}.json`, 'utf8')).includes(value);
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @type {import('@sveltejs/kit').Config} */
|
|
22
|
+
const config = {
|
|
23
|
+
compilerOptions: {
|
|
24
|
+
// Force runes mode everywhere except libraries.
|
|
25
|
+
runes: ({ filename }) => {
|
|
26
|
+
if (filename.split(/[/\\]/).includes('node_modules')) return undefined;
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
kit: {
|
|
31
|
+
paths: { relative: process.env.VITE_STATIC_EXPORT === 'true' },
|
|
32
|
+
outDir: 'build/.svelte-kit',
|
|
33
|
+
files: { assets: process.env.WISCONSIN_APP_STATIC_DIR ?? staticDirectory(process.cwd()) },
|
|
34
|
+
adapter: adapter(),
|
|
35
|
+
// Content is served separately. Rebuilding it should not change the
|
|
36
|
+
// application version, hydration IDs, and every prerendered HTML asset.
|
|
37
|
+
version: { name: applicationVersion(process.cwd()) },
|
|
38
|
+
prerender: {
|
|
39
|
+
concurrency: prerenderConcurrency,
|
|
40
|
+
origin: site.url,
|
|
41
|
+
handleUnseenRoutes: ({ routes, message }) => {
|
|
42
|
+
if (emptyTagRoute(process.cwd(), routes)) return;
|
|
43
|
+
if (
|
|
44
|
+
process.env.VITE_PUBLIC_EDITION === 'true' &&
|
|
45
|
+
routes.every((route) => ['/tags/[...tag]', '/[course]/files/[...file]'].includes(route))
|
|
46
|
+
)
|
|
47
|
+
return;
|
|
48
|
+
throw new Error(message);
|
|
49
|
+
},
|
|
50
|
+
handleHttpError: ({ status, path, referrer, message }) => {
|
|
51
|
+
if (status === 404 && path === '/login') return;
|
|
52
|
+
if (status === 404 && expectedDiagnostic('expected-404', path)) {
|
|
53
|
+
console.warn(`[prerender] unresolved content link: ${path} <- ${referrer}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(message);
|
|
57
|
+
},
|
|
58
|
+
handleMissingId: ({ path, id, referrers, message }) => {
|
|
59
|
+
if (expectedDiagnostic('expected-missing-id', `${path}#${id}`)) {
|
|
60
|
+
console.warn(
|
|
61
|
+
`[prerender] unresolved content anchor: ${path}#${id} <- ${referrers.join(', ')}`
|
|
62
|
+
);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
throw new Error(message);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export default config;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Portions of the content pipeline in this directory (slug.ts, ofm.ts,
|
|
2
|
+
lastmod.ts, autotag.ts and the link-resolution logic) are derived from
|
|
3
|
+
Quartz v4 (https://github.com/jackyzha0/quartz), used under the MIT License:
|
|
4
|
+
|
|
5
|
+
MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2021 jackyzha0
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import cloudflare from '@sveltejs/adapter-cloudflare';
|
|
2
|
+
import { readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { prepareWranglerPaths } from './wrangler-paths.js';
|
|
5
|
+
import { indexSearch } from './search.js';
|
|
6
|
+
import { prewarmCloudflareEmulator } from './lib/cloudflare-prewarm.js';
|
|
7
|
+
|
|
8
|
+
export default function () {
|
|
9
|
+
const directory = prepareWranglerPaths(path.resolve(import.meta.dirname, '..'));
|
|
10
|
+
process.env.WRANGLER_CACHE_DIR ??= path.join(directory, 'cache');
|
|
11
|
+
const adapter = cloudflare({
|
|
12
|
+
platformProxy: { persist: { path: path.join(directory, 'state/v3') } }
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
...adapter,
|
|
16
|
+
async emulate() {
|
|
17
|
+
return prewarmCloudflareEmulator((await adapter.emulate?.()) ?? {});
|
|
18
|
+
},
|
|
19
|
+
/** @param {import('@sveltejs/kit').Builder} builder */
|
|
20
|
+
async adapt(builder) {
|
|
21
|
+
const started = performance.now();
|
|
22
|
+
await adapter.adapt(builder);
|
|
23
|
+
console.log(`adapter: cloudflare in ${((performance.now() - started) / 1000).toFixed(2)}s`);
|
|
24
|
+
if (process.env.VITE_STATIC_EXPORT === 'true') {
|
|
25
|
+
await indexSearch(builder.getBuildDirectory('cloudflare'));
|
|
26
|
+
const shell = path.join(builder.getBuildDirectory('cloudflare'), '_file-browser.html');
|
|
27
|
+
const fallbackStarted = performance.now();
|
|
28
|
+
await builder.generateFallback(shell);
|
|
29
|
+
console.log(
|
|
30
|
+
`adapter: file browser shell in ${((performance.now() - fallbackStarted) / 1000).toFixed(2)}s`
|
|
31
|
+
);
|
|
32
|
+
writeFileSync(
|
|
33
|
+
shell,
|
|
34
|
+
readFileSync(shell, 'utf8').replace(
|
|
35
|
+
'</body>',
|
|
36
|
+
'<noscript><p>Enable JavaScript to use the file browser.</p></noscript></body>'
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
if (process.env.VITE_PUBLIC_EDITION === 'true') {
|
|
40
|
+
writeFileSync(
|
|
41
|
+
'build/generated/public-routes.json',
|
|
42
|
+
JSON.stringify(
|
|
43
|
+
Object.fromEntries(
|
|
44
|
+
[...builder.prerendered.pages].map(([route, page]) => [route, page.file])
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const directory = builder.getBuildDirectory('');
|
|
51
|
+
renameSync(`${directory}/worker.js`, `${directory}/svelte-worker.js`);
|
|
52
|
+
writeFileSync(`${directory}/worker.js`, "export { default } from '../../worker/index.ts';\n");
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|