@the-smithy/create-hearth-site 0.28.0 → 0.29.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@the-smithy/create-hearth-site",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Start a new client website powered by Hearth: pnpm create @the-smithy/hearth-site",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
],
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"vitest": "^5.0.3",
|
|
33
|
-
"@the-smithy/hearth-schema": "^0.
|
|
33
|
+
"@the-smithy/hearth-schema": "^0.29.0"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"test": "node --test \"test/**/*.test.js\" && vitest run test/blocks.test.ts test/overrides.test.ts",
|
package/template/AGENTS.md
CHANGED
|
@@ -12,6 +12,7 @@ Before grepping or reading whole files, use [Drift](https://github.com/The-Smith
|
|
|
12
12
|
- `drift query <symbol>` or `drift find <words>`: where something is defined
|
|
13
13
|
- `drift show <symbol>` or `drift sig <symbol>`: its source, or just its signature
|
|
14
14
|
- `drift outline <file>`: a file's definitions and imports
|
|
15
|
+
- `drift docs <words>`: the section of the docs that covers something. Read it with `drift show <path:start-end>`
|
|
15
16
|
- `drift refs <symbol>`, `drift importers <file>`, `drift tests --for <file>`: what uses it
|
|
16
17
|
- `drift context <symbol or file>`: all of the above before changing something
|
|
17
18
|
- `drift map`: orientation in a new repo
|
|
@@ -86,10 +87,10 @@ Work in this order, and stop at the first step that gets the result:
|
|
|
86
87
|
don't reach it, so it falls behind Hearth. Use one only when everything above can't get there, build it from
|
|
87
88
|
`src/hearth/ui.tsx` and the theme, and say why in `SITE.md`.
|
|
88
89
|
|
|
89
|
-
To style blocks without setting up pages for them, open `/hearth/blocks` while `pnpm dev` is running:
|
|
90
|
+
To style blocks without setting up pages for them, open `/hearth/blocks` while `pnpm dev` is running: each block with
|
|
90
91
|
its options and states, in sample content, drawn with this site's theme, so a change in `src/theme` shows at once.
|
|
91
|
-
|
|
92
|
-
and each element's slot name. Neither is served in production. Global blocks and the site's custom blocks aren't in it.
|
|
92
|
+
It shows one block at a time: `/hearth/blocks?block=hero` goes to one, and `?block=all` shows the whole list. Its
|
|
93
|
+
**Unstyled** switch (`/hearth/blocks/unstyled`) shows them with no design and each element's slot name. Neither is served in production. Global blocks and the site's custom blocks aren't in it.
|
|
93
94
|
Without a browser, the unstyled page is `node_modules/@the-smithy/hearth/block-reference.html`, but it's large: for slots,
|
|
94
95
|
use `src/hearth/theme/defaults.ts`.
|
|
95
96
|
|
package/template/README.md
CHANGED
|
@@ -98,7 +98,7 @@ Keep the API key out of chat: the agent never needs to see it, only `.env.local`
|
|
|
98
98
|
| `src/theme/theme.css` | Brand tokens (colors, fonts, content width) and the site's own CSS. **Start here** |
|
|
99
99
|
| `src/theme/index.ts` | The Tailwind classes of any part of any block, page, form, header or footer, by slot |
|
|
100
100
|
| `src/hearth/theme/defaults.ts` | Every slot and its default classes: read it to find the slot to change |
|
|
101
|
-
| `/hearth/blocks` | Not a file: while `pnpm dev` runs, this address shows
|
|
101
|
+
| `/hearth/blocks` | Not a file: while `pnpm dev` runs, this address shows each block with your theme, one at a time, without setting up pages for them. `?block=hero` goes to one, and `?block=all` shows the whole list. Its **Unstyled** switch shows them with no design, and each element's slot name |
|
|
102
102
|
| `src/theme/fonts.ts` | Web fonts (next/font) |
|
|
103
103
|
| `src/theme/custom-blocks.tsx` | Components for the site's own block types (Settings → Custom blocks in Hearth) |
|
|
104
104
|
| `src/theme/overrides.tsx` | Last resort: replace a whole block's component, the logo, the footer, the event or fundraiser page, their cards or the donation form (see `AGENTS.md`) |
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// Stays on the same block, or on the whole list: #block-hero here is ?block=hero there.
|
|
5
|
+
const SWITCH = `<span class="view-switch"><span class="view-current">Unstyled</span><a href="/hearth/blocks" onclick="var id=location.hash.slice(1);if(id&&id!=='top')this.href='/hearth/blocks?block='+id.replace('block-','')">This site's theme</a></span>`;
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
+
* Each block unstyled, with its options, states and slot names, to build against while developing. Only under
|
|
8
9
|
* `pnpm dev`: the page comes from the SDK, so it matches this site's release, and visitors never see it.
|
|
9
10
|
*/
|
|
10
11
|
export function GET() {
|
|
@@ -24,15 +24,6 @@ export function SampleGuard() {
|
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/** A link to the block library's other view, staying on the same block. */
|
|
28
|
-
export function ViewLink({ href, children }: { href: string; children: ReactNode }) {
|
|
29
|
-
return (
|
|
30
|
-
<a href={href} className="hearth-library-view" onClick={(e) => (e.currentTarget.href = href + window.location.hash)}>
|
|
31
|
-
{children}
|
|
32
|
-
</a>
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
27
|
/** One sample failing (a site without Giving has no fundraisers to offer) leaves the rest of the page working. */
|
|
37
28
|
export class SampleBoundary extends Component<{ children: ReactNode }, { message?: string }> {
|
|
38
29
|
state: { message?: string } = {};
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import type { Metadata } from "next";
|
|
2
|
+
import Link from "next/link";
|
|
2
3
|
import { notFound } from "next/navigation";
|
|
3
4
|
import { Fragment, Suspense, type ReactNode } from "react";
|
|
4
5
|
import { Blocks } from "../blocks";
|
|
5
|
-
import { SampleBoundary, SampleGuard
|
|
6
|
+
import { SampleBoundary, SampleGuard } from "./controls";
|
|
6
7
|
import { FormStates, Ground, GROUND_NAMES } from "./pieces";
|
|
7
8
|
import { blockSamples, libraryGroups } from "./samples";
|
|
8
9
|
|
|
9
10
|
export const metadata: Metadata = { title: "Block library", robots: { index: false, follow: false } };
|
|
10
11
|
|
|
11
12
|
const TONES = ["default", "muted", "inverse", "accent"] as const;
|
|
13
|
+
const START_HERE = [
|
|
14
|
+
{ type: "grounds", label: "Grounds and buttons" },
|
|
15
|
+
{ type: "form-states", label: "Form states" },
|
|
16
|
+
];
|
|
12
17
|
|
|
13
18
|
// Its own plain styles, so that nothing in a site's theme changes the tools around the samples.
|
|
14
19
|
const CSS = `
|
|
@@ -19,6 +24,9 @@ const CSS = `
|
|
|
19
24
|
.hearth-library-switch > * { padding: 4px 12px; }
|
|
20
25
|
.hearth-library-switch > * + * { border-left: 1px solid #d8d8d4; }
|
|
21
26
|
.hearth-library-current { background: #1f4fd8; color: #fff; }
|
|
27
|
+
.hearth-library-steps { display: inline-flex; gap: 8px; }
|
|
28
|
+
.hearth-library-steps > * { padding: 4px 12px; border: 1px solid #d8d8d4; border-radius: 6px; }
|
|
29
|
+
.hearth-library-steps > span { color: #5c5c58; }
|
|
22
30
|
.hearth-library-jump { position: relative; }
|
|
23
31
|
.hearth-library-jump summary { cursor: pointer; padding: 4px 12px; border: 1px solid #d8d8d4; border-radius: 6px; list-style: none; }
|
|
24
32
|
.hearth-library-jump summary::after { content: " ▾"; }
|
|
@@ -52,18 +60,26 @@ function Sample({ id, name, label, action, children }: { id?: string; name: stri
|
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
63
|
+
* The blocks, drawn by the real components with this site's theme and overrides, so that a change in src/theme shows
|
|
64
|
+
* here at once. For `pnpm dev` only. Its samples stand in for a site's content, so nothing depends on which pages
|
|
65
|
+
* exist, and nothing here is saved. It shows one block at a time, from ?block=hero; ?block=all shows the whole list.
|
|
58
66
|
*/
|
|
59
67
|
export default async function BlockLibrary({ searchParams }: { searchParams: Promise<{ block?: string | string[] }> }) {
|
|
60
68
|
if (process.env.NODE_ENV !== "development") notFound();
|
|
61
69
|
const { block } = await searchParams;
|
|
62
70
|
const samples = blockSamples(new Date());
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
const
|
|
71
|
+
const blocks = libraryGroups.flatMap((g) => g.blocks);
|
|
72
|
+
const items: { type: string; label: string }[] = [...START_HERE, ...blocks];
|
|
73
|
+
const everything = block === "all";
|
|
74
|
+
const index = Math.max(
|
|
75
|
+
items.findIndex((item) => item.type === block),
|
|
76
|
+
0,
|
|
77
|
+
);
|
|
78
|
+
const current = items[index];
|
|
79
|
+
const anchor = START_HERE.includes(current) ? current.type : `block-${current.type}`;
|
|
80
|
+
const unstyled = `/hearth/blocks/unstyled#${everything ? "all" : anchor}`;
|
|
81
|
+
const only = (type: string) => (everything ? <Link href={`/hearth/blocks?block=${type}`}>Only this block</Link> : undefined);
|
|
82
|
+
const showing = everything ? blocks : blocks.filter((b) => b.type === current.type);
|
|
67
83
|
return (
|
|
68
84
|
<>
|
|
69
85
|
<style>{CSS}</style>
|
|
@@ -71,42 +87,51 @@ export default async function BlockLibrary({ searchParams }: { searchParams: Pro
|
|
|
71
87
|
<div className="hearth-library-bar">
|
|
72
88
|
<b>Block library</b>
|
|
73
89
|
<span className="hearth-library-switch">
|
|
74
|
-
<
|
|
90
|
+
<a href={unstyled}>Unstyled</a>
|
|
75
91
|
<span className="hearth-library-current">This site's theme</span>
|
|
76
92
|
</span>
|
|
77
|
-
<
|
|
78
|
-
|
|
93
|
+
<span className="hearth-library-switch">
|
|
94
|
+
{everything ? <Link href="/hearth/blocks">One block</Link> : <span className="hearth-library-current">One block</span>}
|
|
95
|
+
{everything ? <span className="hearth-library-current">All blocks</span> : <Link href={`/hearth/blocks?block=all#${anchor}`}>All blocks</Link>}
|
|
96
|
+
</span>
|
|
97
|
+
<details key={everything ? "all" : current.type} className="hearth-library-jump">
|
|
98
|
+
<summary>{everything ? "Choose a block" : current.label}</summary>
|
|
79
99
|
<nav aria-label="Blocks">
|
|
80
|
-
|
|
81
|
-
{libraryGroups.map((g) => (
|
|
100
|
+
{[{ group: "Start here", blocks: START_HERE }, ...libraryGroups].map((g) => (
|
|
82
101
|
<Fragment key={g.group}>
|
|
83
102
|
<p>{g.group}</p>
|
|
84
103
|
{g.blocks.map((b) => (
|
|
85
|
-
<
|
|
104
|
+
<Link key={b.type} href={`/hearth/blocks?block=${b.type}`}>
|
|
86
105
|
{b.label}
|
|
87
|
-
</
|
|
106
|
+
</Link>
|
|
88
107
|
))}
|
|
89
108
|
</Fragment>
|
|
90
109
|
))}
|
|
91
110
|
</nav>
|
|
92
111
|
</details>
|
|
112
|
+
{everything ? null : (
|
|
113
|
+
<span className="hearth-library-steps">
|
|
114
|
+
{index > 0 ? <Link href={`/hearth/blocks?block=${items[index - 1].type}`}>← Previous</Link> : <span>← Previous</span>}
|
|
115
|
+
{index < items.length - 1 ? <Link href={`/hearth/blocks?block=${items[index + 1].type}`}>Next →</Link> : <span>Next →</span>}
|
|
116
|
+
</span>
|
|
117
|
+
)}
|
|
93
118
|
<span className="hearth-library-note">Sample content. Links and forms here do nothing.</span>
|
|
94
119
|
</div>
|
|
95
|
-
{
|
|
96
|
-
?
|
|
97
|
-
|
|
98
|
-
<Sample key={tone} id={tone === "default" ? "grounds" : undefined} name="Grounds" label={`${GROUND_NAMES[tone]} ground`}>
|
|
120
|
+
{everything || current.type === "grounds"
|
|
121
|
+
? TONES.map((tone) => (
|
|
122
|
+
<Sample key={tone} id={tone === "default" ? "grounds" : undefined} name="Grounds" label={`${GROUND_NAMES[tone]} ground`} action={only("grounds")}>
|
|
99
123
|
<Ground tone={tone} />
|
|
100
124
|
</Sample>
|
|
101
|
-
))
|
|
102
|
-
|
|
103
|
-
|
|
125
|
+
))
|
|
126
|
+
: null}
|
|
127
|
+
{everything || current.type === "form-states" ? (
|
|
128
|
+
<Sample id="form-states" name="Form states" label="Errors, filled-in fields, options and the success message" action={only("form-states")}>
|
|
104
129
|
<FormStates />
|
|
105
130
|
</Sample>
|
|
106
|
-
)}
|
|
131
|
+
) : null}
|
|
107
132
|
{showing.map((b) =>
|
|
108
133
|
samples[b.type].map((s, i) => (
|
|
109
|
-
<Sample key={s.block.id} id={i ? undefined : `block-${b.type}`} name={b.label} label={s.label} action={only
|
|
134
|
+
<Sample key={s.block.id} id={i ? undefined : `block-${b.type}`} name={b.label} label={s.label} action={only(b.type)}>
|
|
110
135
|
<Blocks blocks={[s.block]} />
|
|
111
136
|
</Sample>
|
|
112
137
|
)),
|