This PR adds support for mobile user agent emulation in web widgets,
enabling developers to test mobile-responsive websites directly within
WaveTerm.
## Changes
### New Meta Key: `web:useragenttype`
Added a new metadata key that accepts the following values:
- `"default"` (or `null`) - Uses the standard browser user agent
- `"mobile:iphone"` - Emulates iPhone Safari (iOS 17.0)
- `"mobile:android"` - Emulates Android Chrome (Android 13)
### User Interface
**Settings Menu**: Added a "User Agent Type" submenu to web widget
settings (accessible via right-click → Settings) with radio button
options for Default, Mobile: iPhone, and Mobile: Android.
**Visual Indicator**: When a mobile user agent is active, a mobile
device icon appears in the widget's header toolbar with an appropriate
tooltip indicating the current emulation mode.
### Implementation Details
The implementation leverages Electron's webview `useragent` attribute to
override the default user agent string. The setting is persisted in the
block's metadata and automatically applied when the webview is rendered.
User agent strings used:
- **iPhone**: `Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)
AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148
Safari/604.1`
- **Android**: `Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/120.0.6099.43 Mobile Safari/537.36`
## Use Cases
This feature is particularly useful for:
- Testing mobile-responsive web designs
- Debugging mobile-specific website behaviors
- Viewing mobile versions of websites without needing physical devices
- Web development workflows that require testing across different user
agents
## Files Changed
- `pkg/waveobj/wtypemeta.go` - Added `WebUserAgentType` field to
metadata type
- `frontend/types/gotypes.d.ts` - Generated TypeScript types for the new
meta key
- `frontend/app/view/webview/webview.tsx` - Implemented user agent
selection UI and webview configuration
- `pkg/waveobj/metaconsts.go` - Generated Go constants for the new meta
key
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
* add automatic OSC 7 support to bash and zsh
* add new wave OSC 16162 (planck length) to get up-to-date shell
information into blockrtinfo. currently implemented only for zsh. bash
will not support as rich of data as zsh, but we'll be able to do some.
* new rtinfo will be used to provide better context for AI in the
future, and to make sure AI is running safe commands.
* added a small local machine description to tab context (so AI knows
we're running on MacOS, Linux, or Windows)
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