# Changelog All notable changes to Switchboard Core are documented here. ## v0.5.0 — Realtime Primitive + Dialog Audit + Permissions UI ### Added - **Realtime Starlark module**: `realtime.publish(channel, event, data)` lets extensions publish events to WebSocket channels. Gated by the new `realtime.publish` permission. Payloads limited to 7KB (pg_notify safe). Reserved event prefixes (`system.`, `workflow.`, etc.) are blocked. - **WS room protocol**: Clients send `room.subscribe` / `room.unsubscribe` messages to join/leave rooms. Intercepted in the WebSocket readPump before the bus publish gate (like ping). Per-connection cap of 100 rooms. - **SDK realtime module**: `sw.realtime.subscribe(channel, [event], callback)` manages room join/leave over WebSocket and filters incoming `realtime.*` events by room. Returns unsubscribe handle. Auto re-joins all rooms on WebSocket reconnect. `sw.realtime.channels()` debug helper. - **Admin permissions UI**: Packages page gains a "Permissions" button for packages with declared permissions. Inline drawer shows each permission with Grant/Revoke toggle, granted-by user ID, and a "Grant All" bulk action. - **Status badges**: `pending_review` (amber) and `suspended` (red) badges on package rows. Enable toggle disabled with hint when `pending_review`. - **SDK API client**: `permissions()`, `grantPerm()`, `revokePerm()`, `grantAllPerms()` methods added to `sw.api.admin.packages`. - **`starlarkToGoVal()` helper**: Converts Starlark values to Go `any` for JSON marshaling (reverse of existing `goValToStarlark`). - **8 new Go tests**: Route table cases for `realtime.*` and `room.*`, realtime module publish (happy path, empty data, missing args, reserved prefix, payload too large), `starlarkToGoVal` type conversion. ### Changed - **Dialog audit**: 5 bare `confirm()` calls migrated to `await sw.confirm()` with `{ destructive: true }` styling in tasks, schedules, notes (×2), and editor packages. Package archives rebuilt. - SDK version bumped from 0.2.3 to 0.5.0. - Event route table: `realtime.` → DirToClient, `room.subscribe` / `room.unsubscribe` → DirFromClient. - Runner gains `bus` field and `SetBus()` method; wired in main.go. ## v0.4.7 — Note Graph ### Added - **Graph API**: `GET /graph` endpoint returns all non-archived notes as nodes and resolved wikilinks as edges in a single payload. Nodes include id, title, folder_id, and tags. Edges include source, target, and link text. - **Graph view**: Canvas-based force-directed graph visualization replaces the editor pane when "Graph" button is clicked in the topbar. Force simulation uses repulsion (all pairs), attraction (edges), and center gravity with velocity damping. Supports zoom (scroll wheel) and pan (mouse drag). - **Click focus**: Single-click a node to dim unconnected nodes/edges to 15% opacity; clicked node and direct neighbors stay at full brightness. Edges between focused nodes highlighted. Click empty space to reset. - **Shift+click chain**: Hold shift and click a second node to union both neighborhoods — trace thought paths across two hops. - **Double-click open**: Double-click a graph node to navigate to that note in the editor. Graph view closes, editor opens with the selected note. - **Hover tooltips**: Hovering a node shows title, tag pills, and connection count in an HTML overlay (no additional API calls). - **Folder coloring**: Nodes colored by folder using a 10-color palette. Unfiled notes shown in gray. - **Tag filter integration**: Active tag filter dims non-matching nodes to 30% opacity, stacking with click focus. - **Orphan highlighting**: Notes with zero connections shown with dashed stroke. "Hide orphans" checkbox in toolbar to toggle visibility. ### Changed - Notes package version bumped from 0.7.0 to 0.8.0. - Topbar gains a "Graph" toggle button alongside "New Note" and "Import .md". ## v0.4.6 — Sidebar Restructure ### Added - **Sidebar tabs**: Two-tab header ("Notes" / "Outline") replaces the static sidebar header. Notes tab contains folder tree, tag filter, search, and note list. Outline tab shows the document heading tree for the active note. Outline tab disabled (grayed) when no note is selected. - **Sidebar outline**: Document outline relocated from editor-side panel into the sidebar's Outline tab. Nested heading hierarchy (H1→H2→H3) with depth indentation via `padding-left`. Click heading scrolls to it in both preview and CM6 editor modes. - **Active heading tracking**: Scroll listener (RAF-throttled) highlights the current heading in the sidebar outline based on scroll position. Uses `getBoundingClientRect()` in preview/split mode and CM6 viewport line in edit mode. ### Changed - Notes package version bumped from 0.6.0 to 0.7.0. - `EditorPane` exposes headings, scroll function, and active heading index to parent via callback props (`onHeadingsChange`, `scrollToHeadingRef`, `onActiveHeadingChange`). - Sidebar auto-resets to Notes tab when active note is deleted. ### Removed - Old `DocumentOutline` component and `.doc-outline` CSS styles (replaced by sidebar outline). ## v0.4.5 — Editor Modes + Document Outline ### Added - **Tri-state view mode**: Notes open in rendered (read-only) mode by default. Toolbar button cycles through Edit (CM6) and Split (side-by-side) modes. Replaces the old binary preview toggle. - **Split view**: Side-by-side CM6 editor + rendered preview using CSS grid. CM6 instance preserved when toggling between edit and split modes. - **Synced scroll**: In split mode, scrolling the CM6 editor proportionally scrolls the preview pane via linear ratio mapping on `scrollDOM`. - **View mode persistence**: User's preferred mode saved to localStorage (`sw.storage.local('notes')`). New `editor_mode` manifest setting with admin-level default (rendered / edit / split). - **Document outline**: Collapsible TOC panel adjacent to the editor body. `parseHeadings()` extracts heading level, text, and line number (skips fenced code blocks). Click heading to scroll — uses CM6 `EditorView.scrollIntoView()` in edit/split mode, DOM `scrollIntoView()` in rendered mode. Updates on body change with 300ms debounce. ### Changed - Notes package version bumped from 0.5.0 to 0.6.0. - Editor body wrapped in `.notes-editor__content` flex container to accommodate the outline panel. - Wikilink click handling active in both rendered and split modes. - Mobile: split view collapses to single column, outline panel hidden. --- ## v0.4.4 — Rich Editor + Import/Export ### Added - **CodeMirror 6 integration**: Notes editor now uses the vendored CM6 bundle (`CM.noteEditor()`) for rich markdown editing with syntax highlighting, wikilink autocomplete (`[[` triggers note title completion), and inline preview decorations for headings, code blocks, and blockquotes. - **CM6 dynamic loading**: Bundle loaded via `