@treeseed/core 0.10.22 → 0.11.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.
Files changed (108) hide show
  1. package/README.md +69 -125
  2. package/dist/dev-watch.js +2 -1
  3. package/dist/dev.d.ts +1 -1
  4. package/dist/dev.js +51 -35
  5. package/dist/pages/404.astro +1 -1
  6. package/dist/pages/[slug].astro +4 -4
  7. package/dist/pages/agents/[slug].astro +3 -3
  8. package/dist/pages/agents/index.astro +3 -3
  9. package/dist/pages/books/[slug].astro +3 -3
  10. package/dist/pages/books/index.astro +3 -3
  11. package/dist/pages/contact.astro +2 -2
  12. package/dist/pages/decisions/[slug].astro +3 -3
  13. package/dist/pages/decisions/index.astro +3 -3
  14. package/dist/pages/docs-runtime/[...slug].astro +3 -3
  15. package/dist/pages/docs-runtime/index.astro +3 -3
  16. package/dist/pages/index.astro +11 -11
  17. package/dist/pages/notes/[slug].astro +3 -3
  18. package/dist/pages/notes/index.astro +3 -3
  19. package/dist/pages/objectives/[slug].astro +3 -3
  20. package/dist/pages/objectives/index.astro +3 -3
  21. package/dist/pages/people/[slug].astro +3 -3
  22. package/dist/pages/people/index.astro +3 -3
  23. package/dist/pages/proposals/[slug].astro +3 -3
  24. package/dist/pages/proposals/index.astro +3 -3
  25. package/dist/pages/questions/[slug].astro +3 -3
  26. package/dist/pages/questions/index.astro +3 -3
  27. package/dist/pages/ui/index.astro +23 -23
  28. package/dist/platform-resources.js +5 -1
  29. package/dist/scripts/build-dist.js +2 -0
  30. package/dist/scripts/release-verify.js +24 -2
  31. package/dist/scripts/workspace-bootstrap.js +3 -0
  32. package/dist/site.js +49 -11
  33. package/dist/styles/global.css +5 -5
  34. package/package.json +3 -45
  35. package/templates/github/deploy-web.workflow.yml +36 -2
  36. package/dist/components/DevWatchReload.astro +0 -45
  37. package/dist/components/SiteTitle.astro +0 -51
  38. package/dist/components/content/ContentStatusLegend.astro +0 -18
  39. package/dist/components/content/StatusBadge.astro +0 -11
  40. package/dist/components/docs/BookFontControls.astro +0 -180
  41. package/dist/components/docs/DesktopSidebarToggle.astro +0 -88
  42. package/dist/components/docs/DownloadBook.astro +0 -34
  43. package/dist/components/docs/Footer.astro +0 -112
  44. package/dist/components/docs/Header.astro +0 -157
  45. package/dist/components/docs/PageFrame.astro +0 -260
  46. package/dist/components/docs/PageSidebar.astro +0 -63
  47. package/dist/components/docs/PageTitle.astro +0 -39
  48. package/dist/components/docs/Sidebar.astro +0 -41
  49. package/dist/components/docs/ThemeSelect.astro +0 -5
  50. package/dist/components/forms/ContactForm.astro +0 -233
  51. package/dist/components/forms/FooterSubscribeForm.astro +0 -188
  52. package/dist/components/site/BookList.astro +0 -27
  53. package/dist/components/site/CTASection.astro +0 -24
  54. package/dist/components/site/ChronicleList.astro +0 -33
  55. package/dist/components/site/Hero.astro +0 -18
  56. package/dist/components/site/NotesList.astro +0 -29
  57. package/dist/components/site/PathCard.astro +0 -16
  58. package/dist/components/site/ProfileList.astro +0 -30
  59. package/dist/components/site/PublishedContentBody.astro +0 -5
  60. package/dist/components/site/RouteNotFound.astro +0 -25
  61. package/dist/components/site/SectionIntro.astro +0 -9
  62. package/dist/components/site/StageBanner.astro +0 -8
  63. package/dist/components/site/TrustCallout.astro +0 -9
  64. package/dist/components/starlight.js +0 -6
  65. package/dist/components/ui/data/ActionList.astro +0 -51
  66. package/dist/components/ui/data/Badge.astro +0 -19
  67. package/dist/components/ui/data/DataTable.astro +0 -51
  68. package/dist/components/ui/data/KeyValueList.astro +0 -28
  69. package/dist/components/ui/data/MetricCard.astro +0 -25
  70. package/dist/components/ui/data/MetricGrid.astro +0 -27
  71. package/dist/components/ui/data/StatusPill.astro +0 -20
  72. package/dist/components/ui/forms/Button.astro +0 -59
  73. package/dist/components/ui/forms/Field.astro +0 -39
  74. package/dist/components/ui/forms/FormActions.astro +0 -12
  75. package/dist/components/ui/forms/PasswordMeter.astro +0 -80
  76. package/dist/components/ui/forms/RadioGroup.astro +0 -55
  77. package/dist/components/ui/forms/Select.astro +0 -47
  78. package/dist/components/ui/forms/TextInput.astro +0 -58
  79. package/dist/components/ui/forms/Textarea.astro +0 -45
  80. package/dist/components/ui/layout/PageHeader.astro +0 -45
  81. package/dist/components/ui/shell/AppShell.astro +0 -130
  82. package/dist/components/ui/shell/BottomNav.astro +0 -42
  83. package/dist/components/ui/shell/ProjectHeader.astro +0 -66
  84. package/dist/components/ui/shell/PublicFooter.astro +0 -39
  85. package/dist/components/ui/shell/PublicShell.astro +0 -184
  86. package/dist/components/ui/shell/RailNav.astro +0 -42
  87. package/dist/components/ui/shell/ShellIconLink.astro +0 -30
  88. package/dist/components/ui/shell/TopBar.astro +0 -52
  89. package/dist/components/ui/surface/Card.astro +0 -46
  90. package/dist/components/ui/surface/EmptyState.astro +0 -45
  91. package/dist/components/ui/surface/Panel.astro +0 -54
  92. package/dist/components/ui/theme/ThemeMenu.astro +0 -58
  93. package/dist/components/ui/theme/ThemePreviewSwatch.astro +0 -18
  94. package/dist/components/ui/theme/ThemeScript.astro +0 -112
  95. package/dist/components/ui/theme/ThemeSelector.astro +0 -202
  96. package/dist/components/ui/types.js +0 -0
  97. package/dist/layouts/AuthoredEntryLayout.astro +0 -195
  98. package/dist/layouts/BookLayout.astro +0 -35
  99. package/dist/layouts/BridgeLayout.astro +0 -11
  100. package/dist/layouts/ContentLayout.astro +0 -24
  101. package/dist/layouts/MainLayout.astro +0 -76
  102. package/dist/layouts/NoteLayout.astro +0 -26
  103. package/dist/layouts/ProfileLayout.astro +0 -85
  104. package/dist/styles/app-shell.css +0 -626
  105. package/dist/styles/forms.css +0 -274
  106. package/dist/styles/theme.css +0 -198
  107. package/dist/styles/tokens.css +0 -65
  108. package/dist/styles/ui.css +0 -551
