@zorgo/next 1.3.0 → 2.0.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.
Files changed (66) hide show
  1. package/dist/cli.d.mts +9 -0
  2. package/dist/cli.d.ts +9 -0
  3. package/dist/cli.js +44 -66
  4. package/dist/cli.js.map +1 -1
  5. package/dist/cli.mjs +43 -65
  6. package/dist/cli.mjs.map +1 -1
  7. package/dist/components.d.mts +735 -5
  8. package/dist/components.d.ts +735 -5
  9. package/dist/components.js +3692 -525
  10. package/dist/components.js.map +1 -1
  11. package/dist/components.mjs +3663 -540
  12. package/dist/components.mjs.map +1 -1
  13. package/dist/images.d.mts +25 -13
  14. package/dist/images.d.ts +25 -13
  15. package/dist/images.js +18 -8
  16. package/dist/images.js.map +1 -1
  17. package/dist/images.mjs +15 -8
  18. package/dist/images.mjs.map +1 -1
  19. package/dist/index.d.mts +315 -9
  20. package/dist/index.d.ts +315 -9
  21. package/dist/index.js +58 -52
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.mjs +58 -52
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/item-page.d.mts +6 -0
  26. package/dist/item-page.d.ts +6 -0
  27. package/dist/item-page.js +4 -2
  28. package/dist/item-page.js.map +1 -1
  29. package/dist/item-page.mjs +4 -2
  30. package/dist/item-page.mjs.map +1 -1
  31. package/dist/legal.d.mts +31 -0
  32. package/dist/legal.d.ts +31 -0
  33. package/dist/legal.js +322 -0
  34. package/dist/legal.js.map +1 -0
  35. package/dist/legal.mjs +293 -0
  36. package/dist/legal.mjs.map +1 -0
  37. package/dist/seo.d.mts +56 -1
  38. package/dist/seo.d.ts +56 -1
  39. package/dist/seo.js +111 -2
  40. package/dist/seo.js.map +1 -1
  41. package/dist/seo.mjs +106 -1
  42. package/dist/seo.mjs.map +1 -1
  43. package/dist/server.d.mts +27 -1
  44. package/dist/server.d.ts +27 -1
  45. package/dist/server.js +322 -102
  46. package/dist/server.js.map +1 -1
  47. package/dist/server.mjs +322 -104
  48. package/dist/server.mjs.map +1 -1
  49. package/dist/slugs.d.mts +2 -1
  50. package/dist/slugs.d.ts +2 -1
  51. package/dist/slugs.js +2 -0
  52. package/dist/slugs.js.map +1 -1
  53. package/dist/slugs.mjs +1 -0
  54. package/dist/tree/README.md +2 -2
  55. package/dist/tree/app/error.tsx +12 -3
  56. package/dist/tree/app/global-error.tsx +21 -4
  57. package/dist/tree/app/globals.css +41 -0
  58. package/dist/tree/app/not-found.tsx +17 -3
  59. package/dist/tree/app/privacy/page.tsx.gen.ts +28 -9
  60. package/dist/tree/app/terms/page.tsx.gen.ts +26 -10
  61. package/dist/tree/gitignore +6 -1
  62. package/dist/tree/next.config.ts +6 -5
  63. package/dist/tree/open-next.config.ts +7 -0
  64. package/dist/tree/package.json.gen.ts +8 -2
  65. package/package.json +7 -2
  66. package/dist/tree/app/globals.css.gen.ts +0 -124
