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
6 changes: 6 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,12 @@ tasks:
- task: dev:cleardata:linux
- task: dev:cleardata:macos

check:ts:
desc: Typecheck TypeScript code (frontend and electron).
cmd: npx tsc --noEmit
deps:
- npm:install

init:
desc: Initialize the project for development.
cmds:
Expand Down
2 changes: 2 additions & 0 deletions db/migrations-wstore/000010_merge_pinned_tabs.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- This migration cannot be reversed as pinned tab state is lost
-- during the merge operation
23 changes: 23 additions & 0 deletions db/migrations-wstore/000010_merge_pinned_tabs.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- Merge PinnedTabIds into TabIds, preserving tab order
UPDATE db_workspace
SET data = json_set(
data,
'$.tabids',
(
SELECT json_group_array(value)
FROM (
SELECT value, 0 AS src, CAST(key AS INT) AS k
FROM json_each(data, '$.pinnedtabids')
UNION ALL
SELECT value, 1 AS src, CAST(key AS INT) AS k
FROM json_each(data, '$.tabids')
ORDER BY src, k
)
)
)
WHERE json_type(data, '$.pinnedtabids') = 'array'
AND json_array_length(data, '$.pinnedtabids') > 0;

UPDATE db_workspace
SET data = json_remove(data, '$.pinnedtabids')
WHERE json_type(data, '$.pinnedtabids') IS NOT NULL;
9 changes: 4 additions & 5 deletions emain/emain-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ type WindowActionQueueEntry =
}
| {
op: "createtab";
pinned: boolean;
}
| {
op: "closetab";
Expand All @@ -132,7 +131,7 @@ type WindowActionQueueEntry =
};

function isNonEmptyUnsavedWorkspace(workspace: Workspace): boolean {
return !workspace.name && !workspace.icon && (workspace.tabids?.length > 1 || workspace.pinnedtabids?.length > 1);
return !workspace.name && !workspace.icon && workspace.tabids?.length > 1;
}

export class WaveBrowserWindow extends BaseWindow {
Expand Down Expand Up @@ -496,8 +495,8 @@ export class WaveBrowserWindow extends BaseWindow {
}
}

