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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ The project uses Semantic Versioning. Each release contains the same three chang

- None.

## [0.1.16] - 2026-08-25

### New Features

- None.

### Bug Fixes

- Fixed the demo docs Cloudflare Worker deployment by removing a CPU time limit setting that is unsupported on the Cloudflare Workers Free plan, which was blocking deployment.

### Improvements

- None.

## [0.1.15] - 2026-08-25

### New Features
Expand Down
21 changes: 21 additions & 0 deletions e2e/docs-ui.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,27 @@ test.describe('Docs UI', () => {
await expect(page.locator('text=Create a pet').first()).toBeVisible();
});

test('moves only the right sidebar below when the projected center reaches 280px', async ({
page,
}) => {
await page.setViewportSize({ width: 932, height: 900 });
await page.goto('/api-reference');
await expect(page.locator('text=List all pets').first()).toBeVisible({ timeout: 15000 });

const leftSidebar = page.locator('[data-api-reference-left-sidebar]');
const rightSidebar = page.locator('[data-api-reference-right-sidebar]');
const bottomCodePanel = page.locator('[data-api-reference-bottom-code-panel]');

await expect(leftSidebar).toHaveAttribute('data-layout-mode', 'inline');
await expect(rightSidebar).toHaveCount(0);
await expect(bottomCodePanel).toBeVisible();

await page.setViewportSize({ width: 933, height: 900 });
await expect(leftSidebar).toHaveAttribute('data-layout-mode', 'inline');
await expect(rightSidebar).toBeVisible();
await expect(bottomCodePanel).toHaveCount(0);
});