@@ -0,0 +1,41 @@
1
+ /* Hand-owned from the moment this site is scaffolded — prebuild never
2
+ overwrites this file (see ZOR-391). Add custom vars, @font-face, and brand
3
+ classes here; they survive every `pnpm dev` / `pnpm build`.
4
+
5
+ ./generated/branding.css is written by prebuild from franchise_branding and
6
+ carries the Google Fonts @import (or nothing, if this site ships its own
7
+ fonts in public/fonts/) plus the derived color tokens. It must stay the
8
+ first @import below so its font @import — if present — is still the first
9
+ statement in the bundled stylesheet; Tailwind resolves @import itself, so a
10
+ later position would land it after non-import content and browsers would
11
+ silently drop it. */
12
+ @import "./generated/branding.css";
13
+ @import "tailwindcss";
14
+ /* Tailwind v4 skips node_modules; @zorgo/next ships un-obfuscated components so
15
+ their class names are scannable here. */
16
+ @source "../node_modules/@zorgo/next/dist/components.mjs";
17
+ @source "../node_modules/@zorgo/next/dist/item-page.mjs";
18
+ @source "../node_modules/@zorgo/next/dist/legal.mjs";
19
+
20
+ @layer base {
21
+ button,
22
+ [type="button"],
23
+ [type="submit"],
24
+ [type="reset"] {
25
+ cursor: pointer;
26
+ }
27
+
28
+ button:disabled,
29
+ [type="button"]:disabled,
30
+ [type="submit"]:disabled,
31
+ [type="reset"]:disabled {
32
+ cursor: not-allowed;
33
+ }
34
+
35
+ /* TY-02: justified body copy produces visible rivers and wild word spacing.
36
+ Left-aligned is the default with no opt-in — this makes the utility a
37
+ no-op rather than just avoiding it by convention. */
38
+ .text-justify {
39
+ text-align: left;
40
+ }
41
+ }
@@ -1,8 +1,22 @@
1
+ import Link from "next/link";
2
+
3
+ // min-h-full, not a viewport height: this renders inside the layout’s
4
+ // <main>, between a real header and footer, so centring on the viewport would
5
+ // push it below the fold.
1
6
  export default function NotFound() {
2
7
  return (
3
- <div>
4
- <h2>Not Found</h2>
5
- <p>Could not find requested resource</p>
8
+ <div className="mx-auto flex min-h-full max-w-md flex-col items-center justify-center gap-4 px-4 py-24 text-center">
9
+ <p className="text-6xl font-black text-primary">404</p>
10
+ <h1 className="text-3xl font-bold">We can&apos;t find that page</h1>
11
+ <p className="opacity-70">
12
+ It may have moved, or the link that brought you here is out of date.
13
+ </p>
14
+ <Link
15
+ href="/"
16
+ className="mt-2 inline-flex min-h-11 items-center justify-center rounded-xl bg-primary px-6 py-3 font-semibold text-primary-foreground transition-colors duration-300 hover:bg-primary/80"
17
+ >
18
+ Back to the home page
19
+ </Link>
6
20
  </div>
7
21
  );
8
22
  }
@@ -1,15 +1,34 @@
1
1
  import type { ScaffoldOptions } from "../../../../types";
2
2
 
3
+ // The policy text itself lives in @zorgo/next/components (src/components/client/legal.ts),
4
+ // so updating the package updates every scaffolded site. Only the brand name and
5
+ // the "last updated" date are baked in here.
3
6
  export default function generatePrivacyPage(opts: ScaffoldOptions) {
7
+ if (!opts.includePrivacyPage) return;
4
8
 
5
- if (opts.includePrivacyPage) {
6
- return `export default function PrivacyPage() {
7
- return (
8
- <div>
9
- <h1>Privacy Policy</h1>
10
- </div>
11
- )
12
- }`;
13
- }
9
+ const today = new Date().toLocaleDateString("en-US", {
10
+ year: "numeric",
11
+ month: "long",
12
+ day: "numeric",
13
+ });
14
14
 
15
+ return `import { LegalPage, privacyPolicyHtml } from "@zorgo/next/legal";
16
+
17
+ const BRAND = ${JSON.stringify(opts.franchiseName)};
18
+
19
+ export const metadata = {
20
+ title: "Privacy Policy",
21
+ description: \`How \${BRAND} handles your information.\`,
22
+ };
23
+
24
+ export default function PrivacyPage() {
25
+ return (
26
+ <LegalPage
27
+ title="Privacy Policy"
28
+ lastUpdated="${today}"
29
+ html={privacyPolicyHtml({ brand: BRAND, lastUpdated: "${today}" })}
30
+ />
31
+ );
32
+ }
33
+ `;
15
34
  }