async queueCreateTab(pinned = false) {
await this._queueActionInternal({ op: "createtab", pinned });
async queueCreateTab() {
await this._queueActionInternal({ op: "createtab" });
}

async queueCloseTab(tabId: string) {
Expand Down Expand Up @@ -537,7 +536,7 @@ export class WaveBrowserWindow extends BaseWindow {
// have to use "===" here to get the typechecker to work :/
switch (entry.op) {
case "createtab":
tabId = await WorkspaceService.CreateTab(this.workspaceId, null, true, entry.pinned);
tabId = await WorkspaceService.CreateTab(this.workspaceId, null, true);
break;
case "switchtab":
tabId = entry.tabId;
Expand Down
11 changes: 0 additions & 11 deletions frontend/app/aipanel/waveai-model.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -606,17 +606,6 @@ export class WaveAIModel {
return globalStore.get(this.chatId);
}

toolUseKeepalive(toolcallid: string) {
RpcApi.WaveAIToolApproveCommand(
TabRpcClient,
{
toolcallid: toolcallid,
keepalive: true,
},
{ noresponse: true }
);
}

toolUseSendApproval(toolcallid: string, approval: string) {
RpcApi.WaveAIToolApproveCommand(TabRpcClient, {
toolcallid: toolcallid,
Expand Down
22 changes: 1 addition & 21 deletions frontend/app/store/keymodel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
WOS,
} from "@/app/store/global";
import { getActiveTabModel } from "@/app/store/tab-model";
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";
Expand Down Expand Up @@ -125,12 +124,6 @@ function getStaticTabBlockCount(): number {
return tabData?.blockids?.length ?? 0;
}

function isStaticTabPinned(): boolean {
const ws = globalStore.get(atoms.workspace);
const tabId = globalStore.get(atoms.staticTabId);
return ws.pinnedtabids?.includes(tabId) ?? false;
}

function simpleCloseStaticTab() {
const ws = globalStore.get(atoms.workspace);
const tabId = globalStore.get(atoms.staticTabId);
Expand All @@ -139,11 +132,6 @@ function simpleCloseStaticTab() {
}

function uxCloseBlock(blockId: string) {
if (isStaticTabPinned() && getStaticTabBlockCount() === 1) {
TabBarModel.getInstance().jiggleActivePinnedTab();
return;
}

const workspaceLayoutModel = WorkspaceLayoutModel.getInstance();
const isAIPanelOpen = workspaceLayoutModel.getAIPanelVisible();
if (isAIPanelOpen && getStaticTabBlockCount() === 1) {
Expand Down Expand Up @@ -177,10 +165,6 @@ function genericClose() {
WorkspaceLayoutModel.getInstance().setAIPanelVisible(false);
return;
}
if (isStaticTabPinned() && getStaticTabBlockCount() === 1) {
TabBarModel.getInstance().jiggleActivePinnedTab();
return;
}

const workspaceLayoutModel = WorkspaceLayoutModel.getInstance();
const isAIPanelOpen = workspaceLayoutModel.getAIPanelVisible();
Expand Down Expand Up @@ -266,7 +250,7 @@ function switchBlockInDirection(direction: NavigateDirection) {
}

function getAllTabs(ws: Workspace): string[] {
return [...(ws.pinnedtabids ?? []), ...(ws.tabids ?? [])];
return ws.tabids ?? [];
}

function switchTabAbs(index: number) {
Expand Down Expand Up @@ -532,10 +516,6 @@ function registerGlobalKeys() {
return true;
});
globalKeyMap.set("Cmd:Shift:w", () => {
if (isStaticTabPinned()) {
TabBarModel.getInstance().jiggleActivePinnedTab();
return true;
}
simpleCloseStaticTab();
return true;
});
Expand Down
9 changes: 2 additions & 7 deletions frontend/app/store/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,13 @@ export const WindowService = new WindowServiceType();

// workspaceservice.WorkspaceService (workspace)
class WorkspaceServiceType {
// @returns object updates
ChangeTabPinning(workspaceId: string, tabId: string, pinned: boolean): Promise<void> {
return WOS.callBackendService("workspace", "ChangeTabPinning", Array.from(arguments))
}

// @returns CloseTabRtn (and object updates)
CloseTab(workspaceId: string, tabId: string, fromElectron: boolean): Promise<CloseTabRtnType> {
return WOS.callBackendService("workspace", "CloseTab", Array.from(arguments))
}

// @returns tabId (and object updates)
CreateTab(workspaceId: string, tabName: string, activateTab: boolean, pinned: boolean): Promise<string> {
CreateTab(workspaceId: string, tabName: string, activateTab: boolean): Promise<string> {
return WOS.callBackendService("workspace", "CreateTab", Array.from(arguments))
}

Expand Down Expand Up @@ -182,7 +177,7 @@ class WorkspaceServiceType {
}

// @returns object updates
UpdateTabIds(workspaceId: string, tabIds: string[], pinnedTabIds: string[]): Promise<void> {
UpdateTabIds(workspaceId: string, tabIds: string[]): Promise<void> {
return WOS.callBackendService("workspace", "UpdateTabIds", Array.from(arguments))
}

Expand Down
64 changes: 10 additions & 54 deletions frontend/app/tab/tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ import { Button } from "@/element/button";
import { ContextMenuModel } from "@/store/contextmenu";
import { fireAndForget } from "@/util/util";
import clsx from "clsx";
import { useAtomValue } from "jotai";
import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
import { ObjectService } from "../store/services";
import { makeORef, useWaveObjectValue } from "../store/wos";
import "./tab.scss";
import { TabBarModel } from "./tabbar-model";

interface TabProps {
id: string;
Expand All @@ -23,39 +21,21 @@ interface TabProps {
isDragging: boolean;
tabWidth: number;
isNew: boolean;
isPinned: boolean;
onSelect: () => void;
onClose: (event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null) => void;
onDragStart: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
onLoaded: () => void;
onPinChange: () => void;
}

const Tab = memo(
forwardRef<HTMLDivElement, TabProps>(
(
{
id,
active,
isPinned,
isBeforeActive,
isDragging,
tabWidth,
isNew,
onLoaded,
onSelect,
onClose,
onDragStart,
onPinChange,
},
{ id, active, isBeforeActive, isDragging, tabWidth, isNew, onLoaded, onSelect, onClose, onDragStart },
ref
) => {
const [tabData, _] = useWaveObjectValue<Tab>(makeORef("tab", id));
const [originalName, setOriginalName] = useState("");
const [isEditable, setIsEditable] = useState(false);
const [isJiggling, setIsJiggling] = useState(false);

const jiggleTrigger = useAtomValue(TabBarModel.getInstance().jigglePinAtom);

const editableRef = useRef<HTMLDivElement>(null);
const editableTimeoutRef = useRef<NodeJS.Timeout>(null);
Expand Down Expand Up @@ -148,16 +128,6 @@ const Tab = memo(
}
}, [isNew, tabWidth]);

useEffect(() => {
if (active && isPinned && jiggleTrigger > 0) {
setIsJiggling(true);
const timeout = setTimeout(() => {
setIsJiggling(false);
}, 500);
return () => clearTimeout(timeout);
}
}, [jiggleTrigger, active, isPinned]);

// Prevent drag from being triggered on mousedown
const handleMouseDownOnClose = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
event.stopPropagation();
Expand All @@ -167,7 +137,6 @@ const Tab = memo(
(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
e.preventDefault();
let menu: ContextMenuItem[] = [
{ label: isPinned ? "Unpin Tab" : "Pin Tab", click: () => onPinChange() },
{ label: "Rename Tab", click: () => handleRenameTab(null) },
{
label: "Copy TabId",
Expand Down Expand Up @@ -210,7 +179,7 @@ const Tab = memo(
menu.push({ label: "Close Tab", click: () => onClose(null) });
ContextMenuModel.showContextMenu(menu, e);
},
[onPinChange, handleRenameTab, id, onClose, isPinned]
[handleRenameTab, id, onClose]
);

return (
Expand Down Expand Up @@ -239,27 +208,14 @@ const Tab = memo(
>
{tabData?.name}
</div>
{isPinned ? (
<Button
className={clsx("ghost grey pin", { jiggling: isJiggling })}
onClick={(e) => {
e.stopPropagation();
onPinChange();
}}
title="Unpin Tab"
>
<i className="fa fa-solid fa-thumbtack" />
</Button>
) : (
<Button
className="ghost grey close"
onClick={onClose}
onMouseDown={handleMouseDownOnClose}
title="Close Tab"
>
<i className="fa fa-solid fa-xmark" />
</Button>
)}
<Button
className="ghost grey close"
onClick={onClose}
onMouseDown={handleMouseDownOnClose}
title="Close Tab"
>
<i className="fa fa-solid fa-xmark" />
</Button>
</div>
</div>
);
Expand Down
Loading
Loading