handzon-core 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/Footer.astro +6 -1
- package/src/layouts/BaseLayout.astro +12 -5
- package/src/layouts/TutorialLayout.astro +3 -0
- package/src/pages/Home.astro +3 -0
- package/src/pages/TutorialLanding.astro +3 -0
- package/src/pages/TutorialStep.astro +3 -0
- package/src/server/auth/config.ts +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "handzon-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Core framework for Handzon — layouts, components, content + AI libs, and server runtime (handlers, DB, auth, migration runner) consumed by Handzon scaffolds.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
interface Props {
|
|
14
14
|
/** Primary credit label — the site owner. */
|
|
15
15
|
siteName?: string;
|
|
16
|
+
/** Primary footer credit label. Defaults to `siteName`, but can be
|
|
17
|
+
* shorter (for example `render.com`) without changing page titles,
|
|
18
|
+
* nav labels, or Open Graph metadata. */
|
|
19
|
+
siteCreditLabel?: string;
|
|
16
20
|
/** Primary credit link. When set, the footer leads with this credit
|
|
17
21
|
* and demotes the Handzon link to a secondary side link. */
|
|
18
22
|
siteUrl?: string;
|
|
@@ -24,6 +28,7 @@ interface Props {
|
|
|
24
28
|
|
|
25
29
|
const {
|
|
26
30
|
siteName = "Handzon",
|
|
31
|
+
siteCreditLabel = siteName,
|
|
27
32
|
siteUrl,
|
|
28
33
|
repoUrl = "https://github.com/R4ph-t/handzon",
|
|
29
34
|
year = new Date().getFullYear(),
|
|
@@ -35,7 +40,7 @@ const {
|
|
|
35
40
|
<span class="hz-footer-credit">
|
|
36
41
|
© {year}
|
|
37
42
|
<a class="hz-footer-link" href={siteUrl} target="_blank" rel="noopener">
|
|
38
|
-
{
|
|
43
|
+
{siteCreditLabel}
|
|
39
44
|
<svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
40
45
|
<path d="M7 17 17 7" />
|
|
41
46
|
<path d="M7 7h10v10" />
|
|
@@ -38,6 +38,8 @@ interface Props {
|
|
|
38
38
|
* When set, the footer leads with the site credit and demotes the
|
|
39
39
|
* Handzon link to a secondary side link. */
|
|
40
40
|
siteUrl?: string;
|
|
41
|
+
/** Primary footer credit label. Defaults to siteName. */
|
|
42
|
+
siteCreditLabel?: string;
|
|
41
43
|
/**
|
|
42
44
|
* Width of the page's content column. Drives the navbar + footer
|
|
43
45
|
* alignment via the --hz-page-max-width CSS custom property so the
|
|
@@ -68,6 +70,7 @@ const {
|
|
|
68
70
|
showFooter = true,
|
|
69
71
|
repoUrl,
|
|
70
72
|
siteUrl,
|
|
73
|
+
siteCreditLabel,
|
|
71
74
|
// Default to a full-width navbar + footer with a small consistent
|
|
72
75
|
// inset, matching the tutorial step page. Pages with a centred
|
|
73
76
|
// column (home grid, tutorial landing) keep their own content
|
|
@@ -118,13 +121,17 @@ const desc = description ?? tagline;
|
|
|
118
121
|
<div class="hz-page">
|
|
119
122
|
<slot />
|
|
120
123
|
</div>
|
|
121
|
-
{/* Footer is
|
|
122
|
-
wrapper to replace it entirely. With no override, the default
|
|
123
|
-
Footer renders, configurable via siteName/siteUrl/repoUrl. */}
|
|
124
|
-
{/* Footer content is configurable via siteName/siteUrl/repoUrl. For
|
|
124
|
+
{/* Footer content is configurable via siteName/siteCreditLabel/siteUrl/repoUrl. For
|
|
125
125
|
a fully custom footer, a scaffold can pass showFooter={false} and
|
|
126
126
|
render its own markup in the page body. */}
|
|
127
|
-
{showFooter &&
|
|
127
|
+
{showFooter && (
|
|
128
|
+
<Footer
|
|
129
|
+
siteName={siteName}
|
|
130
|
+
siteCreditLabel={siteCreditLabel}
|
|
131
|
+
siteUrl={siteUrl}
|
|
132
|
+
repoUrl={repoUrl}
|
|
133
|
+
/>
|
|
134
|
+
)}
|
|
128
135
|
<script>
|
|
129
136
|
// Render any <pre class="mermaid"> blocks emitted by rehype-mermaid.
|
|
130
137
|
if (document.querySelector("pre.mermaid")) {
|
|
@@ -19,6 +19,7 @@ interface Props {
|
|
|
19
19
|
faviconUrl?: string;
|
|
20
20
|
repoUrl?: string;
|
|
21
21
|
siteUrl?: string;
|
|
22
|
+
siteCreditLabel?: string;
|
|
22
23
|
/** Set false to drop the built-in footer and supply your own. */
|
|
23
24
|
showFooter?: boolean;
|
|
24
25
|
}
|
|
@@ -37,6 +38,7 @@ const {
|
|
|
37
38
|
faviconUrl,
|
|
38
39
|
repoUrl,
|
|
39
40
|
siteUrl,
|
|
41
|
+
siteCreditLabel,
|
|
40
42
|
showFooter = true,
|
|
41
43
|
} = Astro.props;
|
|
42
44
|
|
|
@@ -53,6 +55,7 @@ const stepSlugs = steps.map((s) => parseStepId(s.id).stepSlug);
|
|
|
53
55
|
faviconUrl={faviconUrl}
|
|
54
56
|
repoUrl={repoUrl}
|
|
55
57
|
siteUrl={siteUrl}
|
|
58
|
+
siteCreditLabel={siteCreditLabel}
|
|
56
59
|
showFooter={showFooter}
|
|
57
60
|
>
|
|
58
61
|
<slot name="head" slot="head" />
|
package/src/pages/Home.astro
CHANGED
|
@@ -17,6 +17,7 @@ interface Props {
|
|
|
17
17
|
faviconUrl?: string;
|
|
18
18
|
repoUrl?: string;
|
|
19
19
|
siteUrl?: string;
|
|
20
|
+
siteCreditLabel?: string;
|
|
20
21
|
/** Set false to drop the built-in footer and supply your own. */
|
|
21
22
|
showFooter?: boolean;
|
|
22
23
|
showResumeRail?: boolean;
|
|
@@ -35,6 +36,7 @@ const {
|
|
|
35
36
|
faviconUrl,
|
|
36
37
|
repoUrl,
|
|
37
38
|
siteUrl,
|
|
39
|
+
siteCreditLabel,
|
|
38
40
|
showFooter = true,
|
|
39
41
|
showResumeRail = true,
|
|
40
42
|
emptyStateCommand = "pnpm handzon:new",
|
|
@@ -70,6 +72,7 @@ for (const t of tutorials) {
|
|
|
70
72
|
faviconUrl={faviconUrl}
|
|
71
73
|
repoUrl={repoUrl}
|
|
72
74
|
siteUrl={siteUrl}
|
|
75
|
+
siteCreditLabel={siteCreditLabel}
|
|
73
76
|
showFooter={showFooter}
|
|
74
77
|
nav="userMenu"
|
|
75
78
|
>
|
|
@@ -14,6 +14,7 @@ interface Props {
|
|
|
14
14
|
faviconUrl?: string;
|
|
15
15
|
repoUrl?: string;
|
|
16
16
|
siteUrl?: string;
|
|
17
|
+
siteCreditLabel?: string;
|
|
17
18
|
/** Set false to drop the built-in footer and supply your own. */
|
|
18
19
|
showFooter?: boolean;
|
|
19
20
|
}
|
|
@@ -28,6 +29,7 @@ const {
|
|
|
28
29
|
faviconUrl,
|
|
29
30
|
repoUrl,
|
|
30
31
|
siteUrl,
|
|
32
|
+
siteCreditLabel,
|
|
31
33
|
showFooter = true,
|
|
32
34
|
} = Astro.props;
|
|
33
35
|
const steps = await getStepsForTutorial(tutorial.id);
|
|
@@ -45,6 +47,7 @@ const firstStepSlug = steps[0] ? parseStepId(steps[0].id).stepSlug : null;
|
|
|
45
47
|
faviconUrl={faviconUrl}
|
|
46
48
|
repoUrl={repoUrl}
|
|
47
49
|
siteUrl={siteUrl}
|
|
50
|
+
siteCreditLabel={siteCreditLabel}
|
|
48
51
|
showFooter={showFooter}
|
|
49
52
|
>
|
|
50
53
|
<slot name="head" slot="head" />
|
|
@@ -25,6 +25,7 @@ interface Props {
|
|
|
25
25
|
faviconUrl?: string;
|
|
26
26
|
repoUrl?: string;
|
|
27
27
|
siteUrl?: string;
|
|
28
|
+
siteCreditLabel?: string;
|
|
28
29
|
/** Set false to drop the built-in footer and supply your own. */
|
|
29
30
|
showFooter?: boolean;
|
|
30
31
|
}
|
|
@@ -42,6 +43,7 @@ const {
|
|
|
42
43
|
faviconUrl,
|
|
43
44
|
repoUrl,
|
|
44
45
|
siteUrl,
|
|
46
|
+
siteCreditLabel,
|
|
45
47
|
showFooter = true,
|
|
46
48
|
} = Astro.props;
|
|
47
49
|
const { stepSlug } = parseStepId(currentStep.id);
|
|
@@ -74,6 +76,7 @@ const initialContext = buildContext({
|
|
|
74
76
|
faviconUrl={faviconUrl}
|
|
75
77
|
repoUrl={repoUrl}
|
|
76
78
|
siteUrl={siteUrl}
|
|
79
|
+
siteCreditLabel={siteCreditLabel}
|
|
77
80
|
showFooter={showFooter}
|
|
78
81
|
>
|
|
79
82
|
<slot name="head" slot="head" />
|
|
@@ -56,6 +56,12 @@ function resolveAuthUrl(): string | undefined {
|
|
|
56
56
|
return undefined;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function resolveAuthPrefix(): string {
|
|
60
|
+
const base = import.meta.env.BASE_URL;
|
|
61
|
+
const normalizedBase = base === "/" ? "" : base.replace(/\/$/, "");
|
|
62
|
+
return `${normalizedBase}/api/auth`;
|
|
63
|
+
}
|
|
64
|
+
|
|
59
65
|
export function createAuthConfig({ db }: AuthConfigOptions) {
|
|
60
66
|
// Auth.js v5 reads `AUTH_URL` from process.env on each request, so
|
|
61
67
|
// we resolve once and write it back. Idempotent: if AUTH_URL was
|
|
@@ -73,6 +79,11 @@ export function createAuthConfig({ db }: AuthConfigOptions) {
|
|
|
73
79
|
return defineConfig({ providers: [] });
|
|
74
80
|
}
|
|
75
81
|
return defineConfig({
|
|
82
|
+
// auth-astro defaults to /api/auth, but Astro apps mounted with
|
|
83
|
+
// `base` receive requests at /<base>/api/auth. Use the same base
|
|
84
|
+
// that powers app links and API calls so the catch-all auth route
|
|
85
|
+
// returns a Response instead of falling through with undefined.
|
|
86
|
+
prefix: resolveAuthPrefix(),
|
|
76
87
|
adapter: DrizzleAdapter(db, {
|
|
77
88
|
usersTable: users,
|
|
78
89
|
accountsTable: accounts,
|