Initial commit

This commit is contained in:
pdmarf
2026-04-10 19:51:21 +01:00
commit bc383e4fd8
22 changed files with 2190 additions and 0 deletions

55
CLAUDE.md Normal file
View File

@@ -0,0 +1,55 @@
# 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.