Skip to content

Commit a83480b

Browse files
committed
AI button, panel width, other UI
1 parent 1ddd5a4 commit a83480b

5 files changed

Lines changed: 59 additions & 34 deletions

File tree

frontend/app/aipanel/aipanelheader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const AIPanelHeader = memo(({ onClose, model }: AIPanelHeaderProps) => {
4949
{onClose && (
5050
<button
5151
onClick={onClose}
52-
className="text-gray-400 hover:text-white cursor-pointer transition-colors p-1 rounded flex-shrink-0 ml-1"
52+
className="text-gray-400 hover:text-white cursor-pointer transition-colors p-1 rounded flex-shrink-0 ml-1 focus:outline-none"
5353
title="Close AI Panel"
5454
>
5555
<i className="fa fa-xmark"></i>

frontend/app/tab/tabbar.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { Button } from "@/app/element/button";
55
import { modalsModel } from "@/app/store/modalmodel";
6+
import { workspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
67
import { WindowDrag } from "@/element/windowdrag";
78
import { deleteLayoutModelForTab } from "@/layout/index";
89
import { atoms, createTab, getApi, globalStore, isDev, setActiveTab } from "@/store/global";
@@ -164,7 +165,6 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
164165
const osInstanceRef = useRef<OverlayScrollbars>(null);
165166
const draggerLeftRef = useRef<HTMLDivElement>(null);
166167
const workspaceSwitcherRef = useRef<HTMLDivElement>(null);
167-
const devLabelRef = useRef<HTMLDivElement>(null);
168168
const appMenuButtonRef = useRef<HTMLDivElement>(null);
169169
const tabWidthRef = useRef<number>(TAB_DEFAULT_WIDTH);
170170
const scrollableRef = useRef<boolean>(false);
@@ -232,16 +232,14 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
232232
const configErrorWidth = configErrorButtonRef.current?.getBoundingClientRect().width ?? 0;
233233
const appMenuButtonWidth = appMenuButtonRef.current?.getBoundingClientRect().width ?? 0;
234234
const workspaceSwitcherWidth = workspaceSwitcherRef.current?.getBoundingClientRect().width ?? 0;
235-
const devLabelWidth = devLabelRef.current?.getBoundingClientRect().width ?? 0;
236235

237236
const nonTabElementsWidth =
238237
windowDragLeftWidth +
239238
addBtnWidth +
240239
updateStatusLabelWidth +
241240
configErrorWidth +
242241
appMenuButtonWidth +
243-
workspaceSwitcherWidth +
244-
devLabelWidth;
242+
workspaceSwitcherWidth;
245243
const spaceForTabs = tabbarWrapperWidth - nonTabElementsWidth;
246244

247245
const numberOfTabs = tabIds.length;
@@ -635,10 +633,19 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
635633
getApi().showContextMenu(workspace.oid);
636634
}
637635

636+
function onSparklesClick() {
637+
const currentVisible = workspaceLayoutModel.getAIPanelVisible();
638+
workspaceLayoutModel.setAIPanelVisible(!currentVisible);
639+
}
640+
638641
const tabsWrapperWidth = tabIds.length * tabWidthRef.current;
639-
const devLabel = isDev() ? (
640-
<div ref={devLabelRef} className="dev-label">
641-
<i className="fa fa-brands fa-dev fa-fw" />
642+
const waveaiButton = isDev() ? (
643+
<div
644+
className="flex h-[26px] px-3 justify-end items-center gap-3 rounded-md mr-1 box-border text-accent cursor-pointer bg-hover hover:bg-hoverbg transition-colors"
645+
style={{ WebkitAppRegion: "no-drag" } as React.CSSProperties}
646+
onClick={onSparklesClick}
647+
>
648+
<i className="fa fa-sparkles" />
642649
</div>
643650
) : undefined;
644651
const appMenuButton =
@@ -658,7 +665,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
658665
<div ref={tabbarWrapperRef} className="tab-bar-wrapper">
659666
<WindowDrag ref={draggerLeftRef} className="left" />
660667
{appMenuButton}
661-
{devLabel}
668+
{waveaiButton}
662669
<WorkspaceSwitcher ref={workspaceSwitcherRef} />
663670
<div className="tab-bar" ref={tabBarRef} data-overlayscrollbars-initialize>
664671
<div className="tabs-wrapper" ref={tabsWrapperRef} style={{ width: `${tabsWrapperWidth}px` }}>

frontend/app/tab/workspaceswitcher.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
align-items: center;
1010
gap: 12px;
1111
border-radius: 6px;
12-
margin-right: 13px;
12+
margin-right: 6px;
1313
box-sizing: border-box;
1414
background-color: rgb(from var(--main-text-color) r g b / 0.1) !important;
1515

frontend/app/workspace/widgets.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { Tooltip } from "@/app/element/tooltip";
5-
import { NotificationPopover } from "@/app/notification/notificationpopover";
65
import { ContextMenuModel } from "@/app/store/contextmenu";
76
import { RpcApi } from "@/app/store/wshclientapi";
87
import { TabRpcClient } from "@/app/store/wshrpcutil";
@@ -106,12 +105,12 @@ const Widgets = memo(() => {
106105

107106
if (normalHeight > containerHeight - gracePeriod) {
108107
newMode = "compact";
109-
108+
110109
// Calculate total widget count for supercompact check
111110
const totalWidgets = (widgets?.length || 0) + (showHelp ? 2 : 0);
112111
const minHeightPerWidget = 32;
113112
const requiredHeight = totalWidgets * minHeightPerWidget;
114-
113+
115114
if (requiredHeight > containerHeight) {
116115
newMode = "supercompact";
117116
}
@@ -194,7 +193,9 @@ const Widgets = memo(() => {
194193
{mode === "supercompact" ? (
195194
<>
196195
<div className="grid grid-cols-2 gap-0 w-full">
197-
{widgets?.map((data, idx) => <Widget key={`widget-${idx}`} widget={data} mode={mode} />)}
196+
{widgets?.map((data, idx) => (
197+
<Widget key={`widget-${idx}`} widget={data} mode={mode} />
198+
))}
198199
</div>
199200
<div className="flex-grow" />
200201
{showHelp ? (
@@ -206,7 +207,9 @@ const Widgets = memo(() => {
206207
</>
207208
) : (
208209
<>
209-
{widgets?.map((data, idx) => <Widget key={`widget-${idx}`} widget={data} mode={mode} />)}
210+
{widgets?.map((data, idx) => (
211+
<Widget key={`widget-${idx}`} widget={data} mode={mode} />
212+
))}
210213
<div className="flex-grow" />
211214
{showHelp ? (
212215
<>
@@ -216,7 +219,14 @@ const Widgets = memo(() => {
216219
) : null}
217220
</>
218221
)}
219-
{isDev() ? <NotificationPopover /> : null}
222+
{isDev() ? (
223+
<div
224+
className="dev-label flex justify-center items-center w-full py-1 text-accent text-[30px]"
225+
title="Running Wave Dev Build"
226+
>
227+
<i className="fa fa-brands fa-dev fa-fw" />
228+
</div>
229+
) : null}
220230
</div>
221231

222232
<div
@@ -233,9 +243,15 @@ const Widgets = memo(() => {
233243
<Widget key="measurement-help" widget={helpWidget} mode="normal" />
234244
</>
235245
) : null}
236-
{isDev() ? <NotificationPopover /> : null}
246+
{isDev() ? (
247+
<div
248+
className="dev-label flex justify-center items-center w-full py-1 text-accent text-[30px]"
249+
title="Running Wave Dev Build"
250+
>
251+
<i className="fa fa-brands fa-dev fa-fw" />
252+
</div>
253+
) : null}
237254
</div>
238-
239255
</>
240256
);
241257
});

frontend/app/workspace/workspace-layout-model.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
// Copyright 2025, Command Line Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { isDev } from "@/store/global";
5-
import { ImperativePanelGroupHandle, ImperativePanelHandle } from "react-resizable-panels";
6-
import * as jotai from "jotai";
74
import { getTabMetaKeyAtom } from "@/app/store/global";
5+
import { globalStore } from "@/app/store/jotaiStore";
6+
import * as WOS from "@/app/store/wos";
87
import { RpcApi } from "@/app/store/wshclientapi";
98
import { TabRpcClient } from "@/app/store/wshrpcutil";
10-
import * as WOS from "@/app/store/wos";
11-
import { globalStore } from "@/app/store/jotaiStore";
12-
import { atoms } from "@/store/global";
9+
import { atoms, isDev } from "@/store/global";
10+
import * as jotai from "jotai";
1311
import { debounce } from "lodash-es";
12+
import { ImperativePanelGroupHandle, ImperativePanelHandle } from "react-resizable-panels";
1413

1514
const AIPANEL_DEFAULTWIDTH = 300;
1615
const AIPANEL_MINWIDTH = 250;
@@ -21,17 +20,17 @@ class WorkspaceLayoutModel {
2120
panelGroupRef: ImperativePanelGroupHandle | null;
2221
inResize: boolean;
2322
private aiPanelVisible: boolean;
24-
private aiPanelWidth: number;
23+
private aiPanelWidth: number | null;
2524
private debouncedPersistWidth: (width: number) => void;
2625
private initialized: boolean = false;
27-
26+
2827
constructor() {
2928
this.aiPanelRef = null;
3029
this.panelGroupRef = null;
3130
this.inResize = false;
3231
this.aiPanelVisible = isDev();
33-
this.aiPanelWidth = AIPANEL_DEFAULTWIDTH;
34-
32+
this.aiPanelWidth = null;
33+
3534
this.debouncedPersistWidth = debounce((width: number) => {
3635
try {
3736
RpcApi.SetMetaCommand(TabRpcClient, {
@@ -43,15 +42,15 @@ class WorkspaceLayoutModel {
4342
}
4443
}, 300);
4544
}
46-
45+
4746
private initializeFromTabMeta(): void {
4847
if (this.initialized) return;
4948
this.initialized = true;
50-
49+
5150
try {
5251
const savedVisible = globalStore.get(this.getPanelOpenAtom());
5352
const savedWidth = globalStore.get(this.getPanelWidthAtom());
54-
53+
5554
if (savedVisible != null) {
5655
this.aiPanelVisible = savedVisible;
5756
}
@@ -62,15 +61,15 @@ class WorkspaceLayoutModel {
6261
console.warn("Failed to initialize from tab meta:", e);
6362
}
6463
}
65-
64+
6665
private getTabId(): string {
6766
return globalStore.get(atoms.staticTabId);
6867
}
69-
68+
7069
private getPanelOpenAtom(): jotai.Atom<boolean> {
7170
return getTabMetaKeyAtom(this.getTabId(), "waveai:panelopen");
7271
}
73-
72+
7473
private getPanelWidthAtom(): jotai.Atom<number> {
7574
return getTabMetaKeyAtom(this.getTabId(), "waveai:panelwidth");
7675
}
@@ -133,6 +132,9 @@ class WorkspaceLayoutModel {
133132

134133
getAIPanelWidth(): number {
135134
this.initializeFromTabMeta();
135+
if (this.aiPanelWidth == null) {
136+
this.aiPanelWidth = Math.max(AIPANEL_DEFAULTWIDTH, window.innerWidth / 3);
137+
}
136138
return this.aiPanelWidth;
137139
}
138140

0 commit comments

Comments
 (0)