mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-11-28 05:00:26 +08:00
* Kebab Menu + Switch App Functionality * Updated Secrets Tab (still more to do, but closer) * Better Error Handling (Switch to Secrets Tab) * Add AppInit functionality to Tsunami (fixes initialization issues and races with embedded assets, and error handling) * Tsunami - ListStaticFiles * Tsunami - More Robust Manifest generation (even when there are extra stdout messages)
136 lines
4.8 KiB
TypeScript
136 lines
4.8 KiB
TypeScript
// Copyright 2025, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import { AIPanel } from "@/app/aipanel/aipanel";
|
|
import { RpcApi } from "@/app/store/wshclientapi";
|
|
import { TabRpcClient } from "@/app/store/wshrpcutil";
|
|
import { BuilderAppPanel } from "@/builder/builder-apppanel";
|
|
import { BuilderBuildPanel } from "@/builder/builder-buildpanel";
|
|
import { BuilderFocusManager } from "@/builder/store/builder-focusmanager";
|
|
import { atoms } from "@/store/global";
|
|
import { cn } from "@/util/util";
|
|
import { useAtomValue } from "jotai";
|
|
import { memo, useCallback, useEffect, useState } from "react";
|
|
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
|
|
import { debounce } from "throttle-debounce";
|
|
|
|
const DefaultLayoutPercentages = {
|
|
chat: 50,
|
|
app: 80,
|
|
build: 20,
|
|
};
|
|
|
|
const BuilderWorkspace = memo(() => {
|
|
const builderId = useAtomValue(atoms.builderId);
|
|
const [layout, setLayout] = useState<Record<string, number>>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const focusType = useAtomValue(BuilderFocusManager.getInstance().focusType);
|
|
const isAppFocused = focusType === "app";
|
|
|
|
useEffect(() => {
|
|
const loadLayout = async () => {
|
|
if (!builderId) {
|
|
setLayout(DefaultLayoutPercentages);
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const rtInfo = await RpcApi.GetRTInfoCommand(TabRpcClient, {
|
|
oref: `builder:${builderId}`,
|
|
});
|
|
if (rtInfo?.["builder:layout"]) {
|
|
setLayout(rtInfo["builder:layout"] as Record<string, number>);
|
|
} else {
|
|
setLayout(DefaultLayoutPercentages);
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to load builder layout:", error);
|
|
setLayout(DefaultLayoutPercentages);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
loadLayout();
|
|
}, [builderId]);
|
|
|
|
const saveLayout = useCallback(
|
|
debounce(500, (newLayout: Record<string, number>) => {
|
|
if (!builderId) return;
|
|
|
|
RpcApi.SetRTInfoCommand(TabRpcClient, {
|
|
oref: `builder:${builderId}`,
|
|
data: {
|
|
"builder:layout": newLayout,
|
|
},
|
|
}).catch((error) => {
|
|
console.error("Failed to save builder layout:", error);
|
|
});
|
|
}),
|
|
[builderId]
|
|
);
|
|
|
|
const handleHorizontalLayout = useCallback(
|
|
(sizes: number[]) => {
|
|
const newLayout = { ...layout, chat: sizes[0] };
|
|
setLayout(newLayout);
|
|
saveLayout(newLayout);
|
|
},
|
|
[layout, saveLayout]
|
|
);
|
|
|
|
const handleVerticalLayout = useCallback(
|
|
(sizes: number[]) => {
|
|
const newLayout = { ...layout, app: sizes[0], build: sizes[1] };
|
|
setLayout(newLayout);
|
|
saveLayout(newLayout);
|
|
},
|
|
[layout, saveLayout]
|
|
);
|
|
|
|
if (isLoading || !layout) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className="flex-1 overflow-hidden">
|
|
<PanelGroup direction="horizontal" onLayout={handleHorizontalLayout}>
|
|
<Panel defaultSize={layout.chat} minSize={20}>
|
|
<AIPanel />
|
|
</Panel>
|
|
<PanelResizeHandle className="w-0.5 bg-transparent hover:bg-gray-500/20 transition-colors" />
|
|
<Panel defaultSize={100 - layout.chat} minSize={20}>
|
|
<div
|
|
className={cn(
|
|
"flex flex-col relative h-full",
|
|
isAppFocused ? "border-2 border-accent" : "border-2 border-transparent"
|
|
)}
|
|
style={{
|
|
borderBottomRightRadius: 8,
|
|
}}
|
|
>
|
|
<PanelGroup direction="vertical" onLayout={handleVerticalLayout}>
|
|
<Panel defaultSize={layout.app} minSize={20}>
|
|
<BuilderAppPanel />
|
|
</Panel>
|
|
<PanelResizeHandle className="h-0.5 bg-transparent hover:bg-gray-500/20 transition-colors" />
|
|
<Panel
|
|
defaultSize={layout.build}
|
|
minSize={20}
|
|
maxSize={50}
|
|
style={{ borderBottomRightRadius: 8 }}
|
|
>
|
|
<BuilderBuildPanel />
|
|
</Panel>
|
|
</PanelGroup>
|
|
</div>
|
|
</Panel>
|
|
</PanelGroup>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
BuilderWorkspace.displayName = "BuilderWorkspace";
|
|
|
|
export { BuilderWorkspace };
|