diff --git a/README.md b/README.md index 6b1e52e94..6b6877faa 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,31 @@ -# -

+

+ +
+ enter the shipstorm +
+ +
+ + +
+ + [![Homepage](https://img.shields.io/badge/Homepage-8A2BE2)](https://nativelink.com) + [![GitHub stars](https://img.shields.io/github/stars/tracemachina/nativelink?style=social)](https://github.com/TraceMachina/nativelink) + [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/TraceMachina/nativelink/badge)](https://securityscorecards.dev/viewer/?uri=github.com/TraceMachina/nativelink) + [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8050/badge)](https://www.bestpractices.dev/projects/8050) + [![Slack](https://img.shields.io/badge/slack--channel-blue?logo=slack)](https://nativelink.slack.com/join/shared_invite/zt-281qk1ho0-krT7HfTUIYfQMdwflRuq7A#/shared-invite/email) + [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +
## What's NativeLink? @@ -22,9 +33,11 @@ NativeLink is an efficient, high-performance build cache and remote execution sy NativeLink is trusted in production environments to reduce costs and developer iteration times--handling over **one billion requests** per month for its customers, including large corporations such as **Samsung**. - - NativeLink Explained in 90 seconds - +

+ + NativeLink Explained in 90 seconds + +

## 🔑 Key Features diff --git a/docs/astro.config.ts b/docs/astro.config.ts index 9421b984f..c642809b9 100644 --- a/docs/astro.config.ts +++ b/docs/astro.config.ts @@ -4,14 +4,14 @@ import mdx from "@astrojs/mdx"; import sitemap from "@astrojs/sitemap"; import starlight from "@astrojs/starlight"; import deno from "@deno/astro-adapter"; +import tailwindcss from "@tailwindcss/vite"; // import partytown from "@astrojs/partytown"; import { rehypeHeadingIds } from "@astrojs/markdown-remark"; import { rehypeMermaid } from "@beoe/rehype-mermaid"; // "rehype-mermaid"; -import tailwindcss from "@tailwindcss/vite"; import rehypeAutolinkHeadings from "rehype-autolink-headings"; -import { starlightConfig } from "./starlight.conf.ts"; -// import { default as playformCompress } from "@playform/compress"; + +import { starlightConfig } from "./starlight.conf"; // https://astro.build/config export default defineConfig({ diff --git a/docs/biome.json b/docs/biome.json index eb2cdb535..91bc63dc9 100644 --- a/docs/biome.json +++ b/docs/biome.json @@ -14,8 +14,11 @@ "rules": { "all": true, "style": { - "useNamingConvention": "off", - "noDefaultExport": "off" + "useBlockStatements": "error", + "useShorthandArrayType": "error", + "noShoutyConstants": "warn", + "noDefaultExport": "off", + "useNamingConvention": "off" } } }, diff --git a/docs/bun.lockb b/docs/bun.lockb index b37cfb40f..7b76508d1 100755 Binary files a/docs/bun.lockb and b/docs/bun.lockb differ diff --git a/docs/deno.lock b/docs/deno.lock index 885f5082d..028687487 100644 --- a/docs/deno.lock +++ b/docs/deno.lock @@ -58,25 +58,25 @@ "workspace": { "packageJson": { "dependencies": [ - "npm:@astrojs/check@^0.7.0", + "npm:@astrojs/check@0.9.3", "npm:@astrojs/deno@5.0.1", - "npm:@astrojs/markdown-remark@^5.1.0", - "npm:@astrojs/partytown@^2.1.1", + "npm:@astrojs/markdown-remark@5.2.0", + "npm:@astrojs/partytown@2.1.2", "npm:@astrojs/sitemap@^3.1.6", - "npm:@astrojs/starlight@^0.25.5", + "npm:@astrojs/starlight@0.27.0", "npm:@bazel/bazelisk@^1.19.0", "npm:@beoe/pan-zoom@^0.0.3", "npm:@beoe/rehype-mermaid@^0.0.1", "npm:@biomejs/biome@^1.8.1", "npm:@deno/astro-adapter@^0.1.3", "npm:@lorenzo_lewis/starlight-utils@^0.1.1", - "npm:@tailwindcss/vite@^4.0.0-alpha.19", + "npm:@tailwindcss/vite@^4.0.0-alpha.23", "npm:@types/bun@latest", - "npm:astro@^4.5.12", + "npm:astro@4.15.4", "npm:rehype-autolink-headings@^7.1.0", "npm:rehype-mermaid@^2.1.0", "npm:remark@^15.0.1", - "npm:tailwindcss@^4.0.0-alpha.19", + "npm:tailwindcss@^4.0.0-alpha.23", "npm:typescript@^5.4.5" ] } diff --git a/docs/package.json b/docs/package.json index 27492dd68..9cac9ff5f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -6,9 +6,9 @@ "sync": "astro sync", "astro": "astro", "biome": "biome", - "docs": "bun run docs.build && bun run docs.generate", - "docs.build": "cd .. && unset TMPDIR TMP; bazelisk build nativelink-config:docs_json && cd docs && bun run src/utils/metaphase_aot.ts", - "docs.generate": "bun run src/utils/md_to_mdx_aot.ts", + "docs": "bun run build.docs && bun run generate.docs", + "build.docs": "cd .. && unset TMPDIR TMP; bazelisk build nativelink-config:docs_json && cd docs && bun run src/utils/metaphase_aot.ts", + "generate.docs": "bun run src/utils/md_to_mdx_aot.ts", "build": "bun fix && astro build", "check": "biome ci . && astro check", "dev": "astro dev", @@ -24,19 +24,19 @@ "setup": "bun install && bun run deno install -Arf jsr:@deno/deployctl@1.12.0" }, "dependencies": { - "@astrojs/check": "^0.7.0", + "@astrojs/check": "0.9.3", "@astrojs/deno": "5.0.1", - "@astrojs/markdown-remark": "^5.1.0", - "@astrojs/partytown": "^2.1.1", + "@astrojs/markdown-remark": "5.2.0", + "@astrojs/partytown": "2.1.2", "@astrojs/sitemap": "^3.1.6", - "@astrojs/starlight": "^0.25.5", + "@astrojs/starlight": "0.27.0", "@beoe/pan-zoom": "^0.0.3", "@beoe/rehype-mermaid": "^0.0.1", "@deno/astro-adapter": "^0.1.3", - "@tailwindcss/vite": "^4.0.0-alpha.19", - "astro": "^4.5.12", + "@tailwindcss/vite": "^4.0.0-alpha.23", + "astro": "4.15.4", "rehype-mermaid": "^2.1.0", - "tailwindcss": "^4.0.0-alpha.19" + "tailwindcss": "^4.0.0-alpha.23" }, "devDependencies": { "@bazel/bazelisk": "^1.19.0", diff --git a/docs/src/styles/custom.css b/docs/src/styles/custom.css index 18debe3f9..458d4ab9c 100644 --- a/docs/src/styles/custom.css +++ b/docs/src/styles/custom.css @@ -18,3 +18,52 @@ html[data-theme="dark"] .beoe-light { html { scroll-behavior: smooth; } + +.buttons { + display: flex; + justify-content: center; + gap: 10px; + margin-top: 20px; +} + +.buttons button { + background-color: #4A90E2; /* A nice blue color */ + color: white; + font-size: 18px; + font-weight: bold; + border: none; + border-radius: 50%; + width: 50px; + height: 50px; + cursor: pointer; + transition: transform 0.2s ease, background-color 0.2s ease; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + display: flex; + justify-content: center; + align-items: center; +} + +.buttons button:hover { + background-color: #357ABD; /* Darker blue on hover */ + transform: scale(1.1); +} + +.buttons button:active { + background-color: #2C6BA1; /* Even darker blue on active */ + transform: scale(1.05); +} + +.buttons button:focus { + outline: none; + box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.5); +} + +.buttons button:disabled { + background-color: #B0BEC5; /* Grey color for disabled state */ + cursor: not-allowed; + transform: none; +} + +.buttons button:disabled:hover { + transform: none; +} diff --git a/docs/src/styles/tailwind.css b/docs/src/styles/tailwind.css index b398a123f..ec72093a1 100644 --- a/docs/src/styles/tailwind.css +++ b/docs/src/styles/tailwind.css @@ -1,4 +1,26 @@ -/* @import "tailwindcss"; */ +@import "tailwindcss"; + +a { + @apply underline; +} + + /* Header Links and Sidebar classes generated by astro */ +a.astro-3ii7xxms, +a.astro-vtgkq7vy { + @apply no-underline; +} + +#logo { + @apply flex justify-center items-center; +} + +#description { + @apply text-lg font-semibold; +} + +#badges { + @apply flex-wrap gap-0.5 md:px-20; +} /* @theme { --color-primaryColor: rgb(99, 102, 241); diff --git a/docs/src/utils/md_to_mdx.ts b/docs/src/utils/md_to_mdx.ts index 9d3ec52ed..84ba1bace 100644 --- a/docs/src/utils/md_to_mdx.ts +++ b/docs/src/utils/md_to_mdx.ts @@ -2,19 +2,28 @@ import type { Blockquote, Code, Heading, + Image, InlineCode, Paragraph, + PhrasingContent, Root, RootContent, Text, } from "mdast"; + import { remark } from "remark"; import remarkMdx from "remark-mdx"; import remarkParse from "remark-parse"; import remarkStringify from "remark-stringify"; import { visit } from "unist-util-visit"; -const DEFAULT_TITLE = "Default Title"; +export type MarkdownProps = { + title: string; + description: string; + pagefind?: boolean; + assets?: string[]; +}; + const BLOCK_TYPES = ["caution", "note", "tip"]; export function parseMarkdown(markdown: string): Root { @@ -34,7 +43,7 @@ function extractTitleFromTree(tree: Root): { title: string; index: number; } { - let title = DEFAULT_TITLE; + let title = "Default Title"; let titleIndex = -1; for (let i = 0; i < tree.children.length; i++) { @@ -66,12 +75,12 @@ function extractTextFromNode(node: Heading | Paragraph): string { export function generateFrontMatter( title: string, description: string, - pagefind: boolean, + pagefind = true, ): string { return `--- title: "${title}" description: "${description}" -pagefind: ${pagefind} +pagefind: ${pagefind ? "true" : "false"} --- `; } @@ -183,55 +192,109 @@ function removeValeAndGitCliffComments(markdown: string): string { function processLines(lines: string[]): string[] { const processedLines = []; - let inMermaidBlock = false; - let inCodeBlock = false; + const block = { + code: false, + list: false, + }; for (const line of lines) { - if (isMermaidBlockStart(line)) { - inMermaidBlock = true; - processedLines.push(line); - continue; - } - if (isBlockEnd(line, inMermaidBlock)) { - inMermaidBlock = false; - processedLines.push(line); + if (processCodeBlock(line, block.code, processedLines)) { + block.code = !block.code; continue; } - if (isCodeBlock(line, inMermaidBlock)) { - inCodeBlock = !inCodeBlock; - processedLines.push(line); + block.list = processListBlock(line, block.list, processedLines); + if (block.list) { continue; } - if (inMermaidBlock || inCodeBlock || isSpecialLine(line)) { - processedLines.push(line); + if (processSpecialLine(line, processedLines)) { continue; } processedLines.push(escapeHtml(line)); } + // Ensure any unclosed list is closed + closeList(block.list, processedLines); + return processedLines; } -function isMermaidBlockStart(line: string): boolean { - return line.trim().startsWith("```mermaid"); +// Utility to escape HTML entities +function replaceHtml(unsafe: string): string { + return unsafe + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + +function processCodeBlock( + line: string, + codeBlock: boolean, + processedLines: string[], + // languages: string[], +): boolean { + if (isCodeBlock(line)) { + processedLines.push(line); + return true; + } + + if (codeBlock) { + processedLines.push(line); + return true; + } + + return false; } -function isBlockEnd(line: string, inMermaidBlock: boolean): boolean { - return inMermaidBlock && line.trim() === "```"; +function isCodeBlock(line: string): boolean { + const trimmedLine = line.trim(); + return trimmedLine.startsWith("```"); } -function isCodeBlock(line: string, inMermaidBlock: boolean): boolean { - return line.trim().startsWith("```") && !inMermaidBlock; +function processListBlock( + line: string, + listBlock: boolean, + processedLines: string[], +): boolean { + let isListBlock = listBlock; + + if (/^\d+\.\s+\*\*(.+)\*\*:/.test(line)) { + isListBlock = closeList(isListBlock, processedLines); + processedLines.push(line); + processedLines.push('"); + return false; + } + return inList; } function escapeHtml(line: string): string { @@ -242,27 +305,246 @@ function escapeHtml(line: string): string { } export async function transformMarkdownToMdx( - markdown: string, - description: string, - pagefind = true, + input: string, + docs: MarkdownProps, + // languages: string[], ): Promise { - const preprocessedMarkdown = preProcessMarkdown(markdown); + const preprocessedMarkdown = preProcessMarkdown(input); const tree = parseMarkdown(preprocessedMarkdown); - const { title, content } = extractTitle(tree); - let transformedContent = transformGitHubMarkdown(content); + // Extract title and content + const { content } = extractTitle(tree); + + // Apply transformations in sequence + // Prepend the import statements to the content + let transformedContent = docs.assets + ? [...generateAssetImports(docs.assets), ...content] + : [...content]; + + // Apply transformations for target IDs + const targetIds = ["logo", "description", "badges"]; + for (const targetId of targetIds) { + transformedContent = processTarget(transformedContent, targetId); + } + + // GitHub Markdown specific transformations + transformedContent = transformGitHubMarkdown(transformedContent); + + // Preserve inline code transformedContent = preserveInlineCode(transformedContent); - const modifiedMarkdown = remark() - .use(remarkStringify) - .stringify({ type: "root", children: transformedContent }); + // Reassemble the tree with the transformed content + tree.children = transformedContent; + // Convert the transformed tree back to Markdown + const modifiedMarkdown = remark().use(remarkStringify).stringify(tree); + + // Convert Markdown to MDX const processedMdx = await remark() .use(remarkParse) .use(remarkMdx) .use(remarkStringify) .process(modifiedMarkdown); - const frontMatter = generateFrontMatter(title, description, pagefind); + // Generate front matter + const frontMatter = generateFrontMatter( + docs.title, + docs.description, + docs.pagefind, + ); + + // Return the final MDX content return frontMatter + String(processedMdx); } + +// Function to generate asset import statements +export function generateAssetImports(assets: string[]): RootContent[] { + return assets.map((asset) => { + const assetName = transformToPascalCase(asset); + return { + type: "html", + value: `import ${assetName} from '${asset}';\n`, + }; + }); +} + +// Helper function to convert file paths to PascalCase +function transformToPascalCase(filePath: string): string { + const fileName = filePath.split("/").pop()?.split(".")[0]; + if (!fileName) { + throw new Error("Invalid file path"); + } + + return fileName + .split(/[^a-zA-Z0-9]/) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()) + .join(""); +} + +export function processTarget( + tree: RootContent[], + targetId: string, +): RootContent[] { + const alignRegex = /align=["'](center|left|right)["']/i; + + const content = tree.map((node, index, children) => { + if (isMatchingTargetNode(node, targetId)) { + replaceAlignAttributeInNode(node, alignRegex); + if (targetId === "logo") { + transformImgSrc(node); + } + if (targetId === "badges") { + ensureBadgeClasses(node); + processAndWrapNextBlock(children, index); + } + } + return node; + }); + + return content; +} + +function ensureBadgeClasses(node: RootContent): void { + if (isHtmlNode(node)) { + // Replace the class="" attribute directly in node.value + node.value = node.value.replace(/class="([^"]*)"/g, (_, classes) => { + // Ensure the required classes are present + const requiredClasses = [ + "flex", + "justify-center", + "items-center", + "flex-wrap", + "gap-0.5", + ]; + + // Split the existing classes into an array + const existingClasses = classes.trim().split(/\s+/); + + // Add any missing required classes + for (const requiredClass of requiredClasses) { + if (!existingClasses.includes(requiredClass)) { + existingClasses.push(requiredClass); + } + } + + // Return the updated class attribute + return `class="${existingClasses.join(" ")}"`; + }); + } +} + +// Helper function to check if a node matches the target ID +function isMatchingTargetNode(node: RootContent, targetId: string): boolean { + if (node.type === "html" && typeof node.value === "string") { + const idRegex = new RegExp(`id=["']${targetId}["']`, "i"); + return idRegex.test(node.value); + } + return false; +} + +// Function to replace the align attribute with a CSS class +function replaceAlignAttributeInNode( + node: RootContent, + alignRegex: RegExp, +): void { + if (node.type === "html" && typeof node.value === "string") { + const newValue = node.value.replace(alignRegex, (_, p1) => { + const alignment = p1.toLowerCase() as keyof typeof alignToClassMap; + return `class="${alignToClassMap[alignment]}"`; + }); + node.value = newValue; + } +} + +function transformImgSrc(node: RootContent): void { + if (isHtmlNode(node)) { + node.value = node.value + // Remove the entire tag and replace it with the two tags + .replace( + /[\s\S]*?]*>\s*]*>\s*]*src="[^"]*"[^>]*>\s*<\/picture>/g, + ` + NativeLink Logo + NativeLink Logo + `, + ); + } +} + +// Type guard to check if a node is an HTML node with a value property +function isHtmlNode( + node: RootContent, +): node is RootContent & { value: string } { + return node.type === "html" && typeof node.value === "string"; +} + +function wrapLinksInBlocks(blockNode: RootContent): RootContent[] { + if (blockNode.type !== "paragraph") { + return [blockNode]; + } + + const block = blockNode as Paragraph; + let modified = false; + + const newChildren = block.children.map((child) => { + if ( + child.type === "link" && + child.children.some((c) => c.type === "image") + ) { + const image = child.children.find((c) => c.type === "image") as Image; + if (image) { + modified = true; + const altText = replaceHtml(image.alt || ""); + const imageUrl = replaceHtml(image.url || ""); + const linkUrl = replaceHtml(child.url || ""); + + return { + type: "html", + value: `

\n[![${altText}](${imageUrl})](${linkUrl})\n

`, + } as RootContent; + } + } + return child; + }); + + if (modified) { + return [ + { + type: "paragraph", + children: newChildren.filter((node): node is PhrasingContent => !!node), + }, + ]; + } + return [blockNode]; +} + +// Function to process and wrap the next block in the children array +function processAndWrapNextBlock( + children: RootContent[], + index: number, +): Paragraph | null { + const nextNode = children[index + 1]; + + // Ensure the next node exists and is a paragraph + if (nextNode && nextNode.type === "paragraph") { + // console.log(nextNode) + // Wrap links in blocks if applicable + const wrappedParagraphs = wrapLinksInBlocks(nextNode); + + // If the paragraph has been modified, replace it in the children array + if (wrappedParagraphs.length > 0) { + children.splice(index + 1, 1, ...wrappedParagraphs); + return wrappedParagraphs[0] as Paragraph; + } + } + + // If no wrapping was done, return null to signal no changes + return null; +} + +// Alignments to CSS class mapping +type Alignments = "center" | "left" | "right"; +const alignToClassMap: Record = { + center: "flex justify-center items-center", + left: "flex justify-start items-center", + right: "flex justify-end items-center", +}; diff --git a/docs/src/utils/md_to_mdx_aot.ts b/docs/src/utils/md_to_mdx_aot.ts index a59fc69cf..89df7c049 100644 --- a/docs/src/utils/md_to_mdx_aot.ts +++ b/docs/src/utils/md_to_mdx_aot.ts @@ -22,65 +22,103 @@ async function writeMdxFile( } } -async function convertMarkdownToMdx( - filePath: string, - outputFilePath: string, - description: string, - pagefind = true, -): Promise { +async function convertMarkdownToMdx(file: ConvertFileType): Promise { try { - const markdown = await readMarkdownFile(filePath); - const mdxContent = await transformMarkdownToMdx( - markdown, - description, - pagefind, - ); - await writeMdxFile(outputFilePath, mdxContent); + const markdown = await readMarkdownFile(file.input); + const mdxContent = await transformMarkdownToMdx(markdown, file.docs); + await writeMdxFile(file.output, mdxContent); } catch (err) { console.error(`Error during conversion: ${err}`); throw err; } } -// Convert the actual files. -convertMarkdownToMdx( - "../local-remote-execution/README.md", - "src/content/docs/explanations/lre.mdx", - "Local Remote Execution architecture", -); -convertMarkdownToMdx( - "../CONTRIBUTING.md", - "src/content/docs/contribute/guidelines.mdx", - "NativeLink contribution guidelines", -); -convertMarkdownToMdx( - "README.md", - "src/content/docs/contribute/docs.mdx", - "Working on documentation", -); -convertMarkdownToMdx( - "../nativelink-config/README.md", - "src/content/docs/config/configuration-intro.mdx", - "NativeLink configuration guide", -); -convertMarkdownToMdx( - "../deployment-examples/chromium/README.md", - "src/content/docs/deployment-examples/chromium.mdx", - "NativeLink deployment example for Chromium", -); -convertMarkdownToMdx( - "../deployment-examples/kubernetes/README.md", - "src/content/docs/deployment-examples/kubernetes.mdx", - "NativeLink deployment example for Kubernetes", -); -convertMarkdownToMdx( - "../CHANGELOG.md", - "src/content/docs/reference/changelog.mdx", - "NativeLink's Changelog", - false, // Set pagefind to false for changelog -); -convertMarkdownToMdx( - "../README.md", - "src/content/docs/introduction/setup.mdx", - "Get started with NativeLink", -); +export type ConvertFileType = { + input: string; + output: string; + docs: { + title: string; + description: string; + pagefind?: boolean; + assets?: string[]; + }; +}; + +// Directories +const rootDir = "../"; +const docsDir = "src/content/docs"; +const assetsDir = "@assets"; + +const filesToConvert: ConvertFileType[] = [ + { + input: `${rootDir}/local-remote-execution/README.md`, + output: `${docsDir}/explanations/lre.mdx`, + docs: { + title: "Local Remote Execution", + description: "Local Remote Execution architecture", + }, + }, + { + input: `${rootDir}/CONTRIBUTING.md`, + output: `${docsDir}/contribute/guidelines.mdx`, + docs: { + title: "NativeLink contribution guidelines", + description: "Contribution Guidelines", + }, + }, + { + input: "README.md", + output: `${docsDir}/contribute/docs.mdx`, + docs: { + title: "The NativeLink documentation", + description: "Working on documentation", + }, + }, + { + input: `${rootDir}/nativelink-config/README.md`, + output: `${docsDir}/config/configuration-intro.mdx`, + docs: { + title: "NativeLink configuration guide", + description: "NativeLink configuration guide", + }, + }, + { + input: `${rootDir}/deployment-examples/chromium/README.md`, + output: `${docsDir}/deployment-examples/chromium.mdx`, + docs: { + title: "NativeLink deployment example for Chromium", + description: "NativeLink deployment example for Chromium", + }, + }, + { + input: `${rootDir}/deployment-examples/kubernetes/README.md`, + output: `${docsDir}/deployment-examples/kubernetes.mdx`, + docs: { + title: "Local Remote Execution architecture", + description: "Local Remote Execution architecture", + }, + }, + { + input: `${rootDir}/CHANGELOG.md`, + output: `${docsDir}/reference/changelog.mdx`, + docs: { + title: "Changelog", + description: "NativeLink's Changelog", + pagefind: false, // Set pagefind to false for changelog + }, + }, + { + input: `${rootDir}/README.md`, + output: `${docsDir}/introduction/setup.mdx`, + docs: { + title: "Introduction", + description: "Get started with NativeLink", + pagefind: true, + assets: [`${assetsDir}/logo-dark.svg`, `${assetsDir}/logo-light.svg`], + }, + }, +]; + +filesToConvert.map((file) => { + convertMarkdownToMdx(file); +}); diff --git a/docs/starlight.conf.ts b/docs/starlight.conf.ts index dcda3f7a1..0bc84bc04 100644 --- a/docs/starlight.conf.ts +++ b/docs/starlight.conf.ts @@ -34,7 +34,7 @@ export const starlightConfig = { // See https://diataxis.fr/ for details. { label: "Getting Started", - collapsed: false, + collapsed: true, items: [ { label: "Introduction", @@ -55,7 +55,7 @@ export const starlightConfig = { // content without elaborate explanations. Tutorials should have a // clear goal and a straightforward "follow-these-commands" structure. label: "NativeLink Cloud", - collapsed: false, + collapsed: true, items: [ { label: "Bazel", @@ -84,7 +84,7 @@ export const starlightConfig = { // need to be "complete". They should provide practical guidance for // real-world use-cases. label: "Configuring NativeLink", - collapsed: false, + collapsed: true, items: [ { label: "Configuration Introduction", @@ -105,7 +105,7 @@ export const starlightConfig = { // need to be "complete". They should provide practical guidance for // real-world use-cases. label: "On-Prem Examples", - collapsed: false, + collapsed: true, items: [ { label: "On-Prem Overview", @@ -126,7 +126,7 @@ export const starlightConfig = { // internal functionality and design concepts. Explanations should // explain design decisions, constraints, etc. label: "Understanding NativeLink", - collapsed: false, + collapsed: true, items: [ { label: "Architecture", @@ -147,7 +147,7 @@ export const starlightConfig = { // common questions and confusions about esoteric tooling and // concepts. It aims to help new users feel more at ease and label: "FAQ", - collapsed: false, + collapsed: true, items: [ { label: "Is NativeLink Free?", @@ -188,7 +188,7 @@ export const starlightConfig = { // contributors. They should provide practical guidance for // real-world use-cases. label: "For Contributors", - collapsed: false, + collapsed: true, items: [ { label: "Contribution Guidelines", @@ -217,7 +217,7 @@ export const starlightConfig = { // descriptions with the intent to be used as consulting material. // Mostly autogenerated to stay in sync with the codebase. label: "Reference", - collapsed: false, + collapsed: true, items: [ { label: "Glossary", diff --git a/docs/tsconfig.json b/docs/tsconfig.json index 1057e7cab..4da5cd7af 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@components/*": ["./src/components/*"] + "@components/*": ["./src/components/*"], + "@assets/*": ["./src/assets/*"] } } }