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.
- package/dist/agentDocs-Z5BI2Y2G.js +38 -0
- package/dist/agentDocs-Z5BI2Y2G.js.map +1 -0
- package/dist/chunk-4MPOTHTY.js +9 -0
- package/dist/chunk-4MPOTHTY.js.map +1 -0
- package/dist/chunk-4VLN5EX2.js +9204 -0
- package/dist/chunk-4VLN5EX2.js.map +1 -0
- package/dist/chunk-6PHFHGTZ.js +35 -0
- package/dist/chunk-6PHFHGTZ.js.map +1 -0
- package/dist/chunk-7CFFE2I6.js +55 -0
- package/dist/chunk-7CFFE2I6.js.map +1 -0
- package/dist/chunk-B47VXAHT.js +28 -0
- package/dist/chunk-B47VXAHT.js.map +1 -0
- package/dist/chunk-BRTXBBVQ.js +46 -0
- package/dist/chunk-BRTXBBVQ.js.map +1 -0
- package/dist/chunk-C62C776U.js +79 -0
- package/dist/chunk-C62C776U.js.map +1 -0
- package/dist/chunk-I7KNSICQ.js +114 -0
- package/dist/chunk-I7KNSICQ.js.map +1 -0
- package/dist/chunk-Q73JSGXV.js +123 -0
- package/dist/chunk-Q73JSGXV.js.map +1 -0
- package/dist/chunk-W6QJTGBC.js +57 -0
- package/dist/chunk-W6QJTGBC.js.map +1 -0
- package/dist/cli/index.js +196 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/components/public/index.d.mts +40 -0
- package/dist/components/public/index.js +401 -0
- package/dist/components/public/index.js.map +1 -0
- package/dist/config.d.mts +4 -0
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -0
- package/dist/defineConfig.d.mts +126 -0
- package/dist/defineConfig.js +8 -0
- package/dist/defineConfig.js.map +1 -0
- package/dist/dev-QY534GEH.js +87 -0
- package/dist/dev-QY534GEH.js.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/init-UGUTJFFI.js +145 -0
- package/dist/init-UGUTJFFI.js.map +1 -0
- package/dist/jiti-VYEW7A6R.js +3068 -0
- package/dist/jiti-VYEW7A6R.js.map +1 -0
- package/dist/localReader-I2THES24.js +40 -0
- package/dist/localReader-I2THES24.js.map +1 -0
- package/dist/query.d.mts +112 -0
- package/dist/query.js +11 -0
- package/dist/query.js.map +1 -0
- package/dist/types.d.mts +352 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/typesGen-WBC6CNBG.js +241 -0
- package/dist/typesGen-WBC6CNBG.js.map +1 -0
- package/dist/update-RMGZMS56.js +57 -0
- package/dist/update-RMGZMS56.js.map +1 -0
- package/dist/validate-OTJ6ULMP.js +297 -0
- package/dist/validate-OTJ6ULMP.js.map +1 -0
- package/dist/withOctoCMS.d.mts +6 -0
- package/dist/withOctoCMS.js +9 -0
- package/dist/withOctoCMS.js.map +1 -0
- package/docs/index.md +27 -0
- package/docs/overview.md +113 -0
- package/docs/schema.md +279 -0
- package/globals.css +198 -0
- 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
|