diff --git a/llms.txt b/llms.txt index 709aef8b..7bb1db44 100644 --- a/llms.txt +++ b/llms.txt @@ -72,6 +72,7 @@ For any other update, first run `postplan read --pull ` and de - Fork a site before changing someone else's work. - Compare deployment versions and restore an older snapshot without deleting history. - Use `postplan.db` for opt-in browser data and realtime updates. +- Publish animated HTML (a HyperFrames-style GSAP timeline on `window.__timelines`) and it plays with a scrubbable player bar; comments get seekable `[m:ss]` timestamps. - Dashboards: `\n\n\n\n```\n\n2. Run each query yourself and push the result (CSV with a header row, a JSON array of objects, or\n `{"columns": [...], "rows": [[...]]}`):\n\n```bash\npostplan data push team/kpis revenue revenue.csv --sql revenue.sql --source "Snowflake · analytics" --stale-after 6h\n```\n\n- `kind`: `line` (default), `bar`, `area`, `dot`, `number` (last row\'s `y`), `table`. `x`/`y` default to\n the first two columns; `color` splits series by a column. ISO dates become a time axis.\n- One query per chart, aggregated before pushing: each push is capped at 100 KB and 20,000 cells. Re-push to refresh;\n no redeploy. The badge turns amber after `--stale-after` (default 24h) and red at twice that.\n- To refresh on a schedule, put the queries and `postplan data push` in a cron job or GitHub Action\n that holds the warehouse secrets (log in there with `POSTPLAN_TOKEN`).\n- Static snapshot instead (works for anyone with the link, no live updates): ship the file with the\n site and use ``. Live charts need viewers signed in.\n- Every chart gets a CSV download and an expandable SQL panel. The SQL shown is what the pusher sent:\n provenance, not proof.\n\n## Diagrams & flowcharts — default to mermaid\n\nFor any flowchart, sequence, architecture, or state diagram, reach for **mermaid FIRST**: it auto-layouts from text — no positions, no React, two lines total. Only step up to React Flow (below) when nodes must be interactive (drag, click handlers, custom node components).\n\n```html\n\n
\ngraph LR\n  CLI[postplan CLI] --> API[main worker] --> R2[(R2)]\n  API --> D1[(D1)]\n
\n```\n\nEvery mermaid diagram type works (flowchart, sequenceDiagram, stateDiagram-v2, erDiagram, gantt…); each `
` on the page renders independently. Syntax reference: https://mermaid.js.org → "Diagram Syntax".\n\nThe dividing line vs hand-written SVG is **auto-layout vs designed geometry**, not size:\n\n- **Mermaid** whenever the visual is a graph of labeled nodes/edges or ordered steps (architecture, flows, sequences, state, ER, dependency maps) — even a 4-box flow. Routing connectors by hand in SVG is where effort goes to die and arrows start overlapping.\n- **Inline SVG** when you want control of the geometry and nothing needs routed connectors: small illustrations, icons, gauges, timelines, badges, decoration. SVG inherits the page\'s CSS variables/palette, so bespoke visuals stay on-design.\n- **Charts stay recharts/d3** (below) — mermaid\'s pie/gantt are weak, and hand-SVG charts are wasted effort.\n\nOn a designed page, theme mermaid to the palette so diagrams don\'t look pasted-in: `mermaid.initialize({ startOnLoad: true, theme: \'base\', themeVariables: { primaryColor: \'…\', primaryTextColor: \'…\', lineColor: \'…\', fontFamily: \'…\' } })` — pull the values from the page\'s CSS custom properties.\n\n## Building React apps (no build step)\n\nServed HTML has no CSP, so a React SPA is just an `index.html` with an import map — no bundler, no npm install. Paste this `` recipe (pinned versions; add/remove libs as needed):\n\n```html\n\n\n\n\n```\n\nThen write normal React in `\n  \n\n\n  
\n

Deploy

\n

Review

\n
\n \n\n\n```\n\n## Dashboards — `` + `postplan data push`\n\nFor a dashboard over real data (a warehouse, an API, a CSV export), Postplan never holds the\ncredentials. Whoever already has them (you, a cron job, a GitHub Action) runs each query and pushes\nthe result; every chart then shows its SQL, its source and how fresh it is, and open pages update live.\n\n1. Deploy the page once. Load the component and give each chart an id:\n\n```html\n\n\n\n\n```\n\n2. Run each query yourself and push the result (CSV with a header row, a JSON array of objects, or\n `{"columns": [...], "rows": [[...]]}`):\n\n```bash\npostplan data push team/kpis revenue revenue.csv --sql revenue.sql --source "Snowflake · analytics" --stale-after 6h\n```\n\n- `kind`: `line` (default), `bar`, `area`, `dot`, `number` (last row\'s `y`), `table`. `x`/`y` default to\n the first two columns; `color` splits series by a column. ISO dates become a time axis.\n- One query per chart, aggregated before pushing: each push is capped at 100 KB and 20,000 cells. Re-push to refresh;\n no redeploy. The badge turns amber after `--stale-after` (default 24h) and red at twice that.\n- To refresh on a schedule, put the queries and `postplan data push` in a cron job or GitHub Action\n that holds the warehouse secrets (log in there with `POSTPLAN_TOKEN`).\n- Static snapshot instead (works for anyone with the link, no live updates): ship the file with the\n site and use ``. Live charts need viewers signed in.\n- Every chart gets a CSV download and an expandable SQL panel. The SQL shown is what the pusher sent:\n provenance, not proof.\n\n## Diagrams & flowcharts — default to mermaid\n\nFor any flowchart, sequence, architecture, or state diagram, reach for **mermaid FIRST**: it auto-layouts from text — no positions, no React, two lines total. Only step up to React Flow (below) when nodes must be interactive (drag, click handlers, custom node components).\n\n```html\n\n
\ngraph LR\n  CLI[postplan CLI] --> API[main worker] --> R2[(R2)]\n  API --> D1[(D1)]\n
\n```\n\nEvery mermaid diagram type works (flowchart, sequenceDiagram, stateDiagram-v2, erDiagram, gantt…); each `
` on the page renders independently. Syntax reference: https://mermaid.js.org → "Diagram Syntax".\n\nThe dividing line vs hand-written SVG is **auto-layout vs designed geometry**, not size:\n\n- **Mermaid** whenever the visual is a graph of labeled nodes/edges or ordered steps (architecture, flows, sequences, state, ER, dependency maps) — even a 4-box flow. Routing connectors by hand in SVG is where effort goes to die and arrows start overlapping.\n- **Inline SVG** when you want control of the geometry and nothing needs routed connectors: small illustrations, icons, gauges, timelines, badges, decoration. SVG inherits the page\'s CSS variables/palette, so bespoke visuals stay on-design.\n- **Charts stay recharts/d3** (below) — mermaid\'s pie/gantt are weak, and hand-SVG charts are wasted effort.\n\nOn a designed page, theme mermaid to the palette so diagrams don\'t look pasted-in: `mermaid.initialize({ startOnLoad: true, theme: \'base\', themeVariables: { primaryColor: \'…\', primaryTextColor: \'…\', lineColor: \'…\', fontFamily: \'…\' } })` — pull the values from the page\'s CSS custom properties.\n\n## Building React apps (no build step)\n\nServed HTML has no CSP, so a React SPA is just an `index.html` with an import map — no bundler, no npm install. Paste this `` recipe (pinned versions; add/remove libs as needed):\n\n```html\n\n\n\n\n```\n\nThen write normal React in `
+  
+
+
+  
+

Deploy

+

Review

+
+ + + +``` + ## Dashboards — `` + `postplan data push` For a dashboard over real data (a warehouse, an API, a CSV export), Postplan never holds the diff --git a/packages/shared/motion.ts b/packages/shared/motion.ts new file mode 100644 index 00000000..af976f56 --- /dev/null +++ b/packages/shared/motion.ts @@ -0,0 +1,17 @@ +/** Page → viewer: the motion timeline's `{duration, t, playing}`, sent only by pages that have one. */ +export const MOTION_STATE = 'postplan:motion' +/** Viewer → page: one playback command. */ +export const MOTION_COMMAND = 'postplan:motion-cmd' +/** Upper bound on a playable timeline, in seconds; longer (or infinite) ones are ignored. */ +export const MOTION_MAX_DURATION = 3600 +/** Frame size for step commands. */ +export const MOTION_FPS = 30 +export const MOTION_RATES = [0.5, 1] as const + +export type MotionCommand = + | { cmd: 'play' } + | { cmd: 'pause' } + | { cmd: 'seek'; t: number } + | { cmd: 'rate'; rate: number } + | { cmd: 'step'; dir: 1 | -1 } + | { cmd: 'loop'; on: boolean } diff --git a/packages/web/src/components/audio/AudioScrubber.tsx b/packages/web/src/components/audio/AudioScrubber.tsx index 7a7762e3..d9382c14 100644 --- a/packages/web/src/components/audio/AudioScrubber.tsx +++ b/packages/web/src/components/audio/AudioScrubber.tsx @@ -1,6 +1,6 @@ import { Pause, Play } from 'lucide-react' import { type RefObject, useEffect, useState } from 'react' -import { formatTimestamp } from '@/lib/audio' +import { formatTimestamp } from '@/lib/timestamp' import { cn } from '@/lib/utils' // A seek bar over a caller-owned `