diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 888a7a9..749f931 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -941,13 +941,19 @@ pub fn create_agent( } else { None }; let model_display = model_value.flatten(); - // If independent, create a workspace directory for this agent + // If independent, create a dedicated workspace directory; + // otherwise inherit the default workspace so the gateway doesn't auto-create one. let workspace = if independent.unwrap_or(false) { let ws_dir = paths.base_dir.join("workspaces").join(&agent_id); fs::create_dir_all(&ws_dir).map_err(|e| e.to_string())?; let ws_path = ws_dir.to_string_lossy().to_string(); Some(ws_path) - } else { None }; + } else { + cfg.pointer("/agents/defaults/workspace") + .or_else(|| cfg.pointer("/agents/default/workspace")) + .and_then(Value::as_str) + .map(|s| s.to_string()) + }; // Build agent entry let mut agent_obj = serde_json::Map::new(); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index aa2d7da..5f31326 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -8,6 +8,7 @@ use crate::commands::{ preview_rollback, rollback, run_doctor_command, resolve_api_keys, read_raw_config, resolve_full_api_key, open_url, chat_via_openclaw, backup_before_upgrade, list_backups, restore_from_backup, delete_backup, + list_channels_minimal, list_discord_guild_channels, refresh_discord_guild_channels, restart_gateway, @@ -66,6 +67,7 @@ pub fn run() { list_backups, restore_from_backup, delete_backup, + list_channels_minimal, list_discord_guild_channels, refresh_discord_guild_channels, restart_gateway, diff --git a/src/App.tsx b/src/App.tsx index 46a43fe..e4442cc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,17 +6,10 @@ import { History } from "./pages/History"; import { Settings } from "./pages/Settings"; import { Channels } from "./pages/Channels"; import { Chat } from "./components/Chat"; -import { GlobalLoading } from "./components/GlobalLoading"; import { DiffViewer } from "./components/DiffViewer"; import { api } from "./lib/api"; import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; -import { - Sheet, - SheetContent, - SheetHeader, - SheetTitle, -} from "@/components/ui/sheet"; import { Dialog, DialogContent, @@ -39,12 +32,19 @@ import type { DiscordGuildChannel } from "./lib/types"; type Route = "home" | "recipes" | "cook" | "history" | "channels" | "settings"; +interface ToastItem { + id: number; + message: string; + type: "success" | "error"; +} + +let toastIdCounter = 0; + export function App() { const [route, setRoute] = useState("home"); const [recipeId, setRecipeId] = useState(null); const [recipeSource, setRecipeSource] = useState(undefined); const [discordGuildChannels, setDiscordGuildChannels] = useState([]); - const [globalLoading, setGlobalLoading] = useState(null); const [chatOpen, setChatOpen] = useState(false); // Config dirty state @@ -56,19 +56,19 @@ export function App() { const [applying, setApplying] = useState(false); const [applyError, setApplyError] = useState(""); const [configVersion, setConfigVersion] = useState(0); - const [toast, setToast] = useState<{ message: string; type: "success" | "error" } | null>(null); + const [toasts, setToasts] = useState([]); const pollRef = useRef | null>(null); // Establish baseline on startup useEffect(() => { - api.saveConfigBaseline().catch(() => {}); + api.saveConfigBaseline().catch((e) => console.error("Failed to save config baseline:", e)); }, []); // Poll for dirty state const checkDirty = useCallback(() => { api.checkConfigDirty() .then((state) => setDirty(state.dirty)) - .catch(() => {}); + .catch((e) => console.error("Failed to check config dirty state:", e)); }, []); useEffect(() => { @@ -84,17 +84,9 @@ export function App() { if (!localStorage.getItem("clawpal_profiles_extracted")) { api.extractModelProfilesFromConfig() .then(() => localStorage.setItem("clawpal_profiles_extracted", "1")) - .catch(() => {}); + .catch((e) => console.error("Failed to extract model profiles:", e)); } - api.listDiscordGuildChannels().then(setDiscordGuildChannels).catch(() => {}); - }, []); - - const refreshDiscord = useCallback(() => { - setGlobalLoading("Resolving Discord channel names..."); - api.refreshDiscordGuildChannels() - .then(setDiscordGuildChannels) - .catch(() => {}) - .finally(() => setGlobalLoading(null)); + api.listDiscordGuildChannels().then(setDiscordGuildChannels).catch((e) => console.error("Failed to load Discord channels:", e)); }, []); const bumpConfigVersion = useCallback(() => { @@ -110,13 +102,20 @@ export function App() { setApplyError(""); setShowApplyDialog(true); }) - .catch(() => {}); + .catch((e) => console.error("Failed to load config diff:", e)); }; - const showToast = (message: string, type: "success" | "error" = "success") => { - setToast({ message, type }); - setTimeout(() => setToast(null), 3000); - }; + const showToast = useCallback((message: string, type: "success" | "error" = "success") => { + const id = ++toastIdCounter; + setToasts((prev) => [...prev, { id, message, type }]); + setTimeout(() => { + setToasts((prev) => prev.filter((t) => t.id !== id)); + }, 3000); + }, []); + + const dismissToast = useCallback((id: number) => { + setToasts((prev) => prev.filter((t) => t.id !== id)); + }, []); const handleApplyConfirm = () => { setApplying(true); @@ -145,7 +144,6 @@ export function App() { return ( <> - {globalLoading && }
-
+
+ {/* Chat toggle -- top-right corner */} + {!chatOpen && ( + + )} + {route === "home" && ( )} {route === "recipes" && ( @@ -272,8 +271,7 @@ export function App() { {route === "channels" && ( )} {route === "history" && } @@ -281,19 +279,27 @@ export function App() { )}
-
- {/* Chat Drawer */} - - - - Chat - -
- -
-
-
+ {/* Chat Panel -- inline, pushes main content */} + {chatOpen && ( + + )} + {/* Apply Changes Dialog */} @@ -340,13 +346,26 @@ export function App() { - {/* Toast */} - {toast && ( -
- {toast.message} + {/* Toast Stack */} + {toasts.length > 0 && ( +
+ {toasts.map((toast) => ( +
+ {toast.message} + +
+ ))}
)} diff --git a/src/components/Chat.tsx b/src/components/Chat.tsx index c1f8c8e..feb2216 100644 --- a/src/components/Chat.tsx +++ b/src/components/Chat.tsx @@ -48,7 +48,7 @@ export function Chat() { const bottomRef = useRef(null); useEffect(() => { - api.listAgentIds().then(setAgents).catch(() => {}); + api.listAgentIds().then(setAgents).catch((e) => console.error("Failed to load agent IDs:", e)); }, []); useEffect(() => { @@ -90,7 +90,7 @@ export function Chat() {
setAgentId(e.target.value)} + /> +

+ Letters, numbers, hyphens, and underscores only. +

+
+
+ + +
+
+ { + const val = checked === true; + setIndependent(val); + if (!val) { + setDisplayName(""); + setEmoji(""); + setPersona(""); + } + }} + /> + +
+ {independent && ( + <> +
+ + setDisplayName(e.target.value)} + /> +
+
+ + setEmoji(e.target.value)} + className="w-20" + /> +
+
+ +