@@ -1,188 +0,0 @@
1
- ---
2
- import {
3
- TREESEED_PUBLIC_TURNSTILE_SITE_KEY,
4
- } from 'astro:env/client';
5
- import { FORM_CODE_PARAM, FORM_SUCCESS_PARAM, SUBSCRIBE_ANCHOR_ID } from '../../utils/forms/constants';
6
- import { SITE_FORMS } from '../../utils/site-config';
7
-
8
- const { currentPath = '/' } = Astro.props as { currentPath?: string };
9
- const redirectTarget = `${currentPath}#${SUBSCRIBE_ANCHOR_ID}`;
10
- const shouldBypassTurnstile = import.meta.env.DEV;
11
- const formsApiBaseUrl = SITE_FORMS?.apiBaseUrl ?? '/api/form/submit';
12
- ---
13
-
14
- <section id={SUBSCRIBE_ANCHOR_ID} class="mt-6 border-t border-[color:var(--ts-color-border)] pt-4">
15
- <div class="flex flex-col items-center gap-3 text-center">
16
- <div class="flex flex-col gap-1">
17
- <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--ts-color-info-text)]">Subscribe for updates</p>
18
- <p class="text-sm text-[color:var(--ts-color-text-muted)]">
19
- Get occasional notes when new writing or project updates are published.
20
- </p>
21
- </div>
22
- <p class="js-subscribe-status hidden rounded-md border px-4 py-2 text-sm font-medium"></p>
23
- <form
24
- method="post"
25
- action={formsApiBaseUrl}
26
- class="js-secure-form flex w-full max-w-4xl flex-col items-center gap-3"
27
- data-form-type="subscribe"
28
- data-turnstile-action="subscribe_submit"
29
- >
30
- <input type="hidden" name="formType" value="subscribe" />
31
- <input type="hidden" name="formToken" value="" />
32
- <input type="hidden" name="formSession" value="" />
33
- <input type="hidden" name="redirectTo" value={redirectTarget} />
34
- <div class="sr-only" aria-hidden="true">
35
- <label>
36
- Website
37
- <input type="text" name="website" tabindex="-1" autocomplete="off" />
38
- </label>
39
- </div>
40
- <input type="hidden" name="name" value="" />
41
- <div class="flex w-full flex-col items-center justify-center gap-3 md:flex-row md:flex-wrap">
42
- <input
43
- class="min-h-11 w-full rounded-md border border-[color:var(--ts-color-border-strong)] bg-white/80 px-4 py-2 md:w-auto md:min-w-[16rem] md:max-w-[18rem]"
44
- type="email"
45
- name="email"
46
- placeholder="Email address"
47
- autocomplete="email"
48
- required
49
- />
50
- {TREESEED_PUBLIC_TURNSTILE_SITE_KEY && !shouldBypassTurnstile ? (
51
- <div class="js-turnstile min-h-0"></div>
52
- ) : shouldBypassTurnstile ? (
53
- <p class="text-xs text-[color:var(--ts-color-text-subtle)]">Captcha bypassed locally.</p>
54
- ) : (
55
- <p class="text-xs text-[color:var(--ts-color-text-subtle)]">Turnstile not configured.</p>
56
- )}
57
- <button
58
- type="submit"
59
- class="js-submit inline-flex min-h-11 items-center justify-center whitespace-nowrap border border-[color:var(--ts-color-accent)] bg-[color:var(--ts-color-accent)] px-4 py-2 text-sm font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)] disabled:cursor-not-allowed disabled:opacity-60"
60
- disabled
61
- >
62
- Subscribe
63
- </button>
64
- </div>
65
- </form>
66
- </div>
67
- </section>
68
-
69
- {TREESEED_PUBLIC_TURNSTILE_SITE_KEY && !shouldBypassTurnstile && (
70
- <script is:inline src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" async defer></script>
71
- )}
72
-
73
- <script
74
- is:inline
75
- define:vars={{
76
- siteKey: TREESEED_PUBLIC_TURNSTILE_SITE_KEY,
77
- shouldBypassTurnstile,
78
- formsApiBaseUrl,
79
- successParam: FORM_SUCCESS_PARAM,
80
- codeParam: FORM_CODE_PARAM,
81
- subscribeAnchorId: SUBSCRIBE_ANCHOR_ID,
82
- }}
83
- >
84
- const subscribeMessages = {
85
- success: 'You are subscribed. Thanks for following along.',
86
- invalid_form: 'Please provide your email address.',
87
- captcha_failed: 'Please complete the verification challenge and try again.',
88
- token_invalid: 'Please refresh the page and try again.',
89
- token_expired: 'Please refresh the page and try again.',
90
- rate_limited: 'Too many attempts were detected. Please wait a few minutes.',
91
- delivery_failed: 'We could not finish the signup right now. Please try again shortly.',
92
- };
93
-
94
- const syncSubscribeStatus = () => {
95
- const statusNode = document.querySelector('.js-subscribe-status');
96
- if (!(statusNode instanceof HTMLElement)) return;
97
-
98
- const params = new URLSearchParams(window.location.search);
99
- const status = params.get(successParam);
100
- const code = params.get(codeParam);
101
-
102
- if (!status || !code || window.location.hash !== `#${subscribeAnchorId}`) {
103
- return;
104
- }
105
-
106
- statusNode.textContent = subscribeMessages[code] ?? (status === 'success' ? 'Subscription complete.' : 'We could not complete the signup.');
107
- statusNode.classList.remove('hidden');
108
- if (status === 'success') {
109
- statusNode.className = 'js-subscribe-status rounded-md border border-[color:var(--ts-color-info)] bg-[color:var(--ts-color-info-soft)] px-4 py-2 text-sm font-medium text-[color:var(--ts-color-text)]';
110
- } else {
111
- statusNode.className = 'js-subscribe-status rounded-md border border-[color:var(--ts-color-warning)] bg-[color:var(--ts-color-warning-soft)] px-4 py-2 text-sm font-medium text-[color:var(--ts-color-text)]';
112
- }
113
- };
114
-
115
- if (document.readyState === 'loading') {
116
- document.addEventListener('DOMContentLoaded', syncSubscribeStatus, { once: true });
117
- } else {
118
- syncSubscribeStatus();
119
- }
120
-
121
- const bootSecureForms = () => {
122
- const forms = Array.from(document.querySelectorAll('.js-secure-form'));
123
-
124
- for (const form of forms) {
125
- if (!(form instanceof HTMLFormElement) || form.dataset.ready === 'true') continue;
126
- form.dataset.ready = 'true';
127
-
128
- const formType = form.dataset.formType;
129
- const action = form.dataset.turnstileAction ?? '';
130
- const tokenField = form.querySelector('input[name="formToken"]');
131
- const sessionField = form.querySelector('input[name="formSession"]');
132
- const submitButton = form.querySelector('.js-submit');
133
- const widgetContainer = form.querySelector('.js-turnstile');
134
-
135
- const enableSubmit = () => {
136
- if (submitButton instanceof HTMLButtonElement) {
137
- submitButton.disabled = !(tokenField instanceof HTMLInputElement && tokenField.value);
138
- }
139
- };
140
-
141
- const assignToken = async () => {
142
- const response = await fetch(`${formsApiBaseUrl}?formType=${encodeURIComponent(formType ?? '')}`, {
143
- headers: { accept: 'application/json' },
144
- credentials: 'same-origin',
145
- });
146
-
147
- const data = await response.json();
148
- if (tokenField instanceof HTMLInputElement && data?.formToken) {
149
- tokenField.value = data.formToken;
150
- }
151
- if (sessionField instanceof HTMLInputElement && data?.sessionId) {
152
- sessionField.value = data.sessionId;
153
- }
154
- enableSubmit();
155
- };
156
-
157
- const renderWidget = () => {
158
- if (shouldBypassTurnstile) return;
159
- if (!siteKey || !(widgetContainer instanceof HTMLElement)) return;
160
- if (!('turnstile' in window) || typeof window.turnstile?.render !== 'function') return;
161
- if (widgetContainer.dataset.rendered === 'true') return;
162
-
163
- window.turnstile.render(widgetContainer, {
164
- sitekey: siteKey,
165
- action,
166
- theme: 'light',
167
- });
168
-
169
- widgetContainer.dataset.rendered = 'true';
170
- };
171
-
172
- assignToken().catch((error) => {
173
- console.error('Unable to initialize form token', error);
174
- });
175
-
176
- if (siteKey) {
177
- renderWidget();
178
- window.addEventListener('load', renderWidget, { once: true });
179
- }
180
- }
181
- };
182
-
183
- if (document.readyState === 'loading') {
184
- document.addEventListener('DOMContentLoaded', bootSecureForms, { once: true });
185
- } else {
186
- bootSecureForms();
187
- }
188
- </script>
@@ -1,27 +0,0 @@
1
- ---
2
- type BookItem = {
3
- href: string;
4
- title: string;
5
- summary: string;
6
- meta: string;
7
- landingPath: string;
8
- downloadHref: string;
9
- };
10
-
11
- const { items } = Astro.props as { items: BookItem[] };
12
- ---
13
-
14
- <div class="grid gap-6 md:grid-cols-2">
15
- {items.map((item) => (
16
- <div class="border border-[color:var(--ts-color-border)] bg-[color:var(--ts-color-surface)] p-6">
17
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">{item.meta}</p>
18
- <h3 class="mt-4 text-2xl font-bold text-[color:var(--ts-color-text)]">{item.title}</h3>
19
- <p class="mt-3 text-base leading-8 text-[color:var(--ts-color-text-muted)]">{item.summary}</p>
20
- <div class="mt-5 flex flex-wrap gap-3">
21
- <a href={item.href} class="border border-[color:var(--ts-color-border-strong)] px-4 py-2 text-sm font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">Book page</a>
22
- <a href={item.landingPath} class="border border-[color:var(--ts-color-border-strong)] px-4 py-2 text-sm font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">Open knowledge</a>
23
- <a href={item.downloadHref} class="border border-[color:var(--ts-color-accent)] bg-[color:var(--ts-color-accent)] px-4 py-2 text-sm font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">Download</a>
24
- </div>
25
- </div>
26
- ))}
27
- </div>
@@ -1,24 +0,0 @@
1
- ---
2
- const { title, body, primaryHref, primaryLabel, secondaryHref, secondaryLabel } = Astro.props;
3
- ---
4
-
5
- <section class="site-footer-cta px-0 py-0 text-[color:var(--ts-color-text)]">
6
- <div class="site-footer-cta__inner">
7
- <div class="mx-auto flex max-w-4xl flex-col items-center gap-6">
8
- <div class="max-w-3xl">
9
- <h2 class="font-serif text-3xl font-bold md:text-4xl">{title}</h2>
10
- <p class="mt-4 text-lg leading-9 text-[color:var(--ts-color-text-muted)]">{body}</p>
11
- </div>
12
- <div class="flex flex-wrap justify-center gap-3">
13
- <a href={primaryHref} class="border border-[color:var(--ts-color-accent)] bg-[color:var(--ts-color-accent)] px-5 py-3 text-base font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">{
14
- primaryLabel
15
- }</a>
16
- {secondaryHref && secondaryLabel && (
17
- <a href={secondaryHref} class="border border-[color:var(--ts-color-border-strong)] bg-[color:var(--ts-color-surface-muted)] px-5 py-3 text-base font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">
18
- {secondaryLabel}
19
- </a>
20
- )}
21
- </div>
22
- </div>
23
- </div>
24
- </section>
@@ -1,33 +0,0 @@
1
- ---
2
- import type { ContentStatus } from '../../utils/content-status';
3
- import StatusBadge from '../content/StatusBadge.astro';
4
-
5
- type ChronicleItem = {
6
- href: string;
7
- title: string;
8
- summary: string;
9
- status: ContentStatus;
10
- date: Date;
11
- meta?: string;
12
- tags?: string[];
13
- };
14
-
15
- const { items } = Astro.props as { items: ChronicleItem[] };
16
- ---
17
-
18
- <div class="grid gap-1">
19
- {items.map((item) => (
20
- <a href={item.href} class="block border-t border-[color:var(--ts-color-border)] py-7 transition hover:border-[color:var(--ts-color-border-strong)]">
21
- <div class="flex flex-wrap items-center gap-4">
22
- <StatusBadge status={item.status} />
23
- <p class="text-sm font-medium text-[color:var(--ts-color-text-subtle)]">{item.date.toISOString().slice(0, 10)}</p>
24
- {item.meta && <p class="text-sm text-[color:var(--ts-color-text-subtle)]">{item.meta}</p>}
25
- </div>
26
- <h3 class="mt-4 max-w-3xl text-2xl font-bold text-[color:var(--ts-color-text)] md:text-3xl">{item.title}</h3>
27
- <p class="mt-3 max-w-3xl text-lg leading-9 text-[color:var(--ts-color-text-muted)]">{item.summary}</p>
28
- {item.tags && item.tags.length > 0 && (
29
- <p class="mt-4 text-sm uppercase tracking-[0.14em] text-[color:var(--ts-color-accent-strong)]">{item.tags.join(' / ')}</p>
30
- )}
31
- </a>
32
- ))}
33
- </div>
@@ -1,18 +0,0 @@
1
- <section class="relative overflow-hidden border-y border-[color:var(--ts-color-border-strong)] py-12 md:py-18">
2
- <div class="absolute inset-y-0 left-[68%] hidden w-px bg-[linear-gradient(180deg,transparent,var(--ts-color-border-strong),transparent)] lg:block"></div>
3
- <div class="grid gap-10 lg:grid-cols-[1.45fr_0.7fr] lg:items-start">
4
- <div class="space-y-7 pr-0 lg:pr-10">
5
- <slot name="eyebrow" />
6
- <div class="space-y-4">
7
- <slot name="title" />
8
- <slot name="body" />
9
- </div>
10
- <div class="flex flex-wrap gap-4 pt-2">
11
- <slot name="actions" />
12
- </div>
13
- </div>
14
- <div class="border-l border-[color:var(--ts-color-border)] pl-0 lg:pl-8">
15
- <slot name="aside" />
16
- </div>
17
- </div>
18
- </section>
@@ -1,29 +0,0 @@
1
- ---
2
- import ChronicleList from './ChronicleList.astro';
3
-
4
- type NoteListEntry = {
5
- id: string;
6
- data: {
7
- title: string;
8
- summary: string;
9
- status: 'live' | 'in progress' | 'exploratory' | 'planned' | 'speculative';
10
- date: Date;
11
- author?: string;
12
- tags: string[];
13
- };
14
- };
15
-
16
- const { notes } = Astro.props as { notes: NoteListEntry[] };
17
- ---
18
-
19
- <ChronicleList
20
- items={notes.map((note) => ({
21
- href: `/notes/${note.id}/`,
22
- title: note.data.title,
23
- summary: note.data.summary,
24
- status: note.data.status,
25
- date: note.data.date,
26
- meta: note.data.author,
27
- tags: note.data.tags,
28
- }))}
29
- />
@@ -1,16 +0,0 @@
1
- ---
2
- const { href, title, description, meta } = Astro.props;
3
- ---
4
-
5
- <a href={href} class="group block border-t border-[color:var(--ts-color-border)] py-6 transition hover:border-[color:var(--ts-color-border-strong)]">
6
- <div class="grid gap-3 md:grid-cols-[0.42fr_1fr_auto] md:items-start md:gap-6">
7
- <div>
8
- {meta && <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-accent-strong)]">{meta}</p>}
9
- </div>
10
- <div>
11
- <h3 class="text-2xl font-bold text-[color:var(--ts-color-text)]">{title}</h3>
12
- <p class="mt-3 max-w-2xl text-base leading-8 text-[color:var(--ts-color-text-muted)]">{description}</p>
13
- </div>
14
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)] group-hover:text-[color:var(--ts-color-accent-strong)]">Open path</p>
15
- </div>
16
- </a>
@@ -1,30 +0,0 @@
1
- ---
2
- import StatusBadge from '../content/StatusBadge.astro';
3
-
4
- type ProfileItem = {
5
- href: string;
6
- name: string;
7
- summary: string;
8
- meta: string;
9
- status?: 'live' | 'in progress' | 'exploratory' | 'planned' | 'speculative';
10
- tags?: string[];
11
- };
12
-
13
- const { items } = Astro.props as { items: ProfileItem[] };
14
- ---
15
-
16
- <div class="grid gap-6 md:grid-cols-2">
17
- {items.map((item) => (
18
- <a href={item.href} class="border border-[color:var(--ts-color-border)] bg-[color:var(--ts-color-surface)] p-6 transition hover:border-[color:var(--ts-color-border-strong)]">
19
- <div class="flex flex-wrap items-center gap-3">
20
- {item.status && <StatusBadge status={item.status} />}
21
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">{item.meta}</p>
22
- </div>
23
- <h3 class="mt-4 text-2xl font-bold text-[color:var(--ts-color-text)]">{item.name}</h3>
24
- <p class="mt-3 text-base leading-8 text-[color:var(--ts-color-text-muted)]">{item.summary}</p>
25
- {item.tags && item.tags.length > 0 && (
26
- <p class="mt-4 text-sm uppercase tracking-[0.14em] text-[color:var(--ts-color-accent-strong)]">{item.tags.join(' / ')}</p>
27
- )}
28
- </a>
29
- ))}
30
- </div>
@@ -1,5 +0,0 @@
1
- ---
2
- const { html } = Astro.props as { html: string };
3
- ---
4
-
5
- <Fragment set:html={html} />
@@ -1,25 +0,0 @@
1
- ---
2
- import MainLayout from '../../layouts/MainLayout.astro';
3
-
4
- const {
5
- title = 'Page not found',
6
- description = 'The requested Treeseed content could not be found.',
7
- currentPath = '/404/',
8
- } = Astro.props;
9
- ---
10
-
11
- <MainLayout title={title} description={description} currentPath={currentPath}>
12
- <section class="mx-auto max-w-3xl space-y-6 py-20">
13
- <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--ts-color-accent-strong)]">404</p>
14
- <h1 class="font-serif text-5xl text-[color:var(--ts-color-text)]">{title}</h1>
15
- <p class="text-lg leading-9 text-[color:var(--ts-color-text-muted)]">{description}</p>
16
- <div class="flex flex-wrap gap-4">
17
- <a href="/" class="border border-[color:var(--ts-color-accent)] bg-[color:var(--ts-color-accent)] px-5 py-3 text-base font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">
18
- Go home
19
- </a>
20
- <a href="/books/" class="border border-[color:var(--ts-color-border-strong)] px-5 py-3 text-base font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">
21
- Open books
22
- </a>
23
- </div>
24
- </section>
25
- </MainLayout>
@@ -1,9 +0,0 @@
1
- ---
2
- const { eyebrow, title, description } = Astro.props;
3
- ---
4
-
5
- <header class="max-w-4xl space-y-4">
6
- {eyebrow && <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--ts-color-accent-strong)]">{eyebrow}</p>}
7
- <h2 class="max-w-4xl font-serif text-4xl font-bold tracking-tight text-[color:var(--ts-color-text)] md:text-5xl">{title}</h2>
8
- <p class="max-w-3xl text-lg leading-9 text-[color:var(--ts-color-text-muted)] md:text-xl">{description}</p>
9
- </header>
@@ -1,8 +0,0 @@
1
- ---
2
- import { PROJECT_STAGE } from '../../utils/content-status';
3
- ---
4
-
5
- <div class="inline-flex max-w-full flex-wrap items-center gap-x-3 gap-y-1 border-l-4 border-[color:var(--ts-color-accent)] bg-[color:var(--ts-color-accent-soft)] px-4 py-3 text-[color:var(--ts-color-text)]">
6
- <span class="font-bold uppercase tracking-[0.16em] text-xs">{PROJECT_STAGE.label}</span>
7
- <span class="text-[color:var(--ts-color-text-muted)]">{PROJECT_STAGE.description}</span>
8
- </div>
@@ -1,9 +0,0 @@
1
- ---
2
- const { title, body } = Astro.props;
3
- ---
4
-
5
- <aside class="border-l-4 border-[color:var(--ts-color-warning)] bg-[color:var(--ts-color-warning-soft)] px-5 py-5 text-[color:var(--ts-color-text)]">
6
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-warning-text)]">Trust note</p>
7
- <h3 class="mt-2 text-2xl font-bold">{title}</h3>
8
- <p class="mt-3 text-base leading-8 text-[color:var(--ts-color-text-muted)]">{body}</p>
9
- </aside>
@@ -1,6 +0,0 @@
1
- import { Card, CardGrid, LinkCard } from "../vendor/starlight/components.js";
2
- export {
3
- Card,
4
- CardGrid,
5
- LinkCard
6
- };
@@ -1,51 +0,0 @@
1
- ---
2
- import Badge from './Badge.astro';
3
- import type { Tone } from '../types.js';
4
-
5
- interface ActionListItem {
6
- title: string;
7
- description?: string;
8
- href?: string;
9
- meta?: string;
10
- tone?: Tone;
11
- actionLabel?: string;
12
- }
13
-
14
- interface Props {
15
- items?: ActionListItem[];
16
- class?: string;
17
- }
18
-
19
- const {
20
- items = [],
21
- class: className,
22
- } = Astro.props as Props;
23
- ---
24
-
25
- <div class:list={['ts-action-list', className]}>
26
- {items.length > 0 ? (
27
- <ul class="ts-action-list__items">
28
- {items.map((item) => {
29
- const Inner = (
30
- <>
31
- <span class="ts-action-list__content">
32
- <span class="ts-action-list__title">{item.title}</span>
33
- {item.description ? <span class="ts-action-list__description">{item.description}</span> : null}
34
- </span>
35
- <span class="ts-action-list__meta">
36
- {item.meta ? <Badge tone={item.tone ?? 'muted'} size="sm">{item.meta}</Badge> : null}
37
- {item.actionLabel ? <span class="ts-action-list__action">{item.actionLabel}</span> : null}
38
- </span>
39
- </>
40
- );
41
- return (
42
- <li class="ts-action-list__item" data-tone={item.tone ?? 'default'}>
43
- {item.href ? <a href={item.href} class="ts-action-list__link">{Inner}</a> : <div class="ts-action-list__row">{Inner}</div>}
44
- </li>
45
- );
46
- })}
47
- </ul>
48
- ) : (
49
- <slot />
50
- )}
51
- </div>
@@ -1,19 +0,0 @@
1
- ---
2
- import type { Tone } from '../types.js';
3
-
4
- interface Props {
5
- tone?: Tone;
6
- size?: 'sm' | 'md';
7
- class?: string;
8
- }
9
-
10
- const {
11
- tone = 'default',
12
- size = 'md',
13
- class: className,
14
- } = Astro.props as Props;
15
- ---
16
-
17
- <span class:list={['ts-badge', className]} data-tone={tone} data-size={size}>
18
- <slot />
19
- </span>
@@ -1,51 +0,0 @@
1
- ---
2
- interface DataTableColumn {
3
- key: string;
4
- label: string;
5
- }
6
-
7
- interface Props {
8
- columns: DataTableColumn[];
9
- rows: Array<Record<string, unknown>>;
10
- caption?: string;
11
- emptyLabel?: string;
12
- class?: string;
13
- }
14
-
15
- const {
16
- columns,
17
- rows,
18
- caption,
19
- emptyLabel = 'No rows to show.',
20
- class: className,
21
- } = Astro.props as Props;
22
-
23
- function cellValue(row: Record<string, unknown>, key: string) {
24
- const value = row[key];
25
- return value === null || value === undefined ? '' : String(value);
26
- }
27
- ---
28
-
29
- <div class:list={['ts-data-table-wrap', className]}>
30
- <table class="ts-data-table">
31
- {caption ? <caption>{caption}</caption> : null}
32
- <thead>
33
- <tr>
34
- {columns.map((column) => <th scope="col">{column.label}</th>)}
35
- </tr>
36
- </thead>
37
- <tbody>
38
- {rows.length > 0 ? rows.map((row) => (
39
- <tr>
40
- {columns.map((column) => (
41
- <td data-label={column.label}>{cellValue(row, column.key)}</td>
42
- ))}
43
- </tr>
44
- )) : (
45
- <tr>
46
- <td colspan={columns.length}>{emptyLabel}</td>
47
- </tr>
48
- )}
49
- </tbody>
50
- </table>
51
- </div>
@@ -1,28 +0,0 @@
1
- ---
2
- import type { Tone } from '../types.js';
3
-
4
- interface KeyValueItem {
5
- key: string;
6
- value: string | number;
7
- tone?: Tone;
8
- }
9
-
10
- interface Props {
11
- items: KeyValueItem[];
12
- class?: string;
13
- }
14
-
15
- const {
16
- items,
17
- class: className,
18
- } = Astro.props as Props;
19
- ---
20
-
21
- <dl class:list={['ts-key-value-list', className]}>
22
- {items.map((item) => (
23
- <div class="ts-key-value-list__item" data-tone={item.tone ?? 'default'}>
24
- <dt>{item.key}</dt>
25
- <dd>{item.value}</dd>
26
- </div>
27
- ))}
28
- </dl>
@@ -1,25 +0,0 @@
1
- ---
2
- import type { Tone } from '../types.js';
3
-
4
- interface Props {
5
- label: string;
6
- value: string | number;
7
- description?: string;
8
- tone?: Tone;
9
- class?: string;
10
- }
11
-
12
- const {
13
- label,
14
- value,
15
- description,
16
- tone = 'default',
17
- class: className,
18
- } = Astro.props as Props;
19
- ---
20
-
21
- <article class:list={['ts-metric-card', className]} data-tone={tone}>
22
- <p class="ts-metric-card__label">{label}</p>
23
- <p class="ts-metric-card__value">{value}</p>
24
- {description ? <p class="ts-metric-card__description">{description}</p> : null}
25
- </article>
@@ -1,27 +0,0 @@
1
- ---
2
- import MetricCard from './MetricCard.astro';
3
- import type { Tone } from '../types.js';
4
-
5
- interface MetricItem {
6
- label: string;
7
- value: string | number;
8
- description?: string;
9
- tone?: Tone;
10
- }
11
-
12
- interface Props {
13
- metrics?: MetricItem[];
14
- min?: string;
15
- class?: string;
16
- }
17
-
18
- const {
19
- metrics = [],
20
- min = '12rem',
21
- class: className,
22
- } = Astro.props as Props;
23
- ---
24
-
25
- <div class:list={['ts-metric-grid', className]} style={`--ts-metric-grid-min: ${min}`}>
26
- {metrics.length > 0 ? metrics.map((metric) => <MetricCard {...metric} />) : <slot />}
27
- </div>