diff --git a/next.config.mjs b/next.config.mjs index 88a5241..91b24c0 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -48,6 +48,13 @@ function withMarkdocRscLayer(nextConfig = {}) { /** @type {import('next').NextConfig} */ const nextConfig = { pageExtensions: ['js', 'jsx', 'md', 'ts', 'tsx'], + // Serve a plain Markdown version of every page at `.md` for agents + async rewrites() { + return [ + { source: '/index.md', destination: '/raw-md' }, + { source: '/:path+.md', destination: '/raw-md/:path+' }, + ] + }, } export default withSearch( diff --git a/package.json b/package.json index 241cd69..23c725f 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "typescript": "^5.9.3" }, "devDependencies": { + "@types/js-yaml": "^4.0.9", "@types/linkify-it": "^3.0.5", "@types/node": "^24.12.2", "@types/react": "19.2.14", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b771be1..f519d56 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,6 +84,9 @@ importers: specifier: ^5.9.3 version: 5.9.3 devDependencies: + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 '@types/linkify-it': specifier: ^3.0.5 version: 3.0.5 @@ -909,6 +912,9 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1520,6 +1526,7 @@ packages: eslint@9.39.4: resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -3287,6 +3294,8 @@ snapshots: '@types/estree@1.0.9': {} + '@types/js-yaml@4.0.9': {} + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} diff --git a/public/llms.txt b/public/llms.txt deleted file mode 100644 index 3876b02..0000000 --- a/public/llms.txt +++ /dev/null @@ -1,71 +0,0 @@ -# Inkdrop Documentation - -> Official documentation for Inkdrop - a Markdown note-taking app built for developers who think in code. Features end-to-end encryption, issue-driven note-taking methodology, and extensibility through plugins. - -## New to Inkdrop - -- [Get started](https://docs.inkdrop.app/start-guide): Quick start guide for creating account, downloading and installing Inkdrop -- [How to write Markdown notes](https://docs.inkdrop.app/start-guide/how-to-write-markdown-notes): Introduction to Markdown syntax, formatting, and writing notes with live preview -- [How to organize your notes](https://docs.inkdrop.app/start-guide/how-to-organize-notes): Using notebooks, statuses, and tags to organize notes efficiently -- [Issue-driven tech note-taking](https://docs.inkdrop.app/start-guide/issue-driven-note-taking): One note per issue methodology for focused tech note-taking -- [Key note categories and examples for tech issues](https://docs.inkdrop.app/start-guide/key-note-categories-and-examples): Nine key tech issue categories with real-world note examples -- [Take notes just as you breathe](https://docs.inkdrop.app/start-guide/low-friction-note-taking-system): Creating an inbox-based system for capturing notes without friction -- [Stay motivated and inspired](https://docs.inkdrop.app/start-guide/join-discord-server): Join the community Discord server for motivation and collaboration -- [How tech notes can boost your productivity](https://www.inkdrop.app/note-taking-tips/): External guide on leveraging tech notes for productivity -- [How to take notes while learning a new technology](https://www.youtube.com/watch?v=RhuAn4uLVpc): Video tutorial on effective note-taking strategies - -## Basics - -- [Main user interface](https://docs.inkdrop.app/reference/main-user-interface): Overview of Inkdrop's three-panel interface layout with sidebar, note list, and editor -- [Write notes](https://docs.inkdrop.app/reference/write-notes): Guide to creating, editing, deleting notes with tags, statuses, and preview capabilities -- [Basic writing and formatting syntax](https://docs.inkdrop.app/writing/basic-writing-and-formatting-syntax): Comprehensive Markdown reference covering GitHub-flavored formatting, code blocks, tables, and alerts -- [User data directory](https://docs.inkdrop.app/reference/user-data-directory): Location paths where Inkdrop stores local configuration files and database data -- [Data backup](https://docs.inkdrop.app/reference/data-backup): Continuous JSON backup system to protect notes with restore and export capabilities -- [Configure two-factor authentication](https://docs.inkdrop.app/reference/configure-2fa): Setup guide for two-factor authentication using TOTP authenticator apps - -## Note organization - -- [Organize notebooks](https://docs.inkdrop.app/reference/organize-notebooks): Managing hierarchical notebook structures with sub-notebooks and workspace-specific views -- [Search and filter notes](https://docs.inkdrop.app/reference/search-and-filter-notes): Powerful search capabilities with special qualifiers for filtering by notebook, tag, and status -- [Note statuses](https://docs.inkdrop.app/reference/note-statuses): Task management system using Active, On Hold, Completed, and Dropped statuses -- [Pin notes to the top](https://docs.inkdrop.app/reference/pin-notes-to-top): Feature to stick important notes at the top of note lists for quick access - -## Customization - -- [Customize keybindings](https://docs.inkdrop.app/reference/key-customizations): Configure custom keybindings, multi-stroke shortcuts, and global hotkeys using JSON keymap files -- [Note synchronization](https://docs.inkdrop.app/reference/note-synchronization): Cloud sync options including Inkdrop server or self-hosted CouchDB setup with SSL -- [Extend with plugins](https://docs.inkdrop.app/reference/extend-with-plugins): Install and manage plugins for enhanced functionality, themes, and Markdown extensions -- [Share notes on the web](https://docs.inkdrop.app/reference/share-notes-on-the-web): Publish individual notes with public URLs and automatic internal link conversion -- [Import notes from Evernote](https://docs.inkdrop.app/reference/import-from-evernote): Import HTML-exported Evernote notes into Inkdrop notebooks preserving content -- [Use behind web proxy](https://docs.inkdrop.app/reference/use-behind-web-proxy): Configure HTTP/HTTPS proxy settings for corporate networks including authentication - -## Advanced - -- [Clip web pages from browser](https://docs.inkdrop.app/reference/clip-web-pages-from-browser): Browser extension to save web content as Markdown notes via local server -- [Import your Kindle highlights](https://docs.inkdrop.app/reference/import-kindle-highlights): Browser extension feature to import and sync Kindle reading highlights as notes -- [Restore previous revisions of note](https://docs.inkdrop.app/reference/revision-history): Automatic version tracking with ability to restore previous revisions -- [AI integration](https://docs.inkdrop.app/reference/mcp-server): Model Context Protocol server enabling AI assistants to access and manipulate notes -- [Resize images](https://docs.inkdrop.app/reference/resize-images): Custom CSS stylesheet technique to control image sizes using alt attribute suffixes -- [Integrate with Alfred](https://docs.inkdrop.app/reference/integrate-with-alfred): Alfred workflow for instant note searching and creation on macOS -- [Integrate with Raycast](https://docs.inkdrop.app/reference/integrate-with-raycast): Raycast extension for quick note search and creation using productivity launcher - -## Troubleshooting - -- [Troubleshooting](https://docs.inkdrop.app/reference/troubleshooting): Comprehensive solutions for login issues, sync problems, plugin errors, and payment failures -- [Recover lost password](https://docs.inkdrop.app/reference/recover-password): Explanation that passwords cannot be reset due to client-side encryption security -- [Salvage data from local database](https://docs.inkdrop.app/reference/salvage-data-from-local-database): Recovery tool to extract unencrypted notes from local storage after password loss - -## About - -- [Frequently asked questions](https://docs.inkdrop.app/faq): Common questions about pricing, features, billing, account management, and technical requirements -- [How secure is Inkdrop?](https://docs.inkdrop.app/security): End-to-end encryption implementation using AES-256 with transport security and 2FA -- [Contact the author](https://docs.inkdrop.app/contact): Multiple ways to reach the developer including forum, email, and social media -- [Terms of Service](https://docs.inkdrop.app/terms): Service agreement covering account usage, payments, content ownership, and liability -- [Privacy Policy](https://docs.inkdrop.app/privacy): Detailed privacy policy explaining data collection, storage, third-party services, and GDPR -- [特定商取引法に基づく表記](https://docs.inkdrop.app/notation): Japanese commercial transaction law disclosure with business operator details - -## Further Resources - -- [API reference](https://developers.inkdrop.app/): Customize and create your own plugins -- [User forum](https://forum.inkdrop.app/): Join discussions, get help, and share app insights -- [Plugins](https://my.inkdrop.app/plugins): Extend Inkdrop with community plugins diff --git a/src/app/llms-full.txt/route.ts b/src/app/llms-full.txt/route.ts new file mode 100644 index 0000000..bdf0556 --- /dev/null +++ b/src/app/llms-full.txt/route.ts @@ -0,0 +1,9 @@ +import { getLlmsFullTxt } from '@/lib/markdown' + +export const dynamic = 'force-static' + +export async function GET() { + return new Response(await getLlmsFullTxt(), { + headers: { 'Content-Type': 'text/plain; charset=utf-8' }, + }) +} diff --git a/src/app/llms.txt/route.ts b/src/app/llms.txt/route.ts new file mode 100644 index 0000000..6dc84ae --- /dev/null +++ b/src/app/llms.txt/route.ts @@ -0,0 +1,9 @@ +import { getLlmsTxt } from '@/lib/markdown' + +export const dynamic = 'force-static' + +export async function GET() { + return new Response(await getLlmsTxt(), { + headers: { 'Content-Type': 'text/plain; charset=utf-8' }, + }) +} diff --git a/src/app/raw-md/[[...slug]]/route.ts b/src/app/raw-md/[[...slug]]/route.ts new file mode 100644 index 0000000..1bdaafe --- /dev/null +++ b/src/app/raw-md/[[...slug]]/route.ts @@ -0,0 +1,23 @@ +import { getMarkdownSlugs, getPageMarkdown } from '@/lib/markdown' + +// Serves each docs page as plain Markdown, e.g. `/start-guide/foo.md`. +// Requests are rewritten here from `*.md` URLs in next.config.mjs. +export const dynamic = 'force-static' +export const dynamicParams = false + +export async function generateStaticParams() { + let slugs = await getMarkdownSlugs() + return slugs.map((slug) => ({ slug })) +} + +export async function GET( + _request: Request, + { params }: { params: Promise<{ slug?: Array }> }, +) { + let { slug = [] } = await params + let markdown = await getPageMarkdown(slug) + + return new Response(markdown, { + headers: { 'Content-Type': 'text/markdown; charset=utf-8' }, + }) +} diff --git a/src/components/TableOfContents.tsx b/src/components/TableOfContents.tsx index f4577e2..b844256 100644 --- a/src/components/TableOfContents.tsx +++ b/src/components/TableOfContents.tsx @@ -2,8 +2,10 @@ import { useCallback, useEffect, useState } from 'react' import Link from 'next/link' +import { usePathname } from 'next/navigation' import clsx from 'clsx' +import { RobotHeadIcon } from '@/components/icons/RobotHeadIcon' import { type Section, type Subsection } from '@/lib/sections' export function TableOfContents({ @@ -11,6 +13,8 @@ export function TableOfContents({ }: { tableOfContents: Array
}) { + let pathname = usePathname() + let markdownHref = pathname === '/' ? '/index.md' : `${pathname}.md` let [currentSection, setCurrentSection] = useState(tableOfContents[0]?.id) let getHeadings = useCallback((tableOfContents: Array
) => { @@ -64,11 +68,19 @@ export function TableOfContents({ return (