Skip to content
Draft
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
64 changes: 43 additions & 21 deletions frontend/app/onboarding/onboarding-features.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { FakeLayout } from "./onboarding-layout";

type FeaturePageName = "waveai" | "durable" | "magnify" | "files";

const WaveAIPage = ({ onNext, onSkip }: { onNext: () => void; onSkip: () => void }) => {
export const WaveAIPage = ({ onNext, onSkip }: { onNext: () => void; onSkip: () => void }) => {
const isMac = isMacOS();
const shortcutKey = isMac ? "⌘-Shift-A" : "Alt-Shift-A";
const [fireClicked, setFireClicked] = useState(false);
Expand Down Expand Up @@ -106,7 +106,7 @@ const WaveAIPage = ({ onNext, onSkip }: { onNext: () => void; onSkip: () => void
);
};

const MagnifyBlocksPage = ({
export const MagnifyBlocksPage = ({
onNext,
onSkip,
onPrev,
Expand Down Expand Up @@ -149,13 +149,13 @@ const MagnifyBlocksPage = ({
better view.
</p>
<p>Use the magnify feature to work with complex outputs and large files more efficiently.</p>
<p>
<div>
You can also magnify a block by clicking on the{" "}
<span className="inline-block align-middle [&_svg_path]:!fill-foreground">
<MagnifyIcon enabled={false} />
</span>{" "}
icon in the block header.
</p>
</div>
<p>
A quick {shortcutKey}-M to magnify and another {shortcutKey}-M to unmagnify
</p>
Expand All @@ -172,12 +172,25 @@ const MagnifyBlocksPage = ({
);
};

const FilesPage = ({ onFinish, onPrev }: { onFinish: () => void; onPrev?: () => void }) => {
export const FilesPage = ({ onFinish, onPrev }: { onFinish: () => void; onPrev?: () => void }) => {
const [fireClicked, setFireClicked] = useState(false);
const isMac = isMacOS();
const [commandIndex, setCommandIndex] = useState(0);
const [key, setKey] = useState(0);

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

const handleCommandComplete = () => {
setTimeout(() => {
setCommandIndex((prev) => (prev + 1) % commands.length);
setKey((prev) => prev + 1);
}, 2500);
};

const handleFireClick = () => {
setFireClicked(!fireClicked);
if (!fireClicked) {
Expand All @@ -191,19 +204,30 @@ const FilesPage = ({ onFinish, onPrev }: { onFinish: () => void; onPrev?: () =>
}
};

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

const handleCommandComplete = () => {
setTimeout(() => {
setCommandIndex((prev) => (prev + 1) % commands.length);
setKey((prev) => prev + 1);
}, 2500);
};
return (
<FilesPageContent
onFinish={onFinish}
onPrev={onPrev}
fireClicked={fireClicked}
onFireClick={handleFireClick}
rightPanel={commands[commandIndex](handleCommandComplete)}
/>
);
};

export const FilesPageContent = ({
onFinish,
onPrev,
fireClicked,
onFireClick,
rightPanel,
}: {
onFinish: () => void;
onPrev?: () => void;
fireClicked: boolean;
onFireClick: () => void;
rightPanel: React.ReactNode;
}) => {
return (
<div className="flex flex-col h-full">
<header className="flex items-center gap-4 mb-6 w-full unselectable flex-shrink-0">
Expand Down Expand Up @@ -252,14 +276,12 @@ const FilesPage = ({ onFinish, onPrev }: { onFinish: () => void; onPrev?: () =>
and edit files wherever they are.
</p>

<EmojiButton emoji="🔥" isClicked={fireClicked} onClick={handleFireClick} />
<EmojiButton emoji="🔥" isClicked={fireClicked} onClick={onFireClick} />
</div>
</div>
</div>
<div className="w-[2px] bg-border flex-shrink-0"></div>
<div className="flex items-center justify-center pl-6 flex-shrink-0 w-[400px]">
{commands[commandIndex](handleCommandComplete)}
</div>
<div className="flex items-center justify-center pl-6 flex-shrink-0 w-[400px]">{rightPanel}</div>
</div>
<OnboardingFooter currentStep={4} totalSteps={4} onNext={onFinish} onPrev={onPrev} />
</div>
Expand Down
8 changes: 4 additions & 4 deletions frontend/app/onboarding/onboarding-upgrade-patch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ interface VersionConfig {
nextText?: string;
}

const versions: VersionConfig[] = [
export const UpgradeOnboardingVersions: VersionConfig[] = [
{
version: "v0.12.1",
content: () => <UpgradeOnboardingModal_v0_12_1_Content />,
Expand Down Expand Up @@ -69,11 +69,11 @@ const versions: VersionConfig[] = [
const UpgradeOnboardingPatch = () => {
const modalRef = useRef<HTMLDivElement | null>(null);
const [isCompact, setIsCompact] = useState<boolean>(window.innerHeight < 800);
const [currentIndex, setCurrentIndex] = useState<number>(versions.length - 1);
const [currentIndex, setCurrentIndex] = useState<number>(UpgradeOnboardingVersions.length - 1);

const currentVersion = versions[currentIndex];
const currentVersion = UpgradeOnboardingVersions[currentIndex];
const hasPrev = currentIndex > 0;
const hasNext = currentIndex < versions.length - 1;
const hasNext = currentIndex < UpgradeOnboardingVersions.length - 1;

const updateModalHeight = () => {
const windowHeight = window.innerHeight;
Expand Down
7 changes: 7 additions & 0 deletions frontend/app/store/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import {
isBlank,
isLocalConnName,
isWslConnName,
NullAtom,
} from "@/util/util";
import { isPreviewWindow } from "./windowtype";
import { atom, Atom, PrimitiveAtom, useAtomValue } from "jotai";
import {
atoms,
Expand Down Expand Up @@ -168,6 +170,7 @@ function useOrefMetaKeyAtom<T extends keyof MetaType>(oref: string, key: T): Met
}

function getConnConfigKeyAtom<T extends keyof ConnKeywords>(connName: string, key: T): Atom<ConnKeywords[T]> {
if (isPreviewWindow()) return NullAtom as Atom<ConnKeywords[T]>;
let connCache = getSingleConnAtomCache(connName);
const keyAtomName = "#conn-" + key;
let keyAtom = connCache.get(keyAtomName);
Expand All @@ -185,6 +188,7 @@ function getConnConfigKeyAtom<T extends keyof ConnKeywords>(connName: string, ke
const settingsAtomCache = new Map<string, Atom<any>>();

function getOverrideConfigAtom<T extends keyof SettingsType>(blockId: string, key: T): Atom<SettingsType[T]> {
if (isPreviewWindow()) return NullAtom as Atom<SettingsType[T]>;
const blockCache = getSingleBlockAtomCache(blockId);
const overrideAtomName = "#settingsoverride-" + key;
let overrideAtom = blockCache.get(overrideAtomName);
Expand Down Expand Up @@ -223,6 +227,7 @@ function useOverrideConfigAtom<T extends keyof SettingsType>(blockId: string | n
}

function getSettingsKeyAtom<T extends keyof SettingsType>(key: T): Atom<SettingsType[T]> {
if (isPreviewWindow()) return NullAtom as Atom<SettingsType[T]>;
let settingsKeyAtom = settingsAtomCache.get(key) as Atom<SettingsType[T]>;
if (settingsKeyAtom == null) {
settingsKeyAtom = atom((get) => {
Expand All @@ -242,6 +247,7 @@ function useSettingsKeyAtom<T extends keyof SettingsType>(key: T): SettingsType[
}

function getSettingsPrefixAtom(prefix: string): Atom<SettingsType> {
if (isPreviewWindow()) return NullAtom as Atom<SettingsType>;
let settingsPrefixAtom = settingsAtomCache.get(prefix + ":");
if (settingsPrefixAtom == null) {
// create a stable, closured reference to use as the deepCompareReturnPrev key
Expand Down Expand Up @@ -745,6 +751,7 @@ function setActiveTab(tabId: string) {
}

function recordTEvent(event: string, props?: TEventProps) {
if (isPreviewWindow()) return;
if (props == null) {
props = {};
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/preview/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ function PreviewApp() {
return (
<>
<PreviewHeader previewName={previewName} />
<div className="min-h-screen bg-background text-foreground font-sans flex flex-col items-center justify-center">
<div className="h-screen overflow-y-auto bg-background text-foreground font-sans flex flex-col items-center pt-12 pb-8">
<Suspense fallback={null}>
<PreviewComponent />
</Suspense>
Expand Down
64 changes: 64 additions & 0 deletions frontend/preview/previews/onboarding.preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2026, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

import Logo from "@/app/asset/logo.svg";
import { DurableSessionPage } from "@/app/onboarding/onboarding-durable";
import { FilesPage, MagnifyBlocksPage, WaveAIPage } from "@/app/onboarding/onboarding-features";
import { UpgradeOnboardingVersions } from "@/app/onboarding/onboarding-upgrade-patch";

function OnboardingFeaturesV() {
const noop = () => {};
return (
<div className="flex flex-col w-full gap-8">
<div className="w-[800px] rounded-[10px] p-[30px] relative overflow-hidden bg-panel">
<WaveAIPage onNext={noop} onSkip={noop} />
</div>
<div className="w-[800px] rounded-[10px] p-[30px] relative overflow-hidden bg-panel">
<DurableSessionPage onNext={noop} onSkip={noop} onPrev={noop} />
</div>
<div className="w-[800px] rounded-[10px] p-[30px] relative overflow-hidden bg-panel">
<MagnifyBlocksPage onNext={noop} onSkip={noop} onPrev={noop} />
</div>
<div className="w-[800px] rounded-[10px] p-[30px] relative overflow-hidden bg-panel">
<FilesPage onFinish={noop} onPrev={noop} />
</div>
</div>
);
}

function UpgradeOnboardingPatchV() {
return (
<div className="flex flex-col gap-6 w-full max-w-[900px]">
{UpgradeOnboardingVersions.map((version) => (
<div
key={version.version}
className="w-[650px] rounded-[10px] p-[30px] relative overflow-hidden bg-panel"
>
<div className="absolute inset-0 bg-gradient-to-br from-accent/[0.25] via-transparent to-accent/[0.05] pointer-events-none rounded-[10px]" />
<div className="flex flex-col w-full h-full relative z-10">
<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">
Wave {version.version} Update
</div>
</header>
<div className="flex-1">{version.content()}</div>
</div>
</div>
))}
</div>
);
}

export function OnboardingPreview() {
return (
<div className="w-full max-w-[1300px] py-10 px-4 flex flex-col gap-8">
<div className="text-sm font-mono text-muted">Onboarding features</div>
<OnboardingFeaturesV />
<div className="text-sm font-mono text-muted mt-6">Onboarding patch updates</div>
<UpgradeOnboardingPatchV />
</div>
);
}
13 changes: 10 additions & 3 deletions frontend/util/endpoints.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
// Copyright 2025, Command Line Inc.
// Copyright 2026, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

import { isPreviewWindow } from "@/app/store/windowtype";
import { getEnv } from "./getenv";
import { lazy } from "./util";

export const WebServerEndpointVarName = "WAVE_SERVER_WEB_ENDPOINT";
export const WSServerEndpointVarName = "WAVE_SERVER_WS_ENDPOINT";

export const getWebServerEndpoint = lazy(() => `http://${getEnv(WebServerEndpointVarName)}`);
export const getWebServerEndpoint = lazy(() => {
if (isPreviewWindow()) return null;
return `http://${getEnv(WebServerEndpointVarName)}`;
});

export const getWSServerEndpoint = lazy(() => `ws://${getEnv(WSServerEndpointVarName)}`);
export const getWSServerEndpoint = lazy(() => {
if (isPreviewWindow()) return null;
return `ws://${getEnv(WSServerEndpointVarName)}`;
});
1 change: 1 addition & 0 deletions frontend/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ export {
makeExternLink,
makeIconClass,
mergeMeta,
NullAtom,
parseDataUrl,
sleep,
sortByDisplayOrder,
Expand Down