Skip to content

Commit 605e04f

Browse files
committed
new v0.12.1 onboarding screen
1 parent a7fddea commit 605e04f

6 files changed

Lines changed: 399 additions & 167 deletions

File tree

frontend/app/modals/modalsrenderer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { NewInstallOnboardingModal } from "@/app/onboarding/onboarding";
5-
import { CurrentOnboardingVersion } from "@/app/onboarding/onboarding-features";
5+
import { CurrentOnboardingVersion } from "@/app/onboarding/onboarding-common";
66
import { UpgradeOnboardingModal } from "@/app/onboarding/onboarding-upgrade";
77
import { atoms, globalPrimaryTabStartup, globalStore } from "@/store/global";
88
import { modalsModel } from "@/store/modalmodel";
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright 2025, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
export const CurrentOnboardingVersion = "v0.12.1";

frontend/app/onboarding/onboarding-features.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ import { TabRpcClient } from "@/app/store/wshrpcutil";
1212
import { isMacOS } from "@/util/platformutil";
1313
import { useEffect, useState } from "react";
1414
import { FakeChat } from "./fakechat";
15+
import { CurrentOnboardingVersion } from "./onboarding-common";
1516
import { EditBashrcCommand, ViewLogoCommand, ViewShortcutsCommand } from "./onboarding-command";
1617
import { FakeLayout } from "./onboarding-layout";
1718

18-
export const CurrentOnboardingVersion = "v0.12.0";
19-
2019
type FeaturePageName = "waveai" | "magnify" | "files";
2120

