Skip to content

Commit 9cfed62

Browse files
committed
Add CLAUDE.md with architecture and development guide
1 parent 7cefe9c commit 9cfed62

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

CLAUDE.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build Commands
6+
7+
```bash
8+
# Development with watch mode
9+
npm run dev
10+
11+
# Production build (runs TypeScript check + esbuild)
12+
npm run build
13+
14+
# Version bump (updates manifest.json and versions.json)
15+
npm run version
16+
```
17+
18+
**Important**: The build output `main.js` MUST be tracked in git for Obsidian plugin releases. It's intentionally not in .gitignore.
19+
20+
## Architecture Overview
21+
22+
### Plugin Structure
23+
24+
DashReader is an Obsidian plugin implementing RSVP (Rapid Serial Visual Presentation) speed reading. The architecture follows a clear separation:
25+
26+
- **main.ts** - Plugin entry point, registers commands, ribbon icons, and manages view lifecycle
27+
- **src/rsvp-view.ts** - UI layer (ItemView), handles user interactions, cursor tracking, and display
28+
- **src/rsvp-engine.ts** - Core reading engine, controls timing, word iteration, and micropause logic
29+
- **src/markdown-parser.ts** - Transforms Markdown to plain text while marking headings with `[H1]`, `[H2]` etc.
30+
- **src/settings.ts** - Settings UI using Obsidian's PluginSettingTab
31+
- **src/types.ts** - Shared interfaces and default settings
32+
33+
### Key Architecture Patterns
34+
35+
**View-Engine Separation**: The view (`rsvp-view.ts`) owns the UI and event handling, while the engine (`rsvp-engine.ts`) owns reading logic and timing. They communicate via:
36+
- View → Engine: `setText()`, `play()`, `pause()`, `updateSettings()`
37+
- Engine → View: `onWordChange` callback, `onComplete` callback
38+
39+
**Cursor Position Tracking**: When loading text from the editor:
40+
1. Parse Markdown FIRST to remove syntax (`markdown-parser.ts`)
41+
2. Parse text up to cursor position separately
42+
3. Count words in parsed text (not raw Markdown with frontmatter)
43+
4. Pass word INDEX to engine, not character position
44+
45+
**Heading System**: Headings are marked during parsing (`# Title``[H1]Title`), then:
46+
- View detects markers and displays with proportional font size (H1=2x, H2=1.75x, etc.)
47+
- View adds visual separator lines before headings
48+
- Engine applies longer micropauses (H1=3x, H2=2.5x, etc.)
49+
50+
**Accurate Time Estimation**: `getEstimatedDuration()` and `getRemainingTime()` iterate through ALL remaining words and sum their individual delays, accounting for:
51+
- Heading micropauses
52+
- Punctuation pauses
53+
- Long word pauses
54+
- Section markers (numbers, bullets)
55+
- Progressive acceleration (average of start and target WPM)
56+
57+
### Markdown Parsing Order
58+
59+
The parser (`markdown-parser.ts`) processes in this specific order:
60+
1. Extract frontmatter (remove it entirely)
61+
2. Extract code blocks (keep content, remove delimiters)
62+
3. Mark headings with level tags
63+
4. Remove formatting (bold, italic, highlights)
64+
5. Process links (keep text, remove URLs)
65+
6. Handle callouts
66+
7. Clean up extra whitespace
67+
68+
**Critical**: Always parse BEFORE counting words for cursor positioning.
69+
70+
### Obsidian Integration Points
71+
72+
- **View Type**: Custom ItemView with `VIEW_TYPE_DASHREADER = 'dashreader-view'`
73+
- **Leaf Management**: View is activated via `this.app.workspace.getRightLeaf(false)`
74+
- **Editor Events**: Listens to `active-leaf-change`, `file-open`, `mouseup`, `keyup` with throttling
75+
- **Context Menu**: Adds "Read with DashReader" when text is selected
76+
77+
### Hotkey System
78+
79+
**Important**: Only `Shift+Space` triggers play/pause (not Space alone). This prevents capturing Space when typing in notes.
80+
81+
Other hotkeys from settings:
82+
- `hotkeyRewind/hotkeyForward` - Navigation
83+
- `hotkeyIncrementWpm/hotkeyDecrementWpm` - Speed control
84+
- `hotkeyQuit` - Stop reading
85+
86+
### Settings Architecture
87+
88+
Settings are defined in `src/types.ts` as:
89+
- Interface: `DashReaderSettings`
90+
- Defaults: `DEFAULT_SETTINGS` const
91+
92+
UI is built in `src/settings.ts` using Obsidian's Setting API. Inline settings in the view mirror the main settings tab.
93+
94+
### Micropause System
95+
96+
Micropauses multiply the base delay (`60/WPM * 1000 ms`). Multiple conditions can stack multiplicatively:
97+
98+
```typescript
99+
// Example: H1 heading with period and long word
100+
multiplier = 3.0 (H1) * 1.5 (period) * 1.3 (>8 chars) = 5.85x delay
101+
```
102+
103+
Order of detection in `calculateDelay()`:
104+
1. Headings (`[H1]` through `[H6]`)
105+
2. Section markers (1., I., etc.)
106+
3. List bullets (-, *, +, •)
107+
4. Punctuation (end of word)
108+
5. Long words (>8 characters)
109+
6. Paragraph breaks (`\n`)
110+
111+
## Release Process
112+
113+
For Obsidian plugin submission:
114+
115+
1. Update `manifest.json` version (must match git tag exactly, no `v` prefix)
116+
2. Run `npm run build`
117+
3. Commit changes including `main.js`
118+
4. Create GitHub release with tag matching manifest version (e.g. `1.3.0`)
119+
5. Attach `main.js`, `manifest.json`, `styles.css` as binary assets to the release
120+
121+
The manifest.json at repo root is used by Obsidian to check version. Actual files are fetched from GitHub release assets.
122+
123+
## Testing in Obsidian
124+
125+
```bash
126+
# Install in vault (creates symlink)
127+
./install-local.sh /path/to/vault
128+
129+
# Or manually copy after build
130+
cp main.js manifest.json styles.css /path/to/vault/.obsidian/plugins/dashreader/
131+
132+
# Reload Obsidian
133+
# macOS: Cmd+R
134+
# Windows/Linux: Ctrl+R
135+
```
136+
137+
## Code Conventions
138+
139+
- **Console logging**: Prefix with `DashReader:` for easy filtering
140+
- **Word index vs position**: Always use word index (count) after parsing, never character position from raw text
141+
- **Event throttling**: Cursor tracking uses 150ms throttle to balance responsiveness and performance
142+
- **Styling**: Use CSS variables for theme compatibility (`var(--text-muted)`, etc.)

0 commit comments

Comments
 (0)