Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions .eslintrc.json

This file was deleted.

7 changes: 5 additions & 2 deletions .github/workflows/linkcheck.yml → .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: linkcheck
name: CI

on:
push:
Expand All @@ -23,10 +23,13 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run Biome checks
run: bun run biome check app/ components/ lib/ scripts/ content/ --error-on-warnings

- name: Generate llms.txt files
run: bun run generate-llms
env:
LLMS_BASE_URL: https://docs.hiro.so

- name: Run link validation
run: bun run lint
run: bun run validate-links
26 changes: 26 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
31 changes: 31 additions & 0 deletions .zed/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"format_on_save": "on",
"formatter": "language_server",
"languages": {
"JavaScript": {
"format_on_save": "on",
"formatter": "language_server",
"language_servers": ["biome", "!vtsls", "!typescript-language-server", "!eslint"]
},
"TypeScript": {
"format_on_save": "on",
"formatter": "language_server",
"language_servers": ["biome", "!vtsls", "!typescript-language-server", "!eslint"]
},
"TSX": {
"format_on_save": "on",
"formatter": "language_server",
"language_servers": ["biome", "!vtsls", "!typescript-language-server", "!eslint"]
},
"JSON": {
"format_on_save": "on",
"formatter": "language_server",
"language_servers": ["biome", "..."]
},
"Markdown": {
"format_on_save": "on",
"formatter": "language_server",
"remove_trailing_whitespace_on_save": true
}
}
}
140 changes: 54 additions & 86 deletions app/(docs)/[...slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,80 +1,74 @@
import fs from "fs/promises";
import matter from "gray-matter";
import { source } from "@/lib/source";
import { notFound } from "next/navigation";
import type { HeadingProps } from "@/types";
import { getMDXComponents } from "@/components/mdx";
import { API } from "@/components/reference/api-page";
import { APIPage } from "@/components/openapi/api-page";
import { Badge } from "@/components/ui/badge";
import fs from 'node:fs/promises';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import matter from 'gray-matter';
import * as lucideIcons from 'lucide-react';
import { CheckIcon } from 'lucide-react';
import { notFound } from 'next/navigation';
import { Callout } from '@/components/callout';
import { FeedbackWrapper } from '@/components/feedback/feedback-wrapper';
import { InteractiveHeader, InteractiveLayout } from '@/components/layouts/interactive';
import {
DocsPage,
DocsPageLayout,
DocsPageHeader,
DocsPageBreadcrumb,
DocsPageContent,
DocsPageContentWrapper,
DocsPageBreadcrumb,
DocsPageTitle,
DocsPageDescription,
DocsPageHeader,
DocsPageLayout,
DocsPageProse,
} from "@/components/layouts/page";
import {
InteractiveHeader,
InteractiveLayout,
} from "@/components/layouts/interactive";
import defaultMdxComponents from "fumadocs-ui/mdx";
import { LLMShare } from "@/components/llm-share";
import { CheckIcon } from "lucide-react";
import { TagFilterSystem } from "@/components/ui/tag-filter-system";
import { getAllFilterablePages } from "@/lib/source";
import { Mermaid } from "@/components/mdx/mermaid";
import { Callout } from "@/components/callout";
import * as customIcons from "@/components/ui/icon";
import * as lucideIcons from "lucide-react";
import { FeedbackWrapper } from "@/components/feedback/feedback-wrapper";
DocsPageTitle,
} from '@/components/layouts/page';
import { LLMShare } from '@/components/llm-share';
import { getMDXComponents } from '@/components/mdx';
import { Mermaid } from '@/components/mdx/mermaid';
import { APIPage } from '@/components/openapi/api-page';
import { API } from '@/components/reference/api-page';
import { Badge } from '@/components/ui/badge';
import * as customIcons from '@/components/ui/icon';
import { TagFilterSystem } from '@/components/ui/tag-filter-system';
import { getAllFilterablePages, source } from '@/lib/source';
import type { HeadingProps } from '@/types';

