hd-garden 0.3.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 (91) hide show
  1. package/.agents/skills/garden-interactive-chart/SKILL.md +84 -0
  2. package/.agents/skills/garden-interactive-chart/references/shell.html +49 -0
  3. package/.agents/skills/garden-interactive-chart/references/style.md +61 -0
  4. package/.env.example +56 -0
  5. package/.openai/hosting.json +5 -0
  6. package/LICENSE +21 -0
  7. package/Makefile +48 -0
  8. package/README.md +72 -0
  9. package/app/about/page.tsx +60 -0
  10. package/app/admin/page.tsx +117 -0
  11. package/app/api/admin/distribution/route.ts +120 -0
  12. package/app/api/admin/posts/pin/route.ts +32 -0
  13. package/app/api/admin/thoughts/[id]/route.ts +44 -0
  14. package/app/api/admin/thoughts/route.ts +120 -0
  15. package/app/api/admin/uploads/route.ts +20 -0
  16. package/app/api/auth/github/callback/route.ts +136 -0
  17. package/app/api/auth/github/route.ts +29 -0
  18. package/app/api/auth/logout/route.ts +13 -0
  19. package/app/blog/[...path]/page.tsx +135 -0
  20. package/app/blog/page.tsx +139 -0
  21. package/app/chatgpt-auth.ts +86 -0
  22. package/app/columns/[column]/[...path]/page.tsx +160 -0
  23. package/app/columns/page.tsx +68 -0
  24. package/app/components/AdminDashboard.tsx +410 -0
  25. package/app/components/Comments.tsx +55 -0
  26. package/app/components/ContentEmptyState.tsx +51 -0
  27. package/app/components/ExternalEmbed.tsx +127 -0
  28. package/app/components/GardenAdminGuide.tsx +116 -0
  29. package/app/components/GardenContentGuide.tsx +241 -0
  30. package/app/components/GardenMediaGuide.tsx +81 -0
  31. package/app/components/GiscusComments.tsx +71 -0
  32. package/app/components/GithubLiveOverview.tsx +118 -0
  33. package/app/components/GithubProfile.tsx +326 -0
  34. package/app/components/MarkdownArticle.tsx +144 -0
  35. package/app/components/MermaidDiagram.tsx +117 -0
  36. package/app/components/PageIntro.tsx +32 -0
  37. package/app/components/SiteChrome.tsx +18 -0
  38. package/app/components/SiteFooter.tsx +29 -0
  39. package/app/components/SiteHeader.tsx +420 -0
  40. package/app/components/TableOfContents.tsx +433 -0
  41. package/app/components/ThoughtCard.tsx +242 -0
  42. package/app/components/Webmentions.tsx +194 -0
  43. package/app/components/WidgetChart.tsx +222 -0
  44. package/app/garden/page.tsx +284 -0
  45. package/app/garden-modules.d.ts +62 -0
  46. package/app/globals.css +4454 -0
  47. package/app/layout.tsx +102 -0
  48. package/app/lib/admin-auth.ts +134 -0
  49. package/app/lib/content.ts +112 -0
  50. package/app/lib/fallback-site-config.ts +56 -0
  51. package/app/lib/generated-content.ts +380 -0
  52. package/app/lib/github-content.ts +244 -0
  53. package/app/lib/markdown-plugins.ts +13 -0
  54. package/app/lib/oss.ts +100 -0
  55. package/app/lib/public-thoughts.ts +9 -0
  56. package/app/lib/repository-path.mjs +32 -0
  57. package/app/lib/site.ts +51 -0
  58. package/app/page.tsx +119 -0
  59. package/app/robots.ts +15 -0
  60. package/app/rss.xml/route.ts +84 -0
  61. package/app/site.config.ts +35 -0
  62. package/app/sitemap.ts +46 -0
  63. package/app/thoughts/[slug]/page.tsx +79 -0
  64. package/app/thoughts/page.tsx +33 -0
  65. package/app/yaml.d.ts +4 -0
  66. package/bin/garden.mjs +348 -0
  67. package/build/sites-vite-plugin.ts +39 -0
  68. package/eslint.config.mjs +20 -0
  69. package/next.config.ts +13 -0
  70. package/package.json +112 -0
  71. package/postcss.config.mjs +7 -0
  72. package/public/favicon.svg +4 -0
  73. package/public/images/garden/admin-studio.png +0 -0
  74. package/public/og.png +0 -0
  75. package/scripts/build-content.mjs +582 -0
  76. package/scripts/fetch-embed.mjs +189 -0
  77. package/scripts/garden-paths.mjs +21 -0
  78. package/scripts/new-post.mjs +73 -0
  79. package/scripts/prepare-pages-output.mjs +182 -0
  80. package/scripts/send-webmentions.mjs +27 -0
  81. package/site.config.yaml +48 -0
  82. package/templates/site/Makefile +46 -0
  83. package/templates/site/README.md +13 -0
  84. package/templates/site/content/columns.yaml +1 -0
  85. package/templates/site/content/posts/.gitkeep +0 -0
  86. package/templates/site/content/thoughts/.gitkeep +0 -0
  87. package/templates/site/package.json +19 -0
  88. package/templates/site/site.config.yaml +46 -0
  89. package/tsconfig.json +34 -0
  90. package/vite.config.ts +130 -0
  91. package/worker/index.ts +53 -0
