Massive PR, over 13k LOC updated, 128 commits to implement the first pass at the new Wave AI panel. Two backend adapters (OpenAI and Anthropic), layout changes to support the panel, keyboard shortcuts, and a huge focus/layout change to integrate the panel seamlessly into the UI.
Also fixes some small issues found during the Wave AI journey (zoom fixes, documentation, more scss removal, circular dependency issues, settings, etc)
The current layout system uses a complex bidirectional atom architecture
that forces every layout change to round-trip through the backend
WaveObject, even though **the backend never reads this data** - it only
queues actions via `PendingBackendActions`. By switching to a "write
cache" pattern where local atoms are the source of truth and backend
writes are fire-and-forget, we can eliminate ~70% of the complexity
while maintaining full persistence.
----
Every layout change (split, close, focus, magnify) currently follows
this flow:
```
User action
↓
treeReducer() mutates layoutState
↓
layoutState.generation++ ← Only purpose: trigger the write
↓
Bidirectional atom setter (checks generation)
↓
Write to WaveObject {rootnode, focusednodeid, magnifiednodeid}
↓
WaveObject update notification
↓
Bidirectional atom getter runs
↓
ALL dependent atoms recalculate (every isFocused, etc.)
↓
React re-renders with updated state
```
---
## Proposed "Write Cache" Architecture
### Core Concept
```
User action
↓
Update LOCAL atom (immediate, synchronous)
↓
React re-renders (single tick, all atoms see new state)
↓
[async, fire-and-forget] Persist to WaveObject
```
### Key Principles
1. **Local atoms are source of truth** during runtime
2. **WaveObject is persistence layer** only (read on init, write async)
3. **Backend actions still work** via `PendingBackendActions`
4. **No generation tracking needed** (no need to trigger writes)
checkpoint. good to merge. we have a working tsunami view inside of wave (with lots of caveats). but enough for some dev testing. merge so we dont drift too far from main and while we're at a stable point.
lots of misc connection refactoring / fixes:
* adds blocklogger as a way to writing logging information from the backend directly to the a terminal block
* use blocklogger in conncontroller
* use blocklogger in sshclient
* fix remote name in password prompt
* use sh -c to get around shell weirdness
* remove cmd.exe special cases
* use GetWatcher().GetFullConfig() rather than re-reading the config file
* change order of things we do when establishing a connection. ask for wsh up front. then do domain socket, then connserver
* reduce number of sessions required in the common case when wsh is already installed. running the connserver is now a "multi-command" which checks if it is installed, then asks for the version
* send jwt token over stdin instead of in initial command string
* fix focus bug for frontend conn modal
* track more information in connstatus
* simplify wshinstall function
* add nowshreason
* other misc cleanup
implements `wsh run` command. lots of fixes (and new options) for command blocks. cleans up the UX/UI for command blocks. lots of bug fixes for blockcontrollers. other minor bug fixes.
also makes editor:* vars into settings override atoms.

This functions very similarly to VSCode's pinned tab feature. To pin a
tab, you can right-click on it and select "Pin tab" from the context
menu. Once pinned, a tab will be fixed to the left-most edge of the tab
bar, in order of pinning. Pinned tabs can be dragged around like any
others. If you drag an unpinned tab into the pinned tabs section (any
index less than the highest-index pinned tab), it will be pinned. If you
drag a pinned tab out of the pinned tab section, it will be unpinned.
Pinned tabs' close button is replaced with a persistent pin button,
which can be clicked to unpin them. This adds an extra barrier to
accidentally closing a pinned tab. They can still be closed from the
context menu.
Updates `DeleteBlock` to close its parent tab if the tab has no more
blocks. This will also cascade to close the workspace if it no longer
has any tabs, same for window.
I had to move some block-related functionality around on the backend.
Adds a meta field `pinnedurl` that can be set to override the
`web:defaulturl` setting for a given block. Also adds a home button to
the webview to reset the block url to the homepage
The help view is now an extension of the webview with some of the chrome
removed.
Also updates the cookie dependency to resolve a vulnerability