handzon-core 0.10.0 → 0.12.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": "handzon-core",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
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"
@@ -1,44 +1,77 @@
1
1
  ---
2
2
  /**
3
- * Site footer. A single quiet line at the bottom of every page,
4
- * crediting the framework + linking to the Handzon repo. Sized to
5
- * match the rest of the brutalist surface treatment.
3
+ * Site footer. A single quiet line at the bottom of every page, sized
4
+ * to match the rest of the brutalist surface treatment.
5
+ *
6
+ * Two modes:
7
+ * - Default (no `siteUrl`): one centered "Built with Handzon" credit,
8
+ * preserving the framework's original footer for unbranded scaffolds.
9
+ * - Branded (`siteUrl` set): the site owner's credit leads on the left
10
+ * (linked to `siteUrl`), and "Built with Handzon" moves to a quieter
11
+ * secondary link on the right.
6
12
  */
7
13
  interface Props {
8
- /** Public URL to the framework repo. Override to point elsewhere. */
14
+ /** Primary credit label the site owner. */
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;
20
+ /** Primary credit link. When set, the footer leads with this credit
21
+ * and demotes the Handzon link to a secondary side link. */
22
+ siteUrl?: string;
23
+ /** Handzon project/package link (the secondary "Built with" credit). */
9
24
  repoUrl?: string;
10
25
  /** Year displayed in the credit line; defaults to the current year. */
11
26
  year?: number;
12
27
  }
13
28
 
14
29
  const {
30
+ siteName = "Handzon",
31
+ siteCreditLabel = siteName,
32
+ siteUrl,
15
33
  repoUrl = "https://github.com/R4ph-t/handzon",
16
34
  year = new Date().getFullYear(),
17
35
  } = Astro.props;
18
36
  ---
19
37
  <footer class="hz-footer">
20
- <div class="hz-footer-inner">
21
- <span class="hz-footer-credit">
22
- © {year} · Built with
23
- <a class="hz-footer-link" href={repoUrl} target="_blank" rel="noopener">
24
- Handzon
25
- <svg
26
- viewBox="0 0 24 24"
27
- width="11"
28
- height="11"
29
- fill="none"
30
- stroke="currentColor"
31
- stroke-width="2"
32
- stroke-linecap="round"
33
- stroke-linejoin="round"
34
- aria-hidden="true"
35
- >
36
- <path d="M7 17 17 7" />
37
- <path d="M7 7h10v10" />
38
- </svg>
39
- </a>
40
- </span>
41
- </div>
38
+ {siteUrl ? (
39
+ <div class="hz-footer-inner hz-footer-split">
40
+ <span class="hz-footer-credit">
41
+ © {year}
42
+ <a class="hz-footer-link" href={siteUrl} target="_blank" rel="noopener">
43
+ {siteCreditLabel}
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">
45
+ <path d="M7 17 17 7" />
46
+ <path d="M7 7h10v10" />
47
+ </svg>
48
+ </a>
49
+ </span>
50
+ <span class="hz-footer-built">
51
+ Built with
52
+ <a class="hz-footer-link" href={repoUrl} target="_blank" rel="noopener">
53
+ Handzon
54
+ <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">
55
+ <path d="M7 17 17 7" />
56
+ <path d="M7 7h10v10" />
57
+ </svg>
58
+ </a>
59
+ </span>
60
+ </div>
61
+ ) : (
62
+ <div class="hz-footer-inner">
63
+ <span class="hz-footer-credit">
64
+ © {year} · Built with
65
+ <a class="hz-footer-link" href={repoUrl} target="_blank" rel="noopener">
66
+ Handzon
67
+ <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">
68
+ <path d="M7 17 17 7" />
69
+ <path d="M7 7h10v10" />
70
+ </svg>
71
+ </a>
72
+ </span>
73
+ </div>
74
+ )}
42
75
  </footer>
43
76
 
44
77
  <style is:global>
@@ -64,11 +97,24 @@ const {
64
97
  font-family: var(--font-mono);
65
98
  font-size: 0.75em;
66
99
  }
