@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,112 +0,0 @@
1
- ---
2
- import {
3
- buildTreeseedThemeCss,
4
- getBuiltInColorSchemes,
5
- normalizeThemePreference,
6
- type ThemePreference,
7
- } from '../../../utils/theme.js';
8
-
9
- interface Props {
10
- defaultScheme?: ThemePreference['scheme'];
11
- defaultMode?: ThemePreference['mode'];
12
- includeCss?: boolean;
13
- preferDefaultPreference?: boolean;
14
- }
15
-
16
- const preference = normalizeThemePreference({
17
- scheme: (Astro.props as Props).defaultScheme,
18
- mode: (Astro.props as Props).defaultMode,
19
- });
20
- const { includeCss = true, preferDefaultPreference = false } = Astro.props as Props;
21
- const builtInSchemeIds = getBuiltInColorSchemes().map((scheme) => scheme.id);
22
- const themeCss = buildTreeseedThemeCss();
23
- ---
24
-
25
- {includeCss ? <style is:inline set:html={themeCss}></style> : null}
26
-
27
- <script
28
- is:inline
29
- data-astro-rerun
30
- define:vars={{
31
- builtInSchemeIds,
32
- defaultScheme: preference.scheme,
33
- defaultMode: preference.mode,
34
- preferDefaultPreference,
35
- }}
36
- >
37
- (() => {
38
- const schemeKey = 'treeseed_color_scheme';
39
- const modeKey = 'treeseed_theme_mode';
40
- const modes = new Set(['light', 'dark', 'system']);
41
- const schemes = new Set(builtInSchemeIds);
42
- const root = document.documentElement;
43
-
44
- function readCookie(name) {
45
- const pair = document.cookie
46
- .split('; ')
47
- .find((entry) => entry.startsWith(`${name}=`));
48
- return pair ? decodeURIComponent(pair.slice(name.length + 1)) : '';
49
- }
50
-
51
- function readStored(name) {
52
- try {
53
- return window.sessionStorage.getItem(name) || window.localStorage.getItem(name) || '';
54
- } catch {
55
- return '';
56
- }
57
- }
58
-
59
- function cookie(name, value) {
60
- const secure = window.location.protocol === 'https:' ? '; Secure' : '';
61
- document.cookie = `${name}=${encodeURIComponent(value)}; Max-Age=${60 * 60 * 24 * 365}; Path=/; SameSite=Lax${secure}`;
62
- }
63
-
64
- function store(name, value) {
65
- try {
66
- window.sessionStorage.setItem(name, value);
67
- } catch {
68
- // Session storage can be disabled; local storage/cookies still carry the preference.
69
- }
70
- try {
71
- window.localStorage.setItem(name, value);
72
- } catch {
73
- // Local storage can be disabled; cookies still carry the preference for SSR.
74
- }
75
- cookie(name, value);
76
- }
77
-
78
- function resolveScheme(value) {
79
- return schemes.has(value) ? value : defaultScheme;
80
- }
81
-
82
- function resolveMode(value) {
83
- return modes.has(value) ? value : defaultMode;
84
- }
85
-
86
- function resolveRenderedMode(mode) {
87
- if (mode !== 'system') return mode;
88
- return window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
89
- }
90
-
91
- const storedScheme = readCookie(schemeKey) || readStored(schemeKey);
92
- const storedModeValue = readCookie(modeKey) || readStored(modeKey);
93
- const scheme = preferDefaultPreference ? resolveScheme(defaultScheme) : resolveScheme(storedScheme || defaultScheme);
94
- const storedMode = preferDefaultPreference ? resolveMode(defaultMode) : resolveMode(storedModeValue || defaultMode);
95
- const renderedMode = resolveRenderedMode(storedMode);
96
-
97
- root.dataset.tsScheme = scheme;
98
- root.dataset.tsMode = renderedMode;
99
- if (storedMode === 'system') {
100
- root.dataset.tsModeSource = 'system';
101
- } else {
102
- delete root.dataset.tsModeSource;
103
- }
104
- store(schemeKey, scheme);
105
- store(modeKey, storedMode);
106
- window.matchMedia?.('(prefers-color-scheme: dark)').addEventListener?.('change', () => {
107
- if (root.dataset.tsModeSource === 'system') {
108
- root.dataset.tsMode = resolveRenderedMode('system');
109
- }
110
- });
111
- })();
112
- </script>
@@ -1,202 +0,0 @@
1
- ---
2
- import {
3
- getBuiltInColorSchemes,
4
- normalizeThemePreference,
5
- type ThemePreference,
6
- } from '../../../utils/theme.js';
7
- import ThemePreviewSwatch from './ThemePreviewSwatch.astro';
8
-
9
- interface Props {
10
- label?: string;
11
- selectedScheme?: ThemePreference['scheme'];
12
- selectedMode?: ThemePreference['mode'];
13
- includeHiddenFields?: boolean;
14
- schemeFieldName?: string;
15
- modeFieldName?: string;
16
- compact?: boolean;
17
- }
18
-
19
- const {
20
- label = 'Appearance',
21
- selectedScheme,
22
- selectedMode,
23
- includeHiddenFields = false,
24
- schemeFieldName = 'colorScheme',
25
- modeFieldName = 'themeMode',
26
- compact = true,
27
- } = Astro.props as Props;
28
-
29
- const schemes = getBuiltInColorSchemes();
30
- const preference = normalizeThemePreference({ scheme: selectedScheme, mode: selectedMode });
31
- const activeScheme = schemes.find((scheme) => scheme.id === preference.scheme) ?? schemes[0];
32
- const initialSwatches = preference.mode === 'dark'
33
- ? activeScheme.modeSwatches.dark
34
- : activeScheme.modeSwatches.light;
35
- const modes = [
36
- { value: 'system', label: 'System' },
37
- { value: 'light', label: 'Light' },
38
- { value: 'dark', label: 'Dark' },
39
- ];
40
- ---
41
-
42
- <div
43
- class:list={['ts-theme-selector', compact && 'ts-theme-selector--compact']}
44
- data-ts-theme-selector
45
- data-selected-scheme={preference.scheme}
46
- data-selected-mode={preference.mode}
47
- data-scheme-field={schemeFieldName}
48
- data-mode-field={modeFieldName}
49
- >
50
- <div class="ts-theme-selector__summary">
51
- <ThemePreviewSwatch swatches={initialSwatches} label={`${activeScheme.name} preview`} />
52
- <span class="ts-theme-selector__label">
53
- <span>{label}</span>
54
- <small data-ts-theme-preview-label>{activeScheme.name} · {preference.mode}</small>
55
- </span>
56
- </div>
57
- <div class="ts-theme-selector__controls">
58
- <label class="ts-theme-selector__field">
59
- <span>Color scheme</span>
60
- <select data-ts-theme-scheme-select aria-label="Color scheme">
61
- {schemes.map((scheme) => (
62
- <option value={scheme.id} selected={scheme.id === preference.scheme}>
63
- {scheme.name}
64
- </option>
65
- ))}
66
- </select>
67
- </label>
68
- <label class="ts-theme-selector__field">
69
- <span>Mode</span>
70
- <select data-ts-theme-mode-select aria-label="Theme mode">
71
- {modes.map((mode) => (
72
- <option value={mode.value} selected={mode.value === preference.mode}>
73
- {mode.label}
74
- </option>
75
- ))}
76
- </select>
77
- </label>
78
- </div>
79
- {includeHiddenFields ? (
80
- <>
81
- <input type="hidden" name={schemeFieldName} value={preference.scheme} data-ts-theme-scheme-field />
82
- <input type="hidden" name={modeFieldName} value={preference.mode} data-ts-theme-mode-field />
83
- </>
84
- ) : null}
85
- </div>
86
-
87
- <script is:inline data-astro-rerun define:vars={{ schemes }}>
88
- (() => {
89
- const schemeKey = 'treeseed_color_scheme';
90
- const modeKey = 'treeseed_theme_mode';
91
- const maxAge = 60 * 60 * 24 * 365;
92
- const modeValues = new Set(['light', 'dark', 'system']);
93
- const schemeValues = new Set(schemes.map((scheme) => scheme.id));
94
- const schemeMap = new Map(schemes.map((scheme) => [scheme.id, scheme]));
95
- const media = window.matchMedia?.('(prefers-color-scheme: dark)');
96
-
97
- function cookie(name, value) {
98
- const secure = window.location.protocol === 'https:' ? '; Secure' : '';
99
- document.cookie = `${name}=${encodeURIComponent(value)}; Max-Age=${maxAge}; Path=/; SameSite=Lax${secure}`;
100
- }
101
-
102
- function store(name, value) {
103
- try {
104
- window.sessionStorage.setItem(name, value);
105
- } catch {
106
- // Session storage can be disabled; local storage/cookies still carry the preference.
107
- }
108
- try {
109
- window.localStorage.setItem(name, value);
110
- } catch {
111
- // Storage can be disabled; cookies still carry the preference for SSR.
112
- }
113
- }
114
-
115
- function renderedMode(mode) {
116
- if (mode !== 'system') return mode;
117
- return media?.matches ? 'dark' : 'light';
118
- }
119
-
120
- function applyTheme(scheme, mode, persist = true) {
121
- const root = document.documentElement;
122
- const safeScheme = schemeValues.has(scheme) ? scheme : 'fern';
123
- const safeMode = modeValues.has(mode) ? mode : 'system';
124
- root.dataset.tsScheme = safeScheme;
125
- root.dataset.tsMode = renderedMode(safeMode);
126
- if (safeMode === 'system') {
127
- root.dataset.tsModeSource = 'system';
128
- } else {
129
- delete root.dataset.tsModeSource;
130
- }
131
- if (persist) {
132
- cookie(schemeKey, safeScheme);
133
- cookie(modeKey, safeMode);
134
- store(schemeKey, safeScheme);
135
- store(modeKey, safeMode);
136
- }
137
- window.dispatchEvent(new CustomEvent('treeseed:theme-change', {
138
- detail: { scheme: safeScheme, mode: safeMode, renderedMode: renderedMode(safeMode), persist },
139
- }));
140
- }
141
-
142
- function modeSwatches(scheme, mode) {
143
- const rendered = renderedMode(mode);
144
- return scheme.modeSwatches?.[rendered] || scheme.swatches || [];
145
- }
146
-
147
- function updateSwatch(selector, schemeId, mode) {
148
- const scheme = schemeMap.get(schemeId);
149
- const swatch = selector.querySelector('.ts-theme-swatch');
150
- const previewLabel = selector.querySelector('[data-ts-theme-preview-label]');
151
- if (!scheme || !swatch) return;
152
- swatch.setAttribute('aria-label', `${scheme.name} preview`);
153
- modeSwatches(scheme, mode).slice(0, 4).forEach((value, index) => {
154
- swatch.style.setProperty(`--ts-preview-swatch-${index + 1}`, value);
155
- });
156
- if (previewLabel) {
157
- previewLabel.textContent = `${scheme.name} · ${mode === 'system' ? `System ${renderedMode(mode)}` : mode}`;
158
- }
159
- }
160
-
161
- function currentPreference() {
162
- const root = document.documentElement;
163
- const scheme = schemeValues.has(root.dataset.tsScheme) ? root.dataset.tsScheme : '';
164
- const mode = root.dataset.tsModeSource === 'system' ? 'system' : root.dataset.tsMode;
165
- return {
166
- scheme,
167
- mode: modeValues.has(mode) ? mode : '',
168
- };
169
- }
170
-
171
- function bind(selector) {
172
- const schemeSelect = selector.querySelector('[data-ts-theme-scheme-select]');
173
- const modeSelect = selector.querySelector('[data-ts-theme-mode-select]');
174
- const schemeField = selector.querySelector('[data-ts-theme-scheme-field]');
175
- const modeField = selector.querySelector('[data-ts-theme-mode-field]');
176
- if (!(schemeSelect instanceof HTMLSelectElement) || !(modeSelect instanceof HTMLSelectElement)) return;
177
-
178
- const activePreference = currentPreference();
179
- if (activePreference.scheme) schemeSelect.value = activePreference.scheme;
180
- if (activePreference.mode) modeSelect.value = activePreference.mode;
181
-
182
- function sync(persist = true) {
183
- const scheme = schemeSelect.value;
184
- const mode = modeSelect.value;
185
- if (schemeField instanceof HTMLInputElement) schemeField.value = scheme;
186
- if (modeField instanceof HTMLInputElement) modeField.value = mode;
187
- updateSwatch(selector, scheme, mode);
188
- applyTheme(scheme, mode, persist);
189
- }
190
-
191
- schemeSelect.addEventListener('change', () => sync(true));
192
- modeSelect.addEventListener('change', () => sync(true));
193
- sync(false);
194
- }
195
-
196
- document.querySelectorAll('[data-ts-theme-selector]').forEach(bind);
197
- media?.addEventListener?.('change', () => {
198
- const mode = document.documentElement.dataset.tsModeSource === 'system' ? 'system' : document.documentElement.dataset.tsMode;
199
- applyTheme(document.documentElement.dataset.tsScheme || 'fern', mode || 'system', false);
200
- });
201
- })();
202
- </script>
File without changes
@@ -1,195 +0,0 @@
1
- ---
2
- import MainLayout from './MainLayout.astro';
3
- import StatusBadge from '../components/content/StatusBadge.astro';
4
- import type { RuntimeReferenceEntry } from '../utils/site-content-runtime';
5
-
6
- type LocalContributor = {
7
- data: {
8
- name: string;
9
- };
10
- };
11
-
12
- type MetadataItem = {
13
- label: string;
14
- value?: unknown;
15
- href?: string;
16
- };
17
-
18
- function entryTitle(entry: RuntimeReferenceEntry) {
19
- return entry.data.title ?? entry.data.name ?? entry.id;
20
- }
21
-
22
- function displayValue(value: unknown) {
23
- if (value instanceof Date) return value.toISOString().slice(0, 10);
24
- if (Array.isArray(value)) return value.map((entry) => displayValue(entry)).filter(Boolean).join(', ');
25
- if (typeof value === 'string') return value.trim();
26
- if (typeof value === 'number' || typeof value === 'boolean') return String(value);
27
- return '';
28
- }
29
-
30
- function metadataItem(label: string, value: unknown, href?: string): MetadataItem | null {
31
- const display = displayValue(value);
32
- return display ? { label, value: display, href } : null;
33
- }
34
-
35
- function metadataKey(item: MetadataItem) {
36
- return `${item.label.toLowerCase()}::${displayValue(item.value).toLowerCase()}::${item.href ?? ''}`;
37
- }
38
-
39
- function uniqueMetadata(items: MetadataItem[]) {
40
- const seen = new Set<string>();
41
- return items.filter((item) => {
42
- const key = metadataKey(item);
43
- if (seen.has(key)) return false;
44
- seen.add(key);
45
- return true;
46
- });
47
- }
48
-
49
- const {
50
- entry,
51
- currentPath,
52
- contributor,
53
- contentTypeLabel = 'Content',
54
- contentId,
55
- collectionLabel,
56
- metaLabel,
57
- metaValue,
58
- metadataItems = [],
59
- relatedQuestions = [],
60
- relatedObjectives = [],
61
- relatedNotes = [],
62
- relatedProposals = [],
63
- relatedDecisions = [],
64
- relatedBooks = [],
65
- introText,
66
- } = Astro.props as {
67
- entry: {
68
- title: string;
69
- description: string;
70
- summary: string;
71
- status: 'live' | 'in progress' | 'exploratory' | 'planned' | 'speculative';
72
- date: Date;
73
- motivation?: string;
74
- tags: string[];
75
- canonicalRoute?: string;
76
- author?: string;
77
- };
78
- currentPath: string;
79
- contributor: LocalContributor | RuntimeReferenceEntry | undefined | null;
80
- contentTypeLabel?: string;
81
- contentId?: string;
82
- collectionLabel?: string;
83
- metaLabel?: string;
84
- metaValue?: string;
85
- metadataItems?: MetadataItem[];
86
- relatedQuestions?: RuntimeReferenceEntry[];
87
- relatedObjectives?: RuntimeReferenceEntry[];
88
- relatedNotes?: RuntimeReferenceEntry[];
89
- relatedProposals?: RuntimeReferenceEntry[];
90
- relatedDecisions?: RuntimeReferenceEntry[];
91
- relatedBooks?: RuntimeReferenceEntry[];
92
- introText?: string;
93
- };
94
-
95
- const contributorName = contributor?.data.name ?? entry.author ?? '';
96
- const normalizedTypeLabel = contentTypeLabel.trim() || 'Content';
97
- const contentKind = displayValue(metaValue);
98
- const recordMetadata = uniqueMetadata([
99
- metadataItem('ID', contentId),
100
- metadataItem('Collection', collectionLabel ?? (currentPath.replaceAll('/', '') || undefined)),
101
- metadataItem(metaLabel ?? '', metaValue),
102
- metadataItem('Route', entry.canonicalRoute, entry.canonicalRoute),
103
- ...metadataItems.map((item) => metadataItem(item.label, item.value, item.href)).filter(Boolean),
104
- ].filter(Boolean) as MetadataItem[]);
105
-
106
- const relationSections = [
107
- { label: 'Questions', hrefBase: '/questions', items: relatedQuestions },
108
- { label: 'Objectives', hrefBase: '/objectives', items: relatedObjectives },
109
- { label: 'Notes', hrefBase: '/notes', items: relatedNotes },
110
- { label: 'Proposals', hrefBase: '/proposals', items: relatedProposals },
111
- { label: 'Decisions', hrefBase: '/decisions', items: relatedDecisions },
112
- { label: 'Books', hrefBase: '/books', items: relatedBooks },
113
- ].filter((section) => section.items.length > 0);
114
-
115
- const relationCount = relationSections.reduce((total, section) => total + section.items.length, 0);
116
- const plainIntroText = displayValue(introText ?? entry.motivation);
117
- ---
118
-
119
- <MainLayout title={entry.title} description={entry.description} currentPath={currentPath}>
120
- <article class="max-w-6xl space-y-8">
121
- <header class="grid gap-6 border-b border-[color:var(--ts-color-border)] pb-7 lg:grid-cols-[minmax(0,1fr)_18rem]">
122
- <div class="space-y-4">
123
- <div class="flex flex-wrap items-center gap-2.5">
124
- <p class="rounded-full border border-[color:var(--ts-color-border)] px-3 py-1 text-xs font-semibold uppercase text-[color:var(--ts-color-info-text)]">{normalizedTypeLabel}</p>
125
- {contentKind && <p class="rounded-full bg-[color:var(--ts-color-surface-muted)] px-3 py-1 text-xs font-semibold uppercase text-[color:var(--ts-color-text-subtle)]">{contentKind}</p>}
126
- <StatusBadge status={entry.status} />
127
- <p class="text-sm font-medium text-[color:var(--ts-color-text-subtle)]">{entry.date.toISOString().slice(0, 10)}</p>
128
- {contributorName && <p class="text-sm text-[color:var(--ts-color-text-subtle)]">{contributorName}</p>}
129
- </div>
130
- <h1 class="max-w-4xl font-serif text-4xl font-bold tracking-normal text-[color:var(--ts-color-text)] md:text-5xl">{entry.title}</h1>
131
- <p class="max-w-3xl text-lg leading-8 text-[color:var(--ts-color-text-muted)] md:text-xl md:leading-9">{entry.summary}</p>
132
- {plainIntroText && (
133
- <div class="max-w-3xl border-l-2 border-[color:var(--ts-color-border-strong)] pl-4">
134
- <p class="text-xs font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Why it matters</p>
135
- <p class="mt-1 text-sm leading-7 text-[color:var(--ts-color-text-muted)]">{plainIntroText}</p>
136
- </div>
137
- )}
138
- {entry.tags.length > 0 && (
139
- <ul class="flex flex-wrap gap-2" aria-label="Tags">
140
- {entry.tags.map((tag) => (
141
- <li class="rounded-full bg-[color:var(--ts-color-surface-muted)] px-3 py-1 text-xs font-semibold uppercase text-[color:var(--ts-color-accent-strong)]">{tag}</li>
142
- ))}
143
- </ul>
144
- )}
145
- </div>
146
- <aside class="self-start border-l border-[color:var(--ts-color-border)] pl-5">
147
- <div class="flex items-center justify-between gap-3 border-b border-[color:var(--ts-color-border)] pb-3">
148
- <div>
149
- <p class="text-xs font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Record</p>
150
- <p class="mt-1 text-sm text-[color:var(--ts-color-text-muted)]">{relationCount} linked record{relationCount === 1 ? '' : 's'}</p>
151
- </div>
152
- </div>
153
- <dl class="mt-2 text-sm">
154
- {recordMetadata.map((item) => (
155
- <div class="grid grid-cols-[5.75rem_minmax(0,1fr)] gap-3 py-2.5">
156
- <dt class="text-[color:var(--ts-color-text-subtle)]">{item.label}</dt>
157
- <dd class="min-w-0 break-words font-medium text-[color:var(--ts-color-text)]">
158
- {item.href ? <a class="underline decoration-[color:var(--ts-color-border-strong)] underline-offset-4 hover:text-[color:var(--ts-color-accent-strong)]" href={item.href}>{displayValue(item.value)}</a> : displayValue(item.value)}
159
- </dd>
160
- </div>
161
- ))}
162
- </dl>
163
- </aside>
164
- </header>
165
- <div class="prose max-w-3xl">
166
- <slot />
167
- </div>
168
- {relationSections.length > 0 && (
169
- <section class="border-t border-[color:var(--ts-color-border)] pt-7">
170
- <div class="mb-4 flex flex-wrap items-end justify-between gap-3">
171
- <div>
172
- <p class="text-xs font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Connections</p>
173
- </div>
174
- </div>
175
- <div class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
176
- {relationSections.map((section) => (
177
- <div class="border-l border-[color:var(--ts-color-border)] pl-4">
178
- <p class="text-xs font-semibold uppercase tracking-[0.12em] text-[color:var(--ts-color-text-subtle)]">{section.label}</p>
179
- <ul class="mt-2 space-y-2">
180
- {section.items.map((item) => (
181
- <li>
182
- <a href={`${section.hrefBase}/${item.id}/`} class="block text-[color:var(--ts-color-text)] hover:text-[color:var(--ts-color-accent-strong)]">
183
- <span class="block text-sm font-semibold">{entryTitle(item)}</span>
184
- <span class="block text-xs text-[color:var(--ts-color-text-subtle)]">{item.id}</span>
185
- </a>
186
- </li>
187
- ))}
188
- </ul>
189
- </div>
190
- ))}
191
- </div>
192
- </section>
193
- )}
194
- </article>
195
- </MainLayout>
@@ -1,35 +0,0 @@
1
- ---
2
- import MainLayout from './MainLayout.astro';
3
-
4
- const { entry, currentPath } = Astro.props;
5
- ---
6
-
7
- <MainLayout title={entry.title} description={entry.description} currentPath={currentPath}>
8
- <article class="max-w-5xl space-y-8">
9
- <div class="space-y-4 border-b border-[color:var(--ts-color-border)] pb-8">
10
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">{entry.sectionLabel}</p>
11
- <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--ts-color-text)] md:text-6xl">{entry.title}</h1>
12
- <p class="max-w-4xl text-xl leading-10 text-[color:var(--ts-color-text-muted)]">{entry.summary}</p>
13
- {entry.tags.length > 0 && (
14
- <p class="text-sm uppercase tracking-[0.14em] text-[color:var(--ts-color-accent-strong)]">{entry.tags.join(' / ')}</p>
15
- )}
16
- <div class="flex flex-wrap gap-3 pt-2">
17
- <a href={entry.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>
18
- <a href={entry.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 book</a>
19
- </div>
20
- </div>
21
- <div class="grid gap-6 md:grid-cols-[1.25fr_0.75fr]">
22
- <div class="prose">
23
- <slot />
24
- </div>
25
- <div class="border border-[color:var(--ts-color-border)] bg-[color:var(--ts-color-surface)] p-5">
26
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Book paths</p>
27
- <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
28
- <li>Landing path: <code>{entry.landingPath}</code></li>
29
- <li>Base path: <code>{entry.basePath}</code></li>
30
- <li>Download file: <code>{entry.downloadFileName}</code></li>
31
- </ul>
32
- </div>
33
- </div>
34
- </article>
35
- </MainLayout>
@@ -1,11 +0,0 @@
1
- ---
2
- import MainLayout from './MainLayout.astro';
3
-
4
- const { entry, currentPath } = Astro.props;
5
- ---
6
-
7
- <MainLayout title={entry.title} description={entry.description} currentPath={currentPath}>
8
- <div class="space-y-10">
9
- <slot />
10
- </div>
11
- </MainLayout>
@@ -1,24 +0,0 @@
1
- ---
2
- import MainLayout from './MainLayout.astro';
3
- import StatusBadge from '../components/content/StatusBadge.astro';
4
- import StageBanner from '../components/site/StageBanner.astro';
5
-
6
- const { entry, currentPath } = Astro.props;
7
- ---
8
-
9
- <MainLayout title={entry.title} description={entry.description} currentPath={currentPath}>
10
- <div class="space-y-8">
11
- <StageBanner />
12
- <section class="max-w-5xl space-y-5 border-b border-[color:var(--ts-color-border)] pb-8">
13
- <div class="flex flex-wrap items-center gap-3">
14
- <StatusBadge status={entry.status} />
15
- <p class="text-sm font-medium text-[color:var(--ts-color-text-subtle)]">Updated {entry.updated.toISOString().slice(0, 10)}</p>
16
- </div>
17
- <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--ts-color-text)] md:text-7xl">{entry.title}</h1>
18
- <p class="max-w-4xl text-xl leading-10 text-[color:var(--ts-color-text-muted)]">{entry.summary}</p>
19
- </section>
20
- <article class="prose max-w-4xl">
21
- <slot />
22
- </article>
23
- </div>
24
- </MainLayout>
@@ -1,76 +0,0 @@
1
- ---
2
- import '../styles/global.css';
3
- import PublicShell from '../components/ui/shell/PublicShell.astro';
4
- import ShellIconLink from '../components/ui/shell/ShellIconLink.astro';
5
- import { SITE_NAV_GROUPS } from '../utils/routes';
6
- import { SITE } from '../utils/seo';
7
- import { SITE_THEME_CSS } from '../utils/site-config';
8
- import { normalizeThemePreference } from '../utils/theme.js';
9
-
10
- const { title, description, currentPath = '/' } = Astro.props;
11
- const appearance = normalizeThemePreference({
12
- scheme: SITE.theme?.defaultScheme,
13
- mode: SITE.theme?.defaultMode,
14
- });
15
- const navItems = SITE_NAV_GROUPS.flatMap((group) => group.items);
16
- ---
17
-
18
- <PublicShell
19
- title={title ? `${title} | ${SITE.name}` : SITE.name}
20
- description={description ?? SITE.description}
21
- currentPath={currentPath}
22
- appearance={appearance}
23
- brand={{
24
- name: SITE.name,
25
- tag: SITE.statement,
26
- href: '/',
27
- logoSrc: SITE.logo.src,
28
- logoAlt: SITE.logo.alt,
29
- }}
30
- navItems={navItems}
31
- navGroups={SITE_NAV_GROUPS}
32
- contentWidth="content"
33
- >
34
- <Fragment slot="head">
35
- {SITE_THEME_CSS && <style is:global>{SITE_THEME_CSS}</style>}
36
- </Fragment>
37
- <Fragment slot="actions">
38
- <ShellIconLink href="/app/" label="Manager" icon="manager" />
39
- <a
40
- href={SITE.githubRepository}
41
- target="_blank"
42
- rel="noreferrer"
43
- aria-label={`${SITE.name} GitHub repository`}
44
- title={`${SITE.name} GitHub repository`}
45
- class="ts-public-shell__icon-link"
46
- >
47
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
48
- <path d="M8 0C3.58 0 0 3.67 0 8.2c0 3.63 2.29 6.7 5.47 7.78.4.08.55-.18.55-.39 0-.2-.01-.85-.01-1.54-2.01.38-2.53-.5-2.69-.95-.09-.24-.48-.95-.82-1.15-.28-.15-.68-.54-.01-.55.63-.01 1.08.59 1.23.84.72 1.24 1.87.89 2.33.68.07-.54.28-.89.5-1.09-1.78-.21-3.64-.92-3.64-4.08 0-.9.31-1.64.82-2.22-.08-.21-.36-1.06.08-2.2 0 0 .67-.22 2.2.85A7.34 7.34 0 0 1 8 4.83c.68 0 1.37.09 2.01.27 1.53-1.07 2.2-.85 2.2-.85.44 1.14.16 1.99.08 2.2.51.58.82 1.31.82 2.22 0 3.17-1.87 3.87-3.65 4.08.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.19 0 .21.15.48.55.39A8.22 8.22 0 0 0 16 8.2C16 3.67 12.42 0 8 0Z" />
49
- </svg>
50
- </a>
51
- <a
52
- href={SITE.discordLink}
53
- target="_blank"
54
- rel="noreferrer"
55
- aria-label={`${SITE.name} Discord`}
56
- title={`${SITE.name} Discord`}
57
- class="ts-public-shell__icon-link"
58
- >
59
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
60
- <path d="M20.317 4.369A19.791 19.791 0 0 0 15.885 3c-.191.328-.403.762-.554 1.104a18.27 18.27 0 0 0-5.314 0A11.64 11.64 0 0 0 9.463 3a19.736 19.736 0 0 0-4.433 1.369C2.227 8.617 1.468 12.759 1.848 16.845a19.9 19.9 0 0 0 5.437 2.755c.438-.6.825-1.236 1.157-1.902-.637-.241-1.246-.545-1.818-.9.152-.111.3-.229.444-.347 3.507 1.648 7.316 1.648 10.782 0 .145.118.293.236.444.347-.573.355-1.183.659-1.82.9.332.666.719 1.302 1.158 1.902a19.87 19.87 0 0 0 5.438-2.755c.446-4.737-.762-8.842-3.753-12.476ZM9.954 14.379c-1.053 0-1.918-.966-1.918-2.153 0-1.188.845-2.153 1.918-2.153 1.082 0 1.938.975 1.918 2.153 0 1.187-.846 2.153-1.918 2.153Zm4.092 0c-1.053 0-1.918-.966-1.918-2.153 0-1.188.845-2.153 1.918-2.153 1.082 0 1.938.975 1.918 2.153 0 1.187-.836 2.153-1.918 2.153Z" />
61
- </svg>
62
- </a>
63
- <a
64
- href="/contact/"
65
- aria-label={`Contact ${SITE.name}`}
66
- title={`Contact ${SITE.name}`}
67
- class="ts-public-shell__icon-link ts-public-shell__icon-link--stroke"
68
- >
69
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
70
- <path d="M3.75 6.75h16.5v10.5H3.75z" />
71
- <path d="m4.5 7.5 7.5 6 7.5-6" />
72
- </svg>
73
- </a>
74
- </Fragment>
75
- <slot />
76
- </PublicShell>