diff --git a/apps/penpal/ERD.md b/apps/penpal/ERD.md index 3b8f96366..3b3677d8f 100644 --- a/apps/penpal/ERD.md +++ b/apps/penpal/ERD.md @@ -309,6 +309,8 @@ see-also: - **E-PENPAL-SIDEBAR-RESIZE**: The left sidebar width is managed in `Layout.tsx` React state (`sidebarWidth`), initialized from `localStorage.getItem('sidebarWidth')` (default 240px). A 4px drag handle (`.sidebar-resize-handle`) on the right edge of the sidebar initiates mouse-tracking: `mousedown` captures `startX` and `startWidth`, `mousemove` computes `newWidth = startWidth + (clientX - startX)` clamped to [200, 700], `mouseup` persists to `localStorage`. The `.app` grid template uses an inline style `gridTemplateColumns: \`${sidebarWidth}px 1fr\`` to override the CSS default. The drag handle uses the same visual styling as the existing `.chat-resize-handle` (2px accent-colored indicator on hover). ← [P-PENPAL-SIDEBAR-RESIZE](PRODUCT.md#P-PENPAL-SIDEBAR-RESIZE) +- **E-PENPAL-VIEW-MARGINS**: The `.file-main-scroll` container uses symmetric left and right padding (40px) and no `max-width` cap so that content expands to fill the available column width. The file-page-layout CSS grid separates the content column from the comments panel column with a resize handle; the visual margin between content text and comments panel equals the margin between the left sidebar border and the content text. + ← [P-PENPAL-VIEW-MARGINS](PRODUCT.md#P-PENPAL-VIEW-MARGINS) - **E-PENPAL-FRONTMATTER-STRIP**: `markdown.StripFrontmatter()` checks for `---` prefix, finds the next `\n---` occurrence, and returns content after it with leading newlines trimmed. Applied in `handleRawFile` before serving content and in `publish/render.go` during publishing. The frontend renders the already-stripped content. ← [P-PENPAL-FRONTMATTER](PRODUCT.md#P-PENPAL-FRONTMATTER) diff --git a/apps/penpal/PRODUCT.md b/apps/penpal/PRODUCT.md index 72169c44a..b57dd314c 100644 --- a/apps/penpal/PRODUCT.md +++ b/apps/penpal/PRODUCT.md @@ -200,6 +200,8 @@ Global views aggregate content across all projects. They appear as top-level ite - **P-PENPAL-RENDER**: Clicking a file opens a two-pane view: rendered markdown on the left, comments panel on the right, with a draggable divider. +- **P-PENPAL-VIEW-MARGINS**: The file view maintains equal margins between the content and each side boundary (left sidebar and right comments panel). Content expands to fill available space between the margins. + - **P-PENPAL-GFM**: Markdown rendering supports GitHub Flavored Markdown: tables, task lists, strikethrough, autolinks, and syntax-highlighted code blocks with a dark color scheme (for fenced blocks with a language specifier). - **P-PENPAL-FRONTMATTER**: YAML/TOML frontmatter is stripped from the rendered output so users see only the document content. diff --git a/apps/penpal/TESTING.md b/apps/penpal/TESTING.md index 4dabf4b60..a077af67b 100644 --- a/apps/penpal/TESTING.md +++ b/apps/penpal/TESTING.md @@ -100,6 +100,7 @@ see-also: | File Handler (P-PENPAL-FILE-HANDLER) | — | — | api_manage_test.go | — | | Source Disambiguation (P-PENPAL-SRC-DISAMBIG) | — | Layout.test.tsx | — | — | | Home Label (P-PENPAL-HOME-LABEL) | — | Layout.test.tsx | — | — | +| File View Margins (P-PENPAL-VIEW-MARGINS) | — | file-view-layout.test.ts | — | — | ## Known Coverage Gaps diff --git a/apps/penpal/frontend/package.json b/apps/penpal/frontend/package.json index ad9f4f13e..9be94a45a 100644 --- a/apps/penpal/frontend/package.json +++ b/apps/penpal/frontend/package.json @@ -35,6 +35,7 @@ "@testing-library/react": "^16.0.0", "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", + "@types/node": "^24.11.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@types/unist": "^3.0.3", diff --git a/apps/penpal/frontend/src/index.css b/apps/penpal/frontend/src/index.css index 74d402b5a..d08fddc11 100644 --- a/apps/penpal/frontend/src/index.css +++ b/apps/penpal/frontend/src/index.css @@ -888,7 +888,8 @@ a:hover { text-decoration: underline; } opacity: 1; } .file-main { display: flex; flex-direction: column; overflow: hidden; } -.file-main-scroll { padding: 0 40px 24px 40px; max-width: 900px; overflow-y: auto; flex: 1; } +/* E-PENPAL-VIEW-MARGINS: symmetric padding, no max-width cap — content expands to fill column */ +.file-main-scroll { padding: 0 40px 24px 40px; overflow-y: auto; flex: 1; } /* Sidebar floating card (TOC) */ .sidebar-card { diff --git a/apps/penpal/frontend/src/test/file-view-layout.test.ts b/apps/penpal/frontend/src/test/file-view-layout.test.ts new file mode 100644 index 000000000..bf7e53484 --- /dev/null +++ b/apps/penpal/frontend/src/test/file-view-layout.test.ts @@ -0,0 +1,38 @@ +import { describe, it, expect } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +// E-PENPAL-VIEW-MARGINS: verify file view layout CSS enforces symmetric margins +// and no max-width cap on the scroll container. +describe('file view layout margins', () => { + const here = dirname(fileURLToPath(import.meta.url)); + const css = readFileSync(resolve(here, '../index.css'), 'utf-8'); + + it('file-main-scroll has symmetric left and right padding', () => { + // Extract the .file-main-scroll rule + const match = css.match(/\.file-main-scroll\s*\{([^}]+)\}/); + expect(match).not.toBeNull(); + const rule = match![1]; + + // Padding should include equal left and right values + const paddingMatch = rule.match(/padding:\s*([^;]+)/); + expect(paddingMatch).not.toBeNull(); + const padding = paddingMatch![1].trim(); + + // Parse the padding shorthand — expect "0 Xpx Ypx Xpx" form where left === right + const parts = padding.split(/\s+/); + // 4-value shorthand: top right bottom left + expect(parts.length).toBe(4); + const rightPadding = parts[1]; + const leftPadding = parts[3]; + expect(rightPadding).toBe(leftPadding); + }); + + it('file-main-scroll does not have a max-width constraint', () => { + const match = css.match(/\.file-main-scroll\s*\{([^}]+)\}/); + expect(match).not.toBeNull(); + const rule = match![1]; + expect(rule).not.toMatch(/max-width/); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a80b79c93..99ef4b462 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,6 +129,9 @@ importers: '@types/mdast': specifier: ^4.0.4 version: 4.0.4 + '@types/node': + specifier: ^24.11.0 + version: 24.11.0 '@types/react': specifier: ^19.0.0 version: 19.2.14