100
+ /* Branded mode: site credit left, "Built with Handzon" right. Falls
101
+ * back to a stacked, centered layout on narrow viewports. */
102
+ .hz-footer-split {
103
+ justify-content: space-between;
104
+ gap: 0.75rem 1.5rem;
105
+ flex-wrap: wrap;
106
+ }
67
107
  .hz-footer-credit {
68
108
  display: inline-flex;
69
109
  align-items: center;
70
110
  gap: 0.45rem;
71
111
  }
112
+ .hz-footer-built {
113
+ display: inline-flex;
114
+ align-items: center;
115
+ gap: 0.4rem;
116
+ opacity: 0.85;
117
+ }
72
118
  .hz-footer-link {
73
119
  display: inline-flex;
74
120
  align-items: center;
@@ -34,6 +34,12 @@ interface Props {
34
34
  showFooter?: boolean;
35
35
  /** Footer link URL; defaults to the Handzon repo. */
36
36
  repoUrl?: string;
37
+ /** Primary footer credit link (the site owner, e.g. https://render.com).
38
+ * When set, the footer leads with the site credit and demotes the
39
+ * Handzon link to a secondary side link. */
40
+ siteUrl?: string;
41
+ /** Primary footer credit label. Defaults to siteName. */
42
+ siteCreditLabel?: string;
37
43
  /**
38
44
  * Width of the page's content column. Drives the navbar + footer
39
45
  * alignment via the --hz-page-max-width CSS custom property so the
@@ -63,6 +69,8 @@ const {
63
69
  faviconUrl = "/favicon.svg",
64
70
  showFooter = true,
65
71
  repoUrl,
72
+ siteUrl,
73
+ siteCreditLabel,
66
74
  // Default to a full-width navbar + footer with a small consistent
67
75
  // inset, matching the tutorial step page. Pages with a centred
68
76
  // column (home grid, tutorial landing) keep their own content
@@ -113,7 +121,17 @@ const desc = description ?? tagline;
113
121
  <div class="hz-page">
114
122
  <slot />
115
123
  </div>
116
- {showFooter && <Footer repoUrl={repoUrl} />}
124
+ {/* Footer content is configurable via siteName/siteCreditLabel/siteUrl/repoUrl. For
125
+ a fully custom footer, a scaffold can pass showFooter={false} and
126
+ render its own markup in the page body. */}
127
+ {showFooter && (
128
+ <Footer
129
+ siteName={siteName}
130
+ siteCreditLabel={siteCreditLabel}
131
+ siteUrl={siteUrl}
132
+ repoUrl={repoUrl}
133
+ />
134
+ )}
117
135
  <script>
118
136
  // Render any <pre class="mermaid"> blocks emitted by rehype-mermaid.
119
137
  if (document.querySelector("pre.mermaid")) {
@@ -18,6 +18,10 @@ interface Props {
18
18
  logoHeight?: number;
19
19
  faviconUrl?: string;
20
20
  repoUrl?: string;
21
+ siteUrl?: string;
22
+ siteCreditLabel?: string;
23
+ /** Set false to drop the built-in footer and supply your own. */
24
+ showFooter?: boolean;
21
25
  }
22
26
 
23
27
  const {
@@ -33,6 +37,9 @@ const {
33
37
  logoHeight,
34
38
  faviconUrl,
35
39
  repoUrl,
40
+ siteUrl,
41
+ siteCreditLabel,
42
+ showFooter = true,
36
43
  } = Astro.props;
37
44
 
38
45
  const stepSlugs = steps.map((s) => parseStepId(s.id).stepSlug);
@@ -47,6 +54,9 @@ const stepSlugs = steps.map((s) => parseStepId(s.id).stepSlug);
47
54
  logoHeight={logoHeight}
48
55
  faviconUrl={faviconUrl}
49
56
  repoUrl={repoUrl}
57
+ siteUrl={siteUrl}
58
+ siteCreditLabel={siteCreditLabel}
59
+ showFooter={showFooter}
50
60
  >
51
61
  <slot name="head" slot="head" />
52
62
  <div class="layout">
@@ -16,6 +16,10 @@ interface Props {
16
16
  logoHeight?: number;
17
17
  faviconUrl?: string;
18
18
  repoUrl?: string;
19
+ siteUrl?: string;
20
+ siteCreditLabel?: string;
21
+ /** Set false to drop the built-in footer and supply your own. */
22
+ showFooter?: boolean;
19
23
  showResumeRail?: boolean;
20
24
  emptyStateCommand?: string;
21
25
  /** Tutorials per page on the grid. */
@@ -31,6 +35,9 @@ const {
31
35
  logoHeight,
32
36
  faviconUrl,
33
37
  repoUrl,
38
+ siteUrl,
39
+ siteCreditLabel,
40
+ showFooter = true,
34
41
  showResumeRail = true,
35
42
  emptyStateCommand = "pnpm handzon:new",
36
43
  pageSize = 9,
@@ -64,6 +71,9 @@ for (const t of tutorials) {
64
71
  logoHeight={logoHeight}
65
72
  faviconUrl={faviconUrl}
66
73
  repoUrl={repoUrl}
74
+ siteUrl={siteUrl}
75
+ siteCreditLabel={siteCreditLabel}
76
+ showFooter={showFooter}
67
77
  nav="userMenu"
68
78
  >
69
79
  <slot name="head" slot="head" />
@@ -13,6 +13,10 @@ interface Props {
13
13
  logoHeight?: number;
14
14
  faviconUrl?: string;
15
15
  repoUrl?: string;
16
+ siteUrl?: string;
17
+ siteCreditLabel?: string;
18
+ /** Set false to drop the built-in footer and supply your own. */
19
+ showFooter?: boolean;
16
20
  }
17
21
 
18
22
  const {
@@ -24,6 +28,9 @@ const {
24
28
  logoHeight,
25
29
  faviconUrl,
26
30
  repoUrl,
31
+ siteUrl,
32
+ siteCreditLabel,
33
+ showFooter = true,
27
34
  } = Astro.props;
28
35
  const steps = await getStepsForTutorial(tutorial.id);
29
36
  const duration = tutorial.data.estimatedDuration ?? sumDurations(steps) ?? "";
@@ -39,6 +46,9 @@ const firstStepSlug = steps[0] ? parseStepId(steps[0].id).stepSlug : null;
39
46
  logoHeight={logoHeight}
40
47
  faviconUrl={faviconUrl}
41
48
  repoUrl={repoUrl}
49
+ siteUrl={siteUrl}
50
+ siteCreditLabel={siteCreditLabel}
51
+ showFooter={showFooter}
42
52
  >
43
53
  <slot name="head" slot="head" />
44
54
  <div class="landing">
@@ -24,6 +24,10 @@ interface Props {
24
24
  logoHeight?: number;
25
25
  faviconUrl?: string;
26
26
  repoUrl?: string;
27
+ siteUrl?: string;
28
+ siteCreditLabel?: string;
29
+ /** Set false to drop the built-in footer and supply your own. */
30
+ showFooter?: boolean;
27
31
  }
28
32
 
29
33
  const {
@@ -38,6 +42,9 @@ const {
38
42
  logoHeight,
39
43
  faviconUrl,
40
44
  repoUrl,
45
+ siteUrl,
46
+ siteCreditLabel,
47
+ showFooter = true,
41
48
  } = Astro.props;
42
49
  const { stepSlug } = parseStepId(currentStep.id);
43
50
  const { Content } = await render(currentStep);
@@ -68,6 +75,9 @@ const initialContext = buildContext({
68
75
  logoHeight={logoHeight}
69
76
  faviconUrl={faviconUrl}
70
77
  repoUrl={repoUrl}
78
+ siteUrl={siteUrl}
79
+ siteCreditLabel={siteCreditLabel}
80
+ showFooter={showFooter}
71
81
  >
72
82
  <slot name="head" slot="head" />
73
83
  <Content components={components} />