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
4 changes: 2 additions & 2 deletions .docker/caddy/Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

handle @api {
uri strip_prefix /_roomote-api
reverse_proxy host.docker.internal:13101 {
reverse_proxy host.docker.internal:13001 {
lb_try_duration 10s
lb_try_interval 250ms
}
Expand All @@ -58,7 +58,7 @@
}

handle {
reverse_proxy host.docker.internal:13100 {
reverse_proxy host.docker.internal:13000 {
lb_try_duration 10s
lb_try_interval 250ms
}
Expand Down
1 change: 1 addition & 0 deletions apps/web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/.next
/.source
/out
/public/docs
next-env.d.ts

*storybook.log
Expand Down
5 changes: 1 addition & 4 deletions apps/web/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const nextConfig: NextConfig = {
// Always bundle the env files the runtime may need so preview deploys can
// load preview secrets even when build-time env detection resolves differently.
outputFileTracingIncludes: {
'/*': webEnvFiles,
'/*': [...webEnvFiles, '../docs/**/*'],
},
experimental: {
serverActions: {
Expand Down Expand Up @@ -68,9 +68,6 @@ const nextConfig: NextConfig = {
// (e.g. "taskid-web.preview-john.ngrok.app"), so we need *.domain entries.
allowedDevOrigins: getAllowedDevOrigins(process.env.PREVIEW_DOMAINS),
async redirects() {
// Public product docs now live at the standalone Mintlify site
// (https://docs.roomote.dev). Preserve old in-app /docs URLs by
// redirecting them to the external docs site.
return [
{
source: '/docs',
Expand Down
12 changes: 8 additions & 4 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
"check-types": "NODE_OPTIONS='--max-old-space-size=4096' tsc --noEmit",
"check-types:fast": "tsgo --noEmit",
"test": "dotenvx run -f ../../.env.test -- vitest",
"dev:prepare": "rimraf .next",
"dev": "dotenvx run -f ../../.env.local -- next dev --turbopack",
"build": "NODE_OPTIONS='--max-old-space-size=4096' node ./scripts/run-with-app-env.mjs --default-env production -- next build",
"build:debug": "NODE_OPTIONS='--max-old-space-size=4096' node ./scripts/run-with-app-env.mjs --default-env production -- next build --experimental-debug-memory-usage",
"docs:assets": "node ./scripts/sync-docs-assets.mjs",
"dev:prepare": "rimraf .next && pnpm docs:assets",
"dev": "pnpm docs:assets && dotenvx run -f ../../.env.local -- next dev --turbopack",
"build": "pnpm docs:assets && NODE_OPTIONS='--max-old-space-size=4096' node ./scripts/run-with-app-env.mjs --default-env production -- next build",
"build:debug": "pnpm docs:assets && NODE_OPTIONS='--max-old-space-size=4096' node ./scripts/run-with-app-env.mjs --default-env production -- next build --experimental-debug-memory-usage",
"start": "node ./scripts/run-with-app-env.mjs --default-env production -- next start",
"clean": "rimraf .next .turbo out",
"trigger-task-suggestions": "tsx scripts/trigger-task-suggestions.ts",
Expand Down Expand Up @@ -86,10 +87,12 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
"gray-matter": "^4.0.3",
"lucide-react": "^0.575.0",
"modal": "^0.7.6",
"motion": "^12.29.2",
"next": "^16.2.11",
"next-mdx-remote": "^6.0.0",
"next-themes": "^0.4.6",
"pino": "^9.7.0",
"pino-pretty": "^13.1.3",
Expand All @@ -100,6 +103,7 @@
"react-resizable-panels": "^2.1.7",
"recharts": "^3.8.0",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.1",
"remove-markdown": "^0.6.3",
"shiki": "^3.19.0",
"simple-icons": "16.12.0",
Expand Down
9 changes: 9 additions & 0 deletions apps/web/scripts/sync-docs-assets.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { cpSync, existsSync } from 'node:fs';
import { resolve } from 'node:path';

const source = resolve(import.meta.dirname, '../../docs/logo');
const destination = resolve(import.meta.dirname, '../public/docs/logo');

if (existsSync(source)) {
cpSync(source, destination, { recursive: true });
}
23 changes: 23 additions & 0 deletions apps/web/src/app/(onboarding)/setup/DocsMdx.client.test.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

95 changes: 95 additions & 0 deletions apps/web/src/app/(onboarding)/setup/DocsMdx.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { ComponentProps, ReactNode } from 'react';
import {
Alert,
AlertDescription,
AlertTriangle,
Info,
} from '@/components/system';
import { DOCS_BASE_URL } from '@/lib/docs';

function DocsLink({ href = '', ...props }: ComponentProps<'a'>) {
const resolvedHref = href.startsWith('/') ? `${DOCS_BASE_URL}${href}` : href;

return (
<a
{...props}
href={resolvedHref}
target="_blank"
rel="noopener noreferrer"
/>
);
}

function Warning({ children }: { children: ReactNode }) {
return (
<Alert variant="warning" className="my-6">
<AlertTriangle />
<AlertDescription className="block [&_p]:m-0">
{children}
</AlertDescription>
</Alert>
);
}

function Tip({ children }: { children: ReactNode }) {
return (
<Alert variant="notice" className="my-6">
<Info />
<AlertDescription className="block [&_p]:m-0">
{children}
</AlertDescription>
</Alert>
);
}

function Steps({ children }: { children: ReactNode }) {
return <ol className="my-6 space-y-5">{children}</ol>;
}

function Step({ title, children }: { title: string; children: ReactNode }) {
return (
<li className="relative border-l border-border pl-5 before:absolute before:-left-2 before:grid before:size-4 before:place-items-center before:rounded-full before:bg-primary before:text-[10px] before:font-semibold before:text-primary-foreground before:content-[counter(list-item)]">
<p className="font-medium">{title}</p>
<div className="mt-1 [&_p]:my-0">{children}</div>
</li>
);
}

function IntegrationName({
href,
icon,
name,
}: {
href: string;
icon: string;
name: string;
}) {
const manualIcons: Record<string, string> = {
daytona: '/docs/logo/integrations/daytona.svg',
e2b: '/docs/logo/integrations/e2b.svg',
blaxel: '/docs/logo/integrations/blaxel.svg',
};
const iconSrc =
manualIcons[icon] ??
(icon.startsWith('/')
? `/docs${icon}`
: `https://api.iconify.design/simple-icons:${icon}.svg?color=currentColor`);

return (
<DocsLink href={href} className="inline-flex items-center gap-1.5">
{/* This mirrors the Mintlify snippet and external icons do not need Next optimization. */}
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={iconSrc} alt="" aria-hidden className="size-4" />
<span>{name}</span>
</DocsLink>
);
}

export const docsMdxComponents = {
a: DocsLink,
Warning,
Tip,
Steps,
Step,
IntegrationName,
};
68 changes: 68 additions & 0 deletions apps/web/src/app/(onboarding)/setup/SetupDocs.client.test.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 87 additions & 0 deletions apps/web/src/app/(onboarding)/setup/SetupDocs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use client';

import type { ReactNode } from 'react';
import { useSearchParams } from 'next/navigation';
import { Button, ExternalLink } from '@/components/system';

import { getSetupDocsPath, getSetupDocsStep } from './setup-docs';
import { ArrowLeftToLine, ArrowRightToLine } from 'lucide-react';

export function SetupDocs({
isOpen,
onOpenChange,
children,
}: {
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
children: ReactNode;
}) {
const searchParams = useSearchParams();
const docsPath = getSetupDocsPath(
getSetupDocsStep(searchParams?.get('step') ?? null),
{
authProvider: searchParams?.get('authProvider'),
computeProvider: searchParams?.get('computeProvider'),
modelProvider: searchParams?.get('modelProvider'),
sourceControlProvider: searchParams?.get('sourceControlProvider'),
},
);

return (
<>
<div
className={`fixed top-0 right-0 pr-2 pt-2 pb-3 z-40 hidden items-end min-[900px]:flex flex-col h-full`}
>
{isOpen ? (
<section className="h-full w-[min(30vw,30rem)] overflow-hidden rounded-r-2xl bg-card border-2 border-background border-l-4 border-l-card z-2">
<div className="flex h-11 items-center justify-between pl-6 pr-3 bg-background">
<span className="text-sm font-medium">Setup docs</span>
<div className="flex items-center">
<Button asChild variant="ghost" size="icon">
<a
href={`https://docs.roomote.dev/${docsPath}`}
target="_blank"
rel="noopener noreferrer"
aria-label="Open this documentation page in a new tab"
>
<ExternalLink />
</a>
</Button>
<Button
type="button"
variant="ghost"
size="icon"
aria-label="Close docs"
onClick={() => onOpenChange(false)}
>
<ArrowRightToLine />
</Button>
</div>
</div>
<div className="docs-content h-[calc(100%-2.75rem)] overflow-y-auto px-5 py-6 border-l-2 border-background">
{children}
</div>
</section>
) : null}
<div className="mt-2 mr-2 absolute animate-[enter-down_0.5s_1_1000ms_backwards]">
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => onOpenChange(true)}
className="rounded-full hover:bg-foreground hover:text-accent-bright-foreground gap-0"
>
<ArrowLeftToLine className="mr-1" />
<span className="animate-[squish-out_0.5s_1_6000ms_forwards] mr-1">
Need help?
</span>
<span>Docs</span>
<span className="animate-[squish-out_0.5s_1_6000ms_forwards] ml-1">
are here
</span>
</Button>
</div>
</div>
</>
);
}
24 changes: 24 additions & 0 deletions apps/web/src/app/(onboarding)/setup/SetupDocsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use client';

import { createContext, useContext } from 'react';
import type { ReactNode } from 'react';

const SetupDocsContext = createContext<(content: ReactNode) => void>(() => {});

export function SetupDocsContentProvider({
children,
setContent,
}: {
children: ReactNode;
setContent: (content: ReactNode) => void;
}) {
return (
<SetupDocsContext.Provider value={setContent}>
{children}
</SetupDocsContext.Provider>
);
}

export function useSetSetupDocsContent() {
return useContext(SetupDocsContext);
}
Loading
Loading