@@ -1,16 +1,32 @@
1
1
  import type { ScaffoldOptions } from "../../../../types";
2
2
 
3
- // add bullshit tos here
3
+ // See privacy/page.tsx.gen.ts the terms text is shipped by the package.
4
4
  export default function generateTermsPage(opts: ScaffoldOptions) {
5
+ if (!opts.includeTermsPage) return;
5
6
 
6
- if (opts.includeTermsPage) {
7
- return `export default function TermsPage() {
8
- return (
9
- <div>
10
- <h1>Terms of Service</h1>
11
- </div>
12
- )
13
- }`;
14
- }
7
+ const today = new Date().toLocaleDateString("en-US", {
8
+ year: "numeric",
9
+ month: "long",
10
+ day: "numeric",
11
+ });
15
12
 
13
+ return `import { LegalPage, termsOfUseHtml } from "@zorgo/next/legal";
14
+
15
+ const BRAND = ${JSON.stringify(opts.franchiseName)};
16
+
17
+ export const metadata = {
18
+ title: "Terms of Service",
19
+ description: \`Terms for ordering online from \${BRAND}.\`,
20
+ };
21
+
22
+ export default function TermsPage() {
23
+ return (
24
+ <LegalPage
25
+ title="Online Ordering Terms of Use"
26
+ lastUpdated="${today}"
27
+ html={termsOfUseHtml({ brand: BRAND, lastUpdated: "${today}" })}
28
+ />
29
+ );
30
+ }
31
+ `;
16
32
  }
@@ -39,7 +39,12 @@ next-env.d.ts
39
39
 
40
40
  /.wrangler/
41
41
 
42
- # generated from .env by scripts/prebuild.ts
42
+ # generated from .env / franchise_branding by scripts/prebuild.ts
43
43
  /wrangler.toml
44
44
  /lib/generated/
45
+ /app/generated/
46
+ /public/styles/
45
47
  /public/images/items/
48
+ /public/images/logo.*
49
+ # AVIF ladders encoded from assets/images/ — the originals are what's committed
50
+ /public/images/**/*.avif
@@ -1,6 +1,6 @@
1
1
  import type { NextConfig } from "next";
2
2
  import path from "path";
3
- import { ITEM_IMAGE_WIDTHS } from "@zorgo/next/images";
3
+ import { IMAGE_WIDTHS } from "@zorgo/next/images";
4
4
 
5
5
  const monorepoRoot = path.resolve(__dirname, "../../..");
6
6
 
@@ -10,15 +10,16 @@ const nextConfig: NextConfig = {
10
10
  root: monorepoRoot,
11
11
  },
12
12
  transpilePackages: ["@zorgo", "@zorgo-website-core"],
13
- // Item images are resized to a fixed AVIF ladder at prebuild, so nothing needs
14
- // a runtime optimizer (and no Supabase host in remotePatterns a custom
15
- // loader bypasses that check entirely).
13
+ // Menu item images and the site's own photos in assets/images/ are both
14
+ // resized to a fixed AVIF ladder at prebuild, so nothing needs a runtime
15
+ // optimizer (and no Supabase host in remotePatterns — a custom loader
16
+ // bypasses that check entirely).
16
17
  images: {
17
18
  loader: "custom",
18
19
  loaderFile: "./image-loader.ts",
19
20
  // Must mirror the rungs prebuild emits, or Next advertises srcset widths no
20
21
  // variant has and the browser picks a candidate narrower than it asked for.
21
- deviceSizes: [...ITEM_IMAGE_WIDTHS],
22
+ deviceSizes: [...IMAGE_WIDTHS],
22
23
  imageSizes: [],
23
24
  },
24
25
  };
@@ -1,4 +1,11 @@
1
1
  import { defineCloudflareConfig } from "@opennextjs/cloudflare";
2
+ import staticAssetsIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/static-assets-incremental-cache";
2
3
 