package/tsconfig.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./*"]
23
+ }
24
+ },
25
+ "include": [
26
+ "next-env.d.ts",
27
+ "**/*.ts",
28
+ "**/*.tsx",
29
+ ".next/types/**/*.ts",
30
+ ".next/dev/types/**/*.ts",
31
+ "**/*.mts"
32
+ ],
33
+ "exclude": ["node_modules"]
34
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,130 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import vinext from "vinext";
5
+ import { defineConfig } from "vite";
6
+ import hostingConfig from "./.openai/hosting.json";
7
+ import { sites } from "./build/sites-vite-plugin";
8
+ import { gardenPackageRoot, gardenSiteRoot } from "./scripts/garden-paths.mjs";
9
+
10
+ const SITE_CREATOR_PLACEHOLDER_DATABASE_ID =
11
+ "00000000-0000-4000-8000-000000000000";
12
+
13
+ const { d1, r2 } = hostingConfig;
14
+ const siteRoot = gardenSiteRoot();
15
+ const generatedContent = path.join(siteRoot, ".garden", "generated-content.ts");
16
+ const generatedSiteConfig = path.join(siteRoot, ".garden", "site-config.ts");
17
+ const generatedAliases = {
18
+ "@garden/generated-content": generatedContent,
19
+ "@garden/site-config": generatedSiteConfig,
20
+ };
21
+
22
+ // macOS Seatbelt blocks FSEvents, so Codex previews need polling for HMR.
23
+ const isCodexSeatbeltSandbox = process.env.CODEX_SANDBOX === "seatbelt";
24
+
25
+ const localBindingConfig = {
26
+ main: "./worker/index.ts",
27
+ compatibility_flags: ["nodejs_compat"],
28
+ d1_databases: d1
29
+ ? [
30
+ {
31
+ binding: d1,
32
+ database_name: "site-creator-d1",
33
+ database_id: SITE_CREATOR_PLACEHOLDER_DATABASE_ID,
34
+ },
35
+ ]
36
+ : [],
37
+ r2_buckets: r2
38
+ ? [
39
+ {
40
+ binding: r2,
41
+ bucket_name: "site-creator-r2",
42
+ },
43
+ ]
44
+ : [],
45
+ };
46
+
47
+ function gardenContent() {
48
+ const sync = () => {
49
+ spawnSync(process.execPath, [path.join(gardenPackageRoot, "scripts", "build-content.mjs")], {
50
+ cwd: siteRoot,
51
+ env: { ...process.env, GARDEN_SITE: siteRoot },
52
+ stdio: "inherit",
53
+ });
54
+ };
55
+
56
+ return {
57
+ name: "garden-content",
58
+ enforce: "post" as const,
59
+ config() {
60
+ return {
61
+ resolve: {
62
+ alias: generatedAliases,
63
+ },
64
+ };
65
+ },
66
+ resolveId(id: string) {
67
+ const normalized = id.split("?")[0];
68
+ if (normalized === "@garden/generated-content") return generatedContent;
69
+ if (normalized === "@garden/site-config") return generatedSiteConfig;
70
+ return undefined;
71
+ },
72
+ buildStart() {
73
+ if (!fs.existsSync(generatedContent) || !fs.existsSync(generatedSiteConfig)) {
74
+ sync();
75
+ }
76
+ },
77
+ configureServer(server) {
78
+ const contentDir = path.join(siteRoot, "content");
79
+ const configFile = path.join(siteRoot, "site.config.yaml");
80
+ server.watcher.add([contentDir, configFile]);
81
+ server.watcher.on("change", (file) => {
82
+ if (file.startsWith(contentDir) || file === configFile) {
83
+ sync();
84
+ server.ws.send({ type: "full-reload" });
85
+ }
86
+ });
87
+ },
88
+ };
89
+ }
90
+
91
+ export default defineConfig(async () => {
92
+ // Keep Wrangler and Miniflare state project-local. These are non-secret tool
93
+ // settings; application environment belongs in ignored `.env*` files.
94
+ process.env.WRANGLER_WRITE_LOGS ??= "false";
95
+ process.env.WRANGLER_LOG_PATH ??= path.join(siteRoot, ".wrangler", "wrangler.log");
96
+ process.env.MINIFLARE_REGISTRY_PATH ??= path.join(siteRoot, ".wrangler", "registry");
97
+
98
+ // Wrangler snapshots its log path while the Cloudflare plugin is imported.
99
+ const { cloudflare } = await import("@cloudflare/vite-plugin");
100
+
101
+ return {
102
+ envDir: siteRoot,
103
+ resolve: {
104
+ alias: generatedAliases,
105
+ modules: [
106
+ path.join(siteRoot, "node_modules"),
107
+ path.join(gardenPackageRoot, "node_modules"),
108
+ "node_modules",
109
+ ],
110
+ },
111
+ build: {
112
+ // Mermaid remains a route-level dynamic import. Its intentionally lazy
113
+ // diagram engine is larger than the default warning threshold, but it is
114
+ // never part of the site's initial navigation bundle.
115
+ chunkSizeWarningLimit: 700,
116
+ },
117
+ server: isCodexSeatbeltSandbox
118
+ ? { watch: { useFsEvents: false, usePolling: true } }
119
+ : undefined,
120
+ plugins: [
121
+ gardenContent(),
122
+ vinext(),
123
+ sites(),
124
+ cloudflare({
125
+ viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] },
126
+ config: localBindingConfig,
127
+ }),
128
+ ],
129
+ };
130
+ });
@@ -0,0 +1,53 @@
1
+ /** Cloudflare Worker entry point for the vinext-starter template. */
2
+ import { handleImageOptimization, DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES } from "vinext/server/image-optimization";
3
+ import handler from "vinext/server/app-router-entry";
4
+
5
+ interface Env {
6
+ ASSETS: Fetcher;
7
+ IMAGES: {
8
+ input(stream: ReadableStream): {
9
+ transform(options: Record<string, unknown>): {
10
+ output(options: { format: string; quality: number }): Promise<{ response(): Response }>;
11
+ };
12
+ };
13
+ };
14
+ }
15
+
16
+ declare global {
17
+ // Vinext route modules read the current worker bindings through this bridge.
18
+ // The binding is fixed for the worker instance and is never exposed to clients.
19
+ var __hedonBindings: Env | undefined;
20
+ }
21
+
22
+ interface ExecutionContext {
23
+ waitUntil(promise: Promise<unknown>): void;
24
+ passThroughOnException(): void;
25
+ }
26
+
27
+ // Image security config. SVG sources with .svg extension auto-skip the
28
+ // optimization endpoint on the client side (served directly, no proxy).
29
+ // To route SVGs through the optimizer (with security headers), set
30
+ // dangerouslyAllowSVG: true in next.config.js and uncomment below:
31
+ // const imageConfig: ImageConfig = { dangerouslyAllowSVG: true };
32
+
33
+ const worker = {
34
+ async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
35
+ globalThis.__hedonBindings = env;
36
+ const url = new URL(request.url);
37
+
38
+ if (url.pathname === "/_vinext/image") {
39
+ const allowedWidths = [...DEFAULT_DEVICE_SIZES, ...DEFAULT_IMAGE_SIZES];
40
+ return handleImageOptimization(request, {
41
+ fetchAsset: (path) => env.ASSETS.fetch(new Request(new URL(path, request.url))),
42
+ transformImage: async (body, { width, format, quality }) => {
43
+ const result = await env.IMAGES.input(body).transform(width > 0 ? { width } : {}).output({ format, quality });
44
+ return result.response();
45
+ },
46
+ }, allowedWidths);
47
+ }
48
+
49
+ return handler.fetch(request, env, ctx);
50
+ },
51
+ };
52
+
53
+ export default worker;