diff --git a/cmd/server/main-server.go b/cmd/server/main-server.go
index 5259b60ffa..5cac190894 100644
--- a/cmd/server/main-server.go
+++ b/cmd/server/main-server.go
@@ -234,7 +234,7 @@ func updateTelemetryCounts(lastCounts telemetrydata.TEventProps) telemetrydata.T
SettingsCustomSettings: customSettings,
SettingsCustomAIModes: customAIModes,
}
-
+
secretsCount, err := secretstore.CountSecrets()
if err == nil {
props.UserSet.SettingsSecretsCount = secretsCount
@@ -315,17 +315,19 @@ func startupActivityUpdate(firstLaunch bool) {
cohortISOWeek := fmt.Sprintf("%04d-W%02d", year, week)
userSetOnce.CohortMonth = cohortMonth
userSetOnce.CohortISOWeek = cohortISOWeek
+ fullConfig := wconfig.GetWatcher().GetFullConfig()
props := telemetrydata.TEventProps{
UserSet: &telemetrydata.TEventUserProps{
- ClientVersion: "v" + WaveVersion,
- ClientBuildTime: BuildTime,
- ClientArch: wavebase.ClientArch(),
- ClientOSRelease: wavebase.UnameKernelRelease(),
- ClientIsDev: wavebase.IsDevMode(),
- AutoUpdateChannel: autoUpdateChannel,
- AutoUpdateEnabled: autoUpdateEnabled,
- LocalShellType: shellType,
- LocalShellVersion: shellVersion,
+ ClientVersion: "v" + WaveVersion,
+ ClientBuildTime: BuildTime,
+ ClientArch: wavebase.ClientArch(),
+ ClientOSRelease: wavebase.UnameKernelRelease(),
+ ClientIsDev: wavebase.IsDevMode(),
+ AutoUpdateChannel: autoUpdateChannel,
+ AutoUpdateEnabled: autoUpdateEnabled,
+ LocalShellType: shellType,
+ LocalShellVersion: shellVersion,
+ SettingsTransparent: fullConfig.Settings.WindowTransparent,
},
UserSetOnce: userSetOnce,
}
diff --git a/docs/docs/waveai.mdx b/docs/docs/waveai.mdx
index ab9259d5a0..5189bc6792 100644
--- a/docs/docs/waveai.mdx
+++ b/docs/docs/waveai.mdx
@@ -19,7 +19,7 @@ Context-aware terminal assistant with access to terminal output, widgets, and fi
| Shortcut | Action |
|----------|--------|
| | Toggle AI panel |
-| | Focus AI input |
+| | Focus AI input |
| | Clear chat / start new |
| | Send message |
| | New line |
diff --git a/docs/src/components/kbd.tsx b/docs/src/components/kbd.tsx
index 21eb0b3c68..b42b1d55ce 100644
--- a/docs/src/components/kbd.tsx
+++ b/docs/src/components/kbd.tsx
@@ -44,9 +44,20 @@ function convertKey(platform: Platform, key: string): [any, string, boolean] {
}
// Custom KBD component
-const KbdInternal = ({ k }: { k: string }) => {
+const KbdInternal = ({ k, windows, mac, linux }: { k: string; windows?: string; mac?: string; linux?: string }) => {
const { platform } = useContext(PlatformContext);
- const keys = k.split(":");
+
+ // Determine which key binding to use based on platform overrides
+ let keyBinding = k;
+ if (platform === "windows" && windows) {
+ keyBinding = windows;
+ } else if (platform === "mac" && mac) {
+ keyBinding = mac;
+ } else if (platform === "linux" && linux) {
+ keyBinding = linux;
+ }
+
+ const keys = keyBinding.split(":");
const keyElems = keys.map((key, i) => {
const [displayKey, title, symbol] = convertKey(platform, key);
return (
@@ -58,8 +69,8 @@ const KbdInternal = ({ k }: { k: string }) => {
return
{keyElems}
;
};
-export const Kbd = ({ k }: { k: string }) => {
- return {k}}>{() => };
+export const Kbd = ({ k, windows, mac, linux }: { k: string; windows?: string; mac?: string; linux?: string }) => {
+ return {k}}>{() => };
};
export const KbdChord = ({ karr }: { karr: string[] }) => {
diff --git a/emain/emain-ipc.ts b/emain/emain-ipc.ts
index 0129d4337f..09ba69d1eb 100644
--- a/emain/emain-ipc.ts
+++ b/emain/emain-ipc.ts
@@ -12,13 +12,13 @@ import { RpcApi } from "../frontend/app/store/wshclientapi";
import { getWebServerEndpoint } from "../frontend/util/endpoints";
import * as keyutil from "../frontend/util/keyutil";
import { fireAndForget, parseDataUrl } from "../frontend/util/util";
+import { incrementTermCommandsRun } from "./emain-activity";
import { createBuilderWindow, getAllBuilderWindows, getBuilderWindowByWebContentsId } from "./emain-builder";
import { callWithOriginalXdgCurrentDesktopAsync, unamePlatform } from "./emain-platform";
import { getWaveTabViewByWebContentsId } from "./emain-tabview";
import { handleCtrlShiftState } from "./emain-util";
import { getWaveVersion } from "./emain-wavesrv";
import { createNewWaveWindow, focusedWaveWindow, getWaveWindowByWebContentsId } from "./emain-window";
-import { incrementTermCommandsRun } from "./emain-activity";
import { ElectronWshClient } from "./emain-wsh";
const electronApp = electron.app;
@@ -29,9 +29,7 @@ let webviewKeys: string[] = [];
export function openBuilderWindow(appId?: string) {
const normalizedAppId = appId || "";
const existingBuilderWindows = getAllBuilderWindows();
- const existingWindow = existingBuilderWindows.find(
- (win) => win.builderAppId === normalizedAppId
- );
+ const existingWindow = existingBuilderWindows.find((win) => win.builderAppId === normalizedAppId);
if (existingWindow) {
existingWindow.focus();
return;
@@ -314,12 +312,12 @@ export function initIpcHandlers() {
tabView?.setKeyboardChordMode(true);
});
- if (unamePlatform !== "darwin") {
- const fac = new FastAverageColor();
-
- electron.ipcMain.on("update-window-controls-overlay", async (event, rect: Dimensions) => {
+ const fac = new FastAverageColor();
+ electron.ipcMain.on("update-window-controls-overlay", async (event, rect: Dimensions) => {
+ if (unamePlatform === "darwin") return;
+ try {
const fullConfig = await RpcApi.GetFullConfigCommand(ElectronWshClient);
- if (fullConfig.settings["window:nativetitlebar"]) return;
+ if (fullConfig?.settings?.["window:nativetitlebar"] && unamePlatform !== "win32") return;
const zoomFactor = event.sender.getZoomFactor();
const electronRect: Electron.Rectangle = {
@@ -337,18 +335,18 @@ export function initIpcHandlers() {
color: unamePlatform === "linux" ? color.rgba : "#00000000",
symbolColor: color.isDark ? "white" : "black",
});
- });
- }
+ } catch (e) {
+ console.error("Error updating window controls overlay:", e);
+ }
+ });
electron.ipcMain.on("quicklook", (event, filePath: string) => {
- if (unamePlatform == "darwin") {
- child_process.execFile("/usr/bin/qlmanage", ["-p", filePath], (error, stdout, stderr) => {
- if (error) {
- console.error(`Error opening Quick Look: ${error}`);
- return;
- }
- });
- }
+ if (unamePlatform !== "darwin") return;
+ child_process.execFile("/usr/bin/qlmanage", ["-p", filePath], (error, stdout, stderr) => {
+ if (error) {
+ console.error(`Error opening Quick Look: ${error}`);
+ }
+ });
});
electron.ipcMain.handle("clear-webview-storage", async (event, webContentsId: number) => {
diff --git a/emain/emain-menu.ts b/emain/emain-menu.ts
index 79d7e17362..27b438c4a4 100644
--- a/emain/emain-menu.ts
+++ b/emain/emain-menu.ts
@@ -9,6 +9,7 @@ import { focusedBuilderWindow, getBuilderWindowById } from "./emain-builder";
import { openBuilderWindow } from "./emain-ipc";
import { isDev, unamePlatform } from "./emain-platform";
import { clearTabCache } from "./emain-tabview";
+import { decreaseZoomLevel, increaseZoomLevel } from "./emain-util";
import {
createNewWaveWindow,
createWorkspace,
@@ -123,7 +124,11 @@ function makeEditMenu(fullConfig?: FullConfigType): Electron.MenuItemConstructor
];
}
-function makeFileMenu(numWaveWindows: number, callbacks: AppMenuCallbacks, fullConfig: FullConfigType): Electron.MenuItemConstructorOptions[] {
+function makeFileMenu(
+ numWaveWindows: number,
+ callbacks: AppMenuCallbacks,
+ fullConfig: FullConfigType
+): Electron.MenuItemConstructorOptions[] {
const fileMenu: Electron.MenuItemConstructorOptions[] = [
{
label: "New Window",
@@ -242,12 +247,9 @@ function makeViewMenu(
accelerator: "CommandOrControl+=",
click: (_, window) => {
const wc = getWindowWebContents(window) ?? webContents;
- if (wc == null) {
- return;
+ if (wc) {
+ increaseZoomLevel(wc);
}
- const newZoom = Math.min(5, wc.getZoomFactor() + 0.2);
- wc.setZoomFactor(newZoom);
- wc.send("zoom-factor-change", newZoom);
},
},
{
@@ -255,12 +257,9 @@ function makeViewMenu(
accelerator: "CommandOrControl+Shift+=",
click: (_, window) => {
const wc = getWindowWebContents(window) ?? webContents;
- if (wc == null) {
- return;
+ if (wc) {
+ increaseZoomLevel(wc);
}
- const newZoom = Math.min(5, wc.getZoomFactor() + 0.2);
- wc.setZoomFactor(newZoom);
- wc.send("zoom-factor-change", newZoom);
},
visible: false,
acceleratorWorksWhenHidden: true,
@@ -270,12 +269,9 @@ function makeViewMenu(
accelerator: "CommandOrControl+-",
click: (_, window) => {
const wc = getWindowWebContents(window) ?? webContents;
- if (wc == null) {
- return;
+ if (wc) {
+ decreaseZoomLevel(wc);
}
- const newZoom = Math.max(0.2, wc.getZoomFactor() - 0.2);
- wc.setZoomFactor(newZoom);
- wc.send("zoom-factor-change", newZoom);
},
},
{
@@ -283,12 +279,9 @@ function makeViewMenu(
accelerator: "CommandOrControl+Shift+-",
click: (_, window) => {
const wc = getWindowWebContents(window) ?? webContents;
- if (wc == null) {
- return;
+ if (wc) {
+ decreaseZoomLevel(wc);
}
- const newZoom = Math.max(0.2, wc.getZoomFactor() - 0.2);
- wc.setZoomFactor(newZoom);
- wc.send("zoom-factor-change", newZoom);
},
visible: false,
acceleratorWorksWhenHidden: true,
@@ -380,7 +373,11 @@ export function instantiateAppMenu(workspaceOrBuilderId?: string): Promise {
const menu = await instantiateAppMenu();
electron.Menu.setApplicationMenu(menu);
@@ -389,7 +386,7 @@ export function makeAppMenu() {
waveEventSubscribe({
eventType: "workspace:update",
- handler: makeAppMenu,
+ handler: makeAndSetAppMenu,
});
function getWebContentsByWorkspaceOrBuilderId(workspaceOrBuilderId: string): electron.WebContents {
diff --git a/emain/emain-tabview.ts b/emain/emain-tabview.ts
index 5855198ff5..dcc15b1e5d 100644
--- a/emain/emain-tabview.ts
+++ b/emain/emain-tabview.ts
@@ -2,17 +2,88 @@
// SPDX-License-Identifier: Apache-2.0
import { RpcApi } from "@/app/store/wshclientapi";
-import { adaptFromElectronKeyEvent } from "@/util/keyutil";
+import { adaptFromElectronKeyEvent, checkKeyPressed } from "@/util/keyutil";
import { CHORD_TIMEOUT } from "@/util/sharedconst";
import { Rectangle, shell, WebContentsView } from "electron";
-import { getWaveWindowById } from "emain/emain-window";
+import { createNewWaveWindow, getWaveWindowById } from "emain/emain-window";
import path from "path";
import { configureAuthKeyRequestInjection } from "./authkey";
import { setWasActive } from "./emain-activity";
-import { getElectronAppBasePath, isDevVite } from "./emain-platform";
-import { handleCtrlShiftFocus, handleCtrlShiftState, shFrameNavHandler, shNavHandler } from "./emain-util";
+import { getElectronAppBasePath, isDevVite, unamePlatform } from "./emain-platform";
+import {
+ decreaseZoomLevel,
+ handleCtrlShiftFocus,
+ handleCtrlShiftState,
+ increaseZoomLevel,
+ shFrameNavHandler,
+ shNavHandler,
+} from "./emain-util";
import { ElectronWshClient } from "./emain-wsh";
+function handleWindowsMenuAccelerators(waveEvent: WaveKeyboardEvent, tabView: WaveTabView): boolean {
+ const waveWindow = getWaveWindowById(tabView.waveWindowId);
+
+ if (checkKeyPressed(waveEvent, "Ctrl:Shift:n")) {
+ createNewWaveWindow();
+ return true;
+ }
+
+ if (checkKeyPressed(waveEvent, "Ctrl:Shift:r")) {
+ tabView.webContents.reloadIgnoringCache();
+ return true;
+ }
+
+ if (checkKeyPressed(waveEvent, "Ctrl:v")) {
+ tabView.webContents.paste();
+ return true;
+ }
+
+ if (checkKeyPressed(waveEvent, "Ctrl:0")) {
+ tabView.webContents.setZoomFactor(1);
+ tabView.webContents.send("zoom-factor-change", 1);
+ return true;
+ }
+
+ if (checkKeyPressed(waveEvent, "Ctrl:=") || checkKeyPressed(waveEvent, "Ctrl:Shift:=")) {
+ increaseZoomLevel(tabView.webContents);
+ return true;
+ }
+
+ if (checkKeyPressed(waveEvent, "Ctrl:-") || checkKeyPressed(waveEvent, "Ctrl:Shift:-")) {
+ decreaseZoomLevel(tabView.webContents);
+ return true;
+ }
+
+ if (checkKeyPressed(waveEvent, "F11")) {
+ if (waveWindow) {
+ waveWindow.setFullScreen(!waveWindow.isFullScreen());
+ }
+ return true;
+ }
+
+ for (let i = 1; i <= 9; i++) {
+ if (checkKeyPressed(waveEvent, `Alt:Ctrl:${i}`)) {
+ const workspaceNum = i - 1;
+ RpcApi.WorkspaceListCommand(ElectronWshClient).then((workspaceList) => {
+ if (workspaceList && workspaceNum < workspaceList.length) {
+ const workspace = workspaceList[workspaceNum];
+ if (waveWindow) {
+ waveWindow.switchWorkspace(workspace.workspacedata.oid);
+ }
+ }
+ });
+ return true;
+ }
+ }
+
+ if (checkKeyPressed(waveEvent, "Alt:Shift:i")) {
+ tabView.webContents.toggleDevTools();
+ return true;
+ }
+
+ return false;
+}
+
function computeBgColor(fullConfig: FullConfigType): string {
const settings = fullConfig?.settings;
const isTransparent = settings?.["window:transparent"] ?? false;
@@ -249,6 +320,14 @@ export async function getOrCreateWebViewForTab(waveWindowId: string, tabId: stri
e.preventDefault();
tabView.setKeyboardChordMode(false);
tabView.webContents.send("reinject-key", waveEvent);
+ return;
+ }
+
+ if (unamePlatform === "win32" && input.type == "keyDown") {
+ if (handleWindowsMenuAccelerators(waveEvent, tabView)) {
+ e.preventDefault();
+ return;
+ }
}
});
tabView.webContents.on("zoom-changed", (e) => {
diff --git a/emain/emain-util.ts b/emain/emain-util.ts
index 712aeb52a5..b04fda0dfa 100644
--- a/emain/emain-util.ts
+++ b/emain/emain-util.ts
@@ -8,6 +8,22 @@ export const WaveAppPathVarName = "WAVETERM_APP_PATH";
export const WaveAppResourcesPathVarName = "WAVETERM_RESOURCES_PATH";
export const WaveAppElectronExecPath = "WAVETERM_ELECTRONEXECPATH";
+const MinZoomLevel = 0.4;
+const MaxZoomLevel = 2.6;
+const ZoomDelta = 0.2;
+
+export function increaseZoomLevel(webContents: electron.WebContents): void {
+ const newZoom = Math.min(MaxZoomLevel, webContents.getZoomFactor() + ZoomDelta);
+ webContents.setZoomFactor(newZoom);
+ webContents.send("zoom-factor-change", newZoom);
+}
+
+export function decreaseZoomLevel(webContents: electron.WebContents): void {
+ const newZoom = Math.max(MinZoomLevel, webContents.getZoomFactor() - ZoomDelta);
+ webContents.setZoomFactor(newZoom);
+ webContents.send("zoom-factor-change", newZoom);
+}
+
export function getElectronExecPath(): string {
return process.execPath;
}
diff --git a/emain/emain-window.ts b/emain/emain-window.ts
index 059d7e39b7..6d30daebe8 100644
--- a/emain/emain-window.ts
+++ b/emain/emain-window.ts
@@ -23,7 +23,7 @@ import { ElectronWshClient } from "./emain-wsh";
import { updater } from "./updater";
export type WindowOpts = {
- unamePlatform: string;
+ unamePlatform: NodeJS.Platform;
isPrimaryStartupWindow?: boolean;
foregroundWindow?: boolean;
};
@@ -41,7 +41,10 @@ export function calculateWindowBounds(
let winPosX = pos?.x ?? 100;
let winPosY = pos?.y ?? 100;
- if ((winWidth == null || winWidth === 0 || winHeight == null || winHeight === 0) && settings?.["window:dimensions"]) {
+ if (
+ (winWidth == null || winWidth === 0 || winHeight == null || winHeight === 0) &&
+ settings?.["window:dimensions"]
+ ) {
const dimensions = settings["window:dimensions"];
const match = dimensions.match(/^(\d+)[xX](\d+)$/);
@@ -147,52 +150,64 @@ export class WaveBrowserWindow extends BaseWindow {
console.log("create win", waveWindow.oid);
const winBounds = calculateWindowBounds(waveWindow.winsize, waveWindow.pos, settings);
const winOpts: BaseWindowConstructorOptions = {
- titleBarStyle:
- opts.unamePlatform === "darwin"
- ? "hiddenInset"
- : settings["window:nativetitlebar"]
- ? "default"
- : "hidden",
- titleBarOverlay:
- opts.unamePlatform !== "darwin"
- ? {
- symbolColor: "white",
- color: "#00000000",
- }
- : false,
x: winBounds.x,
y: winBounds.y,
width: winBounds.width,
height: winBounds.height,
minWidth: MinWindowWidth,
minHeight: MinWindowHeight,
- icon:
- opts.unamePlatform == "linux"
- ? path.join(getElectronAppBasePath(), "public/logos/wave-logo-dark.png")
- : undefined,
show: false,
- autoHideMenuBar: !settings?.["window:showmenubar"],
};
+
const isTransparent = settings?.["window:transparent"] ?? false;
const isBlur = !isTransparent && (settings?.["window:blur"] ?? false);
- if (isTransparent) {
- winOpts.transparent = true;
- } else if (isBlur) {
- switch (opts.unamePlatform) {
- case "win32": {
- winOpts.backgroundMaterial = "acrylic";
- break;
- }
- case "darwin": {
- winOpts.vibrancy = "fullscreen-ui";
- break;
- }
+
+ if (opts.unamePlatform === "darwin") {
+ winOpts.titleBarStyle = "hiddenInset";
+ winOpts.titleBarOverlay = false;
+ winOpts.autoHideMenuBar = !settings?.["window:showmenubar"];
+ if (isTransparent) {
+ winOpts.transparent = true;
+ } else if (isBlur) {
+ winOpts.vibrancy = "fullscreen-ui";
+ } else {
+ winOpts.backgroundColor = "#222222";
+ }
+ } else if (opts.unamePlatform === "linux") {
+ winOpts.titleBarStyle = settings["window:nativetitlebar"] ? "default" : "hidden";
+ winOpts.titleBarOverlay = {
+ symbolColor: "white",
+ color: "#00000000",
+ };
+ winOpts.icon = path.join(getElectronAppBasePath(), "public/logos/wave-logo-dark.png");
+ winOpts.autoHideMenuBar = !settings?.["window:showmenubar"];
+ if (isTransparent) {
+ winOpts.transparent = true;
+ } else {
+ winOpts.backgroundColor = "#222222";
+ }
+ } else if (opts.unamePlatform === "win32") {
+ winOpts.titleBarStyle = "hidden";
+ winOpts.titleBarOverlay = {
+ color: "#222222",
+ symbolColor: "#c3c8c2",
+ height: 32,
+ };
+ if (isTransparent) {
+ winOpts.transparent = true;
+ } else if (isBlur) {
+ winOpts.backgroundMaterial = "acrylic";
+ } else {
+ winOpts.backgroundColor = "#222222";
}
- } else {
- winOpts.backgroundColor = "#222222";
}
super(winOpts);
+
+ if (opts.unamePlatform === "win32") {
+ this.setMenu(null);
+ }
+
const fullscreenOnLaunch = fullConfig?.settings["window:fullscreenonlaunch"];
if (fullscreenOnLaunch && opts.foregroundWindow) {
this.once("show", () => {
diff --git a/emain/emain.ts b/emain/emain.ts
index e4d44e8792..093a74bd41 100644
--- a/emain/emain.ts
+++ b/emain/emain.ts
@@ -23,7 +23,7 @@ import {
} from "./emain-activity";
import { initIpcHandlers } from "./emain-ipc";
import { log } from "./emain-log";
-import { makeAppMenu, makeDockTaskbar } from "./emain-menu";
+import { makeAndSetAppMenu, makeDockTaskbar } from "./emain-menu";
import {
checkIfRunningUnderARM64Translation,
getElectronAppBasePath,
@@ -318,7 +318,7 @@ globalEvents.on("windows-updated", () => {
lastWaveWindowCount = wwCount;
lastIsBuilderWindowActive = isBuilderActive;
console.log("windows-updated", wwCount, "builder-active:", isBuilderActive);
- makeAppMenu();
+ makeAndSetAppMenu();
});
async function appMain() {
@@ -360,7 +360,7 @@ async function appMain() {
setTimeout(runActiveTimer, 5000); // start active timer, wait 5s just to be safe
setTimeout(sendDisplaysTDataEvent, 5000);
- makeAppMenu();
+ makeAndSetAppMenu();
makeDockTaskbar();
await configureAutoUpdater();
setGlobalIsStarting(false);
diff --git a/frontend/app/aipanel/aipanel.tsx b/frontend/app/aipanel/aipanel.tsx
index c386348b32..acc76ee5c9 100644
--- a/frontend/app/aipanel/aipanel.tsx
+++ b/frontend/app/aipanel/aipanel.tsx
@@ -7,7 +7,7 @@ import { ErrorBoundary } from "@/app/element/errorboundary";
import { atoms, getSettingsKeyAtom } from "@/app/store/global";
import { globalStore } from "@/app/store/jotaiStore";
import { checkKeyPressed, keydownWrapper } from "@/util/keyutil";
-import { isMacOS } from "@/util/platformutil";
+import { isMacOS, isWindows } from "@/util/platformutil";
import { cn } from "@/util/util";
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
@@ -135,10 +135,20 @@ const AIWelcomeMessage = memo(() => {
to toggle panel
- Ctrl
- Shift
- 0
- to focus
+ {isWindows() ? (
+ <>
+ Alt
+ 0
+ to focus
+ >
+ ) : (
+ <>
+ Ctrl
+ Shift
+ 0
+ to focus
+ >
+ )}
diff --git a/frontend/app/element/windowdrag.scss b/frontend/app/element/windowdrag.scss
deleted file mode 100644
index 5229622ebf..0000000000
--- a/frontend/app/element/windowdrag.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2024, Command Line Inc.
-// SPDX-License-Identifier: Apache-2.0
-
-.window-drag {
- -webkit-app-region: drag !important;
- z-index: var(--zindex-window-drag);
-}
diff --git a/frontend/app/element/windowdrag.tsx b/frontend/app/element/windowdrag.tsx
deleted file mode 100644
index 7e4ed733da..0000000000
--- a/frontend/app/element/windowdrag.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2025, Command Line Inc.
-// SPDX-License-Identifier: Apache-2.0
-
-import clsx from "clsx";
-import React, { forwardRef } from "react";
-
-import "./windowdrag.scss";
-
-interface WindowDragProps {
- className?: string;
- style?: React.CSSProperties;
- children?: React.ReactNode;
-}
-
-const WindowDrag = forwardRef(({ children, className, style }, ref) => {
- return (
-
- {children}
-
- );
-});
-WindowDrag.displayName = "WindowDrag";
-
-export { WindowDrag };
diff --git a/frontend/app/modals/messagemodal.scss b/frontend/app/modals/messagemodal.scss
index 41113f25ba..f553741518 100644
--- a/frontend/app/modals/messagemodal.scss
+++ b/frontend/app/modals/messagemodal.scss
@@ -3,4 +3,8 @@
.message-modal {
min-width: 400px;
+
+ footer {
+ padding: 10px;
+ }
}
diff --git a/frontend/app/modals/modal.tsx b/frontend/app/modals/modal.tsx
index 3683a576ac..5733ec2608 100644
--- a/frontend/app/modals/modal.tsx
+++ b/frontend/app/modals/modal.tsx
@@ -23,7 +23,21 @@ interface ModalProps {
}
const Modal = forwardRef(
- ({ children, className, cancelLabel, okLabel, onCancel, onOk, onClose, onClickBackdrop, okDisabled, cancelDisabled }: ModalProps, ref) => {
+ (
+ {
+ children,
+ className,
+ cancelLabel,
+ okLabel,
+ onCancel,
+ onOk,
+ onClose,
+ onClickBackdrop,
+ okDisabled,
+ cancelDisabled,
+ }: ModalProps,
+ ref
+ ) => {
const renderBackdrop = (onClick) => ;
const renderFooter = () => {
@@ -41,7 +55,14 @@ const Modal = forwardRef(
{children}
{renderFooter() && (
-
+
)}
@@ -68,7 +89,14 @@ interface ModalFooterProps {
cancelDisabled?: boolean;
}
-const ModalFooter = ({ onCancel, onOk, cancelLabel = "Cancel", okLabel = "Ok", okDisabled, cancelDisabled }: ModalFooterProps) => {
+const ModalFooter = ({
+ onCancel,
+ onOk,
+ cancelLabel = "Cancel",
+ okLabel = "Ok",
+ okDisabled,
+ cancelDisabled,
+}: ModalFooterProps) => {
return (
);
};
@@ -87,8 +119,9 @@ interface FlexiModalProps {
onClickBackdrop?: () => void;
}
-interface FlexiModalComponent
- extends React.ForwardRefExoticComponent> {
+interface FlexiModalComponent extends React.ForwardRefExoticComponent<
+ FlexiModalProps & React.RefAttributes
+> {
Content: typeof ModalContent;
Footer: typeof ModalFooter;
}
diff --git a/frontend/app/store/global.ts b/frontend/app/store/global.ts
index 0b6e35e751..47a5441627 100644
--- a/frontend/app/store/global.ts
+++ b/frontend/app/store/global.ts
@@ -72,16 +72,26 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
getApi().onFullScreenChange((isFullScreen) => {
globalStore.set(isFullScreenAtom, isFullScreen);
});
- } catch (_) {
- // do nothing
+ } catch (e) {
+ console.log("failed to initialize isFullScreenAtom", e);
+ }
+
+ const zoomFactorAtom = atom(1.0) as PrimitiveAtom;
+ try {
+ globalStore.set(zoomFactorAtom, getApi().getZoomFactor());
+ getApi().onZoomFactorChange((zoomFactor) => {
+ globalStore.set(zoomFactorAtom, zoomFactor);
+ });
+ } catch (e) {
+ console.log("failed to initialize zoomFactorAtom", e);
}
try {
getApi().onMenuItemAbout(() => {
modalsModel.pushModal("AboutModal");
});
- } catch (_) {
- // do nothing
+ } catch (e) {
+ console.log("failed to initialize onMenuItemAbout handler", e);
}
const clientAtom: Atom = atom((get) => {
@@ -135,8 +145,8 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
getApi().onUpdaterStatusChange((status) => {
globalStore.set(updaterStatusAtom, status);
});
- } catch (_) {
- // do nothing
+ } catch (e) {
+ console.log("failed to initialize updaterStatusAtom", e);
}
const reducedMotionSettingAtom = atom((get) => get(settingsAtom)?.["window:reducedmotion"]);
@@ -187,6 +197,7 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
tabAtom,
staticTabId: staticTabIdAtom,
isFullScreen: isFullScreenAtom,
+ zoomFactorAtom,
controlShiftDelayAtom,
updaterStatusAtom,
prefersReducedMotionAtom,
diff --git a/frontend/app/store/keymodel.ts b/frontend/app/store/keymodel.ts
index 414b3bde1e..32b33abcf4 100644
--- a/frontend/app/store/keymodel.ts
+++ b/frontend/app/store/keymodel.ts
@@ -23,6 +23,7 @@ import { TabBarModel } from "@/app/tab/tabbar-model";
import { WorkspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
import { deleteLayoutModelForTab, getLayoutModelForStaticTab, NavigateDirection } from "@/layout/index";
import * as keyutil from "@/util/keyutil";
+import { isWindows } from "@/util/platformutil";
import { CHORD_TIMEOUT } from "@/util/sharedconst";
import { fireAndForget } from "@/util/util";
import * as jotai from "jotai";
@@ -606,14 +607,25 @@ function registerGlobalKeys() {
return true;
});
}
- globalKeyMap.set("Ctrl:Shift:c{Digit0}", () => {
- WaveAIModel.getInstance().focusInput();
- return true;
- });
- globalKeyMap.set("Ctrl:Shift:c{Numpad0}", () => {
- WaveAIModel.getInstance().focusInput();
- return true;
- });
+ if (isWindows()) {
+ globalKeyMap.set("Alt:c{Digit0}", () => {
+ WaveAIModel.getInstance().focusInput();
+ return true;
+ });
+ globalKeyMap.set("Alt:c{Numpad0}", () => {
+ WaveAIModel.getInstance().focusInput();
+ return true;
+ });
+ } else {
+ globalKeyMap.set("Ctrl:Shift:c{Digit0}", () => {
+ WaveAIModel.getInstance().focusInput();
+ return true;
+ });
+ globalKeyMap.set("Ctrl:Shift:c{Numpad0}", () => {
+ WaveAIModel.getInstance().focusInput();
+ return true;
+ });
+ }
function activateSearch(event: WaveKeyboardEvent): boolean {
const bcm = getBlockComponentModel(getFocusedBlockInStaticTab());
// Ctrl+f is reserved in most shells
diff --git a/frontend/app/tab/tabbar.scss b/frontend/app/tab/tabbar.scss
index dfd4df94f4..9bb5c8058f 100644
--- a/frontend/app/tab/tabbar.scss
+++ b/frontend/app/tab/tabbar.scss
@@ -1,28 +1,6 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
-.tab-bar-wrapper {
- --default-indent: 10px;
- --darwin-not-fullscreen-indent: calc(74px * var(--zoomfactor-inv));
-}
-
-.darwin:not(.fullscreen) .tab-bar-wrapper {
- .window-drag.left {
- width: var(--darwin-not-fullscreen-indent);
- }
-}
-
-.config-error-message {
- max-width: 500px;
- margin-bottom: 20px;
-
- h3 {
- font-weight: bold;
- font-size: 16px;
- margin-bottom: 10px;
- }
-}
-
.tab-bar-wrapper {
padding-top: 6px;
position: relative;
@@ -58,45 +36,13 @@
border: 1px solid var(--border-color);
}
- .dev-label,
- .app-menu-button {
- font-size: 26px;
- user-select: none;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 0 6px 0 0;
- }
-
- .app-menu-button {
- cursor: pointer;
- color: var(--secondary-text-color);
-
- &:hover {
- color: var(--main-text-color);
- }
- }
-
- .dev-label {
- color: var(--accent-color);
- }
-
.tab-bar-right {
display: flex;
flex-direction: row;
gap: 6px;
height: 100%;
align-items: center;
- &:not(:empty) {
- margin-left: auto;
- margin-right: 6px;
- }
- }
-
- .config-error-button {
- color: black;
- padding: 0 6px;
- flex: 0 0 fit-content;
+ margin-left: auto;
}
.add-tab {
@@ -104,16 +50,6 @@
height: 27px;
}
- .window-drag {
- height: 100%;
- width: var(--default-indent);
- flex-shrink: 0;
-
- &.right {
- flex-grow: 1;
- }
- }
-
// Customize scrollbar styles
.os-theme-dark,
.os-theme-light {
diff --git a/frontend/app/tab/tabbar.tsx b/frontend/app/tab/tabbar.tsx
index c8051237d9..2a479aa631 100644
--- a/frontend/app/tab/tabbar.tsx
+++ b/frontend/app/tab/tabbar.tsx
@@ -4,10 +4,9 @@
import { Button } from "@/app/element/button";
import { modalsModel } from "@/app/store/modalmodel";
import { WorkspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
-import { WindowDrag } from "@/element/windowdrag";
import { deleteLayoutModelForTab } from "@/layout/index";
import { atoms, createTab, getApi, globalStore, setActiveTab } from "@/store/global";
-import { PLATFORM, PlatformMacOS } from "@/util/platformutil";
+import { isMacOS, isWindows } from "@/util/platformutil";
import { fireAndForget } from "@/util/util";
import { useAtomValue } from "jotai";
import { OverlayScrollbars } from "overlayscrollbars";
@@ -20,9 +19,9 @@ import "./tabbar.scss";
import { UpdateStatusBanner } from "./updatebanner";
import { WorkspaceSwitcher } from "./workspaceswitcher";
-const TAB_DEFAULT_WIDTH = 130;
-const TAB_MIN_WIDTH = 100;
-const OS_OPTIONS = {
+const TabDefaultWidth = 130;
+const TabMinWidth = 100;
+const OSOptions = {
overflow: {
x: "scroll",
y: "hidden",
@@ -43,13 +42,34 @@ interface TabBarProps {
workspace: Workspace;
}
+const WaveAIButton = memo(() => {
+ const aiPanelOpen = useAtomValue(WorkspaceLayoutModel.getInstance().panelVisibleAtom);
+
+ const onClick = () => {
+ const currentVisible = WorkspaceLayoutModel.getInstance().getAIPanelVisible();
+ WorkspaceLayoutModel.getInstance().setAIPanelVisible(!currentVisible);
+ };
+
+ return (
+
+
+ AI
+
+ );
+});
+WaveAIButton.displayName = "WaveAIButton";
+
const ConfigErrorMessage = () => {
const fullConfig = useAtomValue(atoms.fullConfigAtom);
if (fullConfig?.configerrors == null || fullConfig?.configerrors.length == 0) {
return (
-
-
Configuration Clean
+
+
Configuration Clean
There are no longer any errors detected in your config.
);
@@ -57,8 +77,8 @@ const ConfigErrorMessage = () => {
if (fullConfig?.configerrors.length == 1) {
const singleError = fullConfig.configerrors[0];
return (
-
-
Configuration Error
+
+
Configuration Error
{singleError.file}: {singleError.err}
@@ -66,8 +86,8 @@ const ConfigErrorMessage = () => {
);
}
return (
-
-
Configuration Error
+
+
Configuration Error
{fullConfig.configerrors.map((error, index) => (
-
@@ -92,7 +112,7 @@ const ConfigErrorIcon = ({ buttonRef }: { buttonRef: React.RefObject}
- className="config-error-button red"
+ className="text-black flex-[0_0_fit-content] !h-full !px-3 red"
onClick={handleClick}
>
@@ -164,17 +184,17 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
});
const osInstanceRef = useRef(null);
const draggerLeftRef = useRef(null);
+ const draggerRightRef = useRef(null);
const workspaceSwitcherRef = useRef(null);
const appMenuButtonRef = useRef(null);
- const tabWidthRef = useRef(TAB_DEFAULT_WIDTH);
+ const tabWidthRef = useRef(TabDefaultWidth);
const scrollableRef = useRef(false);
const updateStatusBannerRef = useRef(null);
const configErrorButtonRef = useRef(null);
const prevAllLoadedRef = useRef(false);
const activeTabId = useAtomValue(atoms.staticTabId);
const isFullScreen = useAtomValue(atoms.isFullScreen);
- const aiPanelOpen = useAtomValue(WorkspaceLayoutModel.getInstance().panelVisibleAtom);
-
+ const zoomFactor = useAtomValue(atoms.zoomFactorAtom);
const settings = useAtomValue(atoms.settingsAtom);
let prevDelta: number;
@@ -190,16 +210,12 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
return;
}
// Compare current tabIds with new workspace.tabids
- console.log("tabbar workspace", workspace);
-
const newTabIdsArr = [...(workspace.pinnedtabids ?? []), ...(workspace.tabids ?? [])];
const newPinnedTabSet = new Set(workspace.pinnedtabids ?? []);
const areEqual = strArrayIsEqual(tabIds, newTabIdsArr) && setIsEqual(pinnedTabIds, newPinnedTabSet);
if (!areEqual) {
- console.log("newPinnedTabIds", newPinnedTabSet);
- console.log("newTabIdList", newTabIdsArr);
setTabIds(newTabIdsArr);
setPinnedTabIds(newPinnedTabSet);
}
@@ -228,6 +244,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
const tabbarWrapperWidth = tabbarWrapperRef.current.getBoundingClientRect().width;
const windowDragLeftWidth = draggerLeftRef.current.getBoundingClientRect().width;
+ const windowDragRightWidth = draggerRightRef.current?.getBoundingClientRect().width ?? 0;
const addBtnWidth = addBtnRef.current.getBoundingClientRect().width;
const updateStatusLabelWidth = updateStatusBannerRef.current?.getBoundingClientRect().width ?? 0;
const configErrorWidth = configErrorButtonRef.current?.getBoundingClientRect().width ?? 0;
@@ -236,6 +253,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
const nonTabElementsWidth =
windowDragLeftWidth +
+ windowDragRightWidth +
addBtnWidth +
updateStatusLabelWidth +
configErrorWidth +
@@ -249,7 +267,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
let idealTabWidth = spaceForTabs / numberOfTabs;
// Apply min/max constraints
- idealTabWidth = Math.max(TAB_MIN_WIDTH, Math.min(idealTabWidth, TAB_DEFAULT_WIDTH));
+ idealTabWidth = Math.max(TabMinWidth, Math.min(idealTabWidth, TabDefaultWidth));
// Determine if the tab bar needs to be scrollable
const newScrollable = idealTabWidth * numberOfTabs > spaceForTabs;
@@ -280,7 +298,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
// Initialize/destroy overlay scrollbars
if (newScrollable) {
- osInstanceRef.current = OverlayScrollbars(tabBarRef.current, { ...(OS_OPTIONS as any) });
+ osInstanceRef.current = OverlayScrollbars(tabBarRef.current, { ...(OSOptions as any) });
} else {
if (osInstanceRef.current) {
osInstanceRef.current.destroy();
@@ -414,7 +432,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
// Constrain movement within the container bounds
if (tabBarRef.current) {
const numberOfTabs = tabIds.length;
- const totalDefaultTabWidth = numberOfTabs * TAB_DEFAULT_WIDTH;
+ const totalDefaultTabWidth = numberOfTabs * TabDefaultWidth;
if (totalDefaultTabWidth < tabBarRectWidth) {
// Set to the total default tab width if there's vacant space
tabBarRectWidth = totalDefaultTabWidth;
@@ -634,28 +652,28 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
getApi().showWorkspaceAppMenu(workspace.oid);
}
- function onWaveAIClick() {
- const currentVisible = WorkspaceLayoutModel.getInstance().getAIPanelVisible();
- WorkspaceLayoutModel.getInstance().setAIPanelVisible(!currentVisible);
+ const tabsWrapperWidth = tabIds.length * tabWidthRef.current;
+ const showAppMenuButton = isWindows() || (!isMacOS() && !settings["window:showmenubar"]);
+
+ // Calculate window drag left width based on platform and state
+ let windowDragLeftWidth = 10;
+ if (isMacOS() && !isFullScreen) {
+ if (zoomFactor > 0) {
+ windowDragLeftWidth = 74 / zoomFactor;
+ } else {
+ windowDragLeftWidth = 74;
+ }
}
- const tabsWrapperWidth = tabIds.length * tabWidthRef.current;
- const waveaiButton = (
-
-
- AI
-
- );
- const appMenuButton =
- PLATFORM !== PlatformMacOS && !settings["window:showmenubar"] ? (
-
-
-
- ) : undefined;
+ // Calculate window drag right width
+ let windowDragRightWidth = 6;
+ if (isWindows()) {
+ if (zoomFactor > 0) {
+ windowDragRightWidth = 139 / zoomFactor;
+ } else {
+ windowDragRightWidth = 139;
+ }
+ }
const addtabButtonDecl: IconButtonDecl = {
elemtype: "iconbutton",
@@ -665,9 +683,22 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
};
return (
-
- {appMenuButton}
- {waveaiButton}
+
+ {showAppMenuButton && (
+
+
+
+ )}
+
@@ -699,6 +730,11 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
);
diff --git a/frontend/app/view/codeeditor/schemaendpoints.ts b/frontend/app/view/codeeditor/schemaendpoints.ts
index 0f79837eb1..90defbf19c 100644
--- a/frontend/app/view/codeeditor/schemaendpoints.ts
+++ b/frontend/app/view/codeeditor/schemaendpoints.ts
@@ -1,7 +1,6 @@
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
-import { getApi } from "@/app/store/global";
import { getWebServerEndpoint } from "@/util/endpoints";
type EndpointInfo = {
@@ -10,36 +9,13 @@ type EndpointInfo = {
schema: object;
};
-function prependWildcard(path: string): string {
- return path.startsWith("/") ? `*${path}` : `*/${path}`;
-}
-
-function convertToTildePath(absolutePath: string): string {
- const homeDir = getApi().getHomeDir();
- if (absolutePath.startsWith(homeDir)) {
- return "~" + absolutePath.slice(homeDir.length);
- }
- return absolutePath;
-}
-
-function makeConfigPathMatches(suffix: string): Array
{
- const configPath = `${getApi().getConfigDir()}${suffix}`;
- const tildePath = convertToTildePath(configPath);
- const paths = [configPath, prependWildcard(configPath)];
- if (tildePath !== configPath) {
- paths.push(tildePath);
- paths.push(prependWildcard(tildePath));
- }
- return paths;
-}
-
const allFilepaths: Map> = new Map();
-allFilepaths.set(`${getWebServerEndpoint()}/schema/settings.json`, makeConfigPathMatches("/settings.json"));
-allFilepaths.set(`${getWebServerEndpoint()}/schema/connections.json`, makeConfigPathMatches("/connections.json"));
-allFilepaths.set(`${getWebServerEndpoint()}/schema/aipresets.json`, makeConfigPathMatches("/presets/ai.json"));
-allFilepaths.set(`${getWebServerEndpoint()}/schema/bgpresets.json`, makeConfigPathMatches("/presets/bg.json"));
-allFilepaths.set(`${getWebServerEndpoint()}/schema/waveai.json`, makeConfigPathMatches("/waveai.json"));
-allFilepaths.set(`${getWebServerEndpoint()}/schema/widgets.json`, makeConfigPathMatches("/widgets.json"));
+allFilepaths.set(`${getWebServerEndpoint()}/schema/settings.json`, ["*/WAVECONFIGPATH/settings.json"]);
+allFilepaths.set(`${getWebServerEndpoint()}/schema/connections.json`, ["*/WAVECONFIGPATH/connections.json"]);
+allFilepaths.set(`${getWebServerEndpoint()}/schema/aipresets.json`, ["*/WAVECONFIGPATH/presets/ai.json"]);
+allFilepaths.set(`${getWebServerEndpoint()}/schema/bgpresets.json`, ["*/WAVECONFIGPATH/presets/bg.json"]);
+allFilepaths.set(`${getWebServerEndpoint()}/schema/waveai.json`, ["*/WAVECONFIGPATH/waveai.json"]);
+allFilepaths.set(`${getWebServerEndpoint()}/schema/widgets.json`, ["*/WAVECONFIGPATH/widgets.json"]);
async function getSchemaEndpointInfo(endpoint: string): Promise {
let schema: Object;
diff --git a/frontend/app/view/term/term-model.ts b/frontend/app/view/term/term-model.ts
index 0e783c7ee5..a1b9eb60dd 100644
--- a/frontend/app/view/term/term-model.ts
+++ b/frontend/app/view/term/term-model.ts
@@ -26,6 +26,7 @@ import {
} from "@/store/global";
import * as services from "@/store/services";
import * as keyutil from "@/util/keyutil";
+import { isMacOS, isWindows } from "@/util/platformutil";
import { boundNumber, stringToBase64 } from "@/util/util";
import * as jotai from "jotai";
import * as React from "react";
@@ -496,6 +497,25 @@ export class TermViewModel implements ViewModel {
return false;
}
+ shouldHandleCtrlVPaste(): boolean {
+ // macOS never uses Ctrl-V for paste (uses Cmd-V)
+ if (isMacOS()) {
+ return false;
+ }
+
+ // Get the app:ctrlvpaste setting
+ const ctrlVPasteAtom = getSettingsKeyAtom("app:ctrlvpaste");
+ const ctrlVPasteSetting = globalStore.get(ctrlVPasteAtom);
+
+ // If setting is explicitly set, use it
+ if (ctrlVPasteSetting != null) {
+ return ctrlVPasteSetting;
+ }
+
+ // Default behavior: Windows=true, Linux/other=false
+ return isWindows();
+ }
+
handleTerminalKeydown(event: KeyboardEvent): boolean {
const waveEvent = keyutil.adaptFromReactOrNativeKeyEvent(event);
if (waveEvent.type != "keydown") {
@@ -525,6 +545,15 @@ export class TermViewModel implements ViewModel {
return false;
}
}
+
+ // Check for Ctrl-V paste (platform-dependent)
+ if (this.shouldHandleCtrlVPaste() && keyutil.checkKeyPressed(waveEvent, "Ctrl:v")) {
+ event.preventDefault();
+ event.stopPropagation();
+ getApi().nativePaste();
+ return false;
+ }
+
if (keyutil.checkKeyPressed(waveEvent, "Ctrl:Shift:v")) {
event.preventDefault();
event.stopPropagation();
diff --git a/frontend/app/view/waveconfig/waveconfig.tsx b/frontend/app/view/waveconfig/waveconfig.tsx
index 1bce94474c..861fadeae2 100644
--- a/frontend/app/view/waveconfig/waveconfig.tsx
+++ b/frontend/app/view/waveconfig/waveconfig.tsx
@@ -275,7 +275,7 @@ const WaveConfigView = memo(({ blockId, model }: ViewComponentProps {
)}
{isDev() ? (
@@ -526,7 +526,7 @@ const Widgets = memo(() => {
) : null}
{isDev() ? (
diff --git a/frontend/tailwindsetup.css b/frontend/tailwindsetup.css
index 7661b3f91a..a41c476052 100644
--- a/frontend/tailwindsetup.css
+++ b/frontend/tailwindsetup.css
@@ -70,6 +70,8 @@
--container-xs: 300px;
--container-xxs: 200px;
--container-tiny: 120px;
+
+ --z-window-drag: 100;
}
:root {
diff --git a/frontend/types/custom.d.ts b/frontend/types/custom.d.ts
index 7f78df4304..3476fe5392 100644
--- a/frontend/types/custom.d.ts
+++ b/frontend/types/custom.d.ts
@@ -22,6 +22,7 @@ declare global {
tabAtom: jotai.Atom; // driven from WOS
staticTabId: jotai.Atom;
isFullScreen: jotai.PrimitiveAtom;
+ zoomFactorAtom: jotai.PrimitiveAtom;
controlShiftDelayAtom: jotai.PrimitiveAtom;
prefersReducedMotionAtom: jotai.Atom;
updaterStatusAtom: jotai.PrimitiveAtom;
diff --git a/package-lock.json b/package-lock.json
index 02673dbd0f..6e1f1d7db0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "waveterm",
- "version": "0.13.0-beta.2",
+ "version": "0.13.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "waveterm",
- "version": "0.13.0-beta.2",
+ "version": "0.13.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"workspaces": [
diff --git a/pkg/aiusechat/openaichat/openaichat-backend.go b/pkg/aiusechat/openaichat/openaichat-backend.go
index 4eb6217421..d9aef79612 100644
--- a/pkg/aiusechat/openaichat/openaichat-backend.go
+++ b/pkg/aiusechat/openaichat/openaichat-backend.go
@@ -168,7 +168,7 @@ func processChatStream(
for _, tcDelta := range choice.Delta.ToolCalls {
idx := tcDelta.Index
for len(toolCallsInProgress) <= idx {
- toolCallsInProgress = append(toolCallsInProgress, ToolCall{})
+ toolCallsInProgress = append(toolCallsInProgress, ToolCall{Type: "function"})
}
tc := &toolCallsInProgress[idx]
diff --git a/pkg/telemetry/telemetrydata/telemetrydata.go b/pkg/telemetry/telemetrydata/telemetrydata.go
index deedeea908..db3e3c464a 100644
--- a/pkg/telemetry/telemetrydata/telemetrydata.go
+++ b/pkg/telemetry/telemetrydata/telemetrydata.go
@@ -81,11 +81,12 @@ type TEventUserProps struct {
LocCountryCode string `json:"loc:countrycode,omitempty"`
LocRegionCode string `json:"loc:regioncode,omitempty"`
- SettingsCustomWidgets int `json:"settings:customwidgets,omitempty"`
- SettingsCustomAIPresets int `json:"settings:customaipresets,omitempty"`
- SettingsCustomSettings int `json:"settings:customsettings,omitempty"`
- SettingsCustomAIModes int `json:"settings:customaimodes,omitempty"`
- SettingsSecretsCount int `json:"settings:secretscount,omitempty"`
+ SettingsCustomWidgets int `json:"settings:customwidgets,omitempty"`
+ SettingsCustomAIPresets int `json:"settings:customaipresets,omitempty"`
+ SettingsCustomSettings int `json:"settings:customsettings,omitempty"`
+ SettingsCustomAIModes int `json:"settings:customaimodes,omitempty"`
+ SettingsSecretsCount int `json:"settings:secretscount,omitempty"`
+ SettingsTransparent bool `json:"settings:transparent,omitempty"`
}
type TEventProps struct {