test('displays server URL', async ({ page }) => {
await page.goto('/api-reference');
await expect(page.locator('text=http://localhost:4010').first()).toBeVisible({
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

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

13 changes: 6 additions & 7 deletions packages/cli/__tests__/docs-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('docs runtime', () => {
);
});

it('isolates writable Next.js metadata from the packaged docs UI', () => {
it('copies development sources into the writable Next.js runtime', () => {
const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'cortex-docs-runtime-'));
const docsUiPath = path.join(workspace, 'docs-ui');
const runtimeDir = path.join(docsUiPath, '.cortex-dev-test');
Expand All @@ -50,16 +50,14 @@ describe('docs runtime', () => {
fs.writeFileSync(path.join(runtimeDir, 'next-env.d.ts'), 'runtime-only');

expect(fs.statSync(path.join(runtimeDir, 'app')).isDirectory()).toBe(true);
expect(fs.realpathSync(path.join(runtimeDir, 'app', 'page.tsx'))).toBe(
fs.realpathSync(path.join(docsUiPath, 'app', 'page.tsx')),
);
expect(fs.lstatSync(path.join(runtimeDir, 'app', 'page.tsx')).isSymbolicLink()).toBe(false);
expect(fs.readFileSync(path.join(docsUiPath, 'next-env.d.ts'), 'utf-8')).toBe('next-env.d.ts');
expect(fs.readFileSync(path.join(runtimeDir, 'next-env.d.ts'), 'utf-8')).toBe('runtime-only');

fs.rmSync(workspace, { recursive: true });
});

it('adds renamed source files and removes their stale runtime links', () => {
it('updates changed source files and removes stale runtime files', () => {
const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'cortex-docs-runtime-'));
const docsUiPath = path.join(workspace, 'docs-ui');
const runtimeDir = path.join(docsUiPath, '.cortex-dev-test');
Expand All @@ -81,11 +79,12 @@ describe('docs runtime', () => {

prepareDocsUiRuntime(docsUiPath, runtimeDir);
fs.renameSync(oldSource, newSource);
fs.writeFileSync(newSource, 'export const Card = "new";');
syncDocsUiRuntimeSources(docsUiPath, runtimeDir);

expect(fs.existsSync(path.join(runtimeDir, 'components', 'old-card.tsx'))).toBe(false);
expect(fs.realpathSync(path.join(runtimeDir, 'components', 'new-card.tsx'))).toBe(
fs.realpathSync(newSource),
expect(fs.readFileSync(path.join(runtimeDir, 'components', 'new-card.tsx'), 'utf-8')).toBe(
'export const Card = "new";',
);

fs.rmSync(workspace, { recursive: true });
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cortex-docs/cli",
"version": "0.1.15",
"version": "0.1.16",
"description": "Cortex Docs CLI for SDKs, API documentation, and MCP servers",
"license": "MIT",
"repository": {
Expand Down
25 changes: 13 additions & 12 deletions packages/cli/src/commands/docs/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function resolveDevRuntimeDirName(projectDir: string): string {
}

export function prepareDocsUiRuntime(docsUiPath: string, runtimeDir: string): void {
prepareDocsUiRuntimeWithOptions(docsUiPath, runtimeDir);
prepareDocsUiRuntimeWithOptions(docsUiPath, runtimeDir, { copySources: true });
}

export function prepareDocsUiBuildRuntime(docsUiPath: string, runtimeDir: string): void {
Expand Down Expand Up @@ -55,31 +55,32 @@ export function syncDocsUiRuntimeSources(docsUiPath: string, runtimeDir: string)
for (const name of RUNTIME_DIRECTORIES) {
const source = path.join(docsUiPath, name);
const target = path.join(runtimeDir, name);
if (fs.existsSync(target) && fs.lstatSync(target).isSymbolicLink()) {
fs.unlinkSync(target);
}
mirrorSourceDirectory(source, target);
syncSourceDirectory(source, target);
}
}

function mirrorSourceDirectory(source: string, target: string): void {
function syncSourceDirectory(source: string, target: string): void {
fs.mkdirSync(target, { recursive: true });

for (const entry of fs.readdirSync(target, { withFileTypes: true })) {
const sourceEntry = path.join(source, entry.name);
const targetEntry = path.join(target, entry.name);
if (entry.isSymbolicLink() && !fs.existsSync(sourceEntry)) {
fs.unlinkSync(targetEntry);
}
if (!fs.existsSync(sourceEntry)) fs.rmSync(targetEntry, { recursive: true, force: true });
}

for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
const sourceEntry = path.join(source, entry.name);
const targetEntry = path.join(target, entry.name);
if (entry.isDirectory()) {
mirrorSourceDirectory(sourceEntry, targetEntry);
} else if (!fs.existsSync(targetEntry)) {
fs.symlinkSync(sourceEntry, targetEntry, 'file');
if (fs.existsSync(targetEntry) && !fs.statSync(targetEntry).isDirectory()) {
fs.rmSync(targetEntry, { recursive: true, force: true });
}
syncSourceDirectory(sourceEntry, targetEntry);
} else {
if (fs.existsSync(targetEntry) && fs.statSync(targetEntry).isDirectory()) {
fs.rmSync(targetEntry, { recursive: true, force: true });
}
fs.copyFileSync(sourceEntry, targetEntry);
}
}
}
Expand Down
14 changes: 9 additions & 5 deletions packages/cli/src/commands/docs/serve.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,15 @@ export class DocsServeCommand extends CommandRunner {

const nextBinResolved = resolveNextBin(docsUiPath);

const child = spawn(process.execPath, [nextBinResolved, 'dev', '--port', String(port)], {
cwd: runtimeDir,
env,
stdio: 'inherit',
});
const child = spawn(
process.execPath,
[nextBinResolved, 'dev', '--webpack', '--port', String(port)],
{
cwd: runtimeDir,
env,
stdio: 'inherit',
},
);

let debounce: ReturnType<typeof setTimeout> | null = null;
let runtimeSyncDebounce: ReturnType<typeof setTimeout> | null = null;
Expand Down
4 changes: 2 additions & 2 deletions packages/docs-site/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cortex-docs/docs-site",
"version": "0.1.15",
"version": "0.1.16",
"private": true,
"description": "Product documentation for Cortex Docs",
"scripts": {
Expand All @@ -9,6 +9,6 @@
"clean": "rm -rf .cortex"
},
"dependencies": {
"@cortex-docs/cli": "0.1.15"
"@cortex-docs/cli": "0.1.16"
}
}
43 changes: 43 additions & 0 deletions packages/docs-ui/__tests__/api-reference-layout.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, expect, it } from 'vitest';
import { getApiReferencePanelVisibility } from '../lib/api-reference-layout';

const widths = {
leftSidebarWidth: 224,
rightSidebarWidth: 420,
};

describe('API reference responsive layout', () => {
it('hides the right sidebar when the desktop center panel is exactly 280px wide', () => {
const visibility = getApiReferencePanelVisibility({ ...widths, layoutWidth: 932 });

expect(visibility).toEqual({
centerWidthWithRightSidebar: 280,
showLeftSidebarInline: true,
showRightSidebar: false,
});
});

it('keeps the right sidebar when the desktop center panel is 281px wide', () => {
expect(getApiReferencePanelVisibility({ ...widths, layoutWidth: 933 })).toEqual({
centerWidthWithRightSidebar: 281,
showLeftSidebarInline: true,
showRightSidebar: true,
});
});

it('keeps the left sidebar inline after the right sidebar moves to the bottom', () => {
const visibility = getApiReferencePanelVisibility({ ...widths, layoutWidth: 700 });

expect(visibility.showLeftSidebarInline).toBe(true);
expect(visibility.showRightSidebar).toBe(false);
});

it('only moves the left sidebar off canvas when its center panel reaches 280px', () => {
expect(
getApiReferencePanelVisibility({ ...widths, layoutWidth: 508 }).showLeftSidebarInline,
).toBe(false);
expect(
getApiReferencePanelVisibility({ ...widths, layoutWidth: 509 }).showLeftSidebarInline,
).toBe(true);
});
});
Loading