3
4
  export default defineCloudflareConfig({
5
+ // Without this the cache defaults to "dummy": every prerendered page is a
6
+ // miss, and `dynamicParams = false` turns that miss into a 404. The menu is
7
+ // rebuilt from the prebuilt data on every deploy, so a read-only cache
8
+ // served from Workers assets is enough — swap to r2IncrementalCache if a
9
+ // site ever needs ISR.
10
+ incrementalCache: staticAssetsIncrementalCache,
4
11
  });
@@ -12,8 +12,14 @@ export default function generatePackageJson(opts: ScaffoldOptions): string {
12
12
  "dev:no-prebuild": "next dev",
13
13
  "prebuild": "tsx scripts/prebuild.ts",
14
14
  "build": "tsx scripts/prebuild.ts && next build",
15
- "cloudflare:deploy": "tsx scripts/prebuild.ts && wrangler deploy",
16
- "preview": "tsx scripts/prebuild.ts && wrangler dev",
15
+ "cloudflare:deploy":
16
+ "tsx scripts/prebuild.ts && opennextjs-cloudflare build && wrangler deploy",
17
+ "cloudflare:deploy:staging":
18
+ "tsx scripts/prebuild.ts && opennextjs-cloudflare build && wrangler deploy --env staging",
19
+ "preview":
20
+ "tsx scripts/prebuild.ts && opennextjs-cloudflare build && wrangler dev",
21
+ "preview:staging":
22
+ "tsx scripts/prebuild.ts && opennextjs-cloudflare build && wrangler dev --env staging",
17
23
  },
18
24
  dependencies: PINNED_VERSIONS,
19
25
  devDependencies: DEV_DEPENDENCIES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zorgo/next",
3
- "version": "1.3.0",
3
+ "version": "2.0.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "bin": {
@@ -59,6 +59,11 @@
59
59
  "require": "./dist/images.js",
60
60
  "types": "./dist/images.d.ts"
61
61
  },
62
+ "./legal": {
63
+ "import": "./dist/legal.mjs",
64
+ "require": "./dist/legal.js",
65
+ "types": "./dist/legal.d.ts"
66
+ },
62
67
  "./item-page": {
63
68
  "import": "./dist/item-page.mjs",
64
69
  "require": "./dist/item-page.js",
@@ -87,7 +92,7 @@
87
92
  "zustand": "^4.x.x"
88
93
  },
