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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handzon-core",
3
- "version": "0.11.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
- {siteName}
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 fully overridable: pass `slot="footer"` from any page
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 && <Footer siteName={siteName} siteUrl={siteUrl} repoUrl={repoUrl} />}
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" />
@@ -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,