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
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,6 @@
"QF1003": false
},
"directoryFilters": ["-tsunami/frontend/scaffold"]
}
},
"tailwindCSS.lint.suggestCanonicalClasses": "ignore"
}
2 changes: 1 addition & 1 deletion emain/emain-ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ export function initIpcHandlers() {
});

electron.ipcMain.on("open-builder", (event, appId?: string) => {
fireAndForget(() => createBuilderWindow(appId || "<new>"));
fireAndForget(() => createBuilderWindow(appId || ""));
});

electron.ipcMain.on("open-new-window", () => fireAndForget(createNewWaveWindow));
Expand Down
2 changes: 1 addition & 1 deletion emain/emain-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async function getAppMenu(
if (isDev) {
fileMenu.splice(1, 0, {
label: "New WaveApp Builder Window",
click: () => fireAndForget(() => createBuilderWindow("<new>")),
click: () => fireAndForget(() => createBuilderWindow("")),
});
}
if (numWaveWindows == 0) {
Expand Down
1 change: 1 addition & 0 deletions frontend/app/aipanel/aipanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ const AIPanelComponentInner = memo(({ className, onClose }: AIPanelProps) => {
};
if (windowType === "builder") {
body.builderid = globalStore.get(atoms.builderId);
body.builderappid = globalStore.get(atoms.builderAppId);
} else {
body.tabid = globalStore.get(atoms.staticTabId);
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/modals/modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

.modal-backdrop {
position: fixed;
top: 0;
top: 36px;
left: 0;
right: 0;
bottom: 0;
Expand Down
6 changes: 4 additions & 2 deletions frontend/app/store/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { getWebServerEndpoint } from "@/util/endpoints";
import { fetch } from "@/util/fetchutil";
import { setPlatform } from "@/util/platformutil";
import { deepCompareReturnPrev, fireAndForget, getPrefixedSettings, isBlank } from "@/util/util";
import { base64ToString, deepCompareReturnPrev, fireAndForget, getPrefixedSettings, isBlank } from "@/util/util";
import { atom, Atom, PrimitiveAtom, useAtomValue } from "jotai";
import { globalStore } from "./jotaiStore";
import { modalsModel } from "./modalmodel";
Expand Down Expand Up @@ -54,6 +54,7 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
const windowIdAtom = atom(initOpts.windowId) as PrimitiveAtom<string>;
const clientIdAtom = atom(initOpts.clientId) as PrimitiveAtom<string>;
const builderIdAtom = atom(initOpts.builderId) as PrimitiveAtom<string>;
const builderAppIdAtom = atom<string>(null) as PrimitiveAtom<string>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Type mismatch: builderAppIdAtom initialized with null but typed as string.

Initialize and export as string | null to avoid unsound casts and downstream type errors.

-    const builderAppIdAtom = atom<string>(null) as PrimitiveAtom<string>;
+    const builderAppIdAtom = atom<string | null>(null) as PrimitiveAtom<string | null>;
@@
-        builderAppId: builderAppIdAtom,
+        builderAppId: builderAppIdAtom,

Also ensure GlobalAtomsType in frontend/types/custom.d.ts reflects builderAppId: jotai.PrimitiveAtom<string | null>.

Also applies to: 176-176


🏁 Script executed:

#!/bin/bash
# Check the actual code at line 57 in global.ts
echo "=== Checking frontend/app/store/global.ts around line 57 ==="
cat -n frontend/app/store/global.ts | sed -n '50,65p'
echo ""
echo "=== Checking line 176 (export) ==="
cat -n frontend/app/store/global.ts | sed -n '170,180p'

Length of output: 1467


🏁 Script executed:

#!/bin/bash
# Check the GlobalAtomsType definition in custom.d.ts
echo "=== Checking frontend/types/custom.d.ts for GlobalAtomsType ==="
grep -n -A 20 "GlobalAtomsType" frontend/types/custom.d.ts

Length of output: 1466


🏁 Script executed:

#!/bin/bash
# Check atom imports and type signatures
echo "=== Checking imports in global.ts ==="
head -30 frontend/app/store/global.ts | grep -E "import|from"

Length of output: 765


Fix type mismatch: builderAppIdAtom initialized with null but typed as string.

Line 57 declares atom<string>(null) cast to PrimitiveAtom<string>, but jotai requires the initial value to match the type parameter. Change to atom<string | null>(null) and update the cast accordingly.

Also update frontend/types/custom.d.ts line 12 from builderAppId: jotai.PrimitiveAtom<string> to builderAppId: jotai.PrimitiveAtom<string | null>.

-    const builderAppIdAtom = atom<string>(null) as PrimitiveAtom<string>;
+    const builderAppIdAtom = atom<string | null>(null) as PrimitiveAtom<string | null>;
📝 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 builderAppIdAtom = atom<string>(null) as PrimitiveAtom<string>;
const builderAppIdAtom = atom<string | null>(null) as PrimitiveAtom<string | null>;
🤖 Prompt for AI Agents
In frontend/app/store/global.ts around line 57 the atom is declared as
atom<string>(null) cast to PrimitiveAtom<string>, causing a type mismatch
because the initial value is null; change the atom's generic to string | null
and cast to PrimitiveAtom<string | null> so the initial null value matches the
type. Also update frontend/types/custom.d.ts at around line 12 where
builderAppId is declared as jotai.PrimitiveAtom<string> to
jotai.PrimitiveAtom<string | null> so the type definition matches the atom's
updated type.

const waveWindowTypeAtom = atom((get) => {
const builderId = get(builderIdAtom);
return builderId != null ? "builder" : "tab";
Expand Down Expand Up @@ -172,6 +173,7 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
// initialized in wave.ts (will not be null inside of application)
clientId: clientIdAtom,
builderId: builderIdAtom,
builderAppId: builderAppIdAtom,
waveWindowType: waveWindowTypeAtom,
uiContext: uiContextAtom,
client: clientAtom,
Expand Down Expand Up @@ -547,7 +549,7 @@ async function fetchWaveFile(
if (fileInfo64 == null) {
throw new Error(`missing zone file info for ${zoneId}:${fileName}`);
}
const fileInfo = JSON.parse(atob(fileInfo64));
const fileInfo = JSON.parse(base64ToString(fileInfo64));
const data = await resp.arrayBuffer();
return { data: new Uint8Array(data), fileInfo };
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/app/store/tabrpcclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { WaveAIModel } from "@/app/aipanel/waveai-model";
import { getApi } from "@/app/store/global";
import { WorkspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
import { getLayoutModelForStaticTab } from "@/layout/index";
import { base64ToArray } from "@/util/util";
import { base64ToArrayBuffer } from "@/util/util";
import { RpcResponseHelper, WshClient } from "./wshclient";

export class TabClient extends WshClient {
Expand Down Expand Up @@ -74,7 +74,7 @@ export class TabClient extends WshClient {

if (data.files && data.files.length > 0) {
for (const fileData of data.files) {
const decodedData = base64ToArray(fileData.data64);
const decodedData = base64ToArrayBuffer(fileData.data64);
const blob = new Blob([decodedData], { type: fileData.type });
const file = new File([blob], fileData.name, { type: fileData.type });
await model.addFile(file);
Expand Down
30 changes: 30 additions & 0 deletions frontend/app/store/wshclientapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ class RpcApiType {
return client.wshRpcCall("createsubblock", data, opts);
}

// command "deleteappfile" [call]
DeleteAppFileCommand(client: WshClient, data: CommandDeleteAppFileData, opts?: RpcOpts): Promise<void> {
return client.wshRpcCall("deleteappfile", data, opts);
}

// command "deleteblock" [call]
DeleteBlockCommand(client: WshClient, data: CommandDeleteBlockData, opts?: RpcOpts): Promise<void> {
return client.wshRpcCall("deleteblock", data, opts);
Expand Down Expand Up @@ -297,6 +302,16 @@ class RpcApiType {
return client.wshRpcCall("getwaveairatelimit", null, opts);
}

// command "listallappfiles" [call]
ListAllAppFilesCommand(client: WshClient, data: CommandListAllAppFilesData, opts?: RpcOpts): Promise<CommandListAllAppFilesRtnData> {
return client.wshRpcCall("listallappfiles", data, opts);
}

// command "listalleditableapps" [call]
ListAllEditableAppsCommand(client: WshClient, opts?: RpcOpts): Promise<string[]> {
return client.wshRpcCall("listalleditableapps", null, opts);
}

// command "message" [call]
MessageCommand(client: WshClient, data: CommandMessageData, opts?: RpcOpts): Promise<void> {
return client.wshRpcCall("message", data, opts);
Expand All @@ -312,6 +327,11 @@ class RpcApiType {
return client.wshRpcCall("path", data, opts);
}

// command "readappfile" [call]
ReadAppFileCommand(client: WshClient, data: CommandReadAppFileData, opts?: RpcOpts): Promise<CommandReadAppFileRtnData> {
return client.wshRpcCall("readappfile", data, opts);
}

// command "recordtevent" [call]
RecordTEventCommand(client: WshClient, data: TEvent, opts?: RpcOpts): Promise<void> {
return client.wshRpcCall("recordtevent", data, opts);
Expand Down Expand Up @@ -387,6 +407,11 @@ class RpcApiType {
return client.wshRpcCall("remotewritefile", data, opts);
}

// command "renameappfile" [call]
RenameAppFileCommand(client: WshClient, data: CommandRenameAppFileData, opts?: RpcOpts): Promise<void> {
return client.wshRpcCall("renameappfile", data, opts);
}

// command "resolveids" [call]
ResolveIdsCommand(client: WshClient, data: CommandResolveIdsData, opts?: RpcOpts): Promise<CommandResolveIdsRtnData> {
return client.wshRpcCall("resolveids", data, opts);
Expand Down Expand Up @@ -517,6 +542,11 @@ class RpcApiType {
return client.wshRpcCall("workspacelist", null, opts);
}

// command "writeappfile" [call]
WriteAppFileCommand(client: WshClient, data: CommandWriteAppFileData, opts?: RpcOpts): Promise<void> {
return client.wshRpcCall("writeappfile", data, opts);
}

// command "wshactivity" [call]
WshActivityCommand(client: WshClient, data: {[key: string]: number}, opts?: RpcOpts): Promise<void> {
return client.wshRpcCall("wshactivity", data, opts);
Expand Down
16 changes: 7 additions & 9 deletions frontend/app/view/term/termwrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { TabRpcClient } from "@/app/store/wshrpcutil";
import { WOS, atoms, fetchWaveFile, getSettingsKeyAtom, globalStore, openLink } from "@/store/global";
import * as services from "@/store/services";
import { PLATFORM, PlatformMacOS } from "@/util/platformutil";
import { base64ToArray, fireAndForget } from "@/util/util";
import { base64ToArray, base64ToString, fireAndForget } from "@/util/util";
import { SearchAddon } from "@xterm/addon-search";
import { SerializeAddon } from "@xterm/addon-serialize";
import { WebLinksAddon } from "@xterm/addon-web-links";
Expand Down Expand Up @@ -259,7 +259,7 @@ function handleOsc16162Command(data: string, blockId: string, loaded: boolean, t
globalStore.set(termWrap.lastCommandAtom, rtInfo["shell:lastcmd"]);
} else {
try {
const decodedCmd = atob(cmd.data.cmd64);
const decodedCmd = base64ToString(cmd.data.cmd64);
rtInfo["shell:lastcmd"] = decodedCmd;
globalStore.set(termWrap.lastCommandAtom, decodedCmd);
} catch (e) {
Expand Down Expand Up @@ -364,9 +364,7 @@ export class TermWrap {
this.hasResized = false;
this.lastUpdated = Date.now();
this.promptMarkers = [];
this.shellIntegrationStatusAtom = jotai.atom(null) as jotai.PrimitiveAtom<
"ready" | "running-command" | null
>;
this.shellIntegrationStatusAtom = jotai.atom(null) as jotai.PrimitiveAtom<"ready" | "running-command" | null>;
this.lastCommandAtom = jotai.atom(null) as jotai.PrimitiveAtom<string | null>;
this.terminal = new Terminal(options);
this.fitAddon = new FitAddon();
Expand Down Expand Up @@ -460,25 +458,25 @@ export class TermWrap {
}
this.mainFileSubject = getFileSubject(this.blockId, TermFileName);
this.mainFileSubject.subscribe(this.handleNewFileSubjectData.bind(this));

try {
const rtInfo = await RpcApi.GetRTInfoCommand(TabRpcClient, {
oref: WOS.makeORef("block", this.blockId),
});

if (rtInfo["shell:integration"]) {
const shellState = rtInfo["shell:state"] as ShellIntegrationStatus;
globalStore.set(this.shellIntegrationStatusAtom, shellState || null);
} else {
globalStore.set(this.shellIntegrationStatusAtom, null);
}

const lastCmd = rtInfo["shell:lastcmd"];
globalStore.set(this.lastCommandAtom, lastCmd || null);
} catch (e) {
console.log("Error loading runtime info:", e);
}

try {
await this.loadInitialTerminalData();
} finally {
Expand Down
Loading
Loading