89
94
  "scripts": {
90
- "build": "tsup && tsx scripts/obfuscate.ts",
95
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsup && tsx scripts/obfuscate.ts",
91
96
  "release": "node scripts/publish.mjs",
92
97
  "ship": "changeset && changeset version && node scripts/publish.mjs",
93
98
  "dev": "echo \"No build step. Consumers transpile TS sources.\"",
@@ -1,124 +0,0 @@
1
- import type { ScaffoldOptions } from "../../../types";
2
-
3
- /** Keep in sync with scripts/prebuild.ts HARDCODED_BRANDING until DB branding lands. */
4
- const HARDCODED_BRANDING = {
5
- backgroundBase: "#F5F5F5",
6
- primary: "#3264FF",
7
- accent: "#9664FF",
8
- fontFamily: "Inter",
9
- };
10
-
11
- const FONT_ON_LIGHT = "#0a0a0a";
12
- const FONT_ON_DARK = "#f5f5f5";
13
- /** Channel delta toward white (elevated) / black (recessed). */
14
- const BACKGROUND_SURFACE_DELTA = 15;
15
-
16
- function parseHexRgb(
17
- background: string,
18
- ): { r: number; g: number; b: number } | null {
19
- const hex = background.trim().replace(/^#/, "");
20
- const full =
21
- hex.length === 3
22
- ? hex
23
- .split("")
24
- .map((c) => c + c)
25
- .join("")
26
- : hex;
27
- if (full.length !== 6 || Number.isNaN(Number.parseInt(full, 16))) {
28
- return null;
29
- }
30
- return {
31
- r: Number.parseInt(full.slice(0, 2), 16),
32
- g: Number.parseInt(full.slice(2, 4), 16),
33
- b: Number.parseInt(full.slice(4, 6), 16),
34
- };
35
- }
36
-
37
- function toHex({ r, g, b }: { r: number; g: number; b: number }): string {
38
- return `#${[r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("")}`;
39
- }
40
-
41
- function deriveFontColor(background: string): string {
42
- const rgb = parseHexRgb(background);
43
- if (!rgb) return FONT_ON_LIGHT;
44
- const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
45
- return luminance > 0.5 ? FONT_ON_LIGHT : FONT_ON_DARK;
46
- }
47
-
48
- function deriveElevatedBackground(background: string): string {
49
- const rgb = parseHexRgb(background);
50
- if (!rgb) return background;
51
- return toHex({
52
- r: Math.min(255, rgb.r + BACKGROUND_SURFACE_DELTA),
53
- g: Math.min(255, rgb.g + BACKGROUND_SURFACE_DELTA),
54
- b: Math.min(255, rgb.b + BACKGROUND_SURFACE_DELTA),
55
- });
56
- }
57
-
58
- function deriveRecessedBackground(background: string): string {
59
- const rgb = parseHexRgb(background);
60
- if (!rgb) return background;
61
- return toHex({
62
- r: Math.max(0, rgb.r - BACKGROUND_SURFACE_DELTA),
63
- g: Math.max(0, rgb.g - BACKGROUND_SURFACE_DELTA),
64
- b: Math.max(0, rgb.b - BACKGROUND_SURFACE_DELTA),
65
- });
66
- }
67
-
68
- export default function generateGlobalsCss(_opts: ScaffoldOptions): string {
69
- const branding = HARDCODED_BRANDING;
70
- const fontFamilyParam = branding.fontFamily.replace(/ /g, "+");
71
- const foreground = deriveFontColor(branding.backgroundBase);
72
- const primaryForeground = deriveFontColor(branding.primary);
73
- const accentForeground = deriveFontColor(branding.accent);
74
- const backgroundElevated = deriveElevatedBackground(branding.backgroundBase);
75
- const backgroundRecessed = deriveRecessedBackground(branding.backgroundBase);
76
-
77
- return `@import url("https://fonts.googleapis.com/css2?family=${fontFamilyParam}:wght@400;700&display=swap");
78
- @import "tailwindcss";
79
- /* Tailwind v4 skips node_modules; @zorgo/next ships un-obfuscated components so
80
- their class names are scannable here. */
81
- @source "../node_modules/@zorgo/next/dist/components.mjs";
82
- @source "../node_modules/@zorgo/next/dist/item-page.mjs";
83
-
84
- :root {
85
- --background: ${branding.backgroundBase};
86
- --background-elevated: ${backgroundElevated};
87
- --background-recessed: ${backgroundRecessed};
88
- --foreground: ${foreground};
89
- --primary: ${branding.primary};
90
- --primary-foreground: ${primaryForeground};
91
- --accent: ${branding.accent};
92
- --accent-foreground: ${accentForeground};
93
- --font-sans: "${branding.fontFamily}", sans-serif;
94
- }
95
-
96
- @theme inline {
97
- --color-background: var(--background);
98
- --color-background-elevated: var(--background-elevated);
99
- --color-background-recessed: var(--background-recessed);
100
- --color-foreground: var(--foreground);
101
- --color-primary: var(--primary);
102
- --color-primary-foreground: var(--primary-foreground);
103
- --color-accent: var(--accent);
104
- --color-accent-foreground: var(--accent-foreground);
105
- --font-sans: var(--font-sans);
106
- }
107
-
108
- @layer base {
109
- button,
110
- [type="button"],
111
- [type="submit"],
112
- [type="reset"] {
113
- cursor: pointer;
114
- }
115
-
116
- button:disabled,
117
- [type="button"]:disabled,
118
- [type="submit"]:disabled,
119
- [type="reset"]:disabled {
120
- cursor: not-allowed;
121
- }
122
- }
123
- `;
124
- }