0.29.1原版

This commit is contained in:
anian
2026-07-02 19:14:14 +08:00
commit d94008f0fb
947 changed files with 174905 additions and 0 deletions
@@ -0,0 +1,35 @@
## Background & Context
The MemoDetail page (`web/src/pages/MemoDetail.tsx`) renders a memo's full content with a right sidebar (`MemoDetailSidebar`) on desktop. The sidebar currently shows share controls, timestamps, property badges (links, to-do, code), and tags. Memo content is rendered via `react-markdown` with custom heading components (h1h6) in `MemoContent/markdown/Heading.tsx`. The page already has hash-based scroll-to-element infrastructure for comments (lines 3943 of MemoDetail.tsx).
## Issue Statement
When viewing a memo with structured headings, there is no outline or table of contents in the MemoDetail sidebar, so readers cannot see the document structure at a glance or jump to specific sections. Heading elements rendered by the `Heading` component do not have `id` attributes, preventing any anchor-based navigation to them.
## Current State
- **`web/src/pages/MemoDetail.tsx`** (93 lines) — MemoDetail page. Hash-based `scrollIntoView` logic exists at lines 3943 but only targets comment elements. The sidebar is rendered at lines 8286 as `<MemoDetailSidebar>`.
- **`web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx`** (109 lines) — Desktop sidebar. Contains sections: Share (lines 5865), Created-at (lines 6776), Properties (lines 7889), Tags (lines 91102). Uses a reusable `SidebarSection` helper (lines 2432). No outline section exists.
- **`web/src/components/MemoDetailSidebar/MemoDetailSidebarDrawer.tsx`** (36 lines) — Mobile drawer wrapper for the sidebar.
- **`web/src/components/MemoContent/markdown/Heading.tsx`** (30 lines) — Renders h1h6 with Tailwind classes. Does not generate `id` attributes on heading elements.
- **`web/src/components/MemoContent/index.tsx`** (157 lines) — ReactMarkdown renderer. Maps h1h6 to the `Heading` component (lines 72101). No rehype-slug or equivalent plugin is installed.
- **`web/src/utils/markdown-manipulation.ts`** (143 lines) — Contains `fromMarkdown()` MDAST parsing with GFM extensions. Used for task extraction; no heading extraction exists.
## Non-Goals
- Redesigning the overall MemoDetail layout or sidebar structure beyond adding the outline section.
- Supporting outline for the mobile drawer — the outline section should appear in the desktop sidebar only for now.
- Adding active heading highlighting on scroll (scroll-spy behavior).
- Supporting outline in the memo list/timeline view (compact mode).
- Changing the existing heading visual styles in the content area.
## Open Questions
- Which heading levels to include in the outline? (default: h1h4, matching user request)
- How to generate slug IDs — install `rehype-slug` or custom implementation? (default: lightweight custom slug generation in the Heading component to avoid a new dependency)
- Should the outline section be hidden when no headings exist? (default: yes, hide entirely like Properties/Tags sections)
- Should duplicate heading text produce unique slugs (e.g. `my-heading`, `my-heading-1`)? (default: yes, append numeric suffix for duplicates)
## Scope
**M** — Touches 34 files (Heading component, MemoDetailSidebar, possibly a new outline component, and a slug utility). Existing patterns (SidebarSection, hash scrolling) apply directly. No new subsystem or novel approach required.
@@ -0,0 +1,47 @@
## Task List
T1: Add heading extraction utility [S] — T2: Add slug IDs to Heading component [S] — T3: Create MemoOutline sidebar component [M] — T4: Integrate outline into MemoDetailSidebar [S]
### T1: Add heading extraction utility [S]
**Objective**: Provide a function to extract h1h4 headings from markdown content with slugified IDs, reusing the existing MDAST parsing pattern from `markdown-manipulation.ts`.
**Files**: `web/src/utils/markdown-manipulation.ts`
**Implementation**: Add `HeadingItem` interface (text, level, slug) and `extractHeadings(markdown: string): HeadingItem[]` function. Use existing `fromMarkdown()` + `visit()` pattern. Visit `"heading"` nodes with depth 14, extract text from children, generate slug via `slugify()` helper (lowercase, replace non-alphanumeric with hyphens, deduplicate). Export both.
**Validation**: `cd web && pnpm lint` — no new errors
### T2: Add slug IDs to Heading component [S]
**Objective**: Generate deterministic `id` attributes on h1h6 elements so outline links can scroll to them via `#hash`.
**Files**: `web/src/components/MemoContent/markdown/Heading.tsx`
**Implementation**: In `Heading` (~line 13), extract text from `children` using a `getTextContent(children)` helper that recursively extracts string content from React children. Generate slug with the same `slugify` logic. Apply `id={slug}` to the rendered `<Component>`.
**Validation**: `cd web && pnpm lint` — no new errors
### T3: Create MemoOutline sidebar component [M]
**Objective**: Create a modern, Claude/Linear-style outline component that renders h1h4 headings as anchor links with indentation by level.
**Size**: M (new component file, modern styling)
**Files**:
- Create: `web/src/components/MemoDetailSidebar/MemoOutline.tsx`
**Implementation**:
1. Props: `{ headings: HeadingItem[] }` from `markdown-manipulation.ts`
2. Render a `<nav>` with vertical list of `<a href="#slug">` links
3. Styling per level: h1 no indent, h2 `pl-3`, h3 `pl-6`, h4 `pl-9`. Text size: h1 `text-[13px] font-medium`, h2h4 `text-[13px] font-normal`. Color: `text-muted-foreground` with `hover:text-foreground` transition. Left border accent line (2px) along the nav. Smooth scroll on click via `scrollIntoView`.
4. Each link: `block py-1 truncate transition-colors` with level-based indentation
**Boundaries**: No scroll-spy / active state tracking. No mobile drawer integration.
**Dependencies**: T1
**Expected Outcome**: Component renders a clean, modern outline navigation.
**Validation**: `cd web && pnpm lint` — no new errors
### T4: Integrate outline into MemoDetailSidebar [S]
**Objective**: Add the outline section as the first section in `MemoDetailSidebar`, shown only when headings exist.
**Files**: `web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx`
**Implementation**: Import `extractHeadings` and `MemoOutline`. In `MemoDetailSidebar` (~line 48), compute `headings = useMemo(() => extractHeadings(memo.content), [memo.content])`. Before the Share section (~line 58), add conditional: `{headings.length > 0 && <SidebarSection label="Outline"><MemoOutline headings={headings} /></SidebarSection>}`.
**Validation**: `cd web && pnpm lint && pnpm build` — no errors
## Out-of-Scope Tasks
- Scroll-spy / active heading highlighting in the outline
- Mobile drawer outline support
- Outline in memo list view (compact mode)
- Changing existing heading visual styles in content area