third-audience-mdx 1.0.2 → 1.0.3

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/dashboard/routes/okf-route.ts","../../../src/core/mdx-reader.ts","../../../src/core/markdown-renderer.ts","../../../src/okf/okf-bundle.ts"],"sourcesContent":["import { NextResponse, type NextRequest } from 'next/server'\nimport path from 'path'\nimport { MdxReader } from '../../core/mdx-reader.js'\nimport { generateOkfIndex, generateOkfPage } from '../../okf/okf-bundle.js'\n\nconst reader = new MdxReader({ contentDir: path.join(process.cwd(), process.env.TA_CONTENT_DIR ?? 'content') })\n\n/**\n * Handler for /okf/ and /okf/[...slug].md\n * Rewired from middleware to /api/third-audience/okf/[...path]\n */\nexport async function GET(req: NextRequest, { params }: { params: { path?: string[] } }) {\n const baseUrl = process.env.NEXT_PUBLIC_SITE_URL\n ?? `${req.nextUrl.protocol}//${req.nextUrl.host}`\n\n const allFiles = reader.readAll()\n const segments = params.path ?? []\n\n // /okf/ or /okf/index.md → manifest\n if (segments.length === 0 || (segments.length === 1 && segments[0] === 'index.md')) {\n return new NextResponse(generateOkfIndex(allFiles, baseUrl), {\n headers: { 'Content-Type': 'text/markdown; charset=utf-8' },\n })\n }\n\n // /okf/[slug].md → individual page\n const slug = segments.join('/').replace(/\\.md$/, '')\n const file = allFiles.find(f => f.slug === slug)\n if (!file) return new NextResponse('Not Found', { status: 404 })\n\n return new NextResponse(generateOkfPage(file, allFiles, baseUrl), {\n headers: { 'Content-Type': 'text/markdown; charset=utf-8' },\n })\n}\n","import fs from 'fs'\nimport path from 'path'\nimport matter from 'gray-matter'\n\nexport interface MdxFile {\n slug: string // relative path without extension, e.g. 'blog/my-post'\n filePath: string // absolute path to .mdx file\n frontmatter: Record<string, unknown>\n rawContent: string // body after frontmatter\n}\n\nexport interface MdxReaderOptions {\n contentDir: string // absolute path to content directory\n}\n\nexport class MdxReader {\n private contentDir: string\n\n constructor(options: MdxReaderOptions) {\n this.contentDir = options.contentDir\n }\n\n /** Read a single MDX file by slug. Returns null if not found. */\n read(slug: string): MdxFile | null {\n const candidates = [\n path.join(this.contentDir, `${slug}.mdx`),\n path.join(this.contentDir, `${slug}.md`),\n path.join(this.contentDir, slug, 'index.mdx'),\n path.join(this.contentDir, slug, 'index.md'),\n ]\n\n for (const filePath of candidates) {\n if (fs.existsSync(filePath)) {\n return this.parseFile(slug, filePath)\n }\n }\n\n return null\n }\n\n /** Read all MDX files recursively. */\n readAll(): MdxFile[] {\n if (!fs.existsSync(this.contentDir)) return []\n return this.walkDir(this.contentDir, this.contentDir)\n }\n\n private walkDir(dir: string, root: string): MdxFile[] {\n const results: MdxFile[] = []\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n results.push(...this.walkDir(fullPath, root))\n } else if (entry.name.endsWith('.mdx') || entry.name.endsWith('.md')) {\n const relative = path.relative(root, fullPath)\n const slug = relative.replace(/\\.(mdx|md)$/, '').replace(/\\/index$/, '')\n results.push(this.parseFile(slug, fullPath))\n }\n }\n return results\n }\n\n private parseFile(slug: string, filePath: string): MdxFile {\n const raw = fs.readFileSync(filePath, 'utf-8')\n const { data: frontmatter, content: rawContent } = matter(raw)\n return { slug, filePath, frontmatter, rawContent }\n }\n}\n","import type { MdxFile } from './mdx-reader.js'\n\n/**\n * Strips JSX from MDX content and returns clean Markdown\n * suitable for AI crawlers.\n *\n * Removes:\n * - import/export statements\n * - JSX component tags (<ComponentName ... /> and <ComponentName>...</ComponentName>)\n * - Inline expressions {variable} that aren't standard Markdown\n *\n * Preserves:\n * - All standard Markdown (headings, lists, code blocks, links, images)\n * - Frontmatter (serialized as YAML header)\n */\nexport class MarkdownRenderer {\n render(file: MdxFile): string {\n const header = this.buildFrontmatterHeader(file.frontmatter)\n const body = this.stripJsx(file.rawContent)\n return header ? `${header}\\n\\n${body}` : body\n }\n\n private buildFrontmatterHeader(fm: Record<string, unknown>): string {\n const keys = Object.keys(fm)\n if (keys.length === 0) return ''\n const lines = keys\n .filter(k => fm[k] !== undefined && fm[k] !== null)\n .map(k => `${k}: ${this.yamlValue(fm[k])}`)\n return `---\\n${lines.join('\\n')}\\n---`\n }\n\n private yamlValue(val: unknown): string {\n if (typeof val === 'string') {\n // Quote strings containing special YAML chars\n return /[:#\\[\\]{},&*?|<>=!%@`]/.test(val) ? `\"${val.replace(/\"/g, '\\\\\"')}\"` : val\n }\n if (val instanceof Date) return val.toISOString()\n if (Array.isArray(val)) return `[${val.map(v => this.yamlValue(v)).join(', ')}]`\n return String(val)\n }\n\n private stripJsx(content: string): string {\n let out = content\n\n // Remove import statements: import Foo from '...' / import { Foo } from '...'\n out = out.replace(/^import\\s+.*?['\"].*?['\"]\\s*\\n?/gm, '')\n\n // Remove export statements at line start (export const, export default, export { })\n out = out.replace(/^export\\s+(?:default\\s+)?(?:const|let|var|function|class)\\s+[\\s\\S]*?(?=\\n(?=[^{]|\\n)|\\n{2,})/gm, '')\n out = out.replace(/^export\\s*\\{[^}]*\\}\\s*(?:from\\s+['\"][^'\"]*['\"])?\\s*\\n?/gm, '')\n\n // Remove self-closing JSX tags: <Component ... />\n // Must not match HTML img/br/hr which are valid Markdown\n out = out.replace(/<([A-Z][A-Za-z0-9.]*)[^>]*\\/>/g, '')\n\n // Remove JSX block tags: <Component ...>...</Component>\n // Greedy but bounded by matching closing tag\n out = out.replace(/<([A-Z][A-Za-z0-9.]*)[^>]*>[\\s\\S]*?<\\/\\1>/g, '')\n\n // Remove JSX expression blocks { expression } that span a whole line\n out = out.replace(/^\\s*\\{[^}]+\\}\\s*\\n/gm, '')\n\n // Collapse multiple blank lines to two\n out = out.replace(/\\n{3,}/g, '\\n\\n')\n\n return out.trim()\n }\n}\n","import type { MdxFile } from '../core/mdx-reader.js'\nimport { MarkdownRenderer } from '../core/markdown-renderer.js'\n\nconst renderer = new MarkdownRenderer()\n\n/** Generates the /okf/index.md manifest listing all content. */\nexport function generateOkfIndex(files: MdxFile[], baseUrl: string): string {\n const base = baseUrl.replace(/\\/$/, '')\n const lines = [\n '# Open Knowledge Format (OKF) Bundle',\n '',\n 'This bundle contains all content as clean Markdown files for AI consumption.',\n '',\n '## Contents',\n '',\n ]\n\n for (const file of files) {\n const fm = file.frontmatter\n const title = String(fm.title ?? file.slug)\n const desc = fm.description ? ` — ${String(fm.description)}` : ''\n lines.push(`- [${title}](${base}/okf/${file.slug}.md)${desc}`)\n }\n\n return lines.join('\\n') + '\\n'\n}\n\n/** Renders a single MDX file for OKF, with internal links rewritten to .md siblings. */\nexport function generateOkfPage(file: MdxFile, allFiles: MdxFile[], baseUrl: string): string {\n const markdown = renderer.render(file)\n return rewriteInternalLinks(markdown, allFiles, baseUrl)\n}\n\n/**\n * Rewrites internal links to point at sibling .md files in the OKF bundle.\n * e.g. [link](/blog/post) → [link](/okf/blog/post.md)\n */\nfunction rewriteInternalLinks(markdown: string, allFiles: MdxFile[], baseUrl: string): string {\n const slugSet = new Set(allFiles.map(f => f.slug))\n const base = baseUrl.replace(/\\/$/, '')\n\n return markdown.replace(/\\[([^\\]]+)\\]\\((\\/[^)]+)\\)/g, (match, text, href) => {\n // Strip leading slash and any trailing .md to get candidate slug\n const candidate = href.replace(/^\\//, '').replace(/\\.md$/, '')\n if (slugSet.has(candidate)) {\n return `[${text}](${base}/okf/${candidate}.md)`\n }\n return match\n })\n}\n"],"mappings":"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","names":["import_path","path","fs","matter","path"]}
1
+ {"version":3,"sources":["../../../src/dashboard/routes/okf-route.ts","../../../src/core/mdx-reader.ts","../../../src/core/markdown-renderer.ts","../../../src/okf/okf-bundle.ts"],"sourcesContent":["import { NextResponse, type NextRequest } from 'next/server'\nimport path from 'path'\nimport { MdxReader } from '../../core/mdx-reader.js'\nimport { generateOkfIndex, generateOkfPage } from '../../okf/okf-bundle.js'\n\nconst reader = new MdxReader({ contentDir: path.join(process.cwd(), process.env.TA_CONTENT_DIR ?? 'content') })\n\n/**\n * Handler for /okf/ and /okf/[...slug].md\n * Rewired from middleware to /api/third-audience/okf/[...path]\n */\nexport async function GET(req: NextRequest, { params }: { params: { path?: string[] } }) {\n const baseUrl = process.env.NEXT_PUBLIC_SITE_URL\n ?? `${req.nextUrl.protocol}//${req.nextUrl.host}`\n\n const allFiles = reader.readAll()\n const segments = params.path ?? []\n\n // /okf/ or /okf/index or /okf/index.md → manifest\n if (segments.length === 0 || (segments.length === 1 && (segments[0] === 'index.md' || segments[0] === 'index'))) {\n return new NextResponse(generateOkfIndex(allFiles, baseUrl), {\n headers: { 'Content-Type': 'text/markdown; charset=utf-8' },\n })\n }\n\n // /okf/[slug].md → individual page\n const slug = segments.join('/').replace(/\\.md$/, '')\n const file = allFiles.find(f => f.slug === slug)\n if (!file) return new NextResponse('Not Found', { status: 404 })\n\n return new NextResponse(generateOkfPage(file, allFiles, baseUrl), {\n headers: { 'Content-Type': 'text/markdown; charset=utf-8' },\n })\n}\n","import fs from 'fs'\nimport path from 'path'\nimport matter from 'gray-matter'\n\nexport interface MdxFile {\n slug: string // relative path without extension, e.g. 'blog/my-post'\n filePath: string // absolute path to .mdx file\n frontmatter: Record<string, unknown>\n rawContent: string // body after frontmatter\n}\n\nexport interface MdxReaderOptions {\n contentDir: string // absolute path to content directory\n}\n\nexport class MdxReader {\n private contentDir: string\n\n constructor(options: MdxReaderOptions) {\n this.contentDir = options.contentDir\n }\n\n /** Read a single MDX file by slug. Returns null if not found. */\n read(slug: string): MdxFile | null {\n const candidates = [\n path.join(this.contentDir, `${slug}.mdx`),\n path.join(this.contentDir, `${slug}.md`),\n path.join(this.contentDir, slug, 'index.mdx'),\n path.join(this.contentDir, slug, 'index.md'),\n ]\n\n for (const filePath of candidates) {\n if (fs.existsSync(filePath)) {\n return this.parseFile(slug, filePath)\n }\n }\n\n return null\n }\n\n /** Read all MDX files recursively. */\n readAll(): MdxFile[] {\n if (!fs.existsSync(this.contentDir)) return []\n return this.walkDir(this.contentDir, this.contentDir)\n }\n\n private walkDir(dir: string, root: string): MdxFile[] {\n const results: MdxFile[] = []\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n results.push(...this.walkDir(fullPath, root))\n } else if (entry.name.endsWith('.mdx') || entry.name.endsWith('.md')) {\n const relative = path.relative(root, fullPath)\n const slug = relative.replace(/\\.(mdx|md)$/, '').replace(/\\/index$/, '')\n results.push(this.parseFile(slug, fullPath))\n }\n }\n return results\n }\n\n private parseFile(slug: string, filePath: string): MdxFile {\n const raw = fs.readFileSync(filePath, 'utf-8')\n const { data: frontmatter, content: rawContent } = matter(raw)\n return { slug, filePath, frontmatter, rawContent }\n }\n}\n","import type { MdxFile } from './mdx-reader.js'\n\n/**\n * Strips JSX from MDX content and returns clean Markdown\n * suitable for AI crawlers.\n *\n * Removes:\n * - import/export statements\n * - JSX component tags (<ComponentName ... /> and <ComponentName>...</ComponentName>)\n * - Inline expressions {variable} that aren't standard Markdown\n *\n * Preserves:\n * - All standard Markdown (headings, lists, code blocks, links, images)\n * - Frontmatter (serialized as YAML header)\n */\nexport class MarkdownRenderer {\n render(file: MdxFile): string {\n const header = this.buildFrontmatterHeader(file.frontmatter)\n const body = this.stripJsx(file.rawContent)\n return header ? `${header}\\n\\n${body}` : body\n }\n\n private buildFrontmatterHeader(fm: Record<string, unknown>): string {\n const keys = Object.keys(fm)\n if (keys.length === 0) return ''\n const lines = keys\n .filter(k => fm[k] !== undefined && fm[k] !== null)\n .map(k => `${k}: ${this.yamlValue(fm[k])}`)\n return `---\\n${lines.join('\\n')}\\n---`\n }\n\n private yamlValue(val: unknown): string {\n if (typeof val === 'string') {\n // Quote strings containing special YAML chars\n return /[:#\\[\\]{},&*?|<>=!%@`]/.test(val) ? `\"${val.replace(/\"/g, '\\\\\"')}\"` : val\n }\n if (val instanceof Date) return val.toISOString()\n if (Array.isArray(val)) return `[${val.map(v => this.yamlValue(v)).join(', ')}]`\n return String(val)\n }\n\n private stripJsx(content: string): string {\n let out = content\n\n // Remove import statements: import Foo from '...' / import { Foo } from '...'\n out = out.replace(/^import\\s+.*?['\"].*?['\"]\\s*\\n?/gm, '')\n\n // Remove export statements at line start (export const, export default, export { })\n out = out.replace(/^export\\s+(?:default\\s+)?(?:const|let|var|function|class)\\s+[\\s\\S]*?(?=\\n(?=[^{]|\\n)|\\n{2,})/gm, '')\n out = out.replace(/^export\\s*\\{[^}]*\\}\\s*(?:from\\s+['\"][^'\"]*['\"])?\\s*\\n?/gm, '')\n\n // Remove self-closing JSX tags: <Component ... />\n // Must not match HTML img/br/hr which are valid Markdown\n out = out.replace(/<([A-Z][A-Za-z0-9.]*)[^>]*\\/>/g, '')\n\n // Remove JSX block tags: <Component ...>...</Component>\n // Greedy but bounded by matching closing tag\n out = out.replace(/<([A-Z][A-Za-z0-9.]*)[^>]*>[\\s\\S]*?<\\/\\1>/g, '')\n\n // Remove JSX expression blocks { expression } that span a whole line\n out = out.replace(/^\\s*\\{[^}]+\\}\\s*\\n/gm, '')\n\n // Collapse multiple blank lines to two\n out = out.replace(/\\n{3,}/g, '\\n\\n')\n\n return out.trim()\n }\n}\n","import type { MdxFile } from '../core/mdx-reader.js'\nimport { MarkdownRenderer } from '../core/markdown-renderer.js'\n\nconst renderer = new MarkdownRenderer()\n\nexport interface OkfGraphNode {\n id: string\n title: string\n type: string\n url: string\n}\n\nexport interface OkfGraphEdge {\n source: string\n target: string\n}\n\nexport interface OkfGraphData {\n nodes: OkfGraphNode[]\n edges: OkfGraphEdge[]\n}\n\n/**\n * Builds the knowledge graph data for the OKF viewer.\n * Nodes = content pages; edges = internal links between them.\n * Trims to top 100 most-connected nodes (matching WP plugin behaviour).\n */\nexport function buildOkfGraph(files: MdxFile[], baseUrl: string): OkfGraphData {\n const base = baseUrl.replace(/\\/$/, '')\n const slugSet = new Set(files.map(f => f.slug))\n\n // Build slug → markdown map for link extraction\n const mdMap = new Map<string, string>()\n for (const file of files) {\n mdMap.set(file.slug, renderer.render(file))\n }\n\n // Count degrees to pick top 100\n const degrees = new Map<string, number>(files.map(f => [f.slug, 0]))\n const rawEdges: OkfGraphEdge[] = []\n\n for (const file of files) {\n const md = mdMap.get(file.slug) ?? ''\n const linkRe = /\\[([^\\]]+)\\]\\((\\/[^)]+)\\)/g\n let m: RegExpExecArray | null\n while ((m = linkRe.exec(md)) !== null) {\n const candidate = m[2].replace(/^\\//, '').replace(/\\.md$/, '')\n if (slugSet.has(candidate) && candidate !== file.slug) {\n rawEdges.push({ source: file.slug, target: candidate })\n degrees.set(file.slug, (degrees.get(file.slug) ?? 0) + 1)\n degrees.set(candidate, (degrees.get(candidate) ?? 0) + 1)\n }\n }\n }\n\n // Top 100 nodes by degree\n const top100 = files\n .slice()\n .sort((a, b) => (degrees.get(b.slug) ?? 0) - (degrees.get(a.slug) ?? 0))\n .slice(0, 100)\n const topSet = new Set(top100.map(f => f.slug))\n\n const nodes: OkfGraphNode[] = top100.map(f => ({\n id: f.slug,\n title: String(f.frontmatter.title ?? f.slug),\n type: String(f.frontmatter.type ?? 'WebPage'),\n url: `${base}/${f.slug}`,\n }))\n\n const edges = rawEdges.filter(e => topSet.has(e.source) && topSet.has(e.target))\n\n return { nodes, edges }\n}\n\n/** Generates the /okf/index.md manifest listing all content. */\nexport function generateOkfIndex(files: MdxFile[], baseUrl: string): string {\n const base = baseUrl.replace(/\\/$/, '')\n const lines = [\n '# Open Knowledge Format (OKF) Bundle',\n '',\n 'This bundle contains all content as clean Markdown files for AI consumption.',\n '',\n '## Contents',\n '',\n ]\n\n for (const file of files) {\n const fm = file.frontmatter\n const title = String(fm.title ?? file.slug)\n const desc = fm.description ? ` — ${String(fm.description)}` : ''\n lines.push(`- [${title}](${base}/okf/${file.slug}.md)${desc}`)\n }\n\n return lines.join('\\n') + '\\n'\n}\n\n/** Renders a single MDX file for OKF, with internal links rewritten to .md siblings. */\nexport function generateOkfPage(file: MdxFile, allFiles: MdxFile[], baseUrl: string): string {\n const markdown = renderer.render(file)\n return rewriteInternalLinks(markdown, allFiles, baseUrl)\n}\n\n/**\n * Rewrites internal links to point at sibling .md files in the OKF bundle.\n * e.g. [link](/blog/post) → [link](/okf/blog/post.md)\n */\nfunction rewriteInternalLinks(markdown: string, allFiles: MdxFile[], baseUrl: string): string {\n const slugSet = new Set(allFiles.map(f => f.slug))\n const base = baseUrl.replace(/\\/$/, '')\n\n return markdown.replace(/\\[([^\\]]+)\\]\\((\\/[^)]+)\\)/g, (match, text, href) => {\n // Strip leading slash and any trailing .md to get candidate slug\n const candidate = href.replace(/^\\//, '').replace(/\\.md$/, '')\n if (slugSet.has(candidate)) {\n return `[${text}](${base}/okf/${candidate}.md)`\n }\n return match\n })\n}\n"],"mappings":"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","names":["import_path","path","fs","matter","path"]}
@@ -131,7 +131,7 @@ async function GET(req, { params }) {
131
131
  const baseUrl = process.env.NEXT_PUBLIC_SITE_URL ?? `${req.nextUrl.protocol}//${req.nextUrl.host}`;
132
132
  const allFiles = reader.readAll();
133
133
  const segments = params.path ?? [];
134
- if (segments.length === 0 || segments.length === 1 && segments[0] === "index.md") {
134
+ if (segments.length === 0 || segments.length === 1 && (segments[0] === "index.md" || segments[0] === "index")) {
135
135
  return new NextResponse(generateOkfIndex(allFiles, baseUrl), {
136
136
  headers: { "Content-Type": "text/markdown; charset=utf-8" }
137
137
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/dashboard/routes/okf-route.ts","../../../src/core/mdx-reader.ts","../../../src/core/markdown-renderer.ts","../../../src/okf/okf-bundle.ts"],"sourcesContent":["import { NextResponse, type NextRequest } from 'next/server'\nimport path from 'path'\nimport { MdxReader } from '../../core/mdx-reader.js'\nimport { generateOkfIndex, generateOkfPage } from '../../okf/okf-bundle.js'\n\nconst reader = new MdxReader({ contentDir: path.join(process.cwd(), process.env.TA_CONTENT_DIR ?? 'content') })\n\n/**\n * Handler for /okf/ and /okf/[...slug].md\n * Rewired from middleware to /api/third-audience/okf/[...path]\n */\nexport async function GET(req: NextRequest, { params }: { params: { path?: string[] } }) {\n const baseUrl = process.env.NEXT_PUBLIC_SITE_URL\n ?? `${req.nextUrl.protocol}//${req.nextUrl.host}`\n\n const allFiles = reader.readAll()\n const segments = params.path ?? []\n\n // /okf/ or /okf/index.md → manifest\n if (segments.length === 0 || (segments.length === 1 && segments[0] === 'index.md')) {\n return new NextResponse(generateOkfIndex(allFiles, baseUrl), {\n headers: { 'Content-Type': 'text/markdown; charset=utf-8' },\n })\n }\n\n // /okf/[slug].md → individual page\n const slug = segments.join('/').replace(/\\.md$/, '')\n const file = allFiles.find(f => f.slug === slug)\n if (!file) return new NextResponse('Not Found', { status: 404 })\n\n return new NextResponse(generateOkfPage(file, allFiles, baseUrl), {\n headers: { 'Content-Type': 'text/markdown; charset=utf-8' },\n })\n}\n","import fs from 'fs'\nimport path from 'path'\nimport matter from 'gray-matter'\n\nexport interface MdxFile {\n slug: string // relative path without extension, e.g. 'blog/my-post'\n filePath: string // absolute path to .mdx file\n frontmatter: Record<string, unknown>\n rawContent: string // body after frontmatter\n}\n\nexport interface MdxReaderOptions {\n contentDir: string // absolute path to content directory\n}\n\nexport class MdxReader {\n private contentDir: string\n\n constructor(options: MdxReaderOptions) {\n this.contentDir = options.contentDir\n }\n\n /** Read a single MDX file by slug. Returns null if not found. */\n read(slug: string): MdxFile | null {\n const candidates = [\n path.join(this.contentDir, `${slug}.mdx`),\n path.join(this.contentDir, `${slug}.md`),\n path.join(this.contentDir, slug, 'index.mdx'),\n path.join(this.contentDir, slug, 'index.md'),\n ]\n\n for (const filePath of candidates) {\n if (fs.existsSync(filePath)) {\n return this.parseFile(slug, filePath)\n }\n }\n\n return null\n }\n\n /** Read all MDX files recursively. */\n readAll(): MdxFile[] {\n if (!fs.existsSync(this.contentDir)) return []\n return this.walkDir(this.contentDir, this.contentDir)\n }\n\n private walkDir(dir: string, root: string): MdxFile[] {\n const results: MdxFile[] = []\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n results.push(...this.walkDir(fullPath, root))\n } else if (entry.name.endsWith('.mdx') || entry.name.endsWith('.md')) {\n const relative = path.relative(root, fullPath)\n const slug = relative.replace(/\\.(mdx|md)$/, '').replace(/\\/index$/, '')\n results.push(this.parseFile(slug, fullPath))\n }\n }\n return results\n }\n\n private parseFile(slug: string, filePath: string): MdxFile {\n const raw = fs.readFileSync(filePath, 'utf-8')\n const { data: frontmatter, content: rawContent } = matter(raw)\n return { slug, filePath, frontmatter, rawContent }\n }\n}\n","import type { MdxFile } from './mdx-reader.js'\n\n/**\n * Strips JSX from MDX content and returns clean Markdown\n * suitable for AI crawlers.\n *\n * Removes:\n * - import/export statements\n * - JSX component tags (<ComponentName ... /> and <ComponentName>...</ComponentName>)\n * - Inline expressions {variable} that aren't standard Markdown\n *\n * Preserves:\n * - All standard Markdown (headings, lists, code blocks, links, images)\n * - Frontmatter (serialized as YAML header)\n */\nexport class MarkdownRenderer {\n render(file: MdxFile): string {\n const header = this.buildFrontmatterHeader(file.frontmatter)\n const body = this.stripJsx(file.rawContent)\n return header ? `${header}\\n\\n${body}` : body\n }\n\n private buildFrontmatterHeader(fm: Record<string, unknown>): string {\n const keys = Object.keys(fm)\n if (keys.length === 0) return ''\n const lines = keys\n .filter(k => fm[k] !== undefined && fm[k] !== null)\n .map(k => `${k}: ${this.yamlValue(fm[k])}`)\n return `---\\n${lines.join('\\n')}\\n---`\n }\n\n private yamlValue(val: unknown): string {\n if (typeof val === 'string') {\n // Quote strings containing special YAML chars\n return /[:#\\[\\]{},&*?|<>=!%@`]/.test(val) ? `\"${val.replace(/\"/g, '\\\\\"')}\"` : val\n }\n if (val instanceof Date) return val.toISOString()\n if (Array.isArray(val)) return `[${val.map(v => this.yamlValue(v)).join(', ')}]`\n return String(val)\n }\n\n private stripJsx(content: string): string {\n let out = content\n\n // Remove import statements: import Foo from '...' / import { Foo } from '...'\n out = out.replace(/^import\\s+.*?['\"].*?['\"]\\s*\\n?/gm, '')\n\n // Remove export statements at line start (export const, export default, export { })\n out = out.replace(/^export\\s+(?:default\\s+)?(?:const|let|var|function|class)\\s+[\\s\\S]*?(?=\\n(?=[^{]|\\n)|\\n{2,})/gm, '')\n out = out.replace(/^export\\s*\\{[^}]*\\}\\s*(?:from\\s+['\"][^'\"]*['\"])?\\s*\\n?/gm, '')\n\n // Remove self-closing JSX tags: <Component ... />\n // Must not match HTML img/br/hr which are valid Markdown\n out = out.replace(/<([A-Z][A-Za-z0-9.]*)[^>]*\\/>/g, '')\n\n // Remove JSX block tags: <Component ...>...</Component>\n // Greedy but bounded by matching closing tag\n out = out.replace(/<([A-Z][A-Za-z0-9.]*)[^>]*>[\\s\\S]*?<\\/\\1>/g, '')\n\n // Remove JSX expression blocks { expression } that span a whole line\n out = out.replace(/^\\s*\\{[^}]+\\}\\s*\\n/gm, '')\n\n // Collapse multiple blank lines to two\n out = out.replace(/\\n{3,}/g, '\\n\\n')\n\n return out.trim()\n }\n}\n","import type { MdxFile } from '../core/mdx-reader.js'\nimport { MarkdownRenderer } from '../core/markdown-renderer.js'\n\nconst renderer = new MarkdownRenderer()\n\n/** Generates the /okf/index.md manifest listing all content. */\nexport function generateOkfIndex(files: MdxFile[], baseUrl: string): string {\n const base = baseUrl.replace(/\\/$/, '')\n const lines = [\n '# Open Knowledge Format (OKF) Bundle',\n '',\n 'This bundle contains all content as clean Markdown files for AI consumption.',\n '',\n '## Contents',\n '',\n ]\n\n for (const file of files) {\n const fm = file.frontmatter\n const title = String(fm.title ?? file.slug)\n const desc = fm.description ? ` — ${String(fm.description)}` : ''\n lines.push(`- [${title}](${base}/okf/${file.slug}.md)${desc}`)\n }\n\n return lines.join('\\n') + '\\n'\n}\n\n/** Renders a single MDX file for OKF, with internal links rewritten to .md siblings. */\nexport function generateOkfPage(file: MdxFile, allFiles: MdxFile[], baseUrl: string): string {\n const markdown = renderer.render(file)\n return rewriteInternalLinks(markdown, allFiles, baseUrl)\n}\n\n/**\n * Rewrites internal links to point at sibling .md files in the OKF bundle.\n * e.g. [link](/blog/post) → [link](/okf/blog/post.md)\n */\nfunction rewriteInternalLinks(markdown: string, allFiles: MdxFile[], baseUrl: string): string {\n const slugSet = new Set(allFiles.map(f => f.slug))\n const base = baseUrl.replace(/\\/$/, '')\n\n return markdown.replace(/\\[([^\\]]+)\\]\\((\\/[^)]+)\\)/g, (match, text, href) => {\n // Strip leading slash and any trailing .md to get candidate slug\n const candidate = href.replace(/^\\//, '').replace(/\\.md$/, '')\n if (slugSet.has(candidate)) {\n return `[${text}](${base}/okf/${candidate}.md)`\n }\n return match\n })\n}\n"],"mappings":"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","names":["path","path"]}
1
+ {"version":3,"sources":["../../../src/dashboard/routes/okf-route.ts","../../../src/core/mdx-reader.ts","../../../src/core/markdown-renderer.ts","../../../src/okf/okf-bundle.ts"],"sourcesContent":["import { NextResponse, type NextRequest } from 'next/server'\nimport path from 'path'\nimport { MdxReader } from '../../core/mdx-reader.js'\nimport { generateOkfIndex, generateOkfPage } from '../../okf/okf-bundle.js'\n\nconst reader = new MdxReader({ contentDir: path.join(process.cwd(), process.env.TA_CONTENT_DIR ?? 'content') })\n\n/**\n * Handler for /okf/ and /okf/[...slug].md\n * Rewired from middleware to /api/third-audience/okf/[...path]\n */\nexport async function GET(req: NextRequest, { params }: { params: { path?: string[] } }) {\n const baseUrl = process.env.NEXT_PUBLIC_SITE_URL\n ?? `${req.nextUrl.protocol}//${req.nextUrl.host}`\n\n const allFiles = reader.readAll()\n const segments = params.path ?? []\n\n // /okf/ or /okf/index or /okf/index.md → manifest\n if (segments.length === 0 || (segments.length === 1 && (segments[0] === 'index.md' || segments[0] === 'index'))) {\n return new NextResponse(generateOkfIndex(allFiles, baseUrl), {\n headers: { 'Content-Type': 'text/markdown; charset=utf-8' },\n })\n }\n\n // /okf/[slug].md → individual page\n const slug = segments.join('/').replace(/\\.md$/, '')\n const file = allFiles.find(f => f.slug === slug)\n if (!file) return new NextResponse('Not Found', { status: 404 })\n\n return new NextResponse(generateOkfPage(file, allFiles, baseUrl), {\n headers: { 'Content-Type': 'text/markdown; charset=utf-8' },\n })\n}\n","import fs from 'fs'\nimport path from 'path'\nimport matter from 'gray-matter'\n\nexport interface MdxFile {\n slug: string // relative path without extension, e.g. 'blog/my-post'\n filePath: string // absolute path to .mdx file\n frontmatter: Record<string, unknown>\n rawContent: string // body after frontmatter\n}\n\nexport interface MdxReaderOptions {\n contentDir: string // absolute path to content directory\n}\n\nexport class MdxReader {\n private contentDir: string\n\n constructor(options: MdxReaderOptions) {\n this.contentDir = options.contentDir\n }\n\n /** Read a single MDX file by slug. Returns null if not found. */\n read(slug: string): MdxFile | null {\n const candidates = [\n path.join(this.contentDir, `${slug}.mdx`),\n path.join(this.contentDir, `${slug}.md`),\n path.join(this.contentDir, slug, 'index.mdx'),\n path.join(this.contentDir, slug, 'index.md'),\n ]\n\n for (const filePath of candidates) {\n if (fs.existsSync(filePath)) {\n return this.parseFile(slug, filePath)\n }\n }\n\n return null\n }\n\n /** Read all MDX files recursively. */\n readAll(): MdxFile[] {\n if (!fs.existsSync(this.contentDir)) return []\n return this.walkDir(this.contentDir, this.contentDir)\n }\n\n private walkDir(dir: string, root: string): MdxFile[] {\n const results: MdxFile[] = []\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n results.push(...this.walkDir(fullPath, root))\n } else if (entry.name.endsWith('.mdx') || entry.name.endsWith('.md')) {\n const relative = path.relative(root, fullPath)\n const slug = relative.replace(/\\.(mdx|md)$/, '').replace(/\\/index$/, '')\n results.push(this.parseFile(slug, fullPath))\n }\n }\n return results\n }\n\n private parseFile(slug: string, filePath: string): MdxFile {\n const raw = fs.readFileSync(filePath, 'utf-8')\n const { data: frontmatter, content: rawContent } = matter(raw)\n return { slug, filePath, frontmatter, rawContent }\n }\n}\n","import type { MdxFile } from './mdx-reader.js'\n\n/**\n * Strips JSX from MDX content and returns clean Markdown\n * suitable for AI crawlers.\n *\n * Removes:\n * - import/export statements\n * - JSX component tags (<ComponentName ... /> and <ComponentName>...</ComponentName>)\n * - Inline expressions {variable} that aren't standard Markdown\n *\n * Preserves:\n * - All standard Markdown (headings, lists, code blocks, links, images)\n * - Frontmatter (serialized as YAML header)\n */\nexport class MarkdownRenderer {\n render(file: MdxFile): string {\n const header = this.buildFrontmatterHeader(file.frontmatter)\n const body = this.stripJsx(file.rawContent)\n return header ? `${header}\\n\\n${body}` : body\n }\n\n private buildFrontmatterHeader(fm: Record<string, unknown>): string {\n const keys = Object.keys(fm)\n if (keys.length === 0) return ''\n const lines = keys\n .filter(k => fm[k] !== undefined && fm[k] !== null)\n .map(k => `${k}: ${this.yamlValue(fm[k])}`)\n return `---\\n${lines.join('\\n')}\\n---`\n }\n\n private yamlValue(val: unknown): string {\n if (typeof val === 'string') {\n // Quote strings containing special YAML chars\n return /[:#\\[\\]{},&*?|<>=!%@`]/.test(val) ? `\"${val.replace(/\"/g, '\\\\\"')}\"` : val\n }\n if (val instanceof Date) return val.toISOString()\n if (Array.isArray(val)) return `[${val.map(v => this.yamlValue(v)).join(', ')}]`\n return String(val)\n }\n\n private stripJsx(content: string): string {\n let out = content\n\n // Remove import statements: import Foo from '...' / import { Foo } from '...'\n out = out.replace(/^import\\s+.*?['\"].*?['\"]\\s*\\n?/gm, '')\n\n // Remove export statements at line start (export const, export default, export { })\n out = out.replace(/^export\\s+(?:default\\s+)?(?:const|let|var|function|class)\\s+[\\s\\S]*?(?=\\n(?=[^{]|\\n)|\\n{2,})/gm, '')\n out = out.replace(/^export\\s*\\{[^}]*\\}\\s*(?:from\\s+['\"][^'\"]*['\"])?\\s*\\n?/gm, '')\n\n // Remove self-closing JSX tags: <Component ... />\n // Must not match HTML img/br/hr which are valid Markdown\n out = out.replace(/<([A-Z][A-Za-z0-9.]*)[^>]*\\/>/g, '')\n\n // Remove JSX block tags: <Component ...>...</Component>\n // Greedy but bounded by matching closing tag\n out = out.replace(/<([A-Z][A-Za-z0-9.]*)[^>]*>[\\s\\S]*?<\\/\\1>/g, '')\n\n // Remove JSX expression blocks { expression } that span a whole line\n out = out.replace(/^\\s*\\{[^}]+\\}\\s*\\n/gm, '')\n\n // Collapse multiple blank lines to two\n out = out.replace(/\\n{3,}/g, '\\n\\n')\n\n return out.trim()\n }\n}\n","import type { MdxFile } from '../core/mdx-reader.js'\nimport { MarkdownRenderer } from '../core/markdown-renderer.js'\n\nconst renderer = new MarkdownRenderer()\n\nexport interface OkfGraphNode {\n id: string\n title: string\n type: string\n url: string\n}\n\nexport interface OkfGraphEdge {\n source: string\n target: string\n}\n\nexport interface OkfGraphData {\n nodes: OkfGraphNode[]\n edges: OkfGraphEdge[]\n}\n\n/**\n * Builds the knowledge graph data for the OKF viewer.\n * Nodes = content pages; edges = internal links between them.\n * Trims to top 100 most-connected nodes (matching WP plugin behaviour).\n */\nexport function buildOkfGraph(files: MdxFile[], baseUrl: string): OkfGraphData {\n const base = baseUrl.replace(/\\/$/, '')\n const slugSet = new Set(files.map(f => f.slug))\n\n // Build slug → markdown map for link extraction\n const mdMap = new Map<string, string>()\n for (const file of files) {\n mdMap.set(file.slug, renderer.render(file))\n }\n\n // Count degrees to pick top 100\n const degrees = new Map<string, number>(files.map(f => [f.slug, 0]))\n const rawEdges: OkfGraphEdge[] = []\n\n for (const file of files) {\n const md = mdMap.get(file.slug) ?? ''\n const linkRe = /\\[([^\\]]+)\\]\\((\\/[^)]+)\\)/g\n let m: RegExpExecArray | null\n while ((m = linkRe.exec(md)) !== null) {\n const candidate = m[2].replace(/^\\//, '').replace(/\\.md$/, '')\n if (slugSet.has(candidate) && candidate !== file.slug) {\n rawEdges.push({ source: file.slug, target: candidate })\n degrees.set(file.slug, (degrees.get(file.slug) ?? 0) + 1)\n degrees.set(candidate, (degrees.get(candidate) ?? 0) + 1)\n }\n }\n }\n\n // Top 100 nodes by degree\n const top100 = files\n .slice()\n .sort((a, b) => (degrees.get(b.slug) ?? 0) - (degrees.get(a.slug) ?? 0))\n .slice(0, 100)\n const topSet = new Set(top100.map(f => f.slug))\n\n const nodes: OkfGraphNode[] = top100.map(f => ({\n id: f.slug,\n title: String(f.frontmatter.title ?? f.slug),\n type: String(f.frontmatter.type ?? 'WebPage'),\n url: `${base}/${f.slug}`,\n }))\n\n const edges = rawEdges.filter(e => topSet.has(e.source) && topSet.has(e.target))\n\n return { nodes, edges }\n}\n\n/** Generates the /okf/index.md manifest listing all content. */\nexport function generateOkfIndex(files: MdxFile[], baseUrl: string): string {\n const base = baseUrl.replace(/\\/$/, '')\n const lines = [\n '# Open Knowledge Format (OKF) Bundle',\n '',\n 'This bundle contains all content as clean Markdown files for AI consumption.',\n '',\n '## Contents',\n '',\n ]\n\n for (const file of files) {\n const fm = file.frontmatter\n const title = String(fm.title ?? file.slug)\n const desc = fm.description ? ` — ${String(fm.description)}` : ''\n lines.push(`- [${title}](${base}/okf/${file.slug}.md)${desc}`)\n }\n\n return lines.join('\\n') + '\\n'\n}\n\n/** Renders a single MDX file for OKF, with internal links rewritten to .md siblings. */\nexport function generateOkfPage(file: MdxFile, allFiles: MdxFile[], baseUrl: string): string {\n const markdown = renderer.render(file)\n return rewriteInternalLinks(markdown, allFiles, baseUrl)\n}\n\n/**\n * Rewrites internal links to point at sibling .md files in the OKF bundle.\n * e.g. [link](/blog/post) → [link](/okf/blog/post.md)\n */\nfunction rewriteInternalLinks(markdown: string, allFiles: MdxFile[], baseUrl: string): string {\n const slugSet = new Set(allFiles.map(f => f.slug))\n const base = baseUrl.replace(/\\/$/, '')\n\n return markdown.replace(/\\[([^\\]]+)\\]\\((\\/[^)]+)\\)/g, (match, text, href) => {\n // Strip leading slash and any trailing .md to get candidate slug\n const candidate = href.replace(/^\\//, '').replace(/\\.md$/, '')\n if (slugSet.has(candidate)) {\n return `[${text}](${base}/okf/${candidate}.md)`\n }\n return match\n })\n}\n"],"mappings":"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","names":["path","path"]}
@@ -56,6 +56,21 @@ var NAV = [
56
56
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
57
57
  ] })
58
58
  },
59
+ {
60
+ href: "/third-audience/okf",
61
+ label: "OKF Bundle",
62
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
63
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "6", r: "2" }),
64
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "18", cy: "6", r: "2" }),
65
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "18", r: "2" }),
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "18", cy: "18", r: "2" }),
67
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "2" }),
68
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "6", x2: "10", y2: "11" }),
69
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "16", y1: "6", x2: "14", y2: "11" }),
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "18", x2: "10", y2: "13" }),
71
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "16", y1: "18", x2: "14", y2: "13" })
72
+ ] })
73
+ },
59
74
  {
60
75
  href: "/third-audience/health",
61
76
  label: "System Health",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/dashboard/ui/components/Sidebar.tsx"],"sourcesContent":["'use client'\nimport Link from 'next/link'\nimport { usePathname } from 'next/navigation'\n\nconst NAV = [\n {\n href: '/third-audience',\n label: 'Bot Analytics',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <polyline points=\"22 12 18 12 15 21 9 3 6 12 2 12\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/citations',\n label: 'LLM Traffic',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/bots',\n label: 'Bot Management',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/health',\n label: 'System Health',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <path d=\"M22 12h-4l-3 9L9 3l-3 9H2\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/settings',\n label: 'Settings',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\" />\n </svg>\n ),\n },\n]\n\nexport function Sidebar() {\n const pathname = usePathname()\n\n return (\n <nav className=\"ta-sidebar\">\n <div className=\"ta-sidebar-brand\">\n <svg viewBox=\"0 0 28 28\" fill=\"none\">\n <rect width=\"28\" height=\"28\" rx=\"7\" fill=\"#007aff\" />\n <path d=\"M8 20l4-8 4 8M10 17h4\" stroke=\"#fff\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <circle cx=\"20\" cy=\"9\" r=\"3\" fill=\"#34c759\" />\n </svg>\n <span>Third Audience</span>\n </div>\n {NAV.map(item => (\n <Link\n key={item.href}\n href={item.href}\n className={`ta-nav-item${pathname === item.href ? ' active' : ''}`}\n >\n {item.icon}\n {item.label}\n </Link>\n ))}\n </nav>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,kBAAiB;AACjB,wBAA4B;AAQpB;AANR,IAAM,MAAM;AAAA,EACV;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MACE,4CAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GACtE,sDAAC,cAAS,QAAO,mCAAkC,GACrD;AAAA,EAEJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MACE,4CAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GACtE,sDAAC,UAAK,GAAE,iEAAgE,GAC1E;AAAA,EAEJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MACE,6CAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GACtE;AAAA,kDAAC,UAAK,GAAE,KAAI,GAAE,MAAK,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI,IAAG,KAAI;AAAA,MACxD,4CAAC,UAAK,GAAE,4BAA2B;AAAA,OACrC;AAAA,EAEJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MACE,4CAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GACtE,sDAAC,UAAK,GAAE,6BAA4B,GACtC;AAAA,EAEJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MACE,6CAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GACtE;AAAA,kDAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,MAC9B,4CAAC,UAAK,GAAE,0mBAAymB;AAAA,OACnnB;AAAA,EAEJ;AACF;AAEO,SAAS,UAAU;AACxB,QAAM,eAAW,+BAAY;AAE7B,SACE,6CAAC,SAAI,WAAU,cACb;AAAA,iDAAC,SAAI,WAAU,oBACb;AAAA,mDAAC,SAAI,SAAQ,aAAY,MAAK,QAC5B;AAAA,oDAAC,UAAK,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI,MAAK,WAAU;AAAA,QACnD,4CAAC,UAAK,GAAE,yBAAwB,QAAO,QAAO,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ;AAAA,QAC3G,4CAAC,YAAO,IAAG,MAAK,IAAG,KAAI,GAAE,KAAI,MAAK,WAAU;AAAA,SAC9C;AAAA,MACA,4CAAC,UAAK,4BAAc;AAAA,OACtB;AAAA,IACC,IAAI,IAAI,UACP;AAAA,MAAC,YAAAA;AAAA,MAAA;AAAA,QAEC,MAAM,KAAK;AAAA,QACX,WAAW,cAAc,aAAa,KAAK,OAAO,YAAY,EAAE;AAAA,QAE/D;AAAA,eAAK;AAAA,UACL,KAAK;AAAA;AAAA;AAAA,MALD,KAAK;AAAA,IAMZ,CACD;AAAA,KACH;AAEJ;","names":["Link"]}
1
+ {"version":3,"sources":["../../../../src/dashboard/ui/components/Sidebar.tsx"],"sourcesContent":["'use client'\nimport Link from 'next/link'\nimport { usePathname } from 'next/navigation'\n\nconst NAV = [\n {\n href: '/third-audience',\n label: 'Bot Analytics',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <polyline points=\"22 12 18 12 15 21 9 3 6 12 2 12\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/citations',\n label: 'LLM Traffic',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/bots',\n label: 'Bot Management',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/okf',\n label: 'OKF Bundle',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <circle cx=\"6\" cy=\"6\" r=\"2\" /><circle cx=\"18\" cy=\"6\" r=\"2\" />\n <circle cx=\"6\" cy=\"18\" r=\"2\" /><circle cx=\"18\" cy=\"18\" r=\"2\" />\n <circle cx=\"12\" cy=\"12\" r=\"2\" />\n <line x1=\"8\" y1=\"6\" x2=\"10\" y2=\"11\" /><line x1=\"16\" y1=\"6\" x2=\"14\" y2=\"11\" />\n <line x1=\"8\" y1=\"18\" x2=\"10\" y2=\"13\" /><line x1=\"16\" y1=\"18\" x2=\"14\" y2=\"13\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/health',\n label: 'System Health',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <path d=\"M22 12h-4l-3 9L9 3l-3 9H2\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/settings',\n label: 'Settings',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\" />\n </svg>\n ),\n },\n]\n\nexport function Sidebar() {\n const pathname = usePathname()\n\n return (\n <nav className=\"ta-sidebar\">\n <div className=\"ta-sidebar-brand\">\n <svg viewBox=\"0 0 28 28\" fill=\"none\">\n <rect width=\"28\" height=\"28\" rx=\"7\" fill=\"#007aff\" />\n <path d=\"M8 20l4-8 4 8M10 17h4\" stroke=\"#fff\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <circle cx=\"20\" cy=\"9\" r=\"3\" fill=\"#34c759\" />\n </svg>\n <span>Third Audience</span>\n </div>\n {NAV.map(item => (\n <Link\n key={item.href}\n href={item.href}\n className={`ta-nav-item${pathname === item.href ? ' active' : ''}`}\n >\n {item.icon}\n {item.label}\n </Link>\n ))}\n </nav>\n )\n}\n"],"mappings":"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","names":["Link"]}
@@ -23,6 +23,21 @@ var NAV = [
23
23
  /* @__PURE__ */ jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
24
24
  ] })
25
25
  },
26
+ {
27
+ href: "/third-audience/okf",
28
+ label: "OKF Bundle",
29
+ icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
30
+ /* @__PURE__ */ jsx("circle", { cx: "6", cy: "6", r: "2" }),
31
+ /* @__PURE__ */ jsx("circle", { cx: "18", cy: "6", r: "2" }),
32
+ /* @__PURE__ */ jsx("circle", { cx: "6", cy: "18", r: "2" }),
33
+ /* @__PURE__ */ jsx("circle", { cx: "18", cy: "18", r: "2" }),
34
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "2" }),
35
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "6", x2: "10", y2: "11" }),
36
+ /* @__PURE__ */ jsx("line", { x1: "16", y1: "6", x2: "14", y2: "11" }),
37
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "18", x2: "10", y2: "13" }),
38
+ /* @__PURE__ */ jsx("line", { x1: "16", y1: "18", x2: "14", y2: "13" })
39
+ ] })
40
+ },
26
41
  {
27
42
  href: "/third-audience/health",
28
43
  label: "System Health",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/dashboard/ui/components/Sidebar.tsx"],"sourcesContent":["'use client'\nimport Link from 'next/link'\nimport { usePathname } from 'next/navigation'\n\nconst NAV = [\n {\n href: '/third-audience',\n label: 'Bot Analytics',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <polyline points=\"22 12 18 12 15 21 9 3 6 12 2 12\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/citations',\n label: 'LLM Traffic',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/bots',\n label: 'Bot Management',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/health',\n label: 'System Health',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <path d=\"M22 12h-4l-3 9L9 3l-3 9H2\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/settings',\n label: 'Settings',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\" />\n </svg>\n ),\n },\n]\n\nexport function Sidebar() {\n const pathname = usePathname()\n\n return (\n <nav className=\"ta-sidebar\">\n <div className=\"ta-sidebar-brand\">\n <svg viewBox=\"0 0 28 28\" fill=\"none\">\n <rect width=\"28\" height=\"28\" rx=\"7\" fill=\"#007aff\" />\n <path d=\"M8 20l4-8 4 8M10 17h4\" stroke=\"#fff\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <circle cx=\"20\" cy=\"9\" r=\"3\" fill=\"#34c759\" />\n </svg>\n <span>Third Audience</span>\n </div>\n {NAV.map(item => (\n <Link\n key={item.href}\n href={item.href}\n className={`ta-nav-item${pathname === item.href ? ' active' : ''}`}\n >\n {item.icon}\n {item.label}\n </Link>\n ))}\n </nav>\n )\n}\n"],"mappings":";;;AACA,OAAO,UAAU;AACjB,SAAS,mBAAmB;AAQpB,cAiBF,YAjBE;AANR,IAAM,MAAM;AAAA,EACV;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MACE,oBAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GACtE,8BAAC,cAAS,QAAO,mCAAkC,GACrD;AAAA,EAEJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MACE,oBAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GACtE,8BAAC,UAAK,GAAE,iEAAgE,GAC1E;AAAA,EAEJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MACE,qBAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GACtE;AAAA,0BAAC,UAAK,GAAE,KAAI,GAAE,MAAK,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI,IAAG,KAAI;AAAA,MACxD,oBAAC,UAAK,GAAE,4BAA2B;AAAA,OACrC;AAAA,EAEJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MACE,oBAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GACtE,8BAAC,UAAK,GAAE,6BAA4B,GACtC;AAAA,EAEJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MACE,qBAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GACtE;AAAA,0BAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,MAC9B,oBAAC,UAAK,GAAE,0mBAAymB;AAAA,OACnnB;AAAA,EAEJ;AACF;AAEO,SAAS,UAAU;AACxB,QAAM,WAAW,YAAY;AAE7B,SACE,qBAAC,SAAI,WAAU,cACb;AAAA,yBAAC,SAAI,WAAU,oBACb;AAAA,2BAAC,SAAI,SAAQ,aAAY,MAAK,QAC5B;AAAA,4BAAC,UAAK,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI,MAAK,WAAU;AAAA,QACnD,oBAAC,UAAK,GAAE,yBAAwB,QAAO,QAAO,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ;AAAA,QAC3G,oBAAC,YAAO,IAAG,MAAK,IAAG,KAAI,GAAE,KAAI,MAAK,WAAU;AAAA,SAC9C;AAAA,MACA,oBAAC,UAAK,4BAAc;AAAA,OACtB;AAAA,IACC,IAAI,IAAI,UACP;AAAA,MAAC;AAAA;AAAA,QAEC,MAAM,KAAK;AAAA,QACX,WAAW,cAAc,aAAa,KAAK,OAAO,YAAY,EAAE;AAAA,QAE/D;AAAA,eAAK;AAAA,UACL,KAAK;AAAA;AAAA;AAAA,MALD,KAAK;AAAA,IAMZ,CACD;AAAA,KACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/dashboard/ui/components/Sidebar.tsx"],"sourcesContent":["'use client'\nimport Link from 'next/link'\nimport { usePathname } from 'next/navigation'\n\nconst NAV = [\n {\n href: '/third-audience',\n label: 'Bot Analytics',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <polyline points=\"22 12 18 12 15 21 9 3 6 12 2 12\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/citations',\n label: 'LLM Traffic',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/bots',\n label: 'Bot Management',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/okf',\n label: 'OKF Bundle',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <circle cx=\"6\" cy=\"6\" r=\"2\" /><circle cx=\"18\" cy=\"6\" r=\"2\" />\n <circle cx=\"6\" cy=\"18\" r=\"2\" /><circle cx=\"18\" cy=\"18\" r=\"2\" />\n <circle cx=\"12\" cy=\"12\" r=\"2\" />\n <line x1=\"8\" y1=\"6\" x2=\"10\" y2=\"11\" /><line x1=\"16\" y1=\"6\" x2=\"14\" y2=\"11\" />\n <line x1=\"8\" y1=\"18\" x2=\"10\" y2=\"13\" /><line x1=\"16\" y1=\"18\" x2=\"14\" y2=\"13\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/health',\n label: 'System Health',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <path d=\"M22 12h-4l-3 9L9 3l-3 9H2\" />\n </svg>\n ),\n },\n {\n href: '/third-audience/settings',\n label: 'Settings',\n icon: (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\" />\n </svg>\n ),\n },\n]\n\nexport function Sidebar() {\n const pathname = usePathname()\n\n return (\n <nav className=\"ta-sidebar\">\n <div className=\"ta-sidebar-brand\">\n <svg viewBox=\"0 0 28 28\" fill=\"none\">\n <rect width=\"28\" height=\"28\" rx=\"7\" fill=\"#007aff\" />\n <path d=\"M8 20l4-8 4 8M10 17h4\" stroke=\"#fff\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <circle cx=\"20\" cy=\"9\" r=\"3\" fill=\"#34c759\" />\n </svg>\n <span>Third Audience</span>\n </div>\n {NAV.map(item => (\n <Link\n key={item.href}\n href={item.href}\n className={`ta-nav-item${pathname === item.href ? ' active' : ''}`}\n >\n {item.icon}\n {item.label}\n </Link>\n ))}\n </nav>\n )\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,5 @@
1
+ import * as react from 'react';
2
+
3
+ declare function OkfPage(): react.JSX.Element | null;
4
+
5
+ export { OkfPage };
@@ -0,0 +1,5 @@
1
+ import * as react from 'react';
2
+
3
+ declare function OkfPage(): react.JSX.Element | null;
4
+
5
+ export { OkfPage };