export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
export default async function Page(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();

const fileContent = await fs.readFile(page.data._file.absolutePath, "utf-8");
const fileContent = await fs.readFile(page.data._file.absolutePath, 'utf-8');
const { content: rawMarkdownContent } = matter(fileContent);

const LLMContent = rawMarkdownContent
.split("\n")
.filter((line) => !line.trim().startsWith("import"))
.join("\n")
.split('\n')
.filter((line) => !line.trim().startsWith('import'))
.join('\n')
.trim();

const MDX = page.data.body;

if (!MDX) {
console.error("MDX component is undefined for page:", page.url);
console.error('MDX component is undefined for page:', page.url);
notFound();
}

// Get all filterable pages for tag filtering
const allFilterablePages = getAllFilterablePages();

// Extract section from current page URL for scoped filtering
const currentSection = page.url.split("/").filter(Boolean)[1] || "general";
const currentSection = page.url.split('/').filter(Boolean)[1] || 'general';

// Helper function to get icon component
const getIconComponent = (iconName: string) => {
const iconProps = { className: "w-3 h-3 shrink-0" };
const iconProps = { className: 'w-3 h-3 shrink-0' };

// Check custom icons first
const CustomIcon = (customIcons as any)[iconName];
if (CustomIcon && typeof CustomIcon === "function") {
if (CustomIcon && typeof CustomIcon === 'function') {
return <CustomIcon {...iconProps} />;
}

// Try with "Icon" suffix for custom icons
const CustomIconWithSuffix = (customIcons as any)[`${iconName}Icon`];
if (CustomIconWithSuffix && typeof CustomIconWithSuffix === "function") {
if (CustomIconWithSuffix && typeof CustomIconWithSuffix === 'function') {
return <CustomIconWithSuffix {...iconProps} />;
}

Expand All @@ -88,7 +82,7 @@ export default async function Page(props: {

for (const pattern of lucidePatterns) {
const LucideIcon = (lucideIcons as any)[pattern];
if (LucideIcon && typeof LucideIcon === "function") {
if (LucideIcon && typeof LucideIcon === 'function') {
return <LucideIcon {...iconProps} />;
}
}
Expand Down Expand Up @@ -140,10 +134,8 @@ export default async function Page(props: {
/>
),
h1: ({ children, ...props }: HeadingProps) => {
const H1 =
defaultMdxComponents.h1 as React.ComponentType<HeadingProps>;
const id =
typeof children === "string" ? children : undefined;
const H1 = defaultMdxComponents.h1 as React.ComponentType<HeadingProps>;
const id = typeof children === 'string' ? children : undefined;
return (
<H1 id={id} {...props}>
{children}
Expand All @@ -158,28 +150,21 @@ export default async function Page(props: {
/>
),
hr: (props: React.PropsWithChildren) => (
<hr
{...props}
className="border-t border-border/50 mt-0"
/>
<hr {...props} className="border-t border-border/50 mt-0" />
),
input: (
props: React.InputHTMLAttributes<HTMLInputElement>,
) => {
if (props.type === "checkbox") {
input: (props: React.InputHTMLAttributes<HTMLInputElement>) => {
if (props.type === 'checkbox') {
return (
<div className="relative inline-flex items-center mr-2">
<input {...props} className="sr-only" />
<div
className={`w-4 h-4 border-2 rounded-sm flex items-center justify-center ${
props.checked
? "bg-brand-orange border-brand-orange text-white"
: "border-border bg-background"
? 'bg-brand-orange border-brand-orange text-white'
: 'border-border bg-background'
}`}
>
{props.checked && (
<CheckIcon className="w-3 h-3" />
)}
{props.checked && <CheckIcon className="w-3 h-3" />}
</div>
</div>
);
Expand All @@ -191,10 +176,7 @@ export default async function Page(props: {
/>
</DocsPageProse>
<div className="border-b border-border/50" />
<FeedbackWrapper
pageTitle={page.data.title}
pagePath={page.url}
/>
<FeedbackWrapper pageTitle={page.data.title} pagePath={page.url} />
</DocsPageContent>
</DocsPageContentWrapper>
</DocsPageLayout>
Expand Down Expand Up @@ -243,10 +225,8 @@ export default async function Page(props: {
/>
),
h1: ({ children, ...props }: HeadingProps) => {
const H1 =
defaultMdxComponents.h1 as React.ComponentType<HeadingProps>;
const id =
typeof children === "string" ? children : undefined;
const H1 = defaultMdxComponents.h1 as React.ComponentType<HeadingProps>;
const id = typeof children === 'string' ? children : undefined;
return (
<H1 id={id} {...props}>
{children}
Expand All @@ -261,28 +241,21 @@ export default async function Page(props: {
/>
),
hr: (props: React.PropsWithChildren) => (
<hr
{...props}
className="border-t border-border/50 mt-0"
/>
<hr {...props} className="border-t border-border/50 mt-0" />
),
input: (
props: React.InputHTMLAttributes<HTMLInputElement>,
) => {
if (props.type === "checkbox") {
input: (props: React.InputHTMLAttributes<HTMLInputElement>) => {
if (props.type === 'checkbox') {
return (
<div className="relative inline-flex items-center mr-2">
<input {...props} className="sr-only" />
<div
className={`w-4 h-4 border-2 rounded-sm flex items-center justify-center ${
props.checked
? "bg-brand-orange border-brand-orange text-white"
: "border-border bg-background"
? 'bg-brand-orange border-brand-orange text-white'
: 'border-border bg-background'
}`}
>
{props.checked && (
<CheckIcon className="w-3 h-3" />
)}
{props.checked && <CheckIcon className="w-3 h-3" />}
</div>
</div>
);
Expand All @@ -294,10 +267,7 @@ export default async function Page(props: {
/>
</DocsPageProse>
<div className="border-b border-border/50" />
<FeedbackWrapper
pageTitle={page.data.title}
pagePath={page.url}
/>
<FeedbackWrapper pageTitle={page.data.title} pagePath={page.url} />
</DocsPageContent>
</DocsPageContentWrapper>
</DocsPageLayout>
Expand All @@ -314,9 +284,7 @@ export async function generateStaticParams() {
);
}

export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
export async function generateMetadata(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
Expand Down
Loading