2221
const OnboardingFooter = ({
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// Copyright 2025, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import Logo from "@/app/asset/logo.svg";
5+
import { Button } from "@/app/element/button";
6+
import { FlexiModal } from "@/app/modals/modal";
7+
import { CurrentOnboardingVersion } from "@/app/onboarding/onboarding-common";
8+
import { OnboardingFeatures } from "@/app/onboarding/onboarding-features";
9+
import { atoms, globalStore } from "@/app/store/global";
10+
import { disableGlobalKeybindings, enableGlobalKeybindings, globalRefocus } from "@/app/store/keymodel";
11+
import { modalsModel } from "@/app/store/modalmodel";
12+
import * as WOS from "@/app/store/wos";
13+
import { RpcApi } from "@/app/store/wshclientapi";
14+
import { TabRpcClient } from "@/app/store/wshrpcutil";
15+
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
16+
import { useEffect, useRef, useState } from "react";
17+
import { debounce } from "throttle-debounce";
18+
19+
const UpgradeOnboardingModal_v0_12_0 = () => {
20+
const modalRef = useRef<HTMLDivElement | null>(null);
21+
const [pageName, setPageName] = useState<"welcome" | "features">("welcome");
22+
const [isCompact, setIsCompact] = useState<boolean>(window.innerHeight < 800);
23+
24+
const updateModalHeight = () => {
25+
const windowHeight = window.innerHeight;
26+
setIsCompact(windowHeight < 800);
27+
if (modalRef.current) {
28+
const modalHeight = modalRef.current.offsetHeight;
29+
const maxHeight = windowHeight * 0.9;
30+
if (maxHeight < modalHeight) {
31+
modalRef.current.style.height = `${maxHeight}px`;
32+
} else {
33+
modalRef.current.style.height = "auto";
34+
}
35+
}
36+
};
37+
38+
useEffect(() => {
39+
updateModalHeight();
40+
const debouncedUpdateModalHeight = debounce(150, updateModalHeight);
41+
window.addEventListener("resize", debouncedUpdateModalHeight);
42+
return () => {
43+
window.removeEventListener("resize", debouncedUpdateModalHeight);
44+
};
45+
}, []);
46+
47+
useEffect(() => {
48+
disableGlobalKeybindings();
49+
return () => {
50+
enableGlobalKeybindings();
51+
};
52+
}, []);
53+
54+
const handleStarClick = async () => {
55+
RpcApi.RecordTEventCommand(
56+
TabRpcClient,
57+
{
58+
event: "onboarding:githubstar",
59+
props: { "onboarding:githubstar": "star" },
60+
},
61+
{ noresponse: true }
62+
);
63+
const clientId = globalStore.get(atoms.clientId);
64+
await RpcApi.SetMetaCommand(TabRpcClient, {
65+
oref: WOS.makeORef("client", clientId),
66+
meta: { "onboarding:githubstar": true },
67+
});
68+
window.open("https://github.com/wavetermdev/waveterm?ref=upgrade", "_blank");
69+
setPageName("features");
70+
};
71+
72+
const handleAlreadyStarred = async () => {
73+
RpcApi.RecordTEventCommand(
74+
TabRpcClient,
75+
{
76+
event: "onboarding:githubstar",
77+
props: { "onboarding:githubstar": "already" },
78+
},
79+
{ noresponse: true }
80+
);
81+
const clientId = globalStore.get(atoms.clientId);
82+
await RpcApi.SetMetaCommand(TabRpcClient, {
83+
oref: WOS.makeORef("client", clientId),
84+
meta: { "onboarding:githubstar": true },
85+
});
86+
setPageName("features");
87+
};
88+
89+
const handleMaybeLater = async () => {
90+
RpcApi.RecordTEventCommand(
91+
TabRpcClient,
92+
{
93+
event: "onboarding:githubstar",
94+
props: { "onboarding:githubstar": "later" },
95+
},
96+
{ noresponse: true }
97+
);
98+
const clientId = globalStore.get(atoms.clientId);
99+
await RpcApi.SetMetaCommand(TabRpcClient, {
100+
oref: WOS.makeORef("client", clientId),
101+
meta: { "onboarding:githubstar": false },
102+
});
103+
setPageName("features");
104+
};
105+
106+
const handleFeaturesComplete = () => {
107+
const clientId = globalStore.get(atoms.clientId);
108+
RpcApi.SetMetaCommand(TabRpcClient, {
109+
oref: WOS.makeORef("client", clientId),
110+
meta: { "onboarding:lastversion": CurrentOnboardingVersion },
111+
});
112+
globalStore.set(modalsModel.upgradeOnboardingOpen, false);
113+
setTimeout(() => {
114+
globalRefocus();
115+
}, 10);
116+
};
117+
118+
let pageComp: React.JSX.Element = null;
119+
if (pageName === "welcome") {
120+
pageComp = (
121+
<div className="flex flex-col h-full">
122+
<header className="flex flex-col gap-2 border-b-0 p-0 mt-1 mb-6 w-full unselectable flex-shrink-0">
123+
<div className="flex justify-center">
124+
<Logo />
125+
</div>
126+
<div className="text-center text-[25px] font-normal text-foreground">Welcome to Wave v0.12!</div>
127+
</header>
128+
<OverlayScrollbarsComponent
129+
className="flex-1 overflow-y-auto min-h-0"
130+
options={{ scrollbars: { autoHide: "never" } }}
131+
>
132+
<div className="flex flex-col items-center gap-3 w-full mb-2 unselectable">
133+
<div className="flex flex-col items-center gap-4 max-w-md text-center">
134+
<div className="flex h-[52px] px-3 items-center rounded-lg bg-hover text-accent text-[24px]">
135+
<i className="fa fa-sparkles" />
136+
<span className="font-bold ml-2 font-mono">Wave AI</span>
137+
</div>
138+
<div className="text-secondary leading-relaxed max-w-[420px]">
139+
<p className="mb-4">
140+
Wave AI is your new terminal assistant with full context. It can read your terminal
141+
output, analyze widgets, access files, and help you solve problems&nbsp;faster.
142+
</p>
143+
<p className="p-3 border border-border rounded-md bg-hover/30">
144+
Wave AI is in active beta with included AI credits while we refine the experience.
145+
We're actively improving it and would love your feedback in{" "}
146+
<a target="_blank" href="https://discord.gg/XfvZ334gwU" className="hover:underline">
147+
Discord
148+
</a>
149+
.
150+
</p>
151+
</div>
152+
</div>
153+
154+
<div className="w-full max-w-md border-t border-border my-2"></div>
155+
156+
<div className="flex flex-col items-center gap-3 text-center max-w-[440px]">
157+
<div className="text-foreground text-base">Thanks for being an early Wave adopter! ⭐</div>
158+
<div className="text-secondary text-sm">
159+
A GitHub star shows your support for Wave (and open-source) and helps us reach more
160+
developers.
161+
</div>
162+
</div>
163+
</div>
164+
</OverlayScrollbarsComponent>
165+
<footer className="unselectable flex-shrink-0 mt-4">
166+
<div className="flex flex-row items-center justify-center gap-2.5 [&>button]:!px-5 [&>button]:!py-2 [&>button]:text-sm [&>button]:!h-[37px]">
167+
<Button className="outlined grey font-[600]" onClick={handleAlreadyStarred}>
168+
🙏 Already Starred
169+
</Button>
170+
<Button className="outlined green font-[600]" onClick={handleStarClick}>
171+
⭐ Star Now
172+
</Button>
173+
<Button className="outlined grey font-[600]" onClick={handleMaybeLater}>
174+
Maybe Later
175+
</Button>
176+
</div>
177+
</footer>
178+
</div>
179+
);
180+
} else if (pageName === "features") {
181+
pageComp = <OnboardingFeatures onComplete={handleFeaturesComplete} />;
182+
}
183+
184+
if (pageComp == null) {
185+
return null;
186+
}
187+
188+
const paddingClass = isCompact ? "!py-3 !px-[30px]" : "!p-[30px]";
189+
const widthClass = pageName === "features" ? "w-[800px]" : "w-[560px]";
190+
191+
return (
192+
<FlexiModal className={`${widthClass} rounded-[10px] ${paddingClass} relative overflow-hidden`} ref={modalRef}>
193+
<div className="absolute inset-0 bg-gradient-to-br from-accent/[0.25] via-transparent to-accent/[0.05] pointer-events-none rounded-[10px]" />
194+
<div className="flex flex-col w-full h-full relative z-10">{pageComp}</div>
195+
</FlexiModal>
196+
);
197+
};
198+
199+
UpgradeOnboardingModal_v0_12_0.displayName = "UpgradeOnboardingModal_v0_12_0";
200+
201+
export { UpgradeOnboardingModal_v0_12_0 };

0 commit comments

Comments
 (0)