Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
da0747a
Add documentation routing and layout for Roam and Obsidian (#928)
mdroidian Mar 30, 2026
b9b9ddb
Update ESLint configuration, package scripts, and add documentation i…
mdroidian Mar 31, 2026
0110a5b
Refactor Logo component to use SVG instead of Image component
mdroidian Mar 31, 2026
3fdad2b
Add Nextra CSS reset class for content width
mdroidian Mar 31, 2026
7348435
separate search
mdroidian Mar 31, 2026
625921b
docs landing page
mdroidian Mar 31, 2026
ac0eb71
Add DocsPageTemplate component and refactor documentation page rendering
mdroidian Mar 31, 2026
e984a0d
format
mdroidian Apr 1, 2026
a3ddfcc
Update documentation by removing redundant overview sections and enha…
mdroidian Apr 1, 2026
db72006
exploring your discourse graph
mdroidian Apr 1, 2026
ca1428c
Refactor and update documentation for relations and grammar
mdroidian Apr 1, 2026
ed29a8f
Enhance documentation routing and redirects
mdroidian Apr 1, 2026
311123d
Remove outdated installation instructions for Roam Depot and update r…
mdroidian Apr 1, 2026
b0093cd
Refactor documentation for consistency and clarity
mdroidian Apr 1, 2026
7f4d116
Refactor navigation links and update global styles for marketing site
mdroidian Apr 7, 2026
ab73eb3
Merge branch 'main' into eng-1590-migrate-existing-docs-content-into-…
mdroidian Apr 7, 2026
499e883
eslint-disable @typescript-eslint/naming-convention
mdroidian Apr 7, 2026
dc21f8f
Add search result link styles for light mode
mdroidian Apr 7, 2026
b5520bc
Update package.json
mdroidian Apr 7, 2026
1383145
Update apps/website/docsRouteMap.ts
mdroidian Apr 7, 2026
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: 11 additions & 0 deletions apps/website/app/(docs)/docs/(landing)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import "~/globals.css";

type DocsLandingLayoutProps = {
children: React.ReactNode;
};

const DocsLandingLayout = ({
children,
}: DocsLandingLayoutProps): React.ReactElement => <>{children}</>;

export default DocsLandingLayout;
98 changes: 98 additions & 0 deletions apps/website/app/(docs)/docs/(landing)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type { Metadata } from "next";
import { ArrowRight } from "lucide-react";
import { Card, CardContent } from "@repo/ui/components/ui/card";
import { PlatformBadge } from "~/components/PlatformBadge";
import { Logo } from "~/components/Logo";

export const metadata: Metadata = {
title: "Documentation",
description:
"Choose the Discourse Graphs documentation for Roam Research or Obsidian.",
};

const DOCS_DESTINATIONS = [
{
description:
"Installation, graph building, querying, and advanced workflows for the Roam Research plugin.",
href: "/docs/roam",
platform: "roam",
title: "Roam docs",
},
{
description:
"Setup, node and relation authoring, sync, and workspace configuration for the Obsidian plugin.",
href: "/docs/obsidian",
platform: "obsidian",
title: "Obsidian docs",
},
] as const;

const DocsLandingPage = (): React.ReactElement => {
return (
<div className="marketing-site font-[family:var(--font-inter)] min-h-screen bg-neutral-light text-neutral-dark">
<header className="border-b border-black/5 bg-white/80 backdrop-blur">
<div className="mx-auto flex max-w-6xl items-center justify-between gap-6 px-6 py-4">
<Logo />
{/* Use hard navigations across the marketing/docs boundary because Next client transitions can leave the wrong route CSS active. */}
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
<a
href="/"
className="text-sm font-medium text-neutral-dark/70 hover:text-neutral-dark"
>
Back to site
</a>
</div>
</header>

<main className="px-6 py-16 sm:py-24">
<div className="mx-auto max-w-6xl">
<section className="max-w-3xl">
<p className="text-sm font-semibold uppercase tracking-[0.18em] text-secondary">
Documentation
</p>
<h1 className="mt-4 text-4xl font-semibold tracking-tight text-primary sm:text-5xl">
Choose your docs
</h1>
<p className="mt-6 text-lg leading-8 text-neutral-dark/80">
Discourse Graphs has separate documentation for each client. Pick
the one you are using to get the right setup instructions,
workflows, and reference pages.
</p>
</section>

<section className="mt-12 grid gap-6 md:grid-cols-2">
{DOCS_DESTINATIONS.map(({ description, href, platform, title }) => (
// Use a document navigation here so Nextra and marketing CSS do not coexist during client-side route transitions.
/* eslint-disable-next-line @next/next/no-html-link-for-pages */
<a key={href} href={href} className="group block h-full">
<Card className="h-full rounded-2xl border border-black/5 bg-white shadow-sm transition-transform duration-200 group-hover:-translate-y-1 group-hover:shadow-lg">
<CardContent className="flex h-full flex-col gap-8 p-8">
<div className="flex items-start justify-between gap-4">
<PlatformBadge platform={platform} />
<ArrowRight className="h-5 w-5 shrink-0 text-neutral-dark/40 transition-transform duration-200 group-hover:translate-x-1 group-hover:text-secondary" />
</div>

<div className="space-y-4">
<h2 className="text-2xl font-semibold tracking-tight text-primary">
{title}
</h2>
<p className="text-base leading-7 text-neutral-dark/75">
{description}
</p>
</div>

<p className="mt-auto text-sm font-semibold text-secondary">
Open documentation
</p>
</CardContent>
</Card>
</a>
))}
</section>
</div>
</main>
</div>
);
};

export default DocsLandingPage;
34 changes: 34 additions & 0 deletions apps/website/app/(docs)/docs/_components/DocsPageTemplate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { EvaluateResult } from "nextra";
import { useMDXComponents } from "mdx-components";

type DocsPageTemplateProps = Omit<EvaluateResult, "default"> & {
children: React.ReactNode;
};

const hasPrimaryHeading = (sourceCode: string): boolean =>
/(^|\n)#\s+\S/m.test(sourceCode);

const DocsPageTemplate = ({
children,
metadata,
sourceCode,
...wrapperProps
}: DocsPageTemplateProps): React.ReactElement => {
const { h1, wrapper } = useMDXComponents();
// eslint-disable-next-line @typescript-eslint/naming-convention
const Wrapper = wrapper as React.ComponentType<DocsPageTemplateProps>;
const H1 = h1 as React.ComponentType<
React.HTMLAttributes<HTMLHeadingElement> & {
children: React.ReactNode;
}
>;

return (
<Wrapper metadata={metadata} sourceCode={sourceCode} {...wrapperProps}>
{!hasPrimaryHeading(sourceCode) && <H1>{metadata.title}</H1>}
{children}
</Wrapper>
);
};

export default DocsPageTemplate;
86 changes: 86 additions & 0 deletions apps/website/app/(docs)/docs/_components/DocsThemeLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { PageMapItem } from "nextra";
import { Search } from "nextra/components";
import { Footer, Layout, Navbar } from "nextra-theme-docs";
import { Logo } from "~/components/Logo";

type DocsSearchScope = "roam" | "obsidian";

type DocsThemeLayoutProps = {
children: React.ReactNode;
hideSearch?: boolean;
pageMap: PageMapItem[];
searchScope?: DocsSearchScope;
};

const SEARCH_PLACEHOLDERS: Record<DocsSearchScope, string> = {
obsidian: "Search Obsidian docs...",
roam: "Search Roam docs...",
};

const renderSearch = ({
hideSearch,
searchScope,
}: Pick<DocsThemeLayoutProps, "hideSearch" | "searchScope">):
| React.ReactElement
| null
| undefined => {
if (hideSearch) {
return null;
}

if (!searchScope) {
return undefined;
}

return (
<Search
placeholder={SEARCH_PLACEHOLDERS[searchScope]}
searchOptions={{
filters: {
platform: [searchScope],
},
}}
/>
);
};

const DocsThemeLayout = ({
children,
hideSearch,
pageMap,
searchScope,
}: DocsThemeLayoutProps): React.ReactElement => {
const search = renderSearch({ hideSearch, searchScope });

return (
<div className="nextra-reset">
<Layout
editLink={null}
feedback={{ content: null }}
footer={
<Footer>
Apache 2.0 {new Date().getFullYear()} (c) Discourse Graphs.
</Footer>
}
navbar={
<Navbar
logo={<Logo linked={false} textClassName="text-inherit" />}
projectLink="https://github.com/DiscourseGraphs/discourse-graph"
/>
}
pageMap={pageMap}
search={search}
sidebar={{
defaultMenuCollapseLevel: 1,
}}
toc={{
backToTop: "Back to top",
}}
>
{children}
</Layout>
</div>
);
};

export default DocsThemeLayout;
69 changes: 69 additions & 0 deletions apps/website/app/(docs)/docs/obsidian/[[...mdxPath]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { generateStaticParamsFor, importPage } from "nextra/pages";
import DocsPageTemplate from "../../_components/DocsPageTemplate";

type DocsPageProps = {
params: Promise<{
mdxPath?: string[];
}>;
};

type ImportedPage = Awaited<ReturnType<typeof importPage>>;

const generateAllStaticParams = generateStaticParamsFor("mdxPath");

const loadPage = async (mdxPath?: string[]): Promise<ImportedPage> =>
importPage(["obsidian", ...(mdxPath ?? [])]);

export const generateStaticParams = async (): Promise<
Array<{ mdxPath?: string[] }>
> => {
const staticParams = await generateAllStaticParams();

return staticParams.flatMap(({ mdxPath }) => {
if (!Array.isArray(mdxPath) || mdxPath[0] !== "obsidian") {
return [];
}

const platformPath = mdxPath.slice(1);

return platformPath.length ? [{ mdxPath: platformPath }] : [{}];
});
};

const Page = async ({ params }: DocsPageProps): Promise<React.ReactElement> => {
try {
const { mdxPath } = await params;
const result = await loadPage(mdxPath);
const { default: MDXContent, ...wrapperProps } = result;

return (
<DocsPageTemplate {...wrapperProps}>
<MDXContent params={{ mdxPath: mdxPath ?? [] }} />
</DocsPageTemplate>
);
} catch (error) {
console.error("Error rendering Obsidian docs page:", error);
notFound();
}
};

export const generateMetadata = async ({
params,
}: DocsPageProps): Promise<Metadata> => {
try {
const { mdxPath } = await params;
const { metadata } = await loadPage(mdxPath);

return metadata;
} catch (error) {
console.error("Error generating Obsidian docs metadata:", error);

return {
title: "Obsidian docs",
};
}
};

export default Page;
32 changes: 0 additions & 32 deletions apps/website/app/(docs)/docs/obsidian/[slug]/page.tsx

This file was deleted.

28 changes: 20 additions & 8 deletions apps/website/app/(docs)/docs/obsidian/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { navigation } from "./navigation";
import { Layout } from "~/components/DocsLayout";
import { getPageMap } from "nextra/page-map";
import DocsThemeLayout from "../_components/DocsThemeLayout";
import "../../../(nextra)/nextra-css.css";
import "nextra-theme-docs/style-prefixed.css";

export default function RootLayout({
children,
}: {
type ObsidianDocsLayoutProps = {
children: React.ReactNode;
}) {
return <Layout navigationList={navigation}>{children}</Layout>;
}
};

const ObsidianDocsLayout = async ({
children,
}: ObsidianDocsLayoutProps): Promise<React.ReactElement> => {
const pageMap = await getPageMap("/docs/obsidian");

return (
<DocsThemeLayout pageMap={pageMap} searchScope="obsidian">
{children}
</DocsThemeLayout>
);
};

export default ObsidianDocsLayout;
6 changes: 0 additions & 6 deletions apps/website/app/(docs)/docs/obsidian/page.tsx

This file was deleted.

Loading
Loading