octocms 0.1.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 (64) hide show
  1. package/dist/agentDocs-Z5BI2Y2G.js +38 -0
  2. package/dist/agentDocs-Z5BI2Y2G.js.map +1 -0
  3. package/dist/chunk-4MPOTHTY.js +9 -0
  4. package/dist/chunk-4MPOTHTY.js.map +1 -0
  5. package/dist/chunk-4VLN5EX2.js +9204 -0
  6. package/dist/chunk-4VLN5EX2.js.map +1 -0
  7. package/dist/chunk-6PHFHGTZ.js +35 -0
  8. package/dist/chunk-6PHFHGTZ.js.map +1 -0
  9. package/dist/chunk-7CFFE2I6.js +55 -0
  10. package/dist/chunk-7CFFE2I6.js.map +1 -0
  11. package/dist/chunk-B47VXAHT.js +28 -0
  12. package/dist/chunk-B47VXAHT.js.map +1 -0
  13. package/dist/chunk-BRTXBBVQ.js +46 -0
  14. package/dist/chunk-BRTXBBVQ.js.map +1 -0
  15. package/dist/chunk-C62C776U.js +79 -0
  16. package/dist/chunk-C62C776U.js.map +1 -0
  17. package/dist/chunk-I7KNSICQ.js +114 -0
  18. package/dist/chunk-I7KNSICQ.js.map +1 -0
  19. package/dist/chunk-Q73JSGXV.js +123 -0
  20. package/dist/chunk-Q73JSGXV.js.map +1 -0
  21. package/dist/chunk-W6QJTGBC.js +57 -0
  22. package/dist/chunk-W6QJTGBC.js.map +1 -0
  23. package/dist/cli/index.js +196 -0
  24. package/dist/cli/index.js.map +1 -0
  25. package/dist/components/public/index.d.mts +40 -0
  26. package/dist/components/public/index.js +401 -0
  27. package/dist/components/public/index.js.map +1 -0
  28. package/dist/config.d.mts +4 -0
  29. package/dist/config.js +13 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/defineConfig.d.mts +126 -0
  32. package/dist/defineConfig.js +8 -0
  33. package/dist/defineConfig.js.map +1 -0
  34. package/dist/dev-QY534GEH.js +87 -0
  35. package/dist/dev-QY534GEH.js.map +1 -0
  36. package/dist/index.d.mts +5 -0
  37. package/dist/index.js +17 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/init-UGUTJFFI.js +145 -0
  40. package/dist/init-UGUTJFFI.js.map +1 -0
  41. package/dist/jiti-VYEW7A6R.js +3068 -0
  42. package/dist/jiti-VYEW7A6R.js.map +1 -0
  43. package/dist/localReader-I2THES24.js +40 -0
  44. package/dist/localReader-I2THES24.js.map +1 -0
  45. package/dist/query.d.mts +112 -0
  46. package/dist/query.js +11 -0
  47. package/dist/query.js.map +1 -0
  48. package/dist/types.d.mts +352 -0
  49. package/dist/types.js +1 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/typesGen-WBC6CNBG.js +241 -0
  52. package/dist/typesGen-WBC6CNBG.js.map +1 -0
  53. package/dist/update-RMGZMS56.js +57 -0
  54. package/dist/update-RMGZMS56.js.map +1 -0
  55. package/dist/validate-OTJ6ULMP.js +297 -0
  56. package/dist/validate-OTJ6ULMP.js.map +1 -0
  57. package/dist/withOctoCMS.d.mts +6 -0
  58. package/dist/withOctoCMS.js +9 -0
  59. package/dist/withOctoCMS.js.map +1 -0
  60. package/docs/index.md +27 -0
  61. package/docs/overview.md +113 -0
  62. package/docs/schema.md +279 -0
  63. package/globals.css +198 -0
  64. package/package.json +116 -0
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ resolveProjectRoot
4
+ } from "../chunk-C62C776U.js";
5
+ import {
6
+ fmt,
7
+ log
8
+ } from "../chunk-6PHFHGTZ.js";
9
+ import "../chunk-W6QJTGBC.js";
10
+
11
+ // cli/index.ts
12
+ var VERSION = "0.0.0";
13
+ var HELP = `
14
+ ${fmt.bold("OctoCMS CLI")} v${VERSION}
15
+
16
+ Usage: octocms <command> [options]
17
+
18
+ ${fmt.bold("Commands:")}
19
+ init Initialize OctoCMS in a Next.js project
20
+ dev Start development server with config watching
21
+ types:gen Generate TypeScript types from next.config.ts
22
+ validate Validate all content entries against the schema
23
+ update Regenerate admin route files
24
+ agent-docs Inject AI agent doc links into AGENTS.md
25
+
26
+ ${fmt.bold("Options:")}
27
+ --help Show this help message
28
+ --version Show version number
29
+
30
+ Run ${fmt.cyan("octocms <command> --help")} for command-specific help.
31
+ `;
32
+ var COMMAND_HELP = {
33
+ init: `
34
+ ${fmt.bold("octocms init")} \u2014 Initialize OctoCMS in a Next.js project
35
+
36
+ Creates next.config.ts, admin route files, demo content, and updates
37
+ next.config.ts and tsconfig.json with required configuration.
38
+
39
+ ${fmt.bold("Usage:")} octocms init [options]
40
+
41
+ ${fmt.bold("Options:")}
42
+ --yes, -y Accept all defaults (non-interactive)
43
+ --help Show this help message
44
+ `,
45
+ dev: `
46
+ ${fmt.bold("octocms dev")} \u2014 Start development server with config watching
47
+
48
+ Spawns Next.js dev server and watches next.config.ts. When the config
49
+ changes, types are automatically regenerated.
50
+
51
+ ${fmt.bold("Usage:")} octocms dev [options]
52
+
53
+ ${fmt.bold("Options:")}
54
+ --port <n> Port number (default: 3001)
55
+ --help Show this help message
56
+ `,
57
+ "types:gen": `
58
+ ${fmt.bold("octocms types:gen")} \u2014 Generate TypeScript types from next.config.ts
59
+
60
+ Produces cms/__generated__/types.ts, enums.ts, content.d.ts, and index.ts.
61
+ Validates the config before generating.
62
+
63
+ ${fmt.bold("Usage:")} octocms types:gen
64
+
65
+ ${fmt.bold("Options:")}
66
+ --help Show this help message
67
+ `,
68
+ validate: `
69
+ ${fmt.bold("octocms validate")} \u2014 Validate all content entries against the schema
70
+
71
+ Reads every JSON file in cms/content/, validates structure, field types,
72
+ required fields, select option values, reference targets, and companion files.
73
+
74
+ ${fmt.bold("Usage:")} octocms validate
75
+
76
+ ${fmt.bold("Options:")}
77
+ --help Show this help message
78
+ `,
79
+ "agent-docs": `
80
+ ${fmt.bold("octocms agent-docs")} \u2014 Inject AI agent doc links into AGENTS.md
81
+
82
+ Creates AGENTS.md if it doesn't exist, or appends the OctoCMS section
83
+ if the file exists but doesn't reference the agent docs yet.
84
+
85
+ ${fmt.bold("Usage:")} octocms agent-docs
86
+
87
+ ${fmt.bold("Options:")}
88
+ --help Show this help message
89
+ `,
90
+ update: `
91
+ ${fmt.bold("octocms update")} \u2014 Regenerate admin route files
92
+
93
+ Ensures src/app/cms/layout.tsx and the catch-all page re-export are
94
+ up-to-date with the latest OctoCMS version.
95
+
96
+ ${fmt.bold("Usage:")} octocms update
97
+
98
+ ${fmt.bold("Options:")}
99
+ --help Show this help message
100
+ `
101
+ };
102
+ function parseArgs(argv) {
103
+ var _a;
104
+ const args = argv.slice(2);
105
+ const flags = {};
106
+ let command = null;
107
+ for (let i = 0; i < args.length; i++) {
108
+ const arg = args[i];
109
+ if (arg === "--help" || arg === "-h") {
110
+ flags.help = true;
111
+ } else if (arg === "--version" || arg === "-v") {
112
+ flags.version = true;
113
+ } else if (arg === "--yes" || arg === "-y") {
114
+ flags.yes = true;
115
+ } else if (arg === "--port") {
116
+ flags.port = (_a = args[++i]) != null ? _a : "";
117
+ } else if (arg.startsWith("--port=")) {
118
+ flags.port = arg.slice(7);
119
+ } else if (!arg.startsWith("-") && !command) {
120
+ if (arg === "types:gen" || arg === "agent-docs") {
121
+ command = arg;
122
+ } else {
123
+ command = arg;
124
+ }
125
+ }
126
+ }
127
+ return { command, flags };
128
+ }
129
+ async function main() {
130
+ const { command, flags } = parseArgs(process.argv);
131
+ if (flags.version) {
132
+ console.log(VERSION);
133
+ return;
134
+ }
135
+ if (!command || flags.help) {
136
+ if (command && COMMAND_HELP[command]) {
137
+ console.log(COMMAND_HELP[command]);
138
+ } else {
139
+ console.log(HELP);
140
+ }
141
+ return;
142
+ }
143
+ if (COMMAND_HELP[command] && flags.help) {
144
+ console.log(COMMAND_HELP[command]);
145
+ return;
146
+ }
147
+ try {
148
+ switch (command) {
149
+ case "init": {
150
+ const { initCommand } = await import("../init-UGUTJFFI.js");
151
+ await initCommand(process.cwd(), { yes: flags.yes === true });
152
+ break;
153
+ }
154
+ case "dev": {
155
+ const projectRoot = resolveProjectRoot();
156
+ const { devCommand } = await import("../dev-QY534GEH.js");
157
+ const port = flags.port ? Number(flags.port) : void 0;
158
+ await devCommand(projectRoot, { port });
159
+ break;
160
+ }
161
+ case "types:gen": {
162
+ const projectRoot = resolveProjectRoot();
163
+ const { typesGenCommand } = await import("../typesGen-WBC6CNBG.js");
164
+ await typesGenCommand(projectRoot);
165
+ break;
166
+ }
167
+ case "validate": {
168
+ const projectRoot = resolveProjectRoot();
169
+ const { validateCommand } = await import("../validate-OTJ6ULMP.js");
170
+ await validateCommand(projectRoot);
171
+ break;
172
+ }
173
+ case "update": {
174
+ const projectRoot = resolveProjectRoot();
175
+ const { updateCommand } = await import("../update-RMGZMS56.js");
176
+ await updateCommand(projectRoot);
177
+ break;
178
+ }
179
+ case "agent-docs": {
180
+ const projectRoot = resolveProjectRoot();
181
+ const { agentDocsCommand } = await import("../agentDocs-Z5BI2Y2G.js");
182
+ await agentDocsCommand(projectRoot);
183
+ break;
184
+ }
185
+ default:
186
+ log.error(`Unknown command: ${command}`);
187
+ console.log(HELP);
188
+ process.exitCode = 1;
189
+ }
190
+ } catch (e) {
191
+ log.error(e.message);
192
+ process.exitCode = 1;
193
+ }
194
+ }
195
+ main();
196
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n/* eslint-disable no-console */\n\n/**\n * OctoCMS CLI — command-line tools for managing an OctoCMS project.\n *\n * Commands:\n * init Initialize OctoCMS in a Next.js project\n * dev Start development server with config watching\n * types:gen Generate TypeScript types from next.config.ts\n * validate Validate all content entries against the schema\n * update Regenerate admin route files\n * agent-docs Inject AI agent doc links into AGENTS.md\n *\n * Usage:\n * octocms <command> [options]\n * octocms --help\n * octocms --version\n */\n\nimport { fmt, log } from './lib/logger';\nimport { resolveProjectRoot } from './lib/project';\n\nconst VERSION = '0.0.0';\n\nconst HELP = `\n ${fmt.bold('OctoCMS CLI')} v${VERSION}\n\n Usage: octocms <command> [options]\n\n ${fmt.bold('Commands:')}\n init Initialize OctoCMS in a Next.js project\n dev Start development server with config watching\n types:gen Generate TypeScript types from next.config.ts\n validate Validate all content entries against the schema\n update Regenerate admin route files\n agent-docs Inject AI agent doc links into AGENTS.md\n\n ${fmt.bold('Options:')}\n --help Show this help message\n --version Show version number\n\n Run ${fmt.cyan('octocms <command> --help')} for command-specific help.\n`;\n\nconst COMMAND_HELP: Record<string, string> = {\n init: `\n ${fmt.bold('octocms init')} — Initialize OctoCMS in a Next.js project\n\n Creates next.config.ts, admin route files, demo content, and updates\n next.config.ts and tsconfig.json with required configuration.\n\n ${fmt.bold('Usage:')} octocms init [options]\n\n ${fmt.bold('Options:')}\n --yes, -y Accept all defaults (non-interactive)\n --help Show this help message\n`,\n dev: `\n ${fmt.bold('octocms dev')} — Start development server with config watching\n\n Spawns Next.js dev server and watches next.config.ts. When the config\n changes, types are automatically regenerated.\n\n ${fmt.bold('Usage:')} octocms dev [options]\n\n ${fmt.bold('Options:')}\n --port <n> Port number (default: 3001)\n --help Show this help message\n`,\n 'types:gen': `\n ${fmt.bold('octocms types:gen')} — Generate TypeScript types from next.config.ts\n\n Produces cms/__generated__/types.ts, enums.ts, content.d.ts, and index.ts.\n Validates the config before generating.\n\n ${fmt.bold('Usage:')} octocms types:gen\n\n ${fmt.bold('Options:')}\n --help Show this help message\n`,\n validate: `\n ${fmt.bold('octocms validate')} — Validate all content entries against the schema\n\n Reads every JSON file in cms/content/, validates structure, field types,\n required fields, select option values, reference targets, and companion files.\n\n ${fmt.bold('Usage:')} octocms validate\n\n ${fmt.bold('Options:')}\n --help Show this help message\n`,\n 'agent-docs': `\n ${fmt.bold('octocms agent-docs')} — Inject AI agent doc links into AGENTS.md\n\n Creates AGENTS.md if it doesn't exist, or appends the OctoCMS section\n if the file exists but doesn't reference the agent docs yet.\n\n ${fmt.bold('Usage:')} octocms agent-docs\n\n ${fmt.bold('Options:')}\n --help Show this help message\n`,\n update: `\n ${fmt.bold('octocms update')} — Regenerate admin route files\n\n Ensures src/app/cms/layout.tsx and the catch-all page re-export are\n up-to-date with the latest OctoCMS version.\n\n ${fmt.bold('Usage:')} octocms update\n\n ${fmt.bold('Options:')}\n --help Show this help message\n`,\n};\n\nfunction parseArgs(argv: string[]): { command: string | null; flags: Record<string, string | boolean> } {\n const args = argv.slice(2); // skip node + script path\n const flags: Record<string, string | boolean> = {};\n let command: string | null = null;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n if (arg === '--help' || arg === '-h') {\n flags.help = true;\n } else if (arg === '--version' || arg === '-v') {\n flags.version = true;\n } else if (arg === '--yes' || arg === '-y') {\n flags.yes = true;\n } else if (arg === '--port') {\n flags.port = args[++i] ?? '';\n } else if (arg.startsWith('--port=')) {\n flags.port = arg.slice(7);\n } else if (!arg.startsWith('-') && !command) {\n // Handle compound commands as single tokens\n if (arg === 'types:gen' || arg === 'agent-docs') {\n command = arg;\n } else {\n command = arg;\n }\n }\n }\n\n return { command, flags };\n}\n\nasync function main(): Promise<void> {\n const { command, flags } = parseArgs(process.argv);\n\n if (flags.version) {\n console.log(VERSION);\n return;\n }\n\n if (!command || flags.help) {\n if (command && COMMAND_HELP[command]) {\n console.log(COMMAND_HELP[command]);\n } else {\n console.log(HELP);\n }\n return;\n }\n\n if (COMMAND_HELP[command] && flags.help) {\n console.log(COMMAND_HELP[command]);\n return;\n }\n\n try {\n switch (command) {\n case 'init': {\n // init doesn't need an existing project root — use cwd\n const { initCommand } = await import('./commands/init');\n await initCommand(process.cwd(), { yes: flags.yes === true });\n break;\n }\n case 'dev': {\n const projectRoot = resolveProjectRoot();\n const { devCommand } = await import('./commands/dev');\n const port = flags.port ? Number(flags.port) : undefined;\n await devCommand(projectRoot, { port });\n break;\n }\n case 'types:gen': {\n const projectRoot = resolveProjectRoot();\n const { typesGenCommand } = await import('./commands/typesGen');\n await typesGenCommand(projectRoot);\n break;\n }\n case 'validate': {\n const projectRoot = resolveProjectRoot();\n const { validateCommand } = await import('./commands/validate');\n await validateCommand(projectRoot);\n break;\n }\n case 'update': {\n const projectRoot = resolveProjectRoot();\n const { updateCommand } = await import('./commands/update');\n await updateCommand(projectRoot);\n break;\n }\n case 'agent-docs': {\n const projectRoot = resolveProjectRoot();\n const { agentDocsCommand } = await import('./commands/agentDocs');\n await agentDocsCommand(projectRoot);\n break;\n }\n default:\n log.error(`Unknown command: ${command}`);\n console.log(HELP);\n process.exitCode = 1;\n }\n } catch (e) {\n log.error((e as Error).message);\n process.exitCode = 1;\n }\n}\n\nmain();\n"],"mappings":"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","names":[]}
@@ -0,0 +1,40 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+ import { RichTextDocument } from '../../types.mjs';
4
+
5
+ type MarkdownContentProps = {
6
+ children: string | undefined | null;
7
+ className?: string;
8
+ };
9
+ /**
10
+ * Renders raw Markdown as React elements using react-markdown with GFM support
11
+ * and HTML sanitization. Replaces the previous `dangerouslySetInnerHTML` approach.
12
+ */
13
+ declare const MarkdownContent: ({ children, className }: MarkdownContentProps) => react_jsx_runtime.JSX.Element | null;
14
+
15
+ type RichTextContentProps = {
16
+ document: RichTextDocument | null | undefined;
17
+ /** Map custom component names to React components for rendering `{ type: 'component' }` nodes. */
18
+ components?: Record<string, React.ComponentType<any>>;
19
+ /** Variable substitutions for `{ type: 'variable' }` nodes. */
20
+ variables?: Record<string, string>;
21
+ /** Condition branch selections: map condition field names to the branch key to render. */
22
+ conditions?: Record<string, string>;
23
+ className?: string;
24
+ };
25
+ /**
26
+ * Renders a `RichTextDocument` AST to React elements.
27
+ *
28
+ * Standard markdown nodes become HTML elements.
29
+ * `CmsImage` embeds render as `next/image`.
30
+ * Custom components and variables are resolved from the provided props.
31
+ */
32
+ declare const RichTextContent: ({ document, components, variables, conditions, className }: RichTextContentProps) => react_jsx_runtime.JSX.Element | null;
33
+
34
+ interface SearchBoxProps {
35
+ placeholder?: string;
36
+ className?: string;
37
+ }
38
+ declare function SearchBox({ placeholder, className }: SearchBoxProps): react_jsx_runtime.JSX.Element;
39
+
40
+ export { MarkdownContent, RichTextContent, SearchBox, type SearchBoxProps };
@@ -0,0 +1,401 @@
1
+ import {
2
+ __spreadProps,
3
+ __spreadValues
4
+ } from "../../chunk-7CFFE2I6.js";
5
+
6
+ // components/public/MarkdownContent.tsx
7
+ import Markdown from "react-markdown";
8
+ import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
9
+ import remarkGfm from "remark-gfm";
10
+
11
+ // lib/utils.ts
12
+ import { clsx } from "clsx";
13
+ import { twMerge } from "tailwind-merge";
14
+ var cn = (...inputs) => twMerge(clsx(inputs));
15
+
16
+ // components/public/MarkdownContent.tsx
17
+ import { jsx } from "react/jsx-runtime";
18
+ var _a, _b, _c;
19
+ var sanitizeSchema = __spreadProps(__spreadValues({}, defaultSchema), {
20
+ tagNames: [
21
+ ...(_a = defaultSchema.tagNames) != null ? _a : [],
22
+ "u"
23
+ // underline — not in GitHub default
24
+ ],
25
+ attributes: __spreadProps(__spreadValues({}, defaultSchema.attributes), {
26
+ code: [...(_c = (_b = defaultSchema.attributes) == null ? void 0 : _b.code) != null ? _c : [], "className"],
27
+ img: ["src", "alt", "title"]
28
+ })
29
+ });
30
+ var MarkdownContent = ({ children, className }) => {
31
+ if (!children) {
32
+ return null;
33
+ }
34
+ return /* @__PURE__ */ jsx("div", { className: cn(className), children: /* @__PURE__ */ jsx(Markdown, { remarkPlugins: [remarkGfm], rehypePlugins: [[rehypeSanitize, sanitizeSchema]], children }) });
35
+ };
36
+ var MarkdownContent_default = MarkdownContent;
37
+
38
+ // components/public/RichTextContent.tsx
39
+ import Image from "next/image";
40
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
41
+ var RichTextContent = ({ document: document2, components, variables, conditions, className }) => {
42
+ var _a2;
43
+ if (!document2 || !((_a2 = document2.content) == null ? void 0 : _a2.length)) return null;
44
+ return /* @__PURE__ */ jsx2("div", { className: cn(className), children: document2.content.map((node, i) => /* @__PURE__ */ jsx2(RenderNode, { node, components, variables, conditions }, i)) });
45
+ };
46
+ var RichTextContent_default = RichTextContent;
47
+ function RenderNode({ node, components, variables, conditions }) {
48
+ var _a2;
49
+ switch (node.type) {
50
+ case "paragraph":
51
+ return /* @__PURE__ */ jsx2("p", { children: /* @__PURE__ */ jsx2(RenderChildren, { nodes: node.children, components, variables, conditions }) });
52
+ case "heading": {
53
+ const Tag = `h${node.level}`;
54
+ return /* @__PURE__ */ jsx2(Tag, { children: /* @__PURE__ */ jsx2(RenderChildren, { nodes: node.children, components, variables, conditions }) });
55
+ }
56
+ case "blockquote":
57
+ return /* @__PURE__ */ jsx2("blockquote", { children: /* @__PURE__ */ jsx2(RenderChildren, { nodes: node.children, components, variables, conditions }) });
58
+ case "list":
59
+ if (node.ordered) {
60
+ return /* @__PURE__ */ jsx2("ol", { children: /* @__PURE__ */ jsx2(
61
+ RenderChildren,
62
+ {
63
+ nodes: node.children,
64
+ components,
65
+ variables,
66
+ conditions
67
+ }
68
+ ) });
69
+ }
70
+ return /* @__PURE__ */ jsx2("ul", { children: /* @__PURE__ */ jsx2(RenderChildren, { nodes: node.children, components, variables, conditions }) });
71
+ case "listItem":
72
+ return /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(RenderChildren, { nodes: node.children, components, variables, conditions }) });
73
+ case "thematicBreak":
74
+ return /* @__PURE__ */ jsx2("hr", {});
75
+ case "code":
76
+ return /* @__PURE__ */ jsx2("pre", { children: /* @__PURE__ */ jsx2("code", { className: node.lang ? `language-${node.lang}` : void 0, children: node.value }) });
77
+ case "text":
78
+ return /* @__PURE__ */ jsx2(RenderText, { node });
79
+ case "link":
80
+ return /* @__PURE__ */ jsx2("a", { href: node.url, children: /* @__PURE__ */ jsx2(RenderChildren, { nodes: node.children, components, variables, conditions }) });
81
+ case "image":
82
+ return /* @__PURE__ */ jsx2(RenderImage, { image: node.image });
83
+ case "break":
84
+ return /* @__PURE__ */ jsx2("br", {});
85
+ case "html":
86
+ return null;
87
+ case "variable": {
88
+ const value = variables == null ? void 0 : variables[node.name];
89
+ return /* @__PURE__ */ jsx2(Fragment, { children: value != null ? value : `{${node.name}}` });
90
+ }
91
+ case "component": {
92
+ const Component = components == null ? void 0 : components[node.name];
93
+ if (!Component) return null;
94
+ return /* @__PURE__ */ jsx2(Component, __spreadProps(__spreadValues({}, node.props), { children: ((_a2 = node.children) == null ? void 0 : _a2.length) ? /* @__PURE__ */ jsx2(
95
+ RenderChildren,
96
+ {
97
+ nodes: node.children,
98
+ components,
99
+ variables,
100
+ conditions
101
+ }
102
+ ) : null }));
103
+ }
104
+ case "reference":
105
+ return /* @__PURE__ */ jsx2(RenderReference, { node, components });
106
+ case "condition":
107
+ return /* @__PURE__ */ jsx2(RenderCondition, { node, components, variables, conditions });
108
+ default:
109
+ return null;
110
+ }
111
+ }
112
+ function RenderText({ node }) {
113
+ let element = node.value;
114
+ if (node.marks) {
115
+ for (const mark of node.marks) {
116
+ switch (mark) {
117
+ case "bold":
118
+ element = /* @__PURE__ */ jsx2("strong", { children: element });
119
+ break;
120
+ case "italic":
121
+ element = /* @__PURE__ */ jsx2("em", { children: element });
122
+ break;
123
+ case "underline":
124
+ element = /* @__PURE__ */ jsx2("u", { children: element });
125
+ break;
126
+ case "code":
127
+ element = /* @__PURE__ */ jsx2("code", { children: element });
128
+ break;
129
+ }
130
+ }
131
+ }
132
+ return /* @__PURE__ */ jsx2(Fragment, { children: element });
133
+ }
134
+ function RenderImage({ image }) {
135
+ if (!image.src) return null;
136
+ const hasSize = image.width != null && image.height != null;
137
+ if (hasSize) {
138
+ return /* @__PURE__ */ jsx2(
139
+ Image,
140
+ __spreadValues({
141
+ src: image.src,
142
+ alt: image.alt,
143
+ width: image.width,
144
+ height: image.height
145
+ }, image.blurDataURL ? { placeholder: "blur", blurDataURL: image.blurDataURL } : {})
146
+ );
147
+ }
148
+ return /* @__PURE__ */ jsx2("span", { className: "relative block w-full", style: { aspectRatio: "16/9" }, children: /* @__PURE__ */ jsx2(
149
+ Image,
150
+ __spreadValues({
151
+ src: image.src,
152
+ alt: image.alt,
153
+ fill: true,
154
+ className: "object-cover"
155
+ }, image.blurDataURL ? { placeholder: "blur", blurDataURL: image.blurDataURL } : {})
156
+ ) });
157
+ }
158
+ function RenderReference({
159
+ node,
160
+ components
161
+ }) {
162
+ var _a2, _b2;
163
+ const entry = node.entry;
164
+ if (!entry) return null;
165
+ const collectionType = (_a2 = entry.sys) == null ? void 0 : _a2.type;
166
+ if (collectionType && (components == null ? void 0 : components[collectionType])) {
167
+ const Component = components[collectionType];
168
+ return node.display === "inline" ? /* @__PURE__ */ jsx2(Component, { entry, display: node.display }) : /* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2(Component, { entry, display: node.display }) });
169
+ }
170
+ const title = entry.fields ? Object.values(entry.fields).find((v) => typeof v === "string" && v.length > 0) : null;
171
+ if (node.display === "inline") {
172
+ return /* @__PURE__ */ jsx2("span", { children: (_b2 = title != null ? title : collectionType) != null ? _b2 : "Reference" });
173
+ }
174
+ return /* @__PURE__ */ jsxs("div", { className: "rounded border border-border p-3 my-2 bg-muted/30", children: [
175
+ collectionType && /* @__PURE__ */ jsx2("span", { className: "text-xs text-muted-foreground block mb-0.5", children: collectionType }),
176
+ /* @__PURE__ */ jsx2("span", { className: "text-sm font-medium", children: title != null ? title : "Untitled entry" })
177
+ ] });
178
+ }
179
+ function RenderCondition({
180
+ node,
181
+ components,
182
+ variables,
183
+ conditions
184
+ }) {
185
+ var _a2;
186
+ const branches = node.branches;
187
+ if (!branches || typeof branches !== "object") return null;
188
+ const selectedKey = node.field && conditions ? conditions[node.field] : void 0;
189
+ if (selectedKey) {
190
+ const doc = branches[selectedKey];
191
+ if (!doc || !((_a2 = doc.content) == null ? void 0 : _a2.length)) return null;
192
+ return /* @__PURE__ */ jsx2(Fragment, { children: doc.content.map((child, i) => /* @__PURE__ */ jsx2(RenderNode, { node: child, components, variables, conditions }, i)) });
193
+ }
194
+ return null;
195
+ }
196
+ function RenderChildren({
197
+ nodes,
198
+ components,
199
+ variables,
200
+ conditions
201
+ }) {
202
+ return /* @__PURE__ */ jsx2(Fragment, { children: nodes.map((child, i) => /* @__PURE__ */ jsx2(RenderNode, { node: child, components, variables, conditions }, i)) });
203
+ }
204
+
205
+ // components/public/SearchBox.tsx
206
+ import { useCallback, useEffect, useRef, useState } from "react";
207
+ import { Search, X } from "lucide-react";
208
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
209
+ function escapeRegex(s) {
210
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
211
+ }
212
+ function highlightTerms(text, terms) {
213
+ const filtered = terms.filter((t) => t.length > 0);
214
+ if (filtered.length === 0) return text;
215
+ const pattern = filtered.map((t) => escapeRegex(t) + "\\w*").join("|");
216
+ const parts = text.split(new RegExp(`(${pattern})`, "gi"));
217
+ return parts.map(
218
+ (part, i) => i % 2 === 1 ? /* @__PURE__ */ jsx3("mark", { className: "bg-yellow-200 dark:bg-yellow-800 text-inherit rounded-sm px-0.5", children: part }, i) : part
219
+ );
220
+ }
221
+ function SearchBox({ placeholder = "Search...", className = "" }) {
222
+ const inputRef = useRef(null);
223
+ const dropdownRef = useRef(null);
224
+ const [query, setQuery] = useState("");
225
+ const [results, setResults] = useState([]);
226
+ const [isOpen, setIsOpen] = useState(false);
227
+ const [isSearching, setIsSearching] = useState(false);
228
+ const [selectedIndex, setSelectedIndex] = useState(-1);
229
+ const debounceRef = useRef(void 0);
230
+ const doSearch = useCallback(async (q) => {
231
+ if (!q.trim()) {
232
+ setResults([]);
233
+ setIsOpen(false);
234
+ setSelectedIndex(-1);
235
+ return;
236
+ }
237
+ setIsSearching(true);
238
+ try {
239
+ const response = await fetch(`/api/search?q=${encodeURIComponent(q)}&limit=10`);
240
+ if (response.ok) {
241
+ const data = await response.json();
242
+ setResults(data.results || []);
243
+ setIsOpen(true);
244
+ setSelectedIndex(-1);
245
+ } else {
246
+ setResults([]);
247
+ setIsOpen(false);
248
+ }
249
+ } catch (e) {
250
+ setResults([]);
251
+ setIsOpen(false);
252
+ } finally {
253
+ setIsSearching(false);
254
+ }
255
+ }, []);
256
+ const handleChange = (e) => {
257
+ const value = e.target.value;
258
+ setQuery(value);
259
+ if (debounceRef.current) clearTimeout(debounceRef.current);
260
+ debounceRef.current = setTimeout(() => doSearch(value), 300);
261
+ };
262
+ const handleKeyDown = (e) => {
263
+ if (!isOpen || results.length === 0) {
264
+ return;
265
+ }
266
+ switch (e.key) {
267
+ case "ArrowDown":
268
+ e.preventDefault();
269
+ setSelectedIndex((prev) => prev < results.length - 1 ? prev + 1 : 0);
270
+ break;
271
+ case "ArrowUp":
272
+ e.preventDefault();
273
+ setSelectedIndex((prev) => prev > 0 ? prev - 1 : results.length - 1);
274
+ break;
275
+ case "Enter":
276
+ e.preventDefault();
277
+ if (selectedIndex >= 0 && results[selectedIndex]) {
278
+ navigateToResult(results[selectedIndex]);
279
+ }
280
+ break;
281
+ case "Escape":
282
+ e.preventDefault();
283
+ setIsOpen(false);
284
+ setSelectedIndex(-1);
285
+ break;
286
+ default:
287
+ break;
288
+ }
289
+ };
290
+ const navigateToResult = (result) => {
291
+ window.location.href = result.url;
292
+ };
293
+ useEffect(() => {
294
+ const handleClickOutside = (event) => {
295
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target) && inputRef.current && !inputRef.current.contains(event.target)) {
296
+ setIsOpen(false);
297
+ }
298
+ };
299
+ document.addEventListener("mousedown", handleClickOutside);
300
+ return () => document.removeEventListener("mousedown", handleClickOutside);
301
+ }, []);
302
+ useEffect(() => {
303
+ if (selectedIndex >= 0 && dropdownRef.current) {
304
+ const items = dropdownRef.current.querySelectorAll("[data-result-item]");
305
+ if (items[selectedIndex]) {
306
+ items[selectedIndex].scrollIntoView({ block: "nearest" });
307
+ }
308
+ }
309
+ }, [selectedIndex]);
310
+ const handleClear = () => {
311
+ var _a2;
312
+ setQuery("");
313
+ setResults([]);
314
+ setIsOpen(false);
315
+ setSelectedIndex(-1);
316
+ (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
317
+ };
318
+ return /* @__PURE__ */ jsxs2("div", { className: cn("relative w-full", className), children: [
319
+ /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
320
+ /* @__PURE__ */ jsx3(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 text-gray-400", size: 18 }),
321
+ /* @__PURE__ */ jsx3(
322
+ "input",
323
+ {
324
+ ref: inputRef,
325
+ type: "text",
326
+ value: query,
327
+ onChange: handleChange,
328
+ onKeyDown: handleKeyDown,
329
+ onFocus: () => {
330
+ if (query.trim() && results.length > 0) {
331
+ setIsOpen(true);
332
+ }
333
+ },
334
+ placeholder,
335
+ className: cn(
336
+ "w-full pl-10 pr-10 py-2 text-sm border border-gray-200 rounded-lg",
337
+ "bg-white text-gray-900 placeholder-gray-400",
338
+ "focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
339
+ "dark:bg-gray-900 dark:border-gray-700 dark:text-gray-100 dark:placeholder-gray-500"
340
+ ),
341
+ autoComplete: "off"
342
+ }
343
+ ),
344
+ query && /* @__PURE__ */ jsx3(
345
+ "button",
346
+ {
347
+ type: "button",
348
+ onClick: handleClear,
349
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors",
350
+ "aria-label": "Clear search",
351
+ children: /* @__PURE__ */ jsx3(X, { size: 18 })
352
+ }
353
+ )
354
+ ] }),
355
+ isOpen && (results.length > 0 || isSearching) && /* @__PURE__ */ jsxs2(
356
+ "div",
357
+ {
358
+ ref: dropdownRef,
359
+ className: cn(
360
+ "absolute top-full left-0 right-0 mt-2 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700",
361
+ "rounded-lg shadow-lg z-50 max-h-96 overflow-y-auto"
362
+ ),
363
+ children: [
364
+ isSearching && results.length === 0 && /* @__PURE__ */ jsx3("div", { className: "px-4 py-3 text-sm text-gray-500 dark:text-gray-400 text-center", children: "Searching..." }),
365
+ results.length > 0 && /* @__PURE__ */ jsx3("ul", { className: "divide-y divide-gray-100 dark:divide-gray-800", children: results.map((result, index) => /* @__PURE__ */ jsx3(
366
+ "li",
367
+ {
368
+ "data-result-item": true,
369
+ className: cn(
370
+ "p-0 transition-colors",
371
+ index === selectedIndex ? "bg-blue-50 dark:bg-blue-900/30" : "bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-gray-800"
372
+ ),
373
+ children: /* @__PURE__ */ jsxs2(
374
+ "button",
375
+ {
376
+ type: "button",
377
+ onClick: () => navigateToResult(result),
378
+ className: "w-full text-left px-4 py-3 flex flex-col gap-1",
379
+ children: [
380
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-2", children: [
381
+ /* @__PURE__ */ jsx3("span", { className: "font-medium text-gray-900 dark:text-gray-100 truncate text-sm", children: highlightTerms(result.title, Object.keys(result.match)) }),
382
+ /* @__PURE__ */ jsx3("span", { className: "shrink-0 text-xs text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded", children: result.typeLabel })
383
+ ] }),
384
+ Object.values(result.match).flat().includes("content") && result.snippet && /* @__PURE__ */ jsx3("p", { className: "text-xs text-gray-500 dark:text-gray-400 line-clamp-2 text-left", children: highlightTerms(result.snippet, Object.keys(result.match)) })
385
+ ]
386
+ }
387
+ )
388
+ },
389
+ result.id
390
+ )) })
391
+ ]
392
+ }
393
+ )
394
+ ] });
395
+ }
396
+ export {
397
+ MarkdownContent_default as MarkdownContent,
398
+ RichTextContent_default as RichTextContent,
399
+ SearchBox
400
+ };
401
+ //# sourceMappingURL=index.js.map