docs: rework docs site to have different sections (#2686)
This PR starts putting in place the high-level changes we want to make to the docs site. - It makes separate sections for Reference and Examples and Community. - Gets rid of the secondary sidebar and integrates it into the main sidebar. - Groups the reference articles by type. - Pulls in the examples alongside code and a live playground so people don't have to visit examples.tldraw.com separately. <img width="1458" alt="Screenshot 2024-01-30 at 09 43 46" src="https://github.com/tldraw/tldraw/assets/469604/4f5aa339-3a69-4d9b-9b9f-dfdddea623e8"> Again, this is the top-level changes and there's more to be done for the next PR(s): - create quick start page - clean up installation page - add accordion to Examples page prbly - put fun stuff in header (from footer) - landing page - something for landing page of API - search cmd-k and border - cleanup _sidebarReferenceContentLinks - external links _blank - address potential skew issue with code examples - have a link to other examples (next.js, etc.) ### Change Type - [x] `documentation` — Changes to the documentation only[^2] ### Test Plan 1. Make sure examples work! ### Release Notes - Rework our docs site to pull together the examples app and reference section more cohesively. --------- Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com> Co-authored-by: Steve Ruiz <steveruizok@gmail.com> Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com> Co-authored-by: alex <alex@dytry.ch> Co-authored-by: Lu Wilson <l2wilson94@gmail.com> Co-authored-by: Dan Groshev <git@dgroshev.com>
This commit is contained in:
parent
a43b172b64
commit
3ae48af67c
68 changed files with 94415 additions and 92577 deletions
|
@ -65,6 +65,8 @@ export async function connect(opts = {} as { reset?: boolean }) {
|
|||
status TEXT NOT NULL,
|
||||
date TEXT,
|
||||
sourceUrl TEXT,
|
||||
componentCode TEXT,
|
||||
componentCodeFiles TEXT,
|
||||
keywords TEXT,
|
||||
content TEXT NOT NULL,
|
||||
path TEXT,
|
||||
|
|
|
@ -8,17 +8,17 @@ import { getApiMarkdown } from './getApiMarkdown'
|
|||
|
||||
export async function createApiMarkdown() {
|
||||
const apiInputSection: InputSection = {
|
||||
id: 'gen' as string,
|
||||
id: 'reference' as string,
|
||||
title: 'API Reference',
|
||||
description: "Reference for the tldraw package's APIs (generated).",
|
||||
categories: [],
|
||||
sidebar_behavior: 'show-title',
|
||||
sidebar_behavior: 'reference',
|
||||
}
|
||||
|
||||
const addedCategories = new Set<string>()
|
||||
|
||||
const INPUT_DIR = path.join(process.cwd(), 'api')
|
||||
const OUTPUT_DIR = path.join(CONTENT_DIR, 'gen')
|
||||
const OUTPUT_DIR = path.join(CONTENT_DIR, 'reference')
|
||||
|
||||
if (fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.rmSync(OUTPUT_DIR, { recursive: true })
|
||||
|
@ -85,7 +85,7 @@ export async function createApiMarkdown() {
|
|||
const sectionsJsonPath = path.join(CONTENT_DIR, 'sections.json')
|
||||
const sectionsJson = JSON.parse(fs.readFileSync(sectionsJsonPath, 'utf8')) as InputSection[]
|
||||
sectionsJson.splice(
|
||||
sectionsJson.findIndex((s) => s.id === 'gen'),
|
||||
sectionsJson.findIndex((s) => s.id === 'reference'),
|
||||
1
|
||||
)
|
||||
sectionsJson.push(apiInputSection)
|
||||
|
|
|
@ -11,8 +11,8 @@ export async function generateApiContent(): Promise<GeneratedContent> {
|
|||
const sections = require(path.join(CONTENT_DIRECTORY, 'sections.json')) as InputSection[]
|
||||
|
||||
try {
|
||||
const inputApiSection = sections.find((s) => s.id === 'gen')
|
||||
if (!inputApiSection) throw new Error(`Could not find section with id 'gen'`)
|
||||
const inputApiSection = sections.find((s) => s.id === 'reference')
|
||||
if (!inputApiSection) throw new Error(`Could not find section with id 'reference'`)
|
||||
const outputApiSection = generateSection(inputApiSection, articles, 999999) // always at the end!
|
||||
const contentComplete = { sections: [outputApiSection], articles }
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ export async function generateContent() {
|
|||
}
|
||||
|
||||
for (let i = 0; i < sections.length; i++) {
|
||||
if (sections[i].id === 'gen') continue
|
||||
if (sections[i].id === 'reference') continue
|
||||
result.sections.push(generateSection(sections[i], result.articles, i))
|
||||
}
|
||||
|
||||
|
|
34
apps/docs/scripts/functions/generateExamplesContent.ts
Normal file
34
apps/docs/scripts/functions/generateExamplesContent.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { Articles, GeneratedContent, InputSection } from '../../types/content-types'
|
||||
import { generateSection } from './generateSection'
|
||||
|
||||
const { log: nicelog } = console
|
||||
|
||||
const section: InputSection = {
|
||||
id: 'examples',
|
||||
title: 'Examples',
|
||||
description: 'Code recipes for bending tldraw to your will.',
|
||||
categories: [
|
||||
{ id: 'basic', title: 'Getting Started', description: '', groups: [] },
|
||||
{ id: 'ui', title: 'UI/Theming', description: '', groups: [] },
|
||||
{ id: 'shapes/tools', title: 'Shapes & Tools', description: '', groups: [] },
|
||||
{ id: 'data/assets', title: 'Data & Assets', description: '', groups: [] },
|
||||
{ id: 'editor-api', title: 'Editor API', description: '', groups: [] },
|
||||
{ id: 'collaboration', title: 'Collaboration', description: '', groups: [] },
|
||||
],
|
||||
sidebar_behavior: 'show-links',
|
||||
}
|
||||
|
||||
export async function generateExamplesContent(): Promise<GeneratedContent> {
|
||||
const articles: Articles = {}
|
||||
|
||||
try {
|
||||
const outputExamplesSection = generateSection(section, articles, 0)
|
||||
const contentComplete = { sections: [outputExamplesSection], articles }
|
||||
|
||||
return contentComplete
|
||||
} catch (error) {
|
||||
nicelog(`x Could not generate Examples content`)
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
|
@ -21,21 +21,29 @@ export function generateSection(section: InputSection, articles: Articles, index
|
|||
)
|
||||
|
||||
// The file directory for this section
|
||||
const dir = path.join(CONTENT_DIR, section.id)
|
||||
const isExamples = section.id === 'examples'
|
||||
const dir = isExamples
|
||||
? path.join(process.cwd(), '..', 'examples', 'src', 'examples')
|
||||
: path.join(CONTENT_DIR, section.id)
|
||||
const files = fs.readdirSync(dir, { withFileTypes: false })
|
||||
|
||||
const isGenerated = section.id === 'gen'
|
||||
const isGenerated = section.id === 'reference'
|
||||
|
||||
for (const file of files) {
|
||||
const filename = file.toString()
|
||||
const fileContent = fs.readFileSync(path.join(dir, filename)).toString()
|
||||
const pathname = isExamples ? path.join(dir, filename, 'README.md') : path.join(dir, filename)
|
||||
const fileContent = fs.readFileSync(pathname).toString()
|
||||
const extension = path.extname(filename)
|
||||
const articleId = filename.replace(extension, '')
|
||||
|
||||
const parsed = matter({ content: fileContent }, {})
|
||||
|
||||
// If we're in prod and the article isn't published, skip it
|
||||
if (process.env.NODE_ENV !== 'development' && parsed.data.status !== 'published') {
|
||||
if (
|
||||
process.env.NODE_ENV !== 'development' &&
|
||||
!isExamples &&
|
||||
parsed.data.status !== 'published'
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -48,6 +56,38 @@ export function generateSection(section: InputSection, articles: Articles, index
|
|||
|
||||
const isUncategorized = categoryId === section.id + '_ucg'
|
||||
const isIndex = articleId === section.id
|
||||
const componentCode = parsed.data.component
|
||||
? fs
|
||||
.readFileSync(
|
||||
path.join(
|
||||
dir,
|
||||
filename,
|
||||
`${parsed.data.component}${parsed.data.component.endsWith('.tsx') ? '' : '.tsx'}`
|
||||
)
|
||||
)
|
||||
.toString()
|
||||
: null
|
||||
const componentCodeFiles: { [key: string]: string } = {}
|
||||
if (parsed.data.component) {
|
||||
const exampleParentDirectory = path.join(dir, filename)
|
||||
const codeFilenames = fs.readdirSync(exampleParentDirectory, {
|
||||
withFileTypes: true,
|
||||
recursive: true,
|
||||
})
|
||||
codeFilenames
|
||||
.filter(
|
||||
(file) =>
|
||||
!file.isDirectory() &&
|
||||
file.name !== 'README.md' &&
|
||||
file.name.replace('.tsx', '') !==
|
||||
parsed.data.component.replace('./', '').replace('.tsx', '')
|
||||
)
|
||||
.forEach((file) => {
|
||||
componentCodeFiles[file.name] = fs
|
||||
.readFileSync(path.join(file.path, file.name))
|
||||
.toString()
|
||||
})
|
||||
}
|
||||
|
||||
const article: Article = {
|
||||
id: articleId,
|
||||
|
@ -55,7 +95,7 @@ export function generateSection(section: InputSection, articles: Articles, index
|
|||
sectionIndex: 0,
|
||||
groupIndex: -1,
|
||||
groupId: parsed.data.group ?? null,
|
||||
categoryIndex: parsed.data.order ?? -1,
|
||||
categoryIndex: parsed.data.order ?? parsed.data.priority ?? -1,
|
||||
sectionId: section.id,
|
||||
author,
|
||||
categoryId,
|
||||
|
@ -68,6 +108,8 @@ export function generateSection(section: InputSection, articles: Articles, index
|
|||
sourceUrl: isGenerated // if it's a generated API doc, then we don't have a link
|
||||
? parsed.data.sourceUrl ?? null
|
||||
: `${section.id}/${articleId}${extension}`,
|
||||
componentCode,
|
||||
componentCodeFiles: componentCode ? JSON.stringify(componentCodeFiles) : null,
|
||||
content: parsed.content,
|
||||
path:
|
||||
section.id === 'getting-started'
|
||||
|
@ -166,14 +208,10 @@ export function generateSection(section: InputSection, articles: Articles, index
|
|||
}
|
||||
|
||||
const sortArticles = (articleA: Article, articleB: Article) => {
|
||||
const { categoryIndex: categoryIndexA, date: dateA = '01/01/1970' } = articleA
|
||||
const { categoryIndex: categoryIndexB, date: dateB = '01/01/1970' } = articleB
|
||||
const { categoryIndex: categoryIndexA, title: titleA } = articleA
|
||||
const { categoryIndex: categoryIndexB, title: titleB } = articleB
|
||||
|
||||
return categoryIndexA === categoryIndexB
|
||||
? new Date(dateB!).getTime() > new Date(dateA!).getTime()
|
||||
? 1
|
||||
: -1
|
||||
: categoryIndexA < categoryIndexB
|
||||
? -1
|
||||
: 1
|
||||
? titleA.localeCompare(titleB)
|
||||
: categoryIndexA - categoryIndexB
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { nicelog } from '@/utils/nicelog'
|
|||
import { connect } from './connect'
|
||||
import { generateApiContent } from './generateApiContent'
|
||||
import { generateContent } from './generateContent'
|
||||
import { generateExamplesContent } from './generateExamplesContent'
|
||||
|
||||
export async function refreshContent(opts = {} as { silent: boolean }) {
|
||||
if (!opts.silent) nicelog('◦ Resetting database...')
|
||||
|
@ -16,6 +17,9 @@ export async function refreshContent(opts = {} as { silent: boolean }) {
|
|||
if (!opts.silent) nicelog('◦ Generating / adding regular content to db...')
|
||||
await addContentToDb(db, await generateContent())
|
||||
|
||||
if (!opts.silent) nicelog('◦ Generating / adding Examples content to db...')
|
||||
await addContentToDb(db, await generateExamplesContent())
|
||||
|
||||
if (!opts.silent) nicelog('◦ Generating / adding API content to db...')
|
||||
await addContentToDb(db, await generateApiContent())
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue