diff --git a/docs/docs/releasenotes.mdx b/docs/docs/releasenotes.mdx index d2671ec3b5..fb71d99d64 100644 --- a/docs/docs/releasenotes.mdx +++ b/docs/docs/releasenotes.mdx @@ -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 +- 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** diff --git a/frontend/app/onboarding/onboarding-common.tsx b/frontend/app/onboarding/onboarding-common.tsx index b91bf81fa8..9a506b2567 100644 --- a/frontend/app/onboarding/onboarding-common.tsx +++ b/frontend/app/onboarding/onboarding-common.tsx @@ -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 ( +
+ ); +} diff --git a/frontend/app/onboarding/onboarding-starask.tsx b/frontend/app/onboarding/onboarding-starask.tsx new file mode 100644 index 0000000000..bb7678ab2a --- /dev/null +++ b/frontend/app/onboarding/onboarding-starask.tsx @@ -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(); + }; + + 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"); + }; + + 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 ( +
+
+
+ +
+
Support open-source. Star Wave. ⭐
+
+
+
+
+ Wave is free, open-source, and open-model. Stars help us stay visible against closed + alternatives. One click makes a difference. +
+
+ + + wavetermdev/waveterm + +
+
+
+
+
+ + + +
+
+
+ ); +} + diff --git a/frontend/app/onboarding/onboarding-upgrade-minor.tsx b/frontend/app/onboarding/onboarding-upgrade-minor.tsx index 58d80b5645..b165c0ce7f 100644 --- a/frontend/app/onboarding/onboarding-upgrade-minor.tsx +++ b/frontend/app/onboarding/onboarding-upgrade-minor.tsx @@ -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"; @@ -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 ( +
+
+
+ +
+
Welcome to Wave v0.14!
+
+ +
+
+
+
+ + Wave AI +
+
+ + Durable SSH Sessions +
+
+
+

+ 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 faster. +

+

+ New in v0.13: Wave AI now + supports local models and bring-your-own-key! Use Ollama, LM Studio, vLLM, OpenRouter, + or any OpenAI-compatible provider. +

+

+ New in v0.14: Durable SSH + sessions survive network drops, laptop sleep, and restarts — all without tmux or screen. +

+
+
+ +
+ +
+
Thanks for being an early Wave adopter! ⭐
+
+ A GitHub star shows your support for Wave (and open-source) and helps us reach more + developers. +
+
+
+
+
+
+ + + +
+
+
+ ); +}; + +UpgradeMinorWelcomePage.displayName = "UpgradeMinorWelcomePage"; + const UpgradeOnboardingMinor = () => { const modalRef = useRef(null); const [pageName, setPageName] = useState<"welcome" | "features">("welcome"); @@ -57,7 +135,7 @@ const UpgradeOnboardingMinor = () => { TabRpcClient, { event: "onboarding:githubstar", - props: { "onboarding:githubstar": "star" }, + props: { "onboarding:githubstar": "star", "onboarding:page": "minorupgrade" }, }, { noresponse: true } ); @@ -75,7 +153,7 @@ const UpgradeOnboardingMinor = () => { TabRpcClient, { event: "onboarding:githubstar", - props: { "onboarding:githubstar": "already" }, + props: { "onboarding:githubstar": "already", "onboarding:page": "minorupgrade" }, }, { noresponse: true } ); @@ -92,7 +170,7 @@ const UpgradeOnboardingMinor = () => { TabRpcClient, { event: "onboarding:githubstar", - props: { "onboarding:githubstar": "later" }, + props: { "onboarding:githubstar": "later", "onboarding:page": "minorupgrade" }, }, { noresponse: true } ); @@ -119,73 +197,11 @@ const UpgradeOnboardingMinor = () => { let pageComp: React.JSX.Element = null; if (pageName === "welcome") { pageComp = ( -
-
-
- -
-
Welcome to Wave v0.14!
-
- -
-
-
-
- - Wave AI -
-
- - Durable SSH Sessions -
-
-
-

- 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 faster. -

-

- New in v0.13: Wave AI now - supports local models and bring-your-own-key! Use Ollama, LM Studio, vLLM, - OpenRouter, or any OpenAI-compatible provider. -

-

- New in v0.14: Durable SSH - sessions survive network drops, laptop sleep, and restarts — all without tmux or - screen. -

-
-
- -
- -
-
Thanks for being an early Wave adopter! ⭐
-
- A GitHub star shows your support for Wave (and open-source) and helps us reach more - developers. -
-
-
-
-
-
- - - -
-
-
+ ); } else if (pageName === "features") { pageComp = ; @@ -200,7 +216,7 @@ const UpgradeOnboardingMinor = () => { return ( -
+
{pageComp}
); @@ -208,4 +224,4 @@ const UpgradeOnboardingMinor = () => { UpgradeOnboardingMinor.displayName = "UpgradeOnboardingMinor"; -export { UpgradeOnboardingMinor }; +export { UpgradeMinorWelcomePage, UpgradeOnboardingMinor }; diff --git a/frontend/app/onboarding/onboarding-upgrade-patch.tsx b/frontend/app/onboarding/onboarding-upgrade-patch.tsx index a4e09945de..5984eeef53 100644 --- a/frontend/app/onboarding/onboarding-upgrade-patch.tsx +++ b/frontend/app/onboarding/onboarding-upgrade-patch.tsx @@ -4,7 +4,8 @@ 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 { StarAskPage } from "@/app/onboarding/onboarding-starask"; import { ClientModel } from "@/app/store/client-model"; import { globalStore } from "@/app/store/global"; import { disableGlobalKeybindings, enableGlobalKeybindings, globalRefocus } from "@/app/store/keymodel"; @@ -12,6 +13,7 @@ import { modalsModel } from "@/app/store/modalmodel"; import * as WOS from "@/app/store/wos"; import { RpcApi } from "@/app/store/wshclientapi"; import { TabRpcClient } from "@/app/store/wshrpcutil"; +import { useAtomValue } from "jotai"; import { OverlayScrollbarsComponent } from "overlayscrollbars-react"; import { useEffect, useRef, useState } from "react"; import { debounce } from "throttle-debounce"; @@ -21,6 +23,7 @@ import { UpgradeOnboardingModal_v0_12_3_Content } from "./onboarding-upgrade-v01 import { UpgradeOnboardingModal_v0_13_0_Content } from "./onboarding-upgrade-v0130"; import { UpgradeOnboardingModal_v0_13_1_Content } from "./onboarding-upgrade-v0131"; import { UpgradeOnboardingModal_v0_14_0_Content } from "./onboarding-upgrade-v0140"; +import { UpgradeOnboardingModal_v0_14_1_Content } from "./onboarding-upgrade-v0141"; interface VersionConfig { version: string; @@ -29,6 +32,62 @@ interface VersionConfig { nextText?: string; } +interface UpgradeOnboardingFooterProps { + hasPrev: boolean; + hasNext: boolean; + prevText?: string; + nextText?: string; + onPrev?: () => void; + onNext?: () => void; + onClose: () => void; +} + +export function UpgradeOnboardingFooter({ + hasPrev, + hasNext, + prevText, + nextText, + onPrev, + onNext, + onClose, +}: UpgradeOnboardingFooterProps) { + return ( +
+
+
+ {hasPrev && ( +
+ +
+ )} +
+
+ +
+
+ {hasNext && ( +
+ +
+ )} +
+
+
+ ); +} + export const UpgradeOnboardingVersions: VersionConfig[] = [ { version: "v0.12.1", @@ -63,6 +122,12 @@ export const UpgradeOnboardingVersions: VersionConfig[] = [ version: "v0.14.0", content: () => , prevText: "Prev (v0.13.1)", + nextText: "Next (v0.14.1)", + }, + { + version: "v0.14.1", + content: () => , + prevText: "Prev (v0.14.0)", }, ]; @@ -70,6 +135,9 @@ const UpgradeOnboardingPatch = () => { const modalRef = useRef(null); const [isCompact, setIsCompact] = useState(window.innerHeight < 800); const [currentIndex, setCurrentIndex] = useState(UpgradeOnboardingVersions.length - 1); + const [showStarAsk, setShowStarAsk] = useState(false); + const clientData = useAtomValue(ClientModel.getInstance().clientAtom); + const alreadyStarred = clientData?.meta?.["onboarding:githubstar"] === true; const currentVersion = UpgradeOnboardingVersions[currentIndex]; const hasPrev = currentIndex > 0; @@ -105,16 +173,24 @@ const UpgradeOnboardingPatch = () => { }; }, []); + const doClose = () => { + globalStore.set(modalsModel.upgradeOnboardingOpen, false); + setTimeout(() => { + globalRefocus(); + }, 10); + }; + const handleClose = () => { const clientId = ClientModel.getInstance().clientId; RpcApi.SetMetaCommand(TabRpcClient, { oref: WOS.makeORef("client", clientId), meta: { "onboarding:lastversion": CurrentOnboardingVersion }, }); - globalStore.set(modalsModel.upgradeOnboardingOpen, false); - setTimeout(() => { - globalRefocus(); - }, 10); + if (alreadyStarred) { + doClose(); + } else { + setShowStarAsk(true); + } }; const paddingClass = isCompact ? "!py-3 !px-[30px]" : "!p-[30px]"; @@ -131,9 +207,23 @@ const UpgradeOnboardingPatch = () => { } }; + if (showStarAsk) { + return ( + + +
+ +
+
+ ); + } + return ( -
+
@@ -150,39 +240,15 @@ const UpgradeOnboardingPatch = () => { > {currentVersion.content()} -
-
-
- {hasPrev && ( -
- -
- )} -
-
- -
-
- {hasNext && ( -
- -
- )} -
-
-
+
diff --git a/frontend/app/onboarding/onboarding-upgrade-v0141.tsx b/frontend/app/onboarding/onboarding-upgrade-v0141.tsx new file mode 100644 index 0000000000..82b0b34aef --- /dev/null +++ b/frontend/app/onboarding/onboarding-upgrade-v0141.tsx @@ -0,0 +1,72 @@ +// Copyright 2026, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +const UpgradeOnboardingModal_v0_14_1_Content = () => { + return ( +
+
+

+ Wave v0.14.1 fixes several high-impact terminal bugs and adds new config options for focus, cursor + style, and block navigation. +

+
+ +
+
+ +
+
+
Terminal Fixes
+
+
    +
  • + Claude Code Scroll Fix - Fixed unexpected terminal scroll jumps +
  • +
  • + IME Fix - Fixed Korean/CJK input losing or sticking characters +
  • +
  • + Scroll Position on Resize - Terminal stays at bottom across resizes +
  • +
  • + Terminal Scrollback Save - New context menu item and{" "} + wsh command to save scrollback to a file +
  • +
+
+
+
+ +
+
+ +
+
+
New Config Options
+
+
    +
  • + Focus Follows Cursor - New app:focusfollowscursor setting + (off/on/term) +
  • +
  • + Terminal Cursor Style & Blink - Configure cursor shape and blink + per-block +
  • +
  • + Vim-Style Block Navigation - Ctrl+Shift+H/J/K/L to navigate blocks +
  • +
  • + New AI Providers - Added Groq and NanoGPT as built-in presets +
  • +
+
+
+
+
+ ); +}; + +UpgradeOnboardingModal_v0_14_1_Content.displayName = "UpgradeOnboardingModal_v0_14_1_Content"; + +export { UpgradeOnboardingModal_v0_14_1_Content }; diff --git a/frontend/app/onboarding/onboarding.tsx b/frontend/app/onboarding/onboarding.tsx index 381339e6f0..2755db002f 100644 --- a/frontend/app/onboarding/onboarding.tsx +++ b/frontend/app/onboarding/onboarding.tsx @@ -1,9 +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 { OnboardingGradientBg } from "@/app/onboarding/onboarding-common"; import { OnboardingFeatures } from "@/app/onboarding/onboarding-features"; import { ClientModel } from "@/app/store/client-model"; import { useSettingsKeyAtom } from "@/app/store/global"; @@ -34,6 +35,22 @@ const InitPage = ({ isCompact }: { isCompact: boolean }) => { const [telemetryEnabled, setTelemetryEnabled] = useState(!!telemetrySetting); const setPageName = useSetAtom(pageNameAtom); + const handleStarClick = async () => { + RpcApi.RecordTEventCommand( + TabRpcClient, + { + event: "onboarding:githubstar", + props: { "onboarding:githubstar": "star", "onboarding:page": "init" }, + }, + { noresponse: true } + ); + const clientId = ClientModel.getInstance().clientId; + await RpcApi.SetMetaCommand(TabRpcClient, { + oref: WOS.makeORef("client", clientId), + meta: { "onboarding:githubstar": true }, + }); + }; + const acceptTos = () => { if (!clientData?.tosagreed) { fireAndForget(services.ClientService.AgreeTos); @@ -76,6 +93,7 @@ const InitPage = ({ isCompact }: { isCompact: boolean }) => { href="https://github.com/wavetermdev/waveterm?ref=install" rel="noopener" className="text-accent" + onClick={handleStarClick} > @@ -83,13 +101,14 @@ const InitPage = ({ isCompact }: { isCompact: boolean }) => {
Support us on GitHub
- We're open source and committed to providing a free terminal for individual - users. Please show your support by giving us a star on{" "} + We're open source, open-model, and committed to providing a free terminal + for individual users. Please show your support by giving us a star on{" "} Github (wavetermdev/waveterm) @@ -169,6 +188,14 @@ const NoTelemetryStarPage = ({ isCompact }: { isCompact: boolean }) => { const setPageName = useSetAtom(pageNameAtom); const handleStarClick = async () => { + RpcApi.RecordTEventCommand( + TabRpcClient, + { + event: "onboarding:githubstar", + props: { "onboarding:githubstar": "star", "onboarding:page": "notelemetry" }, + }, + { noresponse: true } + ); const clientId = ClientModel.getInstance().clientId; await RpcApi.SetMetaCommand(TabRpcClient, { oref: WOS.makeORef("client", clientId), @@ -179,6 +206,14 @@ const NoTelemetryStarPage = ({ isCompact }: { isCompact: boolean }) => { }; const handleMaybeLater = async () => { + RpcApi.RecordTEventCommand( + TabRpcClient, + { + event: "onboarding:githubstar", + props: { "onboarding:githubstar": "later", "onboarding:page": "notelemetry" }, + }, + { noresponse: true } + ); const clientId = ClientModel.getInstance().clientId; await RpcApi.SetMetaCommand(TabRpcClient, { oref: WOS.makeORef("client", clientId), @@ -302,7 +337,7 @@ const NewInstallOnboardingModal = () => { return ( -
+
{pageComp}
); diff --git a/frontend/preview/previews/onboarding.preview.tsx b/frontend/preview/previews/onboarding.preview.tsx index 0cf0b91e43..18d555dff8 100644 --- a/frontend/preview/previews/onboarding.preview.tsx +++ b/frontend/preview/previews/onboarding.preview.tsx @@ -3,46 +3,57 @@ import Logo from "@/app/asset/logo.svg"; import { InitPage, NoTelemetryStarPage } from "@/app/onboarding/onboarding"; +import { OnboardingGradientBg } from "@/app/onboarding/onboarding-common"; import { DurableSessionPage } from "@/app/onboarding/onboarding-durable"; import { FilesPage, MagnifyBlocksPage, WaveAIPage } from "@/app/onboarding/onboarding-features"; -import { UpgradeOnboardingVersions } from "@/app/onboarding/onboarding-upgrade-patch"; +import { StarAskPage } from "@/app/onboarding/onboarding-starask"; +import { UpgradeMinorWelcomePage } from "@/app/onboarding/onboarding-upgrade-minor"; +import { UpgradeOnboardingFooter, UpgradeOnboardingVersions } from "@/app/onboarding/onboarding-upgrade-patch"; + +function OnboardingModalWrapper({ width, children }: { width: string; children: React.ReactNode }) { + return ( +
+ +
{children}
+
+ ); +} function OnboardingFeaturesV() { const noop = () => {}; return (
-
+ -
-
+ + -
-
+ + -
-
+ + -
-
+ + -
-
+ + -
+
); } function UpgradeOnboardingPatchV() { + const noop = () => {}; return (
- {UpgradeOnboardingVersions.map((version) => ( -
-
-
+ {UpgradeOnboardingVersions.map((version, idx) => { + const hasPrev = idx > 0; + const hasNext = idx < UpgradeOnboardingVersions.length - 1; + return ( +
@@ -52,18 +63,49 @@ function UpgradeOnboardingPatchV() {
{version.content()}
-
-
- ))} + + + ); + })}
); } +function UpgradeOnboardingMinorV() { + const noop = () => {}; + return ( + + + + ); +} + +function StarAskV() { + const noop = () => {}; + return ( + + + + ); +} + export function OnboardingPreview() { return (
Onboarding features
+
Onboarding minor upgrade
+ +
Onboarding star ask
+
Onboarding patch updates
diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index 313f8dbdec..8b60d91dd2 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -25,6 +25,7 @@ declare global { "ai:thinkinglevel"?: string; "ai:verbosity"?: string; "ai:endpoint"?: string; + "ai:proxyurl"?: string; "ai:azureapiversion"?: string; "ai:apitoken"?: string; "ai:apitokensecretname"?: string; @@ -1483,6 +1484,7 @@ declare global { "onboarding:feature"?: "waveai" | "durable" | "magnify" | "wsh"; "onboarding:version"?: string; "onboarding:githubstar"?: "already" | "star" | "later"; + "onboarding:page"?: string; "display:height"?: number; "display:width"?: number; "display:dpr"?: number; diff --git a/pkg/telemetry/telemetrydata/telemetrydata.go b/pkg/telemetry/telemetrydata/telemetrydata.go index 30ff1db737..1c9269dc5c 100644 --- a/pkg/telemetry/telemetrydata/telemetrydata.go +++ b/pkg/telemetry/telemetrydata/telemetrydata.go @@ -29,6 +29,7 @@ var ValidEventNames = map[string]bool{ "action:other": true, "action:term": true, "action:termdurable": true, + "action:link": true, "wsh:run": true, @@ -135,6 +136,7 @@ type TEventProps struct { OnboardingFeature string `json:"onboarding:feature,omitempty" tstype:"\"waveai\" | \"durable\" | \"magnify\" | \"wsh\""` OnboardingVersion string `json:"onboarding:version,omitempty"` OnboardingGithubStar string `json:"onboarding:githubstar,omitempty" tstype:"\"already\" | \"star\" | \"later\""` + OnboardingPage string `json:"onboarding:page,omitempty"` DisplayHeight int `json:"display:height,omitempty"` DisplayWidth int `json:"display:width,omitempty"`