Commit graph

1124 commits

Author SHA1 Message Date
Mike Sawka
7ef0bcd87f
preview updates (mock electron api, wos checks) (#2986)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-03-06 16:50:41 -08:00
Copilot
3f4484a9e2
Remove dead “move block to new window” path and dependent unused APIs (#3002)
`WindowService.MoveBlockToNewWindow` appears to be unreferenced, and its
supporting path had become isolated. This change removes that dead RPC
surface and the backend/eventbus helpers that existed only for that
flow.

- **Window service cleanup (backend RPC)**
- Removed `MoveBlockToNewWindow_Meta` and `MoveBlockToNewWindow` from
`pkg/service/windowservice/windowservice.go`.
  - Dropped now-unused imports tied to that method (`log`, `eventbus`).

- **Store cleanup**
  - Removed `MoveBlockToTab` from `pkg/wstore/wstore.go`.
  - Removed now-unused `utilfn` import from the same file.

- **Eventbus cleanup**
  - Removed unused event constant `WSEvent_ElectronNewWindow`.
- Removed `getWindowWatchesForWindowId` and `BusyWaitForWindowId`, which
were only used by the deleted move-to-new-window path.
  - Removed now-unused `time` import.

- **Generated frontend service surface**
- Regenerated service bindings and removed
`WindowServiceType.MoveBlockToNewWindow(...)` from
`frontend/app/store/services.ts`.

Example of removed RPC surface:
```ts
// removed from frontend/app/store/services.ts
MoveBlockToNewWindow(currentTabId: string, blockId: string): Promise<void> {
    return WOS.callBackendService("window", "MoveBlockToNewWindow", Array.from(arguments))
}
```

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/wavetermdev/waveterm/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-03-06 16:47:12 -08:00
Mike Sawka
76f78f0f97
fix failing layout test (#2999) 2026-03-06 16:07:11 -08:00
Mike Sawka
c6f61247ce
update about, add sponsor and gradient (#3001) 2026-03-06 15:59:55 -08:00
Copilot
0ab26ef947
Add a mousedown handler to also signal user activity in the app (#2976)
`mousedown` activity signaling was structured such that async telemetry
concerns leaked into event handling. This change moves fire-and-forget
behavior to the model boundary and keeps telemetry failures non-fatal.

- **`mousedown` handler path**
- `AppKeyHandlers` now calls `GlobalModel.getInstance().setIsActive()`
directly (no async wrapper in the handler).

- **`GlobalModel.setIsActive` structure**
  - `setIsActive()` is now synchronous (`void`).
  - Throttle logic remains unchanged.
- Electron telemetry call is executed via `util.fireAndForget(...)`
inside `setIsActive()`.

- **Telemetry error containment**
- `getApi().setIsActive()` is wrapped in `try/catch` inside the
fire-and-forget callback.
- Errors are logged with `console.log("setIsActive error", e)` and do
not bubble.

- **Focused coverage**
  - Added `frontend/app/store/global-model.test.ts` for:
    - fire-and-forget invocation + throttling behavior
    - error logging/swallowing on rejected telemetry call

```ts
setIsActive(): void {
    const now = Date.now();
    if (now - this.lastSetIsActiveTs < GlobalModel.IsActiveThrottleMs) {
        return;
    }
    this.lastSetIsActiveTs = now;
    util.fireAndForget(async () => {
        try {
            await getApi().setIsActive();
        } catch (e) {
            console.log("setIsActive error", e);
        }
    });
}
```

<!-- START COPILOT CODING AGENT TIPS -->
---

🔒 GitHub Advanced Security automatically protects Copilot coding agent
pull requests. You can protect all pull requests by enabling Advanced
Security for your repositories. [Learn more about Advanced
Security.](https://gh.io/cca-advanced-security)

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-03-04 16:25:53 -08:00
Copilot
7f5487d9ac
Remove mock-heavy OSC52 Vitest coverage (#2975)
The issue called out that the OSC52 unit tests were mostly validating
mock setup rather than meaningful behavior. This PR trims that
low-signal coverage by removing the Vitest suite for OSC52.

- **Scope**
  - Deleted `frontend/app/view/term/osc-handlers.test.ts`.
  - No production/runtime code changes.

- **Rationale reflected in changes**
- Removes brittle, mock-dominant tests for logic considered too simple
for this unit-test shape.
  - Keeps the codebase focused on higher-value test coverage.

```diff
- frontend/app/view/term/osc-handlers.test.ts
```

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/wavetermdev/waveterm/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-03-04 16:24:42 -08:00
Copilot
edc20f7ec0
Bring Anthropic usechat backend to OpenAI-level tool-use parity and stream robustness (#2971)
This updates `pkg/aiusechat/anthropic` from partial implementation to
full backend parity for core tool-use orchestration and stream behavior.
The main gaps were unimplemented tool lifecycle methods, missing
persisted tool-use UI state, and weaker disconnect/error handling versus
the OpenAI backend.

- **Tool-use lifecycle parity (critical path)**
  - Implemented Anthropic backend support for:
    - `UpdateToolUseData`
    - `RemoveToolUseCall`
    - `GetFunctionCallInputByToolCallId`
- Wired `pkg/aiusechat/usechat-backend.go` to call Anthropic
implementations instead of stubs.
- Added Anthropic run-step nil-message guard so `nil` responses are not
wrapped into `[]GenAIMessage{nil}`.

- **Persisted tool-use state in Anthropic native messages**
  - Added internal `ToolUseData` storage on Anthropic `tool_use` blocks.
- Ensured internal-only fields are stripped before API requests via
`Clean()`.

- **UI conversion parity for reloaded history**
- Extended `ConvertToUIMessage()` to emit `data-tooluse` parts when
tool-use metadata exists, in addition to `tool-{name}` parts.

- **Streaming UX parity for tool argument deltas**
  - Added `aiutil.SendToolProgress(...)` calls during:
    - `input_json_delta` (incremental updates)
    - `content_block_stop` for `tool_use` (final update)

- **Disconnect/stream robustness**
  - Added `sse.Err()` checks in event handling and decode-error path.
- Added partial-text extraction on client disconnect and deterministic
ordering of partial blocks.
- Cleans up completed blocks from in-flight state to avoid duplicate
partial extraction.

- **Correctness + hygiene alignment**
- Continuation model checks now use `AreModelsCompatible(...)` (instead
of strict string equality).
- Added hostname sanitization in Anthropic error paths (HTTP error
parsing and `httpClient.Do` failures).
- Replaced unconditional Anthropic debug `log.Printf` calls with
`logutil.DevPrintf`.

- **Targeted coverage additions**
  - Added Anthropic tests for:
    - function-call lookup by tool call id
    - tool-use data update + removal
    - `data-tooluse` UI conversion behavior

```go
// usechat-backend.go
func (b *anthropicBackend) RunChatStep(...) (..., []uctypes.GenAIMessage, ...) {
    stopReason, msg, rateLimitInfo, err := anthropic.RunAnthropicChatStep(ctx, sseHandler, chatOpts, cont)
    if msg == nil {
        return stopReason, nil, rateLimitInfo, err
    }
    return stopReason, []uctypes.GenAIMessage{msg}, rateLimitInfo, err
}
```

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/wavetermdev/waveterm/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-03-04 16:20:50 -08:00
Copilot
9d4acb7aad
Add builder-launch strip to AppsFloatingWindow (#2979)
AppsFloatingWindow currently lists local apps but does not provide an
in-context way to open the app builder picker. This change adds a bottom
ghost-style action strip (`+ Build/Edit Apps`) that launches the builder
via Electron preload with `openBuilder(null)`.

- **What changed**
  - Added a new bottom strip action inside `AppsFloatingWindow`:
    - Label: `+ Build/Edit Apps`
- Visual style: ghost-like footer strip with top border, hover state,
full-width click target
- Wired the strip to call the preload API and close the floating window:
    - `getApi().openBuilder(null)` (`null` app id opens the app picker)
- Kept the change scoped to `frontend/app/workspace/widgets.tsx` with no
behavior changes to app-grid item launching.

- **Implementation detail**
  - Imported `getApi` from `@/store/global`
  - Added a memoized handler for builder launch:
  
```tsx
const handleOpenBuilder = useCallback(() => {
    getApi().openBuilder(null);
    onClose();
}, [onClose]);
```

- **UI preview**
-
<screenshot>https://github.com/user-attachments/assets/1448588f-ff1d-41b5-af72-2849135ca1f3</screenshot>

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/wavetermdev/waveterm/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-03-04 16:18:29 -08:00
Copilot
f4acfc9456
Add virtualized flat-list TreeView component and preview sandbox (#2972)
This PR introduces a new frontend TreeView widget intended for
VSCode-style explorer use cases, without backend wiring yet. It provides
a reusable, backend-agnostic API with virtualization, flat visible-row
projection, and preview coverage under `frontend/preview`.

- **What this adds**
- New `TreeView` component in `frontend/app/treeview/treeview.tsx`
designed around:
    - flat `visibleRows` projection with `depth` (not recursive render)
    - TanStack Virtual row virtualization
    - responsive width constraints + horizontal/vertical scrolling
    - single-selection, expand/collapse, and basic keyboard navigation
- New preview: `frontend/preview/previews/treeview.preview.tsx` with
async mocked directory loading and width controls.
- Focused tests: `frontend/app/treeview/treeview.test.ts` for
projection/sorting/synthetic-row behavior.

- **Tree model + projection behavior**
- Defines a canonical `TreeNodeData` wrapper (separate from direct
`FileInfo` coupling) with:
- `id`, `parentId`, `isDirectory`, `mimeType`, flags, `childrenStatus`,
`childrenIds`, `capInfo`
- Builds `visibleRows` from `nodesById + expandedIds` and injects
synthetic rows for:
    - `loading`
    - `error`
    - `capped` (“Showing first N entries”)

- **Interaction model implemented**
  - Click: select row
  - Double-click directory (or chevron click): expand/collapse
  - Double-click file: emits `onOpenFile`
  - Keyboard:
    - Up/Down: move visible selection
    - Left: collapse selected dir or move to parent
    - Right: expand selected dir or move to first child

- **Sorting + icon strategy**
  - Child sorting is deterministic and stable:
    - directories first
    - case-insensitive label order
    - id/path tie-breaker
- Icon resolution supports directory/file/error states and simple
mimetype/extension fallbacks.

- **Example usage**
```tsx
<TreeView
    rootIds={["workspace:/"]}
    initialNodes={{ "workspace:/": { id: "workspace:/", isDirectory: true, childrenStatus: "unloaded" } }}
    fetchDir={async (id, limit) => ({ nodes: data[id].slice(0, limit), capped: data[id].length > limit })}
    maxDirEntries={120}
    minWidth={100}
    maxWidth={400}
    height={420}
    onSelectionChange={(id) => setSelection(id)}
/>
```

- **<screenshot>**
-
https://github.com/user-attachments/assets/6f8b8a2a-f9a1-454d-bf4f-1d4a97b6e123

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-03-03 18:25:42 -08:00
Mike Sawka
98c374b8cd
quick updates to get apptype (#2969) 2026-03-03 15:19:23 -08:00
Copilot
e2b2498741
UI component for vertical tab bar
Dragging in the vertical tab bar had two UX artifacts: the drop marker
reserved layout space (leaving a visible accent gap above selected
tabs), and hover styling could remain on the old row after drop until
the mouse moved. This updates drag visuals to be overlay-based and
forces hover recalculation at drag end.

- **Drop marker moved out of flow (no selected-tab accent gap)**
- Replaced per-row in-flow divider placeholders with a single absolutely
positioned drop line in the scroll container.
- Drop line now aligns to actual tab boundaries (`offsetTop` /
`offsetHeight`) so it covers the divider location directly.

- **Drop target rendering simplified**
- Container is `relative`; marker is conditionally rendered only while
reordering.
- `dropLineTop` is tracked during drag events and used to position the
marker without affecting layout.

- **Stale hover state cleared after drop**
- Added a minimal drag-lifecycle reset mechanism (`hoverResetVersion`)
and used it in `VTab` keys.
- On drag end/drop, rows remount once, clearing browser-retained
`:hover` on the old index immediately.

```tsx
<VTab key={`${tab.id}:${hoverResetVersion}`} ... />

{dragTabId != null && dropIndex != null && dropLineTop != null && (
    <div
        className="pointer-events-none absolute left-0 right-0 border-t-2 border-accent/80"
        style={{ top: dropLineTop, transform: "translateY(-1px)" }}
    />
)}
```

- **<screenshot>**
-
https://github.com/user-attachments/assets/8c25ef6f-c600-484e-a4fa-6ac83657b484

<!-- START COPILOT CODING AGENT TIPS -->
---

🔒 GitHub Advanced Security automatically protects Copilot coding agent
pull requests. You can protect all pull requests by enabling Advanced
Security for your repositories. [Learn more about Advanced
Security.](https://gh.io/cca-advanced-security)

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-03-03 09:00:34 -08:00
Mike Sawka
b5d23e544e
v0.14.1 Release Notes and Onboarding (#2966) 2026-03-03 08:58:31 -08:00
Copilot
df24959e23
Add native 2+ arg RPC support and wire a concrete TestMultiArgCommand through server, generated clients, and CLI (#2963)
This PR extends WSH RPC command signatures to support `ctx + 2+ typed
args` while preserving existing `ctx` and `ctx + 1 arg` behavior. It
also adds a concrete `TestMultiArgCommand` end-to-end so the generated
Go/TS client surfaces can be inspected and exercised from CLI.

- **RPC wire + dispatch model**
- Added `wshrpc.MultiArg` (`args []any`) as the over-the-wire envelope
for 2+ arg commands.
- Extended RPC metadata to track all command arg types
(`CommandDataTypes`) and exposed a helper for normalized access.
  - Updated server adapter unmarshalling to:
    - decode `MultiArg` for 2+ arg commands,
    - validate arg count,
- re-unmarshal each arg into its declared type before invoking typed
handlers.
  - Kept single-arg commands on the existing non-`MultiArg` path.

- **Code generation (Go + TS)**
- Go codegen now emits multi-parameter wrappers for 2+ arg methods and
packs payload as `wshrpc.MultiArg`.
- TS codegen now emits multi-parameter API methods and packs payload as
`{ args: [...] }`.
  - 0/1-arg generation remains unchanged to avoid wire/API churn.

- **Concrete command added for validation**
  - Added to `WshRpcInterface`:
- `TestMultiArgCommand(ctx context.Context, arg1 string, arg2 int, arg3
bool) (string, error)`
- Implemented in `wshserver` with deterministic formatted return output
including source + all args.
- Updated `wsh test` command to call `TestMultiArgCommand` and print the
returned string.

- **Focused coverage**
- Added/updated targeted tests around RPC metadata and Go/TS multi-arg
codegen behavior, including command declaration for `testmultiarg`.

Example generated call shape:

```go
func TestMultiArgCommand(w *wshutil.WshRpc, arg1 string, arg2 int, arg3 bool, opts *wshrpc.RpcOpts) (string, error) {
    return sendRpcRequestCallHelper[string](
        w,
        "testmultiarg",
        wshrpc.MultiArg{Args: []any{arg1, arg2, arg3}},
        opts,
    )
}
```

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-03-02 12:29:04 -08:00
Copilot
9d89f4372c
Complete onboarding preview coverage for init/telemetry-star flow and align preview state wiring (#2965)
The onboarding preview was missing the first two pages in the
new-install flow (`InitPage` and `NoTelemetryStarPage`). This update
adds those views to the preview and aligns state access with the current
store pattern so they render correctly in preview mode.

- **Preview coverage**
- Added `InitPage` and `NoTelemetryStarPage` to
`frontend/preview/previews/onboarding.preview.tsx` so the full early
onboarding path is visible in the preview server.

- **Settings access modernization**
- Replaced full `settingsAtom` usage in `InitPage` with targeted
`useSettingsKeyAtom("telemetry:enabled")`.
- Removes broad settings dependency and uses the existing preview-safe
settings path.

- **Preview bootstrap state init**
- Added `ClientModel.getInstance().initialize(null)` in
`frontend/preview/preview.tsx` to ensure `clientAtom` is initialized in
preview runtime without backend client data.

```tsx
// onboarding.tsx
const telemetrySetting = useSettingsKeyAtom("telemetry:enabled");
const clientData = useAtomValue(ClientModel.getInstance().clientAtom);
const [telemetryEnabled, setTelemetryEnabled] = useState<boolean>(!!telemetrySetting);

// preview.tsx
setWaveWindowType("preview");
ClientModel.getInstance().initialize(null);
```

- **<screenshot>**
- ![Onboarding
preview](https://github.com/user-attachments/assets/bf1aeb3d-3fd0-4169-bd3b-a79e1a865ca8)

<!-- START COPILOT CODING AGENT TIPS -->
---

🔒 GitHub Advanced Security automatically protects Copilot coding agent
pull requests. You can protect all pull requests by enabling Advanced
Security for your repositories. [Learn more about Advanced
Security.](https://gh.io/cca-advanced-security)

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-03-02 12:28:29 -08:00
Copilot
95a97ca932
Add Onboarding Flows to Preview Server (#2960)
`FilesPage` was previously previewed through a special path because
`EditBashrcCommand` (Monaco via `CodeEditor`) threw in preview mode.
With preview/global handling now fixed, preview can run the real Files
onboarding flow end-to-end without command overrides.

- **Files preview now uses the real FilesPage path**
  - `OnboardingPreview` renders `FilesPage` directly.
- Removed preview-only command injection/override behavior for Files
onboarding.

- **Reverted FilesPage customization**
  - Dropped optional command renderer plumbing added for preview.
  - Restored FilesPage to its original internal command rotation:
    `EditBashrcCommand -> ViewShortcutsCommand -> ViewLogoCommand`.

- **Result**
  - No Files-specific preview fork remains.
  - Preview and production use the same FilesPage command lifecycle.

```tsx
const commands = [
    (onComplete: () => void) => <EditBashrcCommand onComplete={onComplete} />,
    (onComplete: () => void) => <ViewShortcutsCommand isMac={isMac} onComplete={onComplete} />,
    (onComplete: () => void) => <ViewLogoCommand onComplete={onComplete} />,
];
```

<screenshot>

![Onboarding FilesPage preview using default
commands](https://github.com/user-attachments/assets/c5baf015-6204-41da-b72b-408ef137f32b)

</screenshot>

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/wavetermdev/waveterm/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-03-02 11:06:01 -08:00
Copilot
9c3cf984e8
Make frontend CSS color validation Chromium-only (remove DOM style fallback) (#2946)
The new color validator is used exclusively in the Electron/Chromium
frontend, so fallback parsing via temporary DOM elements is unnecessary.
This update tightens the implementation to rely on the browser-native
CSS capability check only.

- **Scope**
- Keep `validateCssColor(color: string): string` behavior unchanged
(returns normalized type for valid colors, throws on invalid).
  - Remove non-Chromium fallback logic from validation path.

- **Implementation**
  - **`frontend/util/color-validator.ts`**
    - `isValidCssColor` now exclusively uses:
      - `CSS.supports("color", color)`
- Removed fallback using `document.createElement(...).style.color`
assignment/parsing.

- **Behavioral contract (unchanged)**
- Valid values still return specific type strings (`hex`, `hex8`, `rgb`,
`rgba`, `hsl`, `keyword`, etc.).
  - Invalid values still throw `Error("Invalid CSS color: ...")`.

```ts
function isValidCssColor(color: string): boolean {
    if (typeof CSS == "undefined" || typeof CSS.supports != "function") {
        return false;
    }
    return CSS.supports("color", color);
}
```

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/wavetermdev/waveterm/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-02-27 15:48:56 -08:00
Mike Sawka
e8ebe88651
Fix for Claude Code Scroll to Top Bug (#2956)
There may be more cases here that I don't know about, but this fixes a
good chunk of them. This catches the CC "repaint" transaction and forces
a scrollToBottom. That should handle context repaints and resize
repaints.

Also adds a new (hidden) terminal escape sequence debugger, and (in dev
mode) adds a last 50 writes cache that can be used to look at and debug
output.
2026-02-27 15:34:35 -08:00
Copilot
b2f3129aa0
Add wsh debugterm (#2947)
`wsh debugterm` currently decodes terminal bytes sourced from backend
block files. This extends it with a stdin-driven path so FE-emitted
payloads like `["...", "..."]` can be decoded directly without requiring
block lookup/RPC in that mode.

- **CLI surface**
  - Added new mode: `--mode stdin`
  - Updated mode help text to: `hex`, `decode`, `stdin`
  - Centralized mode validation via `getDebugTermMode()`

- **Execution path split**
  - Added mode-aware pre-run (`debugTermPreRun`):
    - `stdin` mode: skips RPC setup
    - `hex`/`decode`: keeps existing RPC setup behavior
  - `stdin` mode now:
    - reads all stdin
    - parses JSON as `[]string`
    - concatenates entries and runs existing decode formatter

- **Parsing support**
  - Added `parseDebugTermStdinData([]byte) ([]byte, error)`
  - Error messaging explicitly requires a JSON array of strings

- **Tests**
  - Added focused coverage for:
    - valid stdin JSON array parsing + decoded output
    - invalid stdin JSON input

```go
stdinData, _ := io.ReadAll(WrappedStdin)
termData, err := parseDebugTermStdinData(stdinData) // expects []string JSON
if err != nil { return err }
WriteStdout("%s", formatDebugTermDecode(termData))
```

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/wavetermdev/waveterm/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-02-27 10:24:26 -08:00
Mike Sawka
c5afeb8747
simplify scrollbar code in terminal, simplify fit code (#2944) 2026-02-26 16:07:52 -08:00
Mike Sawka
cfbdc2554c
keep the terminal scrolled to the bottom across resizes if it was at the bottom (#2941) 2026-02-25 22:39:46 -08:00
Mike Sawka
296760d2cd
Fix IME issues (#2938) 2026-02-25 16:07:56 -08:00
Mike Sawka
8306c6bbe7
remove orphaned style.scss, make windowtype.ts, small CSS cleanup (#2939)
also duplicates rules.md for kilocode which was not picking it up from
the .roo directory
2026-02-25 16:07:13 -08:00
Mike Sawka
ba7aea715e
Better integration for terminal links (better context-menu and add a tooltip) (#2934)
addresses issue #2901
2026-02-25 13:03:41 -08:00
Copilot
ed7fa40300
Refine RemoteFileMultiInfoCommand semantics: dedup inputs, per-path stat errors, and cancellation (#2935)
This follow-up tightens the new multi-file remote stat RPC behavior to
match expected usage: duplicate input paths are skipped, per-path stat
failures no longer fail the entire call, and cancellation is respected
during iteration.

- **RPC response model**
  - Added `staterror` to `FileInfo`:
    - `StatError string \`json:"staterror,omitempty"\``
  - Generated TS type now exposes `staterror?: string` on `FileInfo`.

- **`RemoteFileMultiInfoCommand` behavior updates**
- **Dedup:** if an input path key is already present in the result map,
the loop continues.
- **Non-fatal stat failures:** on `fileInfoInternal` error, the command
now emits a `FileInfo` entry for that input with:
    - resolved `path`, `dir`, `name`
    - `staterror` populated with `err.Error()`
    - continues processing remaining paths.
- **Cancellation:** checks `ctx.Err()` at the top of each iteration and
returns immediately if canceled.

- **PR scope cleanup**
  - Removed the previously added test file from this PR per request.

```go
if _, found := rtn[path]; found {
    continue
}
if ctx.Err() != nil {
    return nil, ctx.Err()
}
fileInfo, err := impl.fileInfoInternal(cleanedPath, false)
if err != nil {
    rtn[path] = wshrpc.FileInfo{
        Path:      wavebase.ReplaceHomeDir(cleanedPath),
        Dir:       computeDirPart(cleanedPath),
        Name:      filepath.Base(cleanedPath),
        StatError: err.Error(),
    }
    continue
}
```

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-02-25 12:52:50 -08:00
Copilot
624476fa33
Add terminal cursor style/blink config with block-level overrides (#2933)
Adds support for configuring terminal cursor style and blink behavior in
terminal blocks, with hierarchical resolution (block metadata →
connection overrides → global settings). New keys are `term:cursor`
(`block`/`underline`/`bar`, default `block`) and `term:cursorblink`
(`bool`, default `false`).

- **Config surface: new terminal keys**
  - Added to global settings schema/types:
    - `pkg/wconfig/settingsconfig.go`
  - Added to block metadata typing:
    - `pkg/waveobj/wtypemeta.go`
  - Added default values:
    - `pkg/wconfig/defaultconfig/settings.json`
      - `"term:cursor": "block"`
      - `"term:cursorblink": false`

- **Frontend terminal behavior (xterm options)**
  - `frontend/app/view/term/termwrap.ts`
- Added `setCursorStyle()` with value normalization (`underline`/`bar`
else fallback `block`)
    - Added `setCursorBlink()`
- Applies both options on terminal construction via
`getOverrideConfigAtom(...)`
  - `frontend/app/view/term/term-model.ts`
    - Subscribes to `term:cursor` and `term:cursorblink` override atoms
- Propagates live updates to `term.options.cursorStyle` /
`term.options.cursorBlink`
    - Cleans up subscriptions in `dispose()`

- **Generated artifacts**
  - Regenerated config/type outputs after Go type additions:
    - `schema/settings.json`
    - `pkg/wconfig/metaconsts.go`
    - `pkg/waveobj/metaconsts.go`
    - `frontend/types/gotypes.d.ts`

- **Docs**
  - Updated config reference and default config example:
    - `docs/docs/config.mdx`

```ts
// termwrap.ts
this.setCursorStyle(globalStore.get(getOverrideConfigAtom(this.blockId, "term:cursor")));
this.setCursorBlink(globalStore.get(getOverrideConfigAtom(this.blockId, "term:cursorblink")) ?? false);

// term-model.ts (live updates)
const termCursorAtom = getOverrideConfigAtom(blockId, "term:cursor");
this.termCursorUnsubFn = globalStore.sub(termCursorAtom, () => {
    this.termRef.current?.setCursorStyle(globalStore.get(termCursorAtom));
});
```

<!-- START COPILOT CODING AGENT TIPS -->
---

🔒 GitHub Advanced Security automatically protects Copilot coding agent
pull requests. You can protect all pull requests by enabling Advanced
Security for your repositories. [Learn more about Advanced
Security.](https://gh.io/cca-advanced-security)

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-02-25 12:40:20 -08:00
Copilot
96ad7a5c5b
Wire deterministic context-menu close signaling across Electron and renderer (#2932)
Context-menu opens previously produced renderer callbacks only on
selection (0 or 1 events). This change makes the lifecycle
deterministic: every menu open now emits exactly one completion signal,
with `null` on cancel and item id on selection.

- **Main process: single terminal callback per popup**
  - Updated `emain/emain-menu.ts` to use `menu.popup({ callback })`.
  - Tracks whether a menu item click occurred during the popup.
- Emits `contextmenu-click: null` only when the menu closes without
selection.
  - Suppresses duplicate close events when a click already fired.

- **Preload + API typing: nullable context-menu callback payload**
  - Updated preload bridge and `frontend/types/custom.d.ts` so:
    - `onContextMenuClick` now accepts `(id: string | null) => void`.
- Keeps existing channel semantics while allowing explicit cancel
signal.

- **Renderer context menu model: close/select/cancel hooks**
  - Extended `showContextMenu` with optional `opts`:
    - `onSelect?: (item) => void`
    - `onCancel?: () => void`
    - `onClose?: (item: MenuItem | null) => void`
  - Execution order on selection:
    1. original item `click`
    2. `onSelect`
    3. `onClose`
  - Execution order on cancel:
    1. `onCancel`
    2. `onClose(null)`

- **Targeted behavior tests**
  - Expanded `frontend/app/store/contextmenu.test.ts` to verify:
    - singleton wiring still initializes once,
    - selection path ordering (`click -> onSelect -> onClose`),
    - cancel path ordering (`onCancel -> onClose(null)`).

```ts
ContextMenuModel.getInstance().showContextMenu(menu, e, {
    onSelect: (item) => { /* after item.click */ },
    onCancel: () => { /* close without selection */ },
    onClose: (itemOrNull) => { /* always called */ },
});
```

<screenshot>

Not applicable — this is a behavioral/API flow change in native context
menu lifecycle rather than a visual UI update.

</screenshot>

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-02-25 12:39:48 -08:00
Mike Sawka
752265fed7
add a preview component for tabs, update contextmenu model w/ getInstance method (#2927) 2026-02-25 12:39:07 -08:00
Copilot
200863bbbc
Remove unused frontend notification system and dev-only updater notification UI (#2929)
This PR removes the legacy notification pipeline from the frontend
(`pushNotification`, removal helpers, notification atoms/types, and
notification UI components). The updater status notification path was
only wired through this system and dev-only rendering in `App`, so the
entire path is deleted.

- **Store/API cleanup**
  - Removed notification functions from `frontend/app/store/global.ts`:
    - `pushNotification`
    - `removeNotificationById`
    - `removeNotification`
    - internal add/update helper
- Removed notification API exposure from `frontend/wave.ts`
(`window.pushNotification`, `window.removeNotificationById`).

- **Global atom/type cleanup**
- Removed notification atoms from `frontend/app/store/global-atoms.ts`:
    - `notifications`
    - `notificationPopoverMode`
- Removed notification-related fields/types from
`frontend/types/custom.d.ts`:
    - `GlobalAtomsType.notifications`
    - `GlobalAtomsType.notificationPopoverMode`
    - `NotificationType`
    - `NotificationActionType`

- **UI removal**
- Removed notification rendering from `frontend/app/app.tsx` by deleting
the `NotificationBubbles` import and dev-guarded render branch.
- Deleted the entire notification UI module under
`frontend/app/notification/`:
    - `notificationbubbles.tsx/.scss`
    - `notificationitem.tsx/.scss`
    - `notificationpopover.tsx`
    - `updatenotifier.tsx`
    - `usenotification.tsx`

- **Representative diff**
  ```tsx
  // before (app.tsx)
import { NotificationBubbles } from
"./notification/notificationbubbles";
  ...
  {isDev() ? <NotificationBubbles></NotificationBubbles> : null}

  // after
  // notification import and render removed
  ```

<screenshot>

UI reference provided for this change set:  

https://github.com/user-attachments/assets/6f56b16f-95c9-4a5e-ada8-18ecdc9a1ff8

</screenshot>

<!-- START COPILOT CODING AGENT TIPS -->
---

🔒 GitHub Advanced Security automatically protects Copilot coding agent
pull requests. You can protect all pull requests by enabling Advanced
Security for your repositories. [Learn more about Advanced
Security.](https://gh.io/cca-advanced-security)

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-02-24 12:49:13 -08:00
Mike Sawka
578f1f3ba3
quick fix for #2918 (refocus block after magnify button click) (#2930) 2026-02-24 12:48:47 -08:00
Copilot
1c5f2f9840
Remove unused FlashError pipeline (UI + global state + style/type surface) (#2928)
`FlashError` was legacy UI/state that was never wired into real runtime
flows (only self-referenced and window-exported for ad-hoc testing).
This PR removes that dead path end-to-end to reduce global surface area
and stale styling/types.

- **Scope: UI removal (`frontend/app/app.tsx`)**
- Deleted the `FlashError` component and its render site (`<FlashError
/>`).
- Removed now-unused imports tied to that component (`removeFlashError`,
`Fragment`, `useState`).

- **Scope: global store cleanup (`frontend/app/store/global.ts`,
`frontend/app/store/global-atoms.ts`)**
  - Removed `pushFlashError` / `removeFlashError`.
- Removed `flashErrors` atom from global atom initialization and
registry.
- Kept notification path intact (`pushNotification`,
`removeNotificationById`, etc.).

- **Scope: external/debug surface cleanup (`frontend/wave.ts`)**
- Removed `pushFlashError` from imports and from `window` export wiring.

- **Scope: type + style cleanup (`frontend/types/custom.d.ts`,
`frontend/app/app.scss`, `frontend/app/theme.scss`)**
  - Removed `GlobalAtomsType.flashErrors`.
  - Removed `FlashErrorType`.
- Removed `.flash-error-container` style block and related z-index token
`--zindex-flash-error-container`.

```ts
// before
(window as any).pushFlashError = pushFlashError;

// after
(window as any).pushNotification = pushNotification;
```

- **`<screenshot>`**
- User-provided screenshot URL (if suitable for PR timeline context):

https://github.com/user-attachments/assets/aacc2e61-a65c-4dbf-bfcc-f9f99a490f20

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-02-24 12:11:38 -08:00
Mike Sawka
110e2b90a1
update eslint to v9 (and fix all eslint errors) (#2923) 2026-02-23 18:42:17 -08:00
Copilot
a79a534975
Refine global atom split: keep atom init in global-atoms.ts, move About-menu wiring back to global.ts (#2900)
This refactor keeps `global-atoms.ts` as the owner of atom
state/initialization while correcting ownership boundaries for UI/event
wiring. Specifically, the About menu handler was moved out of atom
initialization and back into `global.ts` startup orchestration.

- **Context / intent**
  - `global-atoms.ts` should own atom construction and registries only.
- `global.ts` should orchestrate app-level startup side effects and
re-export compatibility APIs.

- **What changed**
  - **`global-atoms.ts`**
    - Removed `onMenuItemAbout` registration from `initGlobalAtoms`.
- Removed `modalsModel` import (no longer needed; improves layering).
- Continued to own atom state (`atoms`), atom init (`initGlobalAtoms`),
and atom registries/caches.
  - **`global.ts`**
- Added `onMenuItemAbout` registration to `initGlobal` immediately after
`initGlobalAtoms(initOpts)`.
    - Keeps UI-level event wiring with global startup orchestration.

- **Boundary after this change**
  - `global-atoms.ts`: atom ownership + atom graph init.
- `global.ts`: runtime orchestration + menu/event side effects +
compatibility re-exports.

```ts
// global.ts
function initGlobal(initOpts: GlobalInitOptions) {
    globalEnvironment = initOpts.environment;
    globalPrimaryTabStartup = initOpts.primaryTabStartup ?? false;
    setPlatform(initOpts.platform);
    initGlobalAtoms(initOpts);
    try {
        getApi().onMenuItemAbout(() => {
            modalsModel.pushModal("AboutModal");
        });
    } catch (e) {
        console.log("failed to initialize onMenuItemAbout handler", e);
    }
}
```

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-02-23 16:30:20 -08:00
Mike Sawka
386faf7454
fix multi-input to use onData instead of onKey (#2922) 2026-02-23 16:05:01 -08:00
Mike Sawka
dc4acc77d5
tooltips for tab bar buttons (AI and workspace switcher) (#2914) 2026-02-23 15:39:10 -08:00
Copilot
705563ebe0
Refactor ContextMenuModel to lazy singleton via getInstance() and update all call sites (#2920)
`contextmenu.ts` eagerly instantiated its model at import time, which
triggered constructor side effects (`getApi()`) too early. This change
aligns ContextMenuModel with the `client-model.ts` singleton pattern so
initialization is explicit and deferred.

- **Singleton pattern migration (`frontend/app/store/contextmenu.ts`)**
- Replaced eager module-level instance creation with a class-based
singleton:
    - `private constructor()`
    - `static getInstance()`
- Removed `ContextMenuModelType` export and now export
`ContextMenuModel` class only.
- Constructor side effects (`onContextMenuClick` wiring) now run only on
first `getInstance()` call.

- **Usage updates (all context menu entry points)**
  - Updated all direct static-style calls:
    - `ContextMenuModel.showContextMenu(...)`
    - ⟶ `ContextMenuModel.getInstance().showContextMenu(...)`
- Applied across app/builder/preview/term/tab/widget context-menu
handlers to keep behavior consistent while deferring initialization.

- **Focused regression test**
  - Added `frontend/app/store/contextmenu.test.ts` to verify:
    - importing `contextmenu` does not initialize the API binding;
    - first `getInstance()` initializes once;
    - repeated `getInstance()` returns the same singleton instance.

```ts
// before
ContextMenuModel.showContextMenu(menu, e);

// after
ContextMenuModel.getInstance().showContextMenu(menu, e);
```

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-02-23 13:42:02 -08:00
Mike Sawka
9abd590176
UI only preview server (+ deployments) (#2919)
adds a new "preview server" for UI testing. hooking up to cloudflare
pages deployments. for now, just a test "about modal" component. will
add more later.
2026-02-23 12:48:28 -08:00
Copilot
195277de45
Generate WaveEvent as a typed discriminated union with explicit null payloads for no-data events (#2899)
This updates WaveEvent typing to be event-aware instead of `data?: any`,
while keeping safe fallback behavior for unmapped events. It also
codifies known no-payload events as `null` payloads and documents event
payload expectations alongside the Go event constants.

- **Event registry + payload documentation (Go)**
- Added `AllEvents` in `pkg/wps/wpstypes.go` as the canonical list of
Wave event names.
  - Added/updated inline payload annotations on `Event_*` constants.
- Marked confirmed no-payload events with `// type: none` (e.g.
`route:up`, `route:down`, `workspace:update`, `waveapp:appgoupdated`).

- **Dedicated WaveEvent TS generation path**
- Added `pkg/tsgen/tsgenevent.go` with `event -> reflect.Type` metadata
(`WaveEventDataTypes`).
  - Supports three cases:
    - mapped concrete type → strong TS payload type
    - mapped `nil` → `data?: null` (explicit no-data contract)
    - unmapped event → `data?: any` (non-breaking fallback)

- **Custom WaveEvent output and default suppression**
- Suppressed default struct-based `WaveEvent` emission in
`gotypes.d.ts`.
  - Added generated `frontend/types/waveevent.d.ts` containing:
    - `WaveEventName` string-literal union from `AllEvents`
    - discriminated `WaveEvent` union keyed by `event`.

- **Generator wiring + focused coverage**
- Hooked custom event generation into
`cmd/generatets/main-generatets.go`.
  - Added `pkg/tsgen/tsgenevent_test.go` assertions for:
    - typed mapped events
    - explicit `null` for known no-data events
    - `any` fallback for unmapped events.

```ts
type WaveEvent = {
  event: WaveEventName;
  scopes?: string[];
  sender?: string;
  persist?: number;
  data?: any;
} & (
  { event: "block:jobstatus"; data?: BlockJobStatusData } |
  { event: "route:up"; data?: null } |
  { event: "workspace:update"; data?: null } |
  { event: "some:future:event"; data?: any } // fallback if unmapped
);
```

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-02-20 17:04:03 -08:00
Mike Sawka
b774bab97f
add focusallowscursor for waveai panel as well (#2911) 2026-02-20 15:26:36 -08:00
John Feustel
b9067fb60b
Add app:focusfollowscursor setting (off/on/term) for hover-based block focus (#2908)
This PR adds a new app setting to control whether block focus follows
cursor movement:

  - `app:focusfollowscursor: "off" | "on" | "term"`
  - Default is `"off"` (no behavior change unless enabled)

  ## What changed

  - Added hover-focus behavior on block pointer enter.
  - Added guardrails so hover focus does not trigger when:
  - touch input is used
  - pointer buttons are pressed (drag/select scenarios)
  - modal is open
  - pointer events are disabled
  - block is resizing
  - Added config key plumbing across settings types and schema:
  - `pkg/wconfig/settingsconfig.go`
  - `pkg/wconfig/metaconsts.go`
  - `schema/settings.json`
- `pkg/wconfig/defaultconfig/settings.json` ->
`"app:focusfollowscursor": "off"`
  - Added docs for the new key and default example:
  - `docs/docs/config.mdx`

  ## Behavior

  - `"off"`: do not focus on cursor movement
  - `"on"`: focus follows cursor for all block types
  - `"term"`: focus follows cursor only for terminal blocks
2026-02-20 14:47:26 -08:00
John Feustel
3e9c921dd4
Add vim-style Ctrl+Shift+h/j/k/l block navigation and move launcher shortcut from Ctrl+Shift+k to Ctrl+Shift+x (#2909)
## Summary

This PR adds vim-style movement aliases for block navigation and updates
one conflicting
  shortcut.

  ## Changes

  - Added global keybindings:
    - `Ctrl+Shift+h` -> move focus left
    - `Ctrl+Shift+j` -> move focus down
    - `Ctrl+Shift+k` -> move focus up
    - `Ctrl+Shift+l` -> move focus right
  - Kept existing arrow-based movement:
    - `Ctrl+Shift+ArrowUp/Down/Left/Right`
  - **Changed launcher shortcut**:
    - from `Ctrl+Shift+k`
    - to `Ctrl+Shift+x`

  ## Why `k` changed to `x`

`Ctrl+Shift+k` is now used for vim-style upward movement (`k` = up), so
launcher replace
  needed a new binding.
`Ctrl+Shift+x` was chosen to avoid collisions with existing Wave global
keybindings.

  ## Config / behavior notes

- `app:disablectrlshiftarrows` now disables both block-navigation sets:
    - Arrow keys
    - `h/j/k/l` aliases
  - It does **not** disable `Ctrl+Shift+x` (launcher replace).

  ## Docs updated

  - `docs/docs/keybindings.mdx`
    - Added `Ctrl+Shift+h/j/k/l` navigation display
    - Updated launcher replace shortcut to `Ctrl+Shift+x`
  - `docs/docs/config.mdx`
- Clarified `app:disablectrlshiftarrows` scope to include `Arrow` +
`h/j/k/l`
2026-02-20 14:09:44 -08:00
Copilot
e8d4cddb5a
Add optional per-workspace widget visibility via workspaces in widgets.json (#2898)
This change adds an optional `workspaces` field to widget config entries
so widgets can be scoped to specific workspace UUIDs. Widgets with no
`workspaces` field (or an empty array) continue to render globally,
preserving current `widgets.json` behavior.

- **Config model updates**
- Added `workspaces []string` to `wconfig.WidgetConfigType` with
`json:"workspaces,omitempty"`.
- Updated frontend generated type `WidgetConfigType` with `workspaces?:
string[]`.

- **Sidebar widget filtering**
- `frontend/app/workspace/widgets.tsx` now applies workspace-aware
filtering when building the sidebar widget list:
    - include if `workspaces` is missing or empty
    - include if current `workspace.oid` is present in `workspaces`
    - otherwise exclude
  - Existing `defwidget@ai` filtering logic remains intact.

- **Isolated filtering logic + coverage**
  - Added `frontend/app/workspace/widgetfilter.ts` with:
    - `shouldIncludeWidgetForWorkspace(widget, workspaceId)`
- Added focused tests in `frontend/app/workspace/widgetfilter.test.ts`
for:
    - missing/empty `workspaces`
    - matching/non-matching workspace IDs
    - missing active workspace ID

```ts
function shouldIncludeWidgetForWorkspace(widget: WidgetConfigType, workspaceId?: string): boolean {
  const workspaces = widget.workspaces;
  return !Array.isArray(workspaces) || workspaces.length === 0 || (workspaceId != null && workspaces.includes(workspaceId));
}
```

- **Screenshot**
-
<screenshot>https://github.com/user-attachments/assets/b1727003-793b-4eff-8fc1-00eac9c50b83</screenshot>

<!-- START COPILOT CODING AGENT TIPS -->
---

🔒 GitHub Advanced Security automatically protects Copilot coding agent
pull requests. You can protect all pull requests by enabling Advanced
Security for your repositories. [Learn more about Advanced
Security.](https://gh.io/cca-advanced-security)

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
2026-02-19 16:14:22 -08:00
Copilot
51daeddd6b
Extract counters API from global store into dedicated module (#2897)
This refactor moves the counters helpers out of
`frontend/app/store/global.ts` into a dedicated `counters.ts` module,
and updates existing call sites to import from the new location. The
goal is to keep `global.ts` focused on global store/state concerns while
preserving current counters behavior.

- **Module extraction**
  - Added `frontend/app/store/counters.ts` containing:
    - `counterInc(name, incAmt = 1)`
    - `countersClear()`
    - `countersPrint()`
  - Moved logic unchanged from `global.ts`.

- **Global store cleanup**
- Removed counters state/functions from `frontend/app/store/global.ts`.
  - Removed counters exports from `global.ts`’s export surface.

- **Call site updates**
  - Updated imports to use `@/store/counters` in:
    - `frontend/app/block/block.tsx` (`counterInc`)
    - `frontend/wave.ts` (`countersClear`, `countersPrint`)

```ts
// before
import { counterInc } from "@/store/global";

// after
import { counterInc } from "@/store/counters";
```

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
2026-02-19 15:00:55 -08:00
Mike Sawka
1840de4a13
New Context Menu Item + Wsh Command to Save Scrollback of a Terminal Widget (#2892)
This pull request adds a new command-line feature for exporting terminal
scrollback, improves the accuracy of scrollback extraction (especially
for wrapped lines), and introduces a "Save Session As..." menu option in
the frontend to make exporting session logs more user-friendly. The
changes touch both the backend CLI and the frontend, ensuring users can
easily capture and save terminal output for processing or archiving.
2026-02-19 12:49:57 -08:00
Mike Sawka
65fce27f00
feat: add tab:confirmclose setting to prompt before closing tabs (#2893)
- Add tab:confirmclose boolean config option to SettingsType (Go),
schema/settings.json, and gotypes.d.ts
- Update close-tab IPC handler to use ipcMain.handle (async) and accept
confirmClose param
- Show a native confirmation dialog via dialog.showMessageBoxSync when
confirmClose is true
- Update preload.ts to use ipcRenderer.invoke for close-tab, returning
Promise<boolean>
- Update closeTab type signature in custom.d.ts to return
Promise<boolean>
- Update tabbar.tsx and keymodel.ts to await closeTab result and only
delete layout model on confirmed close
- Document tab:confirmclose in docs/docs/config.mdx

---------

Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
2026-02-19 12:49:16 -08:00
Mike Sawka
0cc6c454a9
detect if omz is installed and shell completion mode in zsh (#2891) 2026-02-19 10:22:56 -08:00
Mike Sawka
bb6ef47eef
fix windows compilation error (#2862) 2026-02-11 14:57:27 -08:00
Mike Sawka
4983ebc9ef
Animation for Durable Sessions Onboarding Screen (#2861) 2026-02-11 13:08:53 -08:00
Mike Sawka
fe71b3780a
Misc v0.14 fixes (#2860)
* Don't allow auto updater to downgrade
* Kill shells with SIGHUP not SIGTERM
* Fix old v0.13 references
2026-02-11 10:40:29 -08:00
Mike Sawka
facefdd12e
job events, terminal command types, connect error codes (#2855) 2026-02-10 20:23:06 -08:00
Mike Sawka
4b77ce6d06
patch onboarding modal for v0.14 (#2852) 2026-02-10 12:07:56 -08:00