diff --git a/packages/akiradocs/package.json b/packages/akiradocs/package.json index 13d3f4d..0c6f5fc 100644 --- a/packages/akiradocs/package.json +++ b/packages/akiradocs/package.json @@ -9,7 +9,8 @@ "dev": "npm run compile && set NEXT_PUBLIC_AKIRADOCS_EDIT_MODE=true && next dev", "build": "npm run compile && npm run generate-sitemap && set NEXT_PUBLIC_AKIRADOCS_EDIT_MODE=false && next build", "start": "set NEXT_PUBLIC_AKIRADOCS_EDIT_MODE=false && next start", - "lint": "next lint" + "lint": "next lint", + "migrate-gitbook": "node scripts/migrations/gitbook.js" }, "dependencies": { "@ai-sdk/anthropic": "^1.0.1", diff --git a/packages/akiradocs/scripts/compile.js b/packages/akiradocs/scripts/compile.js index 0aaeed0..3ab70bf 100644 --- a/packages/akiradocs/scripts/compile.js +++ b/packages/akiradocs/scripts/compile.js @@ -95,11 +95,32 @@ async function convertMarkdownToBlocks(content) { continue; } + if (line.trim() === '***') { + if (currentBlock.length > 0) { + blocks.push({ + id: String(blockId++), + type: 'paragraph', + content: currentBlock.join('\n').trim() + }); + currentBlock = []; + } + + blocks.push({ + id: String(blockId++), + type: 'divider', + content: '' + }); + continue; + } + if (line.startsWith('#')) { if (!firstHeadingFound) { const match = line.match(/^#+\s*(.*)/); if (match) { title = match[1].trim(); + if (title.includes('**')) { + title = `${title.replace(/\*\*/g, '')}`; + } firstHeadingFound = true; skipNextLine = true; continue; @@ -119,10 +140,16 @@ async function convertMarkdownToBlocks(content) { if (!match) continue; const level = match[0].length; + let content = line.slice(level).trim(); + + if (content.includes('**')) { + content = `${content.replace(/\*\*/g, '')}`; + } + blocks.push({ id: String(blockId++), type: 'heading', - content: line.slice(level).trim(), + content: content, metadata: { level } }); diff --git a/packages/akiradocs/scripts/migrations/gitbook.js b/packages/akiradocs/scripts/migrations/gitbook.js new file mode 100644 index 0000000..647b024 --- /dev/null +++ b/packages/akiradocs/scripts/migrations/gitbook.js @@ -0,0 +1,125 @@ +const { readFile, writeFile, mkdir } = require('fs/promises'); +const { glob } = require('glob'); +const matter = require('gray-matter'); +const path = require('path'); +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); + +function processGitbookContent(content) { + // Remove Gitbook-specific content references + content = content.replace(/{% content-ref url="(.*?)" %}[\s\S]*?{% endcontent-ref %}/g, ''); + + // Convert Gitbook links to standard markdown links + content = content.replace(/\[(.*?)\]\((.*?)\.md\)/g, (match, text, url) => { + // Remove .md extension and convert to relative path + const cleanUrl = url.replace(/\.md$/, ''); + return `[${text}](${cleanUrl})`; + }); + + return content; +} + +async function migrateGitbookContent(options) { + const { + inputDir, + outputDir = '_contents', + language = 'en', + defaultAuthor = 'Anonymous' + } = options; + + try { + const files = await glob(`${inputDir}/**/*.md`, { cwd: process.cwd() }); + + if (files.length === 0) { + console.warn(`No markdown files found in ${inputDir}`); + return; + } + + console.log(`Found ${files.length} files to migrate...`); + + for (const file of files) { + const content = await readFile(file, 'utf-8'); + const { data: frontmatter, content: markdownContent } = matter(content); + + const processedContent = processGitbookContent(markdownContent); + + const newFrontmatter = { + title: frontmatter.title || '', + description: frontmatter.description || '', + author: frontmatter.author || defaultAuthor, + publishDate: frontmatter.publishDate || new Date().toISOString().split('T')[0], + modifiedDate: frontmatter.modifiedDate || new Date().toISOString().split('T')[0], + category: frontmatter.category || '', + keywords: frontmatter.keywords || [] + }; + + const newContent = matter.stringify(processedContent, newFrontmatter); + + const newPath = file + .replace(new RegExp(`^${inputDir}`), path.join(outputDir, language, 'docs')) + .replace(/SUMMARY\.md$/i, 'index.md'); + + await mkdir(path.dirname(newPath), { recursive: true }); + await writeFile(newPath, newContent); + + console.log(`Migrated ${file} -> ${newPath}`); + } + } catch (error) { + console.error('Error migrating Gitbook content:', error); + process.exit(1); + } +} + +async function main() { + const argv = yargs(hideBin(process.argv)) + .usage('Usage: $0 [options]') + .positional('input-dir', { + describe: 'Input directory containing Gitbook markdown files', + type: 'string' + }) + .option('output-dir', { + alias: 'o', + type: 'string', + description: 'Output directory for processed files', + default: '_contents' + }) + .option('language', { + alias: 'l', + type: 'string', + description: 'Language code for the content', + default: 'en' + }) + .option('author', { + alias: 'a', + type: 'string', + description: 'Default author name for content without authors', + default: 'Anonymous' + }) + .demandCommand(1, 'Please specify an input directory') + .help() + .argv; + + // Clean up input directory path by removing trailing slashes + const inputDir = argv._[0].replace(/\/+$/, ''); + + if (!inputDir) { + console.error('Input directory is required'); + process.exit(1); + } + + await migrateGitbookContent({ + inputDir, + outputDir: argv.outputDir, + language: argv.language, + defaultAuthor: argv.author + }); +} + +module.exports = { + main, + migrateGitbookContent +}; + +if (require.main === module) { + main(); +} diff --git a/packages/akiradocs/src/app/[locale]/[type]/page.tsx b/packages/akiradocs/src/app/[locale]/[type]/page.tsx index c56bd67..1b964cc 100644 --- a/packages/akiradocs/src/app/[locale]/[type]/page.tsx +++ b/packages/akiradocs/src/app/[locale]/[type]/page.tsx @@ -1,7 +1,9 @@ import { Metadata } from 'next' import { redirect } from 'next/navigation' -import { getAllPosts, getRecentContent } from '@/lib/content' +import { getRecentContent } from '@/lib/content' import { getHeaderConfig } from '@/lib/headerConfig' +import { getAkiradocsConfig } from '@/lib/getAkiradocsConfig' +import { Locale } from '@/types/AkiraConfigType' type Props = { params: Promise<{ @@ -13,7 +15,8 @@ type Props = { export const dynamic = 'force-static'; export async function generateStaticParams() { - const locales = ['en', 'es', 'fr']; + const akiraconfig = await getAkiradocsConfig(); + const locales = akiraconfig.localization.locales.map((locale: Locale) => locale.code); const types = ['docs', 'api', 'articles']; const params: { locale: string, type: string }[] = []; @@ -30,17 +33,18 @@ export async function generateMetadata({ params }: Props): Promise { const resolvedParams = await Promise.resolve(params); const { locale, type } = resolvedParams; const headerConfig = getHeaderConfig(); - + const akiraconfig = await getAkiradocsConfig(); return { title: `${type.charAt(0).toUpperCase() + type.slice(1)} - ${headerConfig.title}`, description: `Browse our latest ${type}`, alternates: { canonical: `${process.env.NEXT_PUBLIC_SITE_URL}/${locale}/${type}`, - languages: { - 'en': `${process.env.NEXT_PUBLIC_SITE_URL}/en/${type}`, - 'es': `${process.env.NEXT_PUBLIC_SITE_URL}/es/${type}`, - 'fr': `${process.env.NEXT_PUBLIC_SITE_URL}/fr/${type}`, - } + languages: Object.fromEntries( + akiraconfig.localization.locales.map((locale: Locale) => [ + locale.code, + `${process.env.NEXT_PUBLIC_SITE_URL}/${locale.code}/${type}` + ]) + ) } } } diff --git a/packages/akiradocs/src/lib/renderers/BlockRenderer.tsx b/packages/akiradocs/src/lib/renderers/BlockRenderer.tsx index ba5d2a9..b640646 100644 --- a/packages/akiradocs/src/lib/renderers/BlockRenderer.tsx +++ b/packages/akiradocs/src/lib/renderers/BlockRenderer.tsx @@ -64,16 +64,20 @@ export function BlockRenderer({ block, isEditing, onUpdate }: BlockRendererProps ); case 'heading': + const hasStrong = block.content.includes(''); return ( onUpdate?.(block.id, content)} > - {block.content} + {hasStrong ? block.content.replace(/<\/?strong>/g, '') : block.content} ); case 'list': diff --git a/packages/akiradocs/src/types/AkiraConfigType.tsx b/packages/akiradocs/src/types/AkiraConfigType.tsx index fcb3771..7dce737 100644 --- a/packages/akiradocs/src/types/AkiraConfigType.tsx +++ b/packages/akiradocs/src/types/AkiraConfigType.tsx @@ -28,7 +28,7 @@ type Logo = { icon: string; }; - type Locale = { + export type Locale = { code: string; name: string; flag: string;