@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.
- package/README.md +69 -125
- package/dist/dev-watch.js +2 -1
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +51 -35
- package/dist/pages/404.astro +1 -1
- package/dist/pages/[slug].astro +4 -4
- package/dist/pages/agents/[slug].astro +3 -3
- package/dist/pages/agents/index.astro +3 -3
- package/dist/pages/books/[slug].astro +3 -3
- package/dist/pages/books/index.astro +3 -3
- package/dist/pages/contact.astro +2 -2
- package/dist/pages/decisions/[slug].astro +3 -3
- package/dist/pages/decisions/index.astro +3 -3
- package/dist/pages/docs-runtime/[...slug].astro +3 -3
- package/dist/pages/docs-runtime/index.astro +3 -3
- package/dist/pages/index.astro +11 -11
- package/dist/pages/notes/[slug].astro +3 -3
- package/dist/pages/notes/index.astro +3 -3
- package/dist/pages/objectives/[slug].astro +3 -3
- package/dist/pages/objectives/index.astro +3 -3
- package/dist/pages/people/[slug].astro +3 -3
- package/dist/pages/people/index.astro +3 -3
- package/dist/pages/proposals/[slug].astro +3 -3
- package/dist/pages/proposals/index.astro +3 -3
- package/dist/pages/questions/[slug].astro +3 -3
- package/dist/pages/questions/index.astro +3 -3
- package/dist/pages/ui/index.astro +23 -23
- package/dist/platform-resources.js +5 -1
- package/dist/scripts/build-dist.js +2 -0
- package/dist/scripts/release-verify.js +24 -2
- package/dist/scripts/workspace-bootstrap.js +3 -0
- package/dist/site.js +49 -11
- package/dist/styles/global.css +5 -5
- package/package.json +3 -45
- package/templates/github/deploy-web.workflow.yml +36 -2
- package/dist/components/DevWatchReload.astro +0 -45
- package/dist/components/SiteTitle.astro +0 -51
- package/dist/components/content/ContentStatusLegend.astro +0 -18
- package/dist/components/content/StatusBadge.astro +0 -11
- package/dist/components/docs/BookFontControls.astro +0 -180
- package/dist/components/docs/DesktopSidebarToggle.astro +0 -88
- package/dist/components/docs/DownloadBook.astro +0 -34
- package/dist/components/docs/Footer.astro +0 -112
- package/dist/components/docs/Header.astro +0 -157
- package/dist/components/docs/PageFrame.astro +0 -260
- package/dist/components/docs/PageSidebar.astro +0 -63
- package/dist/components/docs/PageTitle.astro +0 -39
- package/dist/components/docs/Sidebar.astro +0 -41
- package/dist/components/docs/ThemeSelect.astro +0 -5
- package/dist/components/forms/ContactForm.astro +0 -233
- package/dist/components/forms/FooterSubscribeForm.astro +0 -188
- package/dist/components/site/BookList.astro +0 -27
- package/dist/components/site/CTASection.astro +0 -24
- package/dist/components/site/ChronicleList.astro +0 -33
- package/dist/components/site/Hero.astro +0 -18
- package/dist/components/site/NotesList.astro +0 -29
- package/dist/components/site/PathCard.astro +0 -16
- package/dist/components/site/ProfileList.astro +0 -30
- package/dist/components/site/PublishedContentBody.astro +0 -5
- package/dist/components/site/RouteNotFound.astro +0 -25
- package/dist/components/site/SectionIntro.astro +0 -9
- package/dist/components/site/StageBanner.astro +0 -8
- package/dist/components/site/TrustCallout.astro +0 -9
- package/dist/components/starlight.js +0 -6
- package/dist/components/ui/data/ActionList.astro +0 -51
- package/dist/components/ui/data/Badge.astro +0 -19
- package/dist/components/ui/data/DataTable.astro +0 -51
- package/dist/components/ui/data/KeyValueList.astro +0 -28
- package/dist/components/ui/data/MetricCard.astro +0 -25
- package/dist/components/ui/data/MetricGrid.astro +0 -27
- package/dist/components/ui/data/StatusPill.astro +0 -20
- package/dist/components/ui/forms/Button.astro +0 -59
- package/dist/components/ui/forms/Field.astro +0 -39
- package/dist/components/ui/forms/FormActions.astro +0 -12
- package/dist/components/ui/forms/PasswordMeter.astro +0 -80
- package/dist/components/ui/forms/RadioGroup.astro +0 -55
- package/dist/components/ui/forms/Select.astro +0 -47
- package/dist/components/ui/forms/TextInput.astro +0 -58
- package/dist/components/ui/forms/Textarea.astro +0 -45
- package/dist/components/ui/layout/PageHeader.astro +0 -45
- package/dist/components/ui/shell/AppShell.astro +0 -130
- package/dist/components/ui/shell/BottomNav.astro +0 -42
- package/dist/components/ui/shell/ProjectHeader.astro +0 -66
- package/dist/components/ui/shell/PublicFooter.astro +0 -39
- package/dist/components/ui/shell/PublicShell.astro +0 -184
- package/dist/components/ui/shell/RailNav.astro +0 -42
- package/dist/components/ui/shell/ShellIconLink.astro +0 -30
- package/dist/components/ui/shell/TopBar.astro +0 -52
- package/dist/components/ui/surface/Card.astro +0 -46
- package/dist/components/ui/surface/EmptyState.astro +0 -45
- package/dist/components/ui/surface/Panel.astro +0 -54
- package/dist/components/ui/theme/ThemeMenu.astro +0 -58
- package/dist/components/ui/theme/ThemePreviewSwatch.astro +0 -18
- package/dist/components/ui/theme/ThemeScript.astro +0 -112
- package/dist/components/ui/theme/ThemeSelector.astro +0 -202
- package/dist/components/ui/types.js +0 -0
- package/dist/layouts/AuthoredEntryLayout.astro +0 -195
- package/dist/layouts/BookLayout.astro +0 -35
- package/dist/layouts/BridgeLayout.astro +0 -11
- package/dist/layouts/ContentLayout.astro +0 -24
- package/dist/layouts/MainLayout.astro +0 -76
- package/dist/layouts/NoteLayout.astro +0 -26
- package/dist/layouts/ProfileLayout.astro +0 -85
- package/dist/styles/app-shell.css +0 -626
- package/dist/styles/forms.css +0 -274
- package/dist/styles/theme.css +0 -198
- package/dist/styles/tokens.css +0 -65
- package/dist/styles/ui.css +0 -551
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import MobileMenuToggle from '../../vendor/starlight/components/MobileMenuToggle.astro';
|
|
3
|
-
import { getBookForPath } from '../../utils/starlight-nav';
|
|
4
|
-
|
|
5
|
-
const { hasSidebar, toc } = Astro.locals.starlightRoute;
|
|
6
|
-
const hasToc = Boolean(toc);
|
|
7
|
-
const isBookPage = Boolean(getBookForPath(Astro.url.pathname));
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
<div class:list={['page sl-flex', isBookPage && 'page--book']}>
|
|
11
|
-
<header class="header"><slot name="header" /></header>
|
|
12
|
-
{
|
|
13
|
-
hasSidebar && (
|
|
14
|
-
<nav class="sidebar print:hidden" aria-label={Astro.locals.t('sidebarNav.accessibleLabel')}>
|
|
15
|
-
<MobileMenuToggle />
|
|
16
|
-
<div id="starlight__sidebar" class="sidebar-pane docs-left-sidebar">
|
|
17
|
-
<div class="sidebar-content sl-flex">
|
|
18
|
-
<slot name="sidebar" />
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
</nav>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
<div class="main-frame"><slot /></div>
|
|
25
|
-
<div class="docs-footer-host" data-docs-footer-host></div>
|
|
26
|
-
</div>
|
|
27
|
-
|
|
28
|
-
<script is:inline define:vars={{ hasSidebar, hasToc, isBookPage }}>
|
|
29
|
-
const desktopQuery = window.matchMedia('(min-width: 72rem)');
|
|
30
|
-
const storageKey = 'docs.desktop-sidebar-state';
|
|
31
|
-
const bookFontScaleStorageKey = 'docs.book-font-scale';
|
|
32
|
-
const defaultBookFontScale = 1;
|
|
33
|
-
const minBookFontScale = 0.85;
|
|
34
|
-
const maxBookFontScale = 1.35;
|
|
35
|
-
const root = document.documentElement;
|
|
36
|
-
const leftSidebar = document.getElementById('starlight__sidebar');
|
|
37
|
-
const rightSidebar = document.getElementById('starlight__page-toc');
|
|
38
|
-
const pageFrame = document.querySelector('.page');
|
|
39
|
-
const footerHost = document.querySelector('[data-docs-footer-host]');
|
|
40
|
-
|
|
41
|
-
const getDefaultState = () => ({
|
|
42
|
-
left: !hasSidebar,
|
|
43
|
-
right: !hasToc,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const readState = () => {
|
|
47
|
-
try {
|
|
48
|
-
const parsed = JSON.parse(localStorage.getItem(storageKey) || '{}');
|
|
49
|
-
return {
|
|
50
|
-
left: hasSidebar ? parsed.left === true : true,
|
|
51
|
-
right: hasToc ? parsed.right === true : true,
|
|
52
|
-
};
|
|
53
|
-
} catch {
|
|
54
|
-
return getDefaultState();
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const writeState = (state) => {
|
|
59
|
-
try {
|
|
60
|
-
localStorage.setItem(storageKey, JSON.stringify(state));
|
|
61
|
-
} catch {
|
|
62
|
-
// Ignore storage failures and keep the UI functional.
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const syncFooterOffset = () => {
|
|
67
|
-
if (!(footerHost instanceof HTMLElement) || !desktopQuery.matches) {
|
|
68
|
-
root.style.setProperty('--docs-footer-offset', '0px');
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const footerTop = footerHost.getBoundingClientRect().top;
|
|
73
|
-
const offset = Math.max(0, window.innerHeight - footerTop);
|
|
74
|
-
root.style.setProperty('--docs-footer-offset', `${offset}px`);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const clampBookFontScale = (value) =>
|
|
78
|
-
Math.min(maxBookFontScale, Math.max(minBookFontScale, value));
|
|
79
|
-
|
|
80
|
-
const readBookFontScale = () => {
|
|
81
|
-
try {
|
|
82
|
-
const rawValue = localStorage.getItem(bookFontScaleStorageKey);
|
|
83
|
-
const parsedValue = rawValue === null ? defaultBookFontScale : Number(rawValue);
|
|
84
|
-
return Number.isFinite(parsedValue) ? clampBookFontScale(parsedValue) : defaultBookFontScale;
|
|
85
|
-
} catch {
|
|
86
|
-
return defaultBookFontScale;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const applyBookFontState = () => {
|
|
91
|
-
root.dataset.docsBookPage = String(isBookPage);
|
|
92
|
-
if (!isBookPage) {
|
|
93
|
-
root.style.removeProperty('--docs-book-font-scale');
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
root.style.setProperty('--docs-book-font-scale', String(readBookFontScale()));
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const setExpanded = (side, expanded) => {
|
|
101
|
-
root.dataset[`docs${side === 'left' ? 'Left' : 'Right'}SidebarExpanded`] = String(expanded);
|
|
102
|
-
const toggle = document.querySelector(`[data-docs-sidebar-toggle="${side}"]`);
|
|
103
|
-
if (toggle instanceof HTMLButtonElement) {
|
|
104
|
-
toggle.setAttribute('aria-expanded', String(expanded));
|
|
105
|
-
const label = toggle.dataset.label || '';
|
|
106
|
-
toggle.setAttribute('aria-label', `${expanded ? 'Hide' : 'Show'} ${label}`);
|
|
107
|
-
const text = toggle.querySelector('.desktop-sidebar-toggle__text');
|
|
108
|
-
if (text) {
|
|
109
|
-
text.textContent = `${expanded ? 'Hide' : 'Show'} ${label}`;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const syncAccessibility = () => {
|
|
115
|
-
const desktop = desktopQuery.matches;
|
|
116
|
-
const leftExpanded = root.dataset.docsLeftSidebarExpanded === 'true';
|
|
117
|
-
const rightExpanded = root.dataset.docsRightSidebarExpanded === 'true';
|
|
118
|
-
|
|
119
|
-
if (leftSidebar) {
|
|
120
|
-
leftSidebar.toggleAttribute('inert', desktop && !leftExpanded);
|
|
121
|
-
leftSidebar.setAttribute('aria-hidden', String(desktop && !leftExpanded));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (rightSidebar) {
|
|
125
|
-
rightSidebar.toggleAttribute('inert', desktop && !rightExpanded);
|
|
126
|
-
rightSidebar.setAttribute('aria-hidden', String(desktop && !rightExpanded));
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const applyState = (state) => {
|
|
131
|
-
setExpanded('left', hasSidebar ? state.left : false);
|
|
132
|
-
setExpanded('right', hasToc ? state.right : false);
|
|
133
|
-
syncAccessibility();
|
|
134
|
-
syncFooterOffset();
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const toggleSide = (side) => {
|
|
138
|
-
const current =
|
|
139
|
-
root.dataset[`docs${side === 'left' ? 'Left' : 'Right'}SidebarExpanded`] === 'true';
|
|
140
|
-
const nextState = {
|
|
141
|
-
left: root.dataset.docsLeftSidebarExpanded === 'true',
|
|
142
|
-
right: root.dataset.docsRightSidebarExpanded === 'true',
|
|
143
|
-
[side]: !current,
|
|
144
|
-
};
|
|
145
|
-
applyState(nextState);
|
|
146
|
-
writeState(nextState);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const initialize = () => {
|
|
150
|
-
applyBookFontState();
|
|
151
|
-
const state = readState();
|
|
152
|
-
applyState(state);
|
|
153
|
-
|
|
154
|
-
if (pageFrame instanceof HTMLElement && footerHost instanceof HTMLElement) {
|
|
155
|
-
const footerShell = pageFrame.querySelector('.docs-footer-shell');
|
|
156
|
-
if (footerShell instanceof HTMLElement && footerShell.parentElement !== footerHost) {
|
|
157
|
-
footerHost.append(footerShell);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
syncFooterOffset();
|
|
162
|
-
|
|
163
|
-
for (const button of document.querySelectorAll('[data-docs-sidebar-toggle]')) {
|
|
164
|
-
if (!(button instanceof HTMLButtonElement) || button.dataset.docsSidebarBound === 'true') continue;
|
|
165
|
-
button.dataset.docsSidebarBound = 'true';
|
|
166
|
-
button.addEventListener('click', () => {
|
|
167
|
-
const side = button.dataset.docsSidebarToggle;
|
|
168
|
-
if (side === 'left' || side === 'right') {
|
|
169
|
-
toggleSide(side);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
initialize();
|
|
176
|
-
desktopQuery.addEventListener('change', syncAccessibility);
|
|
177
|
-
desktopQuery.addEventListener('change', syncFooterOffset);
|
|
178
|
-
window.addEventListener('scroll', syncFooterOffset, { passive: true });
|
|
179
|
-
window.addEventListener('resize', syncFooterOffset);
|
|
180
|
-
</script>
|
|
181
|
-
|
|
182
|
-
<style>
|
|
183
|
-
@layer starlight.core {
|
|
184
|
-
.page {
|
|
185
|
-
flex-direction: column;
|
|
186
|
-
min-height: 100vh;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
.header {
|
|
190
|
-
z-index: var(--sl-z-index-navbar);
|
|
191
|
-
position: fixed;
|
|
192
|
-
inset-inline-start: 0;
|
|
193
|
-
inset-block-start: 0;
|
|
194
|
-
width: 100%;
|
|
195
|
-
height: var(--sl-nav-height);
|
|
196
|
-
border-bottom: 1px solid var(--sl-color-hairline-shade);
|
|
197
|
-
padding: var(--sl-nav-pad-y) var(--sl-nav-pad-x);
|
|
198
|
-
padding-inline-end: var(--sl-nav-pad-x);
|
|
199
|
-
background-color: var(--sl-color-bg-nav);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
:global([data-has-sidebar]) .header {
|
|
203
|
-
padding-inline-end: calc(
|
|
204
|
-
var(--sl-nav-gap) + var(--sl-nav-pad-x) + var(--sl-menu-button-size)
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.sidebar-pane {
|
|
209
|
-
visibility: var(--sl-sidebar-visibility, hidden);
|
|
210
|
-
position: fixed;
|
|
211
|
-
z-index: var(--sl-z-index-menu);
|
|
212
|
-
inset-block: var(--sl-nav-height) var(--docs-footer-offset, 0px);
|
|
213
|
-
inset-inline-start: 0;
|
|
214
|
-
width: 100%;
|
|
215
|
-
background-color: var(--sl-color-black);
|
|
216
|
-
overflow-y: auto;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
:global([aria-expanded='true']) ~ .sidebar-pane {
|
|
220
|
-
--sl-sidebar-visibility: visible;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.sidebar-content {
|
|
224
|
-
height: 100%;
|
|
225
|
-
min-height: max-content;
|
|
226
|
-
padding: 1rem var(--sl-sidebar-pad-x) 0;
|
|
227
|
-
flex-direction: column;
|
|
228
|
-
gap: 1rem;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
@media (min-width: 50rem) {
|
|
232
|
-
.sidebar-content::after {
|
|
233
|
-
content: '';
|
|
234
|
-
padding-bottom: 1px;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
.main-frame {
|
|
239
|
-
padding-top: calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));
|
|
240
|
-
padding-inline-start: var(--sl-content-inline-start);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
.docs-footer-host {
|
|
244
|
-
width: 100%;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
@media (min-width: 50rem) {
|
|
248
|
-
:global([data-has-sidebar]) .header {
|
|
249
|
-
padding-inline-end: var(--sl-nav-pad-x);
|
|
250
|
-
}
|
|
251
|
-
.sidebar-pane {
|
|
252
|
-
--sl-sidebar-visibility: visible;
|
|
253
|
-
width: var(--sl-sidebar-width);
|
|
254
|
-
background-color: var(--sl-color-bg-sidebar);
|
|
255
|
-
border-inline-end: 1px solid var(--sl-color-hairline-shade);
|
|
256
|
-
overflow-y: auto;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
</style>
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import MobileTableOfContents from '../../vendor/starlight/components/MobileTableOfContents.astro';
|
|
3
|
-
import TableOfContents from '../../vendor/starlight/components/TableOfContents.astro';
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
{
|
|
7
|
-
Astro.locals.starlightRoute.toc && (
|
|
8
|
-
<>
|
|
9
|
-
<div class="lg:sl-hidden">
|
|
10
|
-
<MobileTableOfContents />
|
|
11
|
-
</div>
|
|
12
|
-
<aside
|
|
13
|
-
id="starlight__page-toc"
|
|
14
|
-
class="right-sidebar-panel sl-hidden lg:sl-block"
|
|
15
|
-
aria-label={Astro.locals.t('tableOfContents.onThisPage')}
|
|
16
|
-
>
|
|
17
|
-
<div class="sl-container">
|
|
18
|
-
<TableOfContents />
|
|
19
|
-
</div>
|
|
20
|
-
</aside>
|
|
21
|
-
</>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
<style>
|
|
26
|
-
@layer starlight.core {
|
|
27
|
-
.right-sidebar-panel {
|
|
28
|
-
padding: 1rem var(--sl-sidebar-pad-x);
|
|
29
|
-
}
|
|
30
|
-
.sl-container {
|
|
31
|
-
width: calc(var(--sl-sidebar-width) - 2 * var(--sl-sidebar-pad-x));
|
|
32
|
-
}
|
|
33
|
-
.right-sidebar-panel :global(h2) {
|
|
34
|
-
color: var(--sl-color-white);
|
|
35
|
-
font-size: var(--sl-text-h5);
|
|
36
|
-
font-weight: 600;
|
|
37
|
-
line-height: var(--sl-line-height-headings);
|
|
38
|
-
margin-bottom: 0.5rem;
|
|
39
|
-
}
|
|
40
|
-
.right-sidebar-panel :global(:where(a)) {
|
|
41
|
-
display: block;
|
|
42
|
-
font-size: var(--sl-text-xs);
|
|
43
|
-
text-decoration: none;
|
|
44
|
-
color: var(--sl-color-gray-3);
|
|
45
|
-
overflow-wrap: anywhere;
|
|
46
|
-
}
|
|
47
|
-
.right-sidebar-panel :global(:where(a):hover) {
|
|
48
|
-
color: var(--sl-color-white);
|
|
49
|
-
}
|
|
50
|
-
@media (min-width: 72rem) {
|
|
51
|
-
.sl-container {
|
|
52
|
-
max-width: calc(
|
|
53
|
-
(
|
|
54
|
-
(
|
|
55
|
-
100vw - var(--sl-sidebar-width) - 2 * var(--sl-content-pad-x) - 2 *
|
|
56
|
-
var(--sl-sidebar-pad-x)
|
|
57
|
-
) * 0.25
|
|
58
|
-
)
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
</style>
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { getBookForPath } from '../../utils/starlight-nav';
|
|
3
|
-
|
|
4
|
-
const isBookPage = Boolean(getBookForPath(Astro.url.pathname));
|
|
5
|
-
const PAGE_TITLE_ID = '_top';
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
<h1 id={PAGE_TITLE_ID} class:list={['docs-page-title', isBookPage && 'docs-page-title--book']}>
|
|
9
|
-
{Astro.locals.starlightRoute.entry.data.title}
|
|
10
|
-
</h1>
|
|
11
|
-
|
|
12
|
-
<style>
|
|
13
|
-
@layer starlight.core {
|
|
14
|
-
.docs-page-title {
|
|
15
|
-
margin-top: 1rem;
|
|
16
|
-
font-size: var(--sl-text-h1);
|
|
17
|
-
line-height: var(--sl-line-height-headings);
|
|
18
|
-
font-weight: 600;
|
|
19
|
-
color: var(--sl-color-white);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.docs-page-title--book {
|
|
23
|
-
margin-top: 0;
|
|
24
|
-
font-size: clamp(1rem, 1.1vw, 1.4rem);
|
|
25
|
-
line-height: 1.18;
|
|
26
|
-
font-weight: 800;
|
|
27
|
-
color: var(--ts-color-accent-strong);
|
|
28
|
-
text-wrap: pretty;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@media (min-width: 72rem) {
|
|
32
|
-
.docs-page-title--book {
|
|
33
|
-
max-width: var(--docs-book-panel-width);
|
|
34
|
-
margin-inline: auto;
|
|
35
|
-
text-wrap: balance;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
</style>
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import MobileMenuFooter from '../../vendor/starlight/components/MobileMenuFooter.astro';
|
|
3
|
-
import SidebarPersister from '../../vendor/starlight/components/SidebarPersister.astro';
|
|
4
|
-
import SidebarSublist from '../../vendor/starlight/components/SidebarSublist.astro';
|
|
5
|
-
import { TREESEED_LINKS, getBookForPath } from '../../utils/starlight-nav';
|
|
6
|
-
|
|
7
|
-
const { sidebar } = Astro.locals.starlightRoute;
|
|
8
|
-
const activeBook = getBookForPath(Astro.url.pathname);
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
<SidebarPersister>
|
|
12
|
-
{
|
|
13
|
-
activeBook && (
|
|
14
|
-
<div class="docs-sidebar-actions">
|
|
15
|
-
<a href={TREESEED_LINKS.home} class="download-button docs-sidebar-books-button">
|
|
16
|
-
All Books
|
|
17
|
-
</a>
|
|
18
|
-
</div>
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
<SidebarSublist sublist={sidebar} />
|
|
22
|
-
</SidebarPersister>
|
|
23
|
-
|
|
24
|
-
<div class="md:sl-hidden">
|
|
25
|
-
<MobileMenuFooter />
|
|
26
|
-
</div>
|
|
27
|
-
|
|
28
|
-
<style>
|
|
29
|
-
@layer starlight.components {
|
|
30
|
-
.docs-sidebar-actions {
|
|
31
|
-
margin: 0 0 1.75rem;
|
|
32
|
-
padding: 0 0.5rem;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.docs-sidebar-books-button {
|
|
36
|
-
display: inline-flex;
|
|
37
|
-
width: 100%;
|
|
38
|
-
justify-content: center;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
</style>
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import {
|
|
3
|
-
TREESEED_PUBLIC_TURNSTILE_SITE_KEY,
|
|
4
|
-
} from 'astro:env/client';
|
|
5
|
-
import { CONTACT_TYPE_LABELS } from '../../utils/forms/constants';
|
|
6
|
-
import { CONTACT_TYPES } from '../../types/forms';
|
|
7
|
-
import { SITE_FORMS } from '../../utils/site-config';
|
|
8
|
-
|
|
9
|
-
const { status, code } = Astro.props as {
|
|
10
|
-
status?: string | null;
|
|
11
|
-
code?: string | null;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const messages: Record<string, string> = {
|
|
15
|
-
success: 'Your message was sent successfully.',
|
|
16
|
-
invalid_request: 'The request could not be accepted. Please refresh and try again.',
|
|
17
|
-
invalid_form: 'Please complete the required fields and try again.',
|
|
18
|
-
captcha_failed: 'Please complete the verification challenge and try again.',
|
|
19
|
-
token_invalid: 'The form session expired. Refresh the page and try again.',
|
|
20
|
-
token_expired: 'The form session expired. Refresh the page and try again.',
|
|
21
|
-
token_replayed: 'This form token has already been used. Please refresh the page.',
|
|
22
|
-
rate_limited: 'Too many attempts were detected. Please wait a few minutes and try again.',
|
|
23
|
-
delivery_failed: 'We could not deliver the message right now. Please try again shortly.',
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const statusMessage = code ? messages[code] ?? null : null;
|
|
27
|
-
const shouldBypassTurnstile = import.meta.env.DEV;
|
|
28
|
-
const formsApiBaseUrl = SITE_FORMS?.apiBaseUrl ?? '/api/form/submit';
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
<section class="max-w-3xl">
|
|
32
|
-
{statusMessage && (
|
|
33
|
-
<div class:list={[
|
|
34
|
-
'rounded-lg border px-4 py-3 text-sm font-medium',
|
|
35
|
-
status === 'success'
|
|
36
|
-
? 'border-[color:var(--ts-color-info)] bg-[color:var(--ts-color-info-soft)] text-[color:var(--ts-color-text)]'
|
|
37
|
-
: 'border-[color:var(--ts-color-warning)] bg-[color:var(--ts-color-warning-soft)] text-[color:var(--ts-color-text)]',
|
|
38
|
-
]}>
|
|
39
|
-
{statusMessage}
|
|
40
|
-
</div>
|
|
41
|
-
)}
|
|
42
|
-
|
|
43
|
-
<form
|
|
44
|
-
method="post"
|
|
45
|
-
action={formsApiBaseUrl}
|
|
46
|
-
class="js-secure-form mt-6 grid gap-5"
|
|
47
|
-
data-form-type="contact"
|
|
48
|
-
data-turnstile-action="contact_submit"
|
|
49
|
-
>
|
|
50
|
-
<p class="text-sm text-[color:var(--ts-color-text-subtle)]">
|
|
51
|
-
Fields marked <span class="font-semibold text-[color:var(--ts-color-warning)]">*</span> are required.
|
|
52
|
-
</p>
|
|
53
|
-
<input type="hidden" name="formType" value="contact" />
|
|
54
|
-
<input type="hidden" name="formToken" value="" />
|
|
55
|
-
<input type="hidden" name="formSession" value="" />
|
|
56
|
-
<input type="hidden" name="redirectTo" value="/contact/" />
|
|
57
|
-
<div class="sr-only" aria-hidden="true">
|
|
58
|
-
<label>
|
|
59
|
-
Website
|
|
60
|
-
<input type="text" name="website" tabindex="-1" autocomplete="off" />
|
|
61
|
-
</label>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<div class="grid gap-5 md:grid-cols-2">
|
|
65
|
-
<label class="grid gap-2">
|
|
66
|
-
<span class="text-sm font-semibold text-[color:var(--ts-color-text)]">Name <span aria-hidden="true" class="text-[color:var(--ts-color-warning)]">*</span></span>
|
|
67
|
-
<input
|
|
68
|
-
class="min-h-12 rounded-md border border-[color:var(--ts-color-border-strong)] bg-white/70 px-4 py-3"
|
|
69
|
-
type="text"
|
|
70
|
-
name="name"
|
|
71
|
-
autocomplete="name"
|
|
72
|
-
required
|
|
73
|
-
aria-required="true"
|
|
74
|
-
/>
|
|
75
|
-
</label>
|
|
76
|
-
<label class="grid gap-2">
|
|
77
|
-
<span class="text-sm font-semibold text-[color:var(--ts-color-text)]">Email <span aria-hidden="true" class="text-[color:var(--ts-color-warning)]">*</span></span>
|
|
78
|
-
<input
|
|
79
|
-
class="min-h-12 rounded-md border border-[color:var(--ts-color-border-strong)] bg-white/70 px-4 py-3"
|
|
80
|
-
type="email"
|
|
81
|
-
name="email"
|
|
82
|
-
autocomplete="email"
|
|
83
|
-
required
|
|
84
|
-
aria-required="true"
|
|
85
|
-
/>
|
|
86
|
-
</label>
|
|
87
|
-
</div>
|
|
88
|
-
|
|
89
|
-
<div class="grid gap-5 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
|
|
90
|
-
<label class="grid gap-2">
|
|
91
|
-
<span class="text-sm font-semibold text-[color:var(--ts-color-text)]">Organization</span>
|
|
92
|
-
<input
|
|
93
|
-
class="min-h-12 rounded-md border border-[color:var(--ts-color-border-strong)] bg-white/70 px-4 py-3"
|
|
94
|
-
type="text"
|
|
95
|
-
name="organization"
|
|
96
|
-
autocomplete="organization"
|
|
97
|
-
/>
|
|
98
|
-
</label>
|
|
99
|
-
<label class="grid gap-2">
|
|
100
|
-
<span class="text-sm font-semibold text-[color:var(--ts-color-text)]">Topic <span aria-hidden="true" class="text-[color:var(--ts-color-warning)]">*</span></span>
|
|
101
|
-
<select
|
|
102
|
-
class="ts-control ts-control--select min-h-12 rounded-md border border-[color:var(--ts-color-border-strong)] px-4 py-3"
|
|
103
|
-
name="contactType"
|
|
104
|
-
required
|
|
105
|
-
aria-required="true"
|
|
106
|
-
>
|
|
107
|
-
{CONTACT_TYPES.map((contactType) => (
|
|
108
|
-
<option value={contactType}>{CONTACT_TYPE_LABELS[contactType]}</option>
|
|
109
|
-
))}
|
|
110
|
-
</select>
|
|
111
|
-
</label>
|
|
112
|
-
</div>
|
|
113
|
-
|
|
114
|
-
<label class="grid gap-2">
|
|
115
|
-
<span class="text-sm font-semibold text-[color:var(--ts-color-text)]">Subject <span aria-hidden="true" class="text-[color:var(--ts-color-warning)]">*</span></span>
|
|
116
|
-
<input
|
|
117
|
-
class="min-h-12 rounded-md border border-[color:var(--ts-color-border-strong)] bg-white/70 px-4 py-3"
|
|
118
|
-
type="text"
|
|
119
|
-
name="subject"
|
|
120
|
-
required
|
|
121
|
-
aria-required="true"
|
|
122
|
-
/>
|
|
123
|
-
</label>
|
|
124
|
-
|
|
125
|
-
<label class="grid gap-2">
|
|
126
|
-
<span class="text-sm font-semibold text-[color:var(--ts-color-text)]">Message <span aria-hidden="true" class="text-[color:var(--ts-color-warning)]">*</span></span>
|
|
127
|
-
<textarea
|
|
128
|
-
class="min-h-48 rounded-md border border-[color:var(--ts-color-border-strong)] bg-white/70 px-4 py-3"
|
|
129
|
-
name="message"
|
|
130
|
-
required
|
|
131
|
-
aria-required="true"
|
|
132
|
-
></textarea>
|
|
133
|
-
</label>
|
|
134
|
-
|
|
135
|
-
{TREESEED_PUBLIC_TURNSTILE_SITE_KEY && !shouldBypassTurnstile ? (
|
|
136
|
-
<div class="js-turnstile rounded-md border border-dashed border-[color:var(--ts-color-border-strong)] p-3"></div>
|
|
137
|
-
) : shouldBypassTurnstile ? (
|
|
138
|
-
<p class="rounded-md border border-[color:var(--ts-color-info)] bg-[color:var(--ts-color-info-soft)] px-4 py-3 text-sm text-[color:var(--ts-color-text)]">
|
|
139
|
-
Local development mode is bypassing Turnstile so you can test the form workflow without Cloudflare captcha.
|
|
140
|
-
</p>
|
|
141
|
-
) : (
|
|
142
|
-
<p class="rounded-md border border-[color:var(--ts-color-warning)] bg-[color:var(--ts-color-warning-soft)] px-4 py-3 text-sm text-[color:var(--ts-color-text)]">
|
|
143
|
-
Turnstile is not configured yet. Add `TREESEED_PUBLIC_TURNSTILE_SITE_KEY` before deploying forms.
|
|
144
|
-
</p>
|
|
145
|
-
)}
|
|
146
|
-
|
|
147
|
-
<div class="flex flex-wrap items-center justify-between gap-4">
|
|
148
|
-
<p class="text-sm leading-7 text-[color:var(--ts-color-text-subtle)]">
|
|
149
|
-
By sending this message, you agree that we may reply by email about this inquiry.
|
|
150
|
-
</p>
|
|
151
|
-
<button
|
|
152
|
-
type="submit"
|
|
153
|
-
class="js-submit inline-flex min-h-12 items-center justify-center border border-[color:var(--ts-color-accent)] bg-[color:var(--ts-color-accent)] px-6 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)] disabled:cursor-not-allowed disabled:opacity-60"
|
|
154
|
-
disabled
|
|
155
|
-
>
|
|
156
|
-
Send message
|
|
157
|
-
</button>
|
|
158
|
-
</div>
|
|
159
|
-
</form>
|
|
160
|
-
</section>
|
|
161
|
-
|
|
162
|
-
<script
|
|
163
|
-
is:inline
|
|
164
|
-
define:vars={{ siteKey: TREESEED_PUBLIC_TURNSTILE_SITE_KEY, shouldBypassTurnstile, formsApiBaseUrl }}
|
|
165
|
-
>
|
|
166
|
-
const bootSecureForms = () => {
|
|
167
|
-
const forms = Array.from(document.querySelectorAll('.js-secure-form'));
|
|
168
|
-
|
|
169
|
-
for (const form of forms) {
|
|
170
|
-
if (!(form instanceof HTMLFormElement) || form.dataset.ready === 'true') continue;
|
|
171
|
-
form.dataset.ready = 'true';
|
|
172
|
-
|
|
173
|
-
const formType = form.dataset.formType;
|
|
174
|
-
const action = form.dataset.turnstileAction ?? '';
|
|
175
|
-
const tokenField = form.querySelector('input[name="formToken"]');
|
|
176
|
-
const sessionField = form.querySelector('input[name="formSession"]');
|
|
177
|
-
const submitButton = form.querySelector('.js-submit');
|
|
178
|
-
const widgetContainer = form.querySelector('.js-turnstile');
|
|
179
|
-
|
|
180
|
-
const enableSubmit = () => {
|
|
181
|
-
if (submitButton instanceof HTMLButtonElement) {
|
|
182
|
-
submitButton.disabled = !(tokenField instanceof HTMLInputElement && tokenField.value);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const assignToken = async () => {
|
|
187
|
-
const response = await fetch(`${formsApiBaseUrl}?formType=${encodeURIComponent(formType ?? '')}`, {
|
|
188
|
-
headers: { accept: 'application/json' },
|
|
189
|
-
credentials: 'same-origin',
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
const data = await response.json();
|
|
193
|
-
if (tokenField instanceof HTMLInputElement && data?.formToken) {
|
|
194
|
-
tokenField.value = data.formToken;
|
|
195
|
-
}
|
|
196
|
-
if (sessionField instanceof HTMLInputElement && data?.sessionId) {
|
|
197
|
-
sessionField.value = data.sessionId;
|
|
198
|
-
}
|
|
199
|
-
enableSubmit();
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const renderWidget = () => {
|
|
203
|
-
if (shouldBypassTurnstile) return;
|
|
204
|
-
if (!siteKey || !(widgetContainer instanceof HTMLElement)) return;
|
|
205
|
-
if (!('turnstile' in window) || typeof window.turnstile?.render !== 'function') return;
|
|
206
|
-
if (widgetContainer.dataset.rendered === 'true') return;
|
|
207
|
-
|
|
208
|
-
window.turnstile.render(widgetContainer, {
|
|
209
|
-
sitekey: siteKey,
|
|
210
|
-
action,
|
|
211
|
-
theme: 'light',
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
widgetContainer.dataset.rendered = 'true';
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
assignToken().catch((error) => {
|
|
218
|
-
console.error('Unable to initialize form token', error);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
if (siteKey) {
|
|
222
|
-
renderWidget();
|
|
223
|
-
window.addEventListener('load', renderWidget, { once: true });
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
if (document.readyState === 'loading') {
|
|
229
|
-
document.addEventListener('DOMContentLoaded', bootSecureForms, { once: true });
|
|
230
|
-
} else {
|
|
231
|
-
bootSecureForms();
|
|
232
|
-
}
|
|
233
|
-
</script>
|