Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/docs/releasenotes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,34 @@ sidebar_position: 200

# Release Notes

### v0.14.1 — Mar 3, 2026

Wave v0.14.1 fixes several high-impact terminal bugs (Claude Code scrolling, IME input) and adds new config options: focus-follows-cursor, cursor style customization, workspace-scoped widgets, and vim-style block navigation.

**Terminal Improvements:**

- **Claude Code Scroll Fix** - Fixed a long-standing bug that caused terminal windows to jump to the top unexpectedly, affecting many Claude Code users
- **IME Fix** - Fixed Korean/CJK input where characters were lost or stuck in composition and only committed on Space
- **Scroll Position Preserved on Resize** - Terminal now stays scrolled to the bottom across resizes when it was already at the bottom
- **Better Link Handling** - Terminal URLs now have improved context menus and tooltips for easier navigation
- **Terminal Scrollback Save** - New context menu item and `wsh` command to save terminal scrollback to a file

**New Features:**

- **Focus Follows Cursor** - New `app:focusfollowscursor` setting (off/on/term) for hover-based block focus
- **Terminal Cursor Style & Blink** - New settings for cursor style (block/bar/underline) and blink, configurable per-block
- **Tab Close Confirmation** - New `tab:confirmclose` setting to prompt before closing a tab
- **Workspace-Scoped Widgets** - New optional `workspaces` field in `widgets.json` to show/hide widgets per-workspace
- **Vim-Style Block Navigation** - Added Ctrl+Shift+H/J/K/L to navigate between blocks
- **New AI Providers** - Added Groq and NanoGPT as built-in AI provider presets

**Other Changes:**

- Fixed intermittant bugs with connection switching in terminal blocks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix spelling typo in the release notes.

Line 32 uses “intermittant”; it should be “intermittent”.

✏️ Suggested fix
-- Fixed intermittant bugs with connection switching in terminal blocks
+- Fixed intermittent bugs with connection switching in terminal blocks
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Fixed intermittant bugs with connection switching in terminal blocks
- Fixed intermittent bugs with connection switching in terminal blocks
🧰 Tools
🪛 LanguageTool

[grammar] ~32-~32: Ensure spelling is correct
Context: ...er presets Other Changes: - Fixed intermittant bugs with connection switching in termi...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/docs/releasenotes.mdx` at line 32, Replace the misspelled word
"intermittant" in the release notes sentence "Fixed intermittant bugs with
connection switching in terminal blocks" with the correct spelling
"intermittent" so the line reads "Fixed intermittent bugs with connection
switching in terminal blocks".

- Widgets.json schema improvements for better editor validation
- Package updates and dependency upgrades
- Internal code cleanup and refactoring

### v0.14.0 — Feb 10, 2026

**Durable SSH Sessions and Enhanced Connection Monitoring**
Expand Down
8 changes: 7 additions & 1 deletion frontend/app/onboarding/onboarding-common.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
// Copyright 2026, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

export const CurrentOnboardingVersion = "v0.14.0";
export const CurrentOnboardingVersion = "v0.14.1";

export function OnboardingGradientBg() {
return (
<div className="absolute inset-0 bg-gradient-to-br from-accent/[0.25] via-transparent to-accent/[0.05] pointer-events-none rounded-[10px]" />
);
}
122 changes: 122 additions & 0 deletions frontend/app/onboarding/onboarding-starask.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2026, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

import Logo from "@/app/asset/logo.svg";
import { Button } from "@/app/element/button";
import { ClientModel } from "@/app/store/client-model";
import * as WOS from "@/app/store/wos";
import { RpcApi } from "@/app/store/wshclientapi";
import { TabRpcClient } from "@/app/store/wshrpcutil";

type StarAskPageProps = {
onClose: () => void;
page?: string;
};

export function StarAskPage({ onClose, page = "upgrade" }: StarAskPageProps) {
const handleStarClick = async () => {
RpcApi.RecordTEventCommand(
TabRpcClient,
{
event: "onboarding:githubstar",
props: { "onboarding:githubstar": "star", "onboarding:page": page },
},
{ noresponse: true }
);
const clientId = ClientModel.getInstance().clientId;
await RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("client", clientId),
meta: { "onboarding:githubstar": true },
});
window.open(`https://github.com/wavetermdev/waveterm?ref=${page}`, "_blank");
onClose();
};
Comment on lines +17 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "onboarding-starask.tsx" -type f

Repository: wavetermdev/waveterm

Length of output: 112


🏁 Script executed:

cat -n ./frontend/app/onboarding/onboarding-starask.tsx

Repository: wavetermdev/waveterm

Length of output: 5951


Wrap SetMetaCommand calls in try/finally to ensure onClose() always executes.

If SetMetaCommand rejects at lines 27, 45, or 74, the subsequent onClose() calls at lines 32, 49, and 78 will not run, leaving the modal open unintentionally. Ensure these handlers always close the modal regardless of RPC success or failure:

Suggested pattern
     const handleMaybeLater = async () => {
         RpcApi.RecordTEventCommand(
             TabRpcClient,
             {
                 event: "onboarding:githubstar",
                 props: { "onboarding:githubstar": "later", "onboarding:page": page },
             },
             { noresponse: true }
         );
-        const clientId = ClientModel.getInstance().clientId;
-        await RpcApi.SetMetaCommand(TabRpcClient, {
-            oref: WOS.makeORef("client", clientId),
-            meta: { "onboarding:githubstar": false },
-        });
-        onClose();
+        try {
+            const clientId = ClientModel.getInstance().clientId;
+            await RpcApi.SetMetaCommand(TabRpcClient, {
+                oref: WOS.makeORef("client", clientId),
+                meta: { "onboarding:githubstar": false },
+            });
+        } finally {
+            onClose();
+        }
     };

Also applies to handleStarClick and handleAlreadyStarred.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleStarClick = async () => {
RpcApi.RecordTEventCommand(
TabRpcClient,
{
event: "onboarding:githubstar",
props: { "onboarding:githubstar": "star", "onboarding:page": page },
},
{ noresponse: true }
);
const clientId = ClientModel.getInstance().clientId;
await RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("client", clientId),
meta: { "onboarding:githubstar": true },
});
window.open(`https://github.com/wavetermdev/waveterm?ref=${page}`, "_blank");
onClose();
};
const handleStarClick = async () => {
RpcApi.RecordTEventCommand(
TabRpcClient,
{
event: "onboarding:githubstar",
props: { "onboarding:githubstar": "star", "onboarding:page": page },
},
{ noresponse: true }
);
try {
const clientId = ClientModel.getInstance().clientId;
await RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("client", clientId),
meta: { "onboarding:githubstar": true },
});
} finally {
window.open(`https://github.com/wavetermdev/waveterm?ref=${page}`, "_blank");
onClose();
}
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/onboarding/onboarding-starask.tsx` around lines 17 - 33, The
handlers handleStarClick and handleAlreadyStarred call RpcApi.SetMetaCommand and
then onClose(), but if SetMetaCommand rejects the modal stays open; wrap the
RpcApi.SetMetaCommand(...) call in a try/finally so onClose() is executed
regardless of success or error. Specifically, in handleStarClick and
handleAlreadyStarred, surround the await RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("client", ClientModel.getInstance().clientId), meta: {
"onboarding:githubstar": true } }) (and the corresponding meta call in the other
handler) with try { await ... } finally { onClose(); } while leaving the
event-recording and window.open logic as appropriate.


const handleAlreadyStarred = async () => {
RpcApi.RecordTEventCommand(
TabRpcClient,
{
event: "onboarding:githubstar",
props: { "onboarding:githubstar": "already", "onboarding:page": page },
},
{ noresponse: true }
);
const clientId = ClientModel.getInstance().clientId;
await RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("client", clientId),
meta: { "onboarding:githubstar": true },
});
onClose();
};

const handleRepoLinkClick = () => {
RpcApi.RecordTEventCommand(
TabRpcClient,
{
event: "action:link",
props: { "action:type": "githubrepo", "onboarding:page": page },
},
{ noresponse: true }
);
window.open("https://github.com/wavetermdev/waveterm", "_blank");
};
Comment on lines +52 to +62
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "onboarding-starask.tsx" -o -name "*starask*"

Repository: wavetermdev/waveterm

Length of output: 112


🏁 Script executed:

wc -l ./frontend/app/onboarding/onboarding-starask.tsx

Repository: wavetermdev/waveterm

Length of output: 116


🏁 Script executed:

cat -n ./frontend/app/onboarding/onboarding-starask.tsx

Repository: wavetermdev/waveterm

Length of output: 5951


Use a semantic link for the repo target to enable keyboard accessibility.

The <div> element at line 95 with an onClick handler is not keyboard-accessible—it can't be focused with Tab and won't respond to Enter/Space keys. Replace it with an <a> tag:

Accessible alternative
-                    <div
+                    <a
+                        href="https://github.com/wavetermdev/waveterm"
+                        target="_blank"
+                        rel="noopener noreferrer"
                         className="group flex items-center justify-center gap-2 text-secondary text-sm mt-1 cursor-pointer transition-colors"
                         onClick={handleRepoLinkClick}
                     >
                         <i className="fa-brands fa-github text-foreground text-lg group-hover:text-accent transition-colors" />
                         <span className="text-foreground font-mono text-sm group-hover:text-accent group-hover:underline transition-colors">
                             wavetermdev/waveterm
                         </span>
-                    </div>
+                    </a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/onboarding/onboarding-starask.tsx` around lines 52 - 62, The
clickable repo area uses a non-semantic <div> with handleRepoLinkClick, which is
not keyboard-accessible; replace that <div> with a semantic <a> element that
includes href="https://github.com/wavetermdev/waveterm", target="_blank",
rel="noopener noreferrer" and keeps the onClick handler
(RpcApi.RecordTEventCommand call in handleRepoLinkClick) and an appropriate
aria-label so it is focusable and activates with Enter/Space via native browser
behavior.


const handleMaybeLater = async () => {
RpcApi.RecordTEventCommand(
TabRpcClient,
{
event: "onboarding:githubstar",
props: { "onboarding:githubstar": "later", "onboarding:page": page },
},
{ noresponse: true }
);
const clientId = ClientModel.getInstance().clientId;
await RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("client", clientId),
meta: { "onboarding:githubstar": false },
});
onClose();
};

return (
<div className="flex flex-col h-full">
<header className="flex flex-col gap-2 border-b-0 p-0 mt-1 mb-6 w-full unselectable flex-shrink-0">
<div className="flex justify-center">
<Logo />
</div>
<div className="text-center text-[25px] font-normal text-foreground">Support open-source. Star Wave. ⭐</div>
</header>
<div className="flex-1 flex flex-col items-center justify-center gap-5 unselectable">
<div className="flex flex-col items-center gap-4 max-w-[460px] text-center">
<div className="text-secondary text-sm leading-relaxed">
Wave is free, open-source, and open-model. Stars help us stay visible against closed
alternatives. One click makes a difference.
</div>
<div
className="group flex items-center justify-center gap-2 text-secondary text-sm mt-1 cursor-pointer transition-colors"
onClick={handleRepoLinkClick}
>
<i className="fa-brands fa-github text-foreground text-lg group-hover:text-accent transition-colors" />
<span className="text-foreground font-mono text-sm group-hover:text-accent group-hover:underline transition-colors">
wavetermdev/waveterm
</span>
</div>
</div>
</div>
<footer className="unselectable flex-shrink-0 mt-6">
<div className="flex flex-row items-center justify-center gap-2.5 [&>button]:!px-5 [&>button]:!py-2 [&>button]:text-sm [&>button]:!h-[37px]">
<Button className="outlined grey font-[600]" onClick={handleAlreadyStarred}>
🙏 Already Starred
</Button>
<Button className="outlined green font-[600]" onClick={handleStarClick}>
⭐ Star Now
</Button>
<Button className="outlined grey font-[600]" onClick={handleMaybeLater}>
Maybe Later
</Button>
</div>
</footer>
</div>
);
}

164 changes: 90 additions & 74 deletions frontend/app/onboarding/onboarding-upgrade-minor.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright 2025, Command Line Inc.
// Copyright 2026, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

import Logo from "@/app/asset/logo.svg";
import { Button } from "@/app/element/button";
import { FlexiModal } from "@/app/modals/modal";
import { CurrentOnboardingVersion } from "@/app/onboarding/onboarding-common";
import { CurrentOnboardingVersion, OnboardingGradientBg } from "@/app/onboarding/onboarding-common";
import { OnboardingFeatures } from "@/app/onboarding/onboarding-features";
import { ClientModel } from "@/app/store/client-model";
import { globalStore } from "@/app/store/global";
Expand All @@ -17,6 +17,84 @@ import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
import { useEffect, useRef, useState } from "react";
import { debounce } from "throttle-debounce";

type UpgradeMinorWelcomePageProps = {
onStarClick: () => void;
onAlreadyStarred: () => void;
onMaybeLater: () => void;
};

const UpgradeMinorWelcomePage = ({ onStarClick, onAlreadyStarred, onMaybeLater }: UpgradeMinorWelcomePageProps) => {
return (
<div className="flex flex-col h-full">
<header className="flex flex-col gap-2 border-b-0 p-0 mt-1 mb-4 w-full unselectable flex-shrink-0">
<div className="flex justify-center">
<Logo />
</div>
<div className="text-center text-[25px] font-normal text-foreground">Welcome to Wave v0.14!</div>
</header>
<OverlayScrollbarsComponent
className="flex-1 overflow-y-auto min-h-0"
options={{ scrollbars: { autoHide: "never" } }}
>
<div className="flex flex-col items-center gap-3 w-full mb-2 unselectable">
<div className="flex flex-col items-center gap-4">
<div className="flex flex-row gap-4 items-center">
<div className="flex h-[52px] px-3 items-center rounded-lg bg-hover text-accent text-[24px]">
<i className="fa fa-sparkles" />
<span className="font-bold ml-2 font-mono">Wave AI</span>
</div>
<div className="flex h-[52px] px-3 items-center rounded-lg bg-hover text-[18px]">
<i className="fa-sharp fa-solid fa-shield text-sky-500" />
<span className="font-bold ml-2 text-accent">Durable SSH Sessions</span>
</div>
</div>
<div className="text-secondary leading-relaxed max-w-[600px] text-left">
<p className="mb-4">
Wave AI is your terminal assistant with full context. It can read your terminal output,
analyze widgets, read and write files, and help you solve problems&nbsp;faster.
</p>
<p className="mb-4">
<span className="font-semibold text-foreground">New in v0.13:</span> Wave AI now
supports local models and bring-your-own-key! Use Ollama, LM Studio, vLLM, OpenRouter,
or any OpenAI-compatible provider.
</p>
<p className="mb-4">
<span className="font-semibold text-foreground">New in v0.14:</span> Durable SSH
sessions survive network drops, laptop sleep, and restarts — all without tmux or screen.
</p>
</div>
</div>

<div className="w-full max-w-[550px] border-t border-border my-2"></div>

<div className="flex flex-col items-center gap-3 text-center max-w-[550px]">
<div className="text-foreground text-base">Thanks for being an early Wave adopter! ⭐</div>
<div className="text-secondary text-sm text-left">
A GitHub star shows your support for Wave (and open-source) and helps us reach more
developers.
</div>
</div>
</div>
</OverlayScrollbarsComponent>
<footer className="unselectable flex-shrink-0 mt-4">
<div className="flex flex-row items-center justify-center gap-2.5 [&>button]:!px-5 [&>button]:!py-2 [&>button]:text-sm [&>button]:!h-[37px]">
<Button className="outlined grey font-[600]" onClick={onAlreadyStarred}>
🙏 Already Starred
</Button>
<Button className="outlined green font-[600]" onClick={onStarClick}>
⭐ Star Now
</Button>
<Button className="outlined grey font-[600]" onClick={onMaybeLater}>
Maybe Later
</Button>
</div>
</footer>
</div>
);
};

UpgradeMinorWelcomePage.displayName = "UpgradeMinorWelcomePage";

const UpgradeOnboardingMinor = () => {
const modalRef = useRef<HTMLDivElement | null>(null);
const [pageName, setPageName] = useState<"welcome" | "features">("welcome");
Expand Down Expand Up @@ -57,7 +135,7 @@ const UpgradeOnboardingMinor = () => {
TabRpcClient,
{
event: "onboarding:githubstar",
props: { "onboarding:githubstar": "star" },
props: { "onboarding:githubstar": "star", "onboarding:page": "minorupgrade" },
},
{ noresponse: true }
);
Expand All @@ -75,7 +153,7 @@ const UpgradeOnboardingMinor = () => {
TabRpcClient,
{
event: "onboarding:githubstar",
props: { "onboarding:githubstar": "already" },
props: { "onboarding:githubstar": "already", "onboarding:page": "minorupgrade" },
},
{ noresponse: true }
);
Expand All @@ -92,7 +170,7 @@ const UpgradeOnboardingMinor = () => {
TabRpcClient,
{
event: "onboarding:githubstar",
props: { "onboarding:githubstar": "later" },
props: { "onboarding:githubstar": "later", "onboarding:page": "minorupgrade" },
},
{ noresponse: true }
);
Expand All @@ -119,73 +197,11 @@ const UpgradeOnboardingMinor = () => {
let pageComp: React.JSX.Element = null;
if (pageName === "welcome") {
pageComp = (
<div className="flex flex-col h-full">
<header className="flex flex-col gap-2 border-b-0 p-0 mt-1 mb-4 w-full unselectable flex-shrink-0">
<div className="flex justify-center">
<Logo />
</div>
<div className="text-center text-[25px] font-normal text-foreground">Welcome to Wave v0.14!</div>
</header>
<OverlayScrollbarsComponent
className="flex-1 overflow-y-auto min-h-0"
options={{ scrollbars: { autoHide: "never" } }}
>
<div className="flex flex-col items-center gap-3 w-full mb-2 unselectable">
<div className="flex flex-col items-center gap-4">
<div className="flex flex-row gap-4 items-center">
<div className="flex h-[52px] px-3 items-center rounded-lg bg-hover text-accent text-[24px]">
<i className="fa fa-sparkles" />
<span className="font-bold ml-2 font-mono">Wave AI</span>
</div>
<div className="flex h-[52px] px-3 items-center rounded-lg bg-hover text-[18px]">
<i className="fa-sharp fa-solid fa-shield text-sky-500" />
<span className="font-bold ml-2 text-accent">Durable SSH Sessions</span>
</div>
</div>
<div className="text-secondary leading-relaxed max-w-[600px] text-left">
<p className="mb-4">
Wave AI is your terminal assistant with full context. It can read your terminal
output, analyze widgets, read and write files, and help you solve
problems&nbsp;faster.
</p>
<p className="mb-4">
<span className="font-semibold text-foreground">New in v0.13:</span> Wave AI now
supports local models and bring-your-own-key! Use Ollama, LM Studio, vLLM,
OpenRouter, or any OpenAI-compatible provider.
</p>
<p className="mb-4">
<span className="font-semibold text-foreground">New in v0.14:</span> Durable SSH
sessions survive network drops, laptop sleep, and restarts — all without tmux or
screen.
</p>
</div>
</div>

<div className="w-full max-w-[550px] border-t border-border my-2"></div>

<div className="flex flex-col items-center gap-3 text-center max-w-[550px]">
<div className="text-foreground text-base">Thanks for being an early Wave adopter! ⭐</div>
<div className="text-secondary text-sm text-left">
A GitHub star shows your support for Wave (and open-source) and helps us reach more
developers.
</div>
</div>
</div>
</OverlayScrollbarsComponent>
<footer className="unselectable flex-shrink-0 mt-4">
<div className="flex flex-row items-center justify-center gap-2.5 [&>button]:!px-5 [&>button]:!py-2 [&>button]:text-sm [&>button]:!h-[37px]">
<Button className="outlined grey font-[600]" onClick={handleAlreadyStarred}>
🙏 Already Starred
</Button>
<Button className="outlined green font-[600]" onClick={handleStarClick}>
⭐ Star Now
</Button>
<Button className="outlined grey font-[600]" onClick={handleMaybeLater}>
Maybe Later
</Button>
</div>
</footer>
</div>
<UpgradeMinorWelcomePage
onStarClick={handleStarClick}
onAlreadyStarred={handleAlreadyStarred}
onMaybeLater={handleMaybeLater}
/>
);
} else if (pageName === "features") {
pageComp = <OnboardingFeatures onComplete={handleFeaturesComplete} />;
Expand All @@ -200,12 +216,12 @@ const UpgradeOnboardingMinor = () => {

return (
<FlexiModal className={`${widthClass} rounded-[10px] ${paddingClass} relative overflow-hidden`} ref={modalRef}>
<div className="absolute inset-0 bg-gradient-to-br from-accent/[0.25] via-transparent to-accent/[0.05] pointer-events-none rounded-[10px]" />
<OnboardingGradientBg />
<div className="flex flex-col w-full h-full relative z-10">{pageComp}</div>
</FlexiModal>
);
};

UpgradeOnboardingMinor.displayName = "UpgradeOnboardingMinor";

export { UpgradeOnboardingMinor };
export { UpgradeMinorWelcomePage, UpgradeOnboardingMinor };
Loading
Loading