56 lines
3.2 KiB
Markdown
56 lines
3.2 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Build & Deploy
|
|
|
|
```bash
|
|
# Build (TypeScript → bundled JS)
|
|
npm run build
|
|
|
|
# Watch mode during development
|
|
npm run dev
|
|
|
|
# Deploy to Stream Deck after building
|
|
cp com.pdma.notion-timer.sdPlugin/bin/plugin.js "$HOME/Library/Application Support/com.elgato.StreamDeck/Plugins/com.pdma.notion-timer.sdPlugin/bin/plugin.js"
|
|
pkill -f "notion-timer" # Stream Deck restarts the process automatically
|
|
|
|
# Full install (first time — copy plugin folder, not symlink; Stream Deck doesn't follow symlinks)
|
|
cp -r com.pdma.notion-timer.sdPlugin "$HOME/Library/Application Support/com.elgato.StreamDeck/Plugins/"
|
|
```
|
|
|
|
There is no test suite. Manual testing is done by pressing buttons in the Stream Deck app and checking the Notion database.
|
|
|
|
## Architecture
|
|
|
|
The plugin has two distinct runtime contexts that communicate via WebSocket (managed by the SDK):
|
|
|
|
**Plugin process** (`src/plugin.ts` → `bin/plugin.js`) runs in Node.js 20 under Stream Deck:
|
|
- One `TimerToggle` class (extends `SingletonAction`) handles all button instances of the action
|
|
- `settingsCache` (Map keyed by `action.id`) is required because the SDK doesn't expose other actions' settings — it must be updated after every `setSettings()` call, not just on appear
|
|
- Single-timer enforcement: when starting a timer, iterate `this.actions` and stop any other active timer via `settingsCache`
|
|
- Global settings (token, DB IDs, user ID) are stored as hardcoded `DEFAULTS` in `plugin.ts`
|
|
|
|
**Property Inspector** (`ui/property-inspector.html`) runs in a browser frame inside the Stream Deck app:
|
|
- Uses Elgato's official `$PI` library (files in `ui/libs/`) — **do not modify these files**
|
|
- The libs were copied from an installed Time Tracker plugin; they are not in npm
|
|
- Settings are saved via `$PI.setSettings()`, not a custom WebSocket implementation
|
|
- Event listeners must be attached inside `$PI.onConnected()`, not `DOMContentLoaded`
|
|
- Projects are sent from the plugin to the PI via `streamDeck.ui.sendToPropertyInspector()` when `onPropertyInspectorDidAppear` fires (plugin pushes, PI doesn't pull)
|
|
|
|
**Critical SDK v2 patterns** (different from v1 examples online):
|
|
- Use `streamDeck.ui.sendToPropertyInspector()` not `ev.action.sendToPropertyInspector()`
|
|
- Use `streamDeck.ui.onSendToPlugin()` not `SingletonAction.onSendToPlugin` (the method never fires in v2)
|
|
- `SingletonAction.onDidReceiveSettings` also does not reliably fire — settings come via `$PI.onDidReceiveSettings` on the PI side
|
|
|
|
**Notion API** (`src/notion.ts`):
|
|
- Notion pages have icons of `type: "emoji"` (direct emoji string) or `type: "icon"` (named SVG icon with `name` + `color` fields)
|
|
- Named icons are mapped to emoji via lookup tables: `book`+color → colored book emoji, `skip-forward`+color → ⏭+circle, others in `ICON_NAME`
|
|
- Timer entries: created with `Status: "Running"`, patched with `Status: "Stopped"` and `End Time`
|
|
|
|
## Debug
|
|
|
|
Stream Deck logs are at `~/Library/Logs/ElgatoStreamDeck/`. The plugin process stderr also appears there.
|
|
|
|
The `streamDeck.ui.onSendToPlugin` handler in `plugin.ts` still writes to `/tmp/sd-debug.log` — this should be removed once debugging is no longer needed.
|