refactor: calling service related interfaces (#831)

* chore: server

* chore: add

* refactor: calling service related interfaces

* chore: server list

* chore: add

* chore: add

* update

* chore: remove logs

* focs: update notes

* docs: remove server doc

---------

Co-authored-by: ayang <473033518@qq.com>
This commit is contained in:
BiggerRain
2025-07-31 15:59:35 +08:00
committed by GitHub
parent 855fb2a168
commit b833769c25
43 changed files with 719 additions and 425 deletions

View File

@@ -23,6 +23,7 @@ Information about release notes of Coco App is provided here.
### ✈️ Improvements
- refactor: calling service related interfaces #831
- refactor: split query_coco_fusion() #836
- chore: web component loading font icon #838
- chore: delete unused code files and dependencies #841

View File

@@ -86,6 +86,12 @@ export const Get = <T>(
} else {
res = result?.data as FcResponse<T>;
}
// web component log
infoLog({
username: "@/api/axiosRequest.ts",
logName: url,
})(res);
resolve([null, res as FcResponse<T>]);
})
.catch((err) => {
@@ -103,7 +109,7 @@ export const Post = <T>(
return new Promise((resolve) => {
const appStore = JSON.parse(localStorage.getItem("app-store") || "{}");
let baseURL = appStore.state?.endpoint_http
let baseURL = appStore.state?.endpoint_http;
if (!baseURL || baseURL === "undefined") {
baseURL = "";
}

View File

@@ -1,5 +1,4 @@
import { invoke } from "@tauri-apps/api/core";
import { emit } from "@tauri-apps/api/event";
import {
Server,
@@ -18,17 +17,42 @@ import {
import { useAppStore } from "@/stores/appStore";
import { useAuthStore } from "@/stores/authStore";
import { useConnectStore } from "@/stores/connectStore";
import { SETTINGS_WINDOW_LABEL } from "@/constants";
import platformAdapter from "@/utils/platformAdapter";
export function handleLogout(serverId?: string) {
const setIsCurrentLogin = useAuthStore.getState().setIsCurrentLogin;
const { currentService, setCurrentService, serverList, setServerList } =
export async function getCurrentWindowService() {
const currentService = useConnectStore.getState().currentService;
const cloudSelectService = useConnectStore.getState().cloudSelectService;
const windowLabel = await platformAdapter.getCurrentWindowLabel();
return windowLabel === SETTINGS_WINDOW_LABEL
? cloudSelectService
: currentService;
}
export async function setCurrentWindowService(service: any) {
const windowLabel = await platformAdapter.getCurrentWindowLabel();
const { setCurrentService, setCloudSelectService } =
useConnectStore.getState();
const id = serverId || currentService?.id;
return windowLabel === SETTINGS_WINDOW_LABEL
? setCloudSelectService(service)
: setCurrentService(service);
}
export async function handleLogout(serverId?: string) {
const setIsCurrentLogin = useAuthStore.getState().setIsCurrentLogin;
const { serverList, setServerList } = useConnectStore.getState();
const service = await getCurrentWindowService();
const id = serverId || service?.id;
if (!id) return;
// Update the status first
setIsCurrentLogin(false);
emit("login_or_logout", false);
if (currentService?.id === id) {
setCurrentService({ ...currentService, profile: null });
if (service?.id === id) {
await setCurrentWindowService({ ...service, profile: null });
}
const updatedServerList = serverList.map((server) =>
server.id === id ? { ...server, profile: null } : server
@@ -55,13 +79,14 @@ async function invokeWithErrorHandler<T>(
args?: Record<string, any>
): Promise<T> {
const isCurrentLogin = useAuthStore.getState().isCurrentLogin;
const currentService = useConnectStore.getState().currentService;
const service = await getCurrentWindowService();
// Not logged in
console.log(command, isCurrentLogin, currentService?.profile);
// console.log("isCurrentLogin", command, isCurrentLogin);
if (
!WHITELIST_SERVERS.includes(command) &&
(!isCurrentLogin || !currentService?.profile)
(!isCurrentLogin || !service?.profile)
) {
console.error("This command requires authentication");
throw new Error("This command requires authentication");
@@ -89,6 +114,18 @@ async function invokeWithErrorHandler<T>(
}
}
// Server Data log
let parsedResult = result;
let logData = result;
if (typeof result === "string") {
parsedResult = JSON.parse(result);
logData = parsedResult;
}
infoLog({
username: "@/commands/servers.ts",
logName: command,
})(logData);
return result;
} catch (error: any) {
const errorMessage = error || "Command execution failed";
@@ -380,4 +417,4 @@ export const query_coco_fusion = (payload: {
export const get_app_search_source = () => {
return invokeWithErrorHandler<void>("get_app_search_source");
};
};

View File

@@ -57,8 +57,6 @@ export const AssistantFetcher = ({
let assistantList = response?.hits?.hits ?? [];
console.log("assistantList", assistantList);
if (
!currentAssistant?._id ||
currentService?.id !== lastServerId.current

View File

@@ -17,6 +17,8 @@ import { Server as IServer } from "@/types/server";
import StatusIndicator from "@/components/Cloud/StatusIndicator";
import { useAuthStore } from "@/stores/authStore";
import { useSearchStore } from "@/stores/searchStore";
import { useServers } from "@/hooks/useServers";
import { getCurrentWindowService, setCurrentWindowService } from "@/commands";
interface ServerListProps {
clearChat: () => void;
@@ -25,17 +27,20 @@ interface ServerListProps {
export function ServerList({ clearChat }: ServerListProps) {
const { t } = useTranslation();
const isCurrentLogin = useAuthStore((state) => state.isCurrentLogin);
const setIsCurrentLogin = useAuthStore((state) => state.setIsCurrentLogin);
const serviceList = useShortcutsStore((state) => state.serviceList);
const serviceListShortcut = useShortcutsStore(
(state) => state.serviceListShortcut
);
const setEndpoint = useAppStore((state) => state.setEndpoint);
const setCurrentService = useConnectStore((state) => state.setCurrentService);
const isTauri = useAppStore((state) => state.isTauri);
const currentService = useConnectStore((state) => state.currentService);
const cloudSelectService = useConnectStore((state) => {
return state.cloudSelectService;
});
const { setMessages } = useChatStore();
const [serverList, setServerList] = useState<IServer[]>([]);
const [list, setList] = useState<IServer[]>([]);
const [isRefreshing, setIsRefreshing] = useState(false);
const [highlightId, setHighlightId] = useState<string>("");
@@ -49,44 +54,49 @@ export function ServerList({ clearChat }: ServerListProps) {
const popoverRef = useRef<HTMLDivElement>(null);
const serverListButtonRef = useRef<HTMLButtonElement>(null);
const fetchServers = useCallback(
async (resetSelection: boolean) => {
platformAdapter.commands("list_coco_servers").then((res: any) => {
console.log("list_coco_servers", res);
if (!Array.isArray(res)) {
// If res is not an array, it might be an error message or something else.
// Log it and don't proceed.
// console.log("list_coco_servers did not return an array:", res);
setServerList([]); // Clear the list or handle as appropriate
return;
}
const enabledServers = (res as IServer[])?.filter(
(server) => server.enabled && server.available
);
const { refreshServerList } = useServers();
const serverList = useConnectStore((state) => state.serverList);
setServerList(enabledServers);
const switchServer = async (server: IServer) => {
if (!server) return;
try {
// Switch UI first, then switch server connection
await setCurrentWindowService(server);
setEndpoint(server.endpoint);
setMessages(""); // Clear previous messages
clearChat();
//
if (!server.public && !server.profile) {
setIsCurrentLogin(false);
return;
}
//
setIsCurrentLogin(true);
} catch (error) {
console.error("switchServer:", error);
}
};
if (resetSelection && enabledServers.length > 0) {
const currentServiceExists = enabledServers.find(
(server) => server.id === currentService?.id
);
const fetchServers = useCallback(async () => {
const service = await getCurrentWindowService();
if (currentServiceExists) {
switchServer(currentServiceExists);
} else {
switchServer(enabledServers[enabledServers.length - 1]);
}
}
const enabledServers = serverList.filter(
(server) => server.enabled && server.available
);
setList(enabledServers);
if (enabledServers.length > 0) {
const serviceExists = enabledServers.find((server) => {
return server.id === service?.id;
});
},
[currentService?.id]
);
useEffect(() => {
if (!isTauri) return;
fetchServers(true);
}, [currentService?.enabled]);
if (serviceExists) {
switchServer(serviceExists);
} else {
switchServer(enabledServers[enabledServers.length - 1]);
}
}
}, [currentService?.id, cloudSelectService?.id, serverList]);
useEffect(() => {
if (!askAiServerId || serverList.length === 0) return;
@@ -104,25 +114,12 @@ export function ServerList({ clearChat }: ServerListProps) {
useEffect(() => {
if (!isTauri) return;
fetchServers(true);
const unlisten = platformAdapter.listenEvent("login_or_logout", (event) => {
//console.log("Login or Logout:", currentService, event.payload);
if (event.payload !== isCurrentLogin) {
setIsCurrentLogin(!!event.payload);
}
fetchServers(true);
});
return () => {
// Cleanup logic if needed
unlisten.then((fn) => fn());
};
}, []);
fetchServers();
}, [serverList]);
const handleRefresh = async () => {
setIsRefreshing(true);
await fetchServers(false);
await refreshServerList();
setTimeout(() => setIsRefreshing(false), 1000);
};
@@ -130,29 +127,10 @@ export function ServerList({ clearChat }: ServerListProps) {
platformAdapter.emitEvent("open_settings", "connect");
};
const switchServer = async (server: IServer) => {
if (!server) return;
try {
// Switch UI first, then switch server connection
setCurrentService(server);
setEndpoint(server.endpoint);
setMessages(""); // Clear previous messages
clearChat();
//
if (!server.public && !server.profile) {
setIsCurrentLogin(false);
return;
}
//
setIsCurrentLogin(true);
} catch (error) {
console.error("switchServer:", error);
}
};
useKeyPress(
["uparrow", "downarrow", "enter"],
(event, key) => {
async (event, key) => {
const service = await getCurrentWindowService();
const isClose = isNil(serverListButtonRef.current?.dataset["open"]);
const length = serverList.length;
@@ -162,9 +140,7 @@ export function ServerList({ clearChat }: ServerListProps) {
event.preventDefault();
const currentIndex = serverList.findIndex((server) => {
return (
server.id === (highlightId === "" ? currentService?.id : highlightId)
);
return server.id === (highlightId === "" ? service?.id : highlightId);
});
let nextIndex = currentIndex;
@@ -197,7 +173,7 @@ export function ServerList({ clearChat }: ServerListProps) {
<Popover ref={popoverRef} className="relative">
<PopoverButton ref={serverListButtonRef} className="flex items-center">
<VisibleKey
shortcut={serviceList}
shortcut={serviceListShortcut}
onKeyPress={() => {
serverListButtonRef.current?.click();
}}
@@ -240,8 +216,8 @@ export function ServerList({ clearChat }: ServerListProps) {
</div>
</div>
<div className="space-y-1">
{serverList.length > 0 ? (
serverList.map((server) => (
{list.length > 0 ? (
list.map((server) => (
<div
key={server.id}
onClick={() => switchServer(server)}

View File

@@ -82,8 +82,6 @@ const AudioRecording: FC<AudioRecordingProps> = (props) => {
}
);
console.log("response", response);
const text = response?.results
.flatMap((item: any) => item?.transcription?.transcripts)
.map((item: any) => item?.text?.replace(/<\|[\/\w]+\|>/g, ""))

View File

@@ -1,5 +1,4 @@
import { useEffect, useRef, useState, useCallback } from "react";
import { emit } from "@tauri-apps/api/event";
import { DataSourcesList } from "./DataSourcesList";
import { Sidebar } from "./Sidebar";
@@ -9,6 +8,8 @@ import { useConnectStore } from "@/stores/connectStore";
import ServiceInfo from "./ServiceInfo";
import ServiceAuth from "./ServiceAuth";
import platformAdapter from "@/utils/platformAdapter";
import type { Server } from "@/types/server";
import { useServers } from "@/hooks/useServers";
export default function Cloud() {
const SidebarRef = useRef<{ refreshData: () => void }>(null);
@@ -17,100 +18,63 @@ export default function Cloud() {
const [isConnect, setIsConnect] = useState(true);
const { currentService, setCurrentService, serverList, setServerList } =
useConnectStore();
const {
cloudSelectService,
setCloudSelectService,
serverList,
setServerList,
} = useConnectStore();
const [refreshLoading, setRefreshLoading] = useState(false);
const { addServer, refreshServerList } = useServers();
// fetch the servers
useEffect(() => {
fetchServers(true);
}, []);
fetchServers();
}, [serverList]);
useEffect(() => {
// console.log("currentService", currentService);
setRefreshLoading(false);
setIsConnect(true);
}, [JSON.stringify(currentService)]);
}, [cloudSelectService?.id]);
const fetchServers = async (resetSelection: boolean) => {
platformAdapter
.commands("list_coco_servers")
.then((res: any) => {
if (errors.length > 0) {
res = (res || []).map((item: any) => {
if (item.id === currentService?.id) {
item.health = {
services: null,
status: null,
};
}
return item;
});
const fetchServers = useCallback(async () => {
let res = serverList;
if (errors.length > 0) {
res = res.map((item: Server) => {
if (item.id === cloudSelectService?.id) {
item.health = {
services: item.health?.services || {},
status: item.health?.status || "red",
};
}
console.log("list_coco_servers", res);
setServerList(res);
if (resetSelection && res.length > 0) {
const matched = res.find((server: any) => {
return server.id === currentService?.id;
});
if (matched) {
setCurrentService(matched);
} else {
setCurrentService(res[res.length - 1]);
}
}
})
};
const addServer = (endpointLink: string) => {
if (!endpointLink) {
throw new Error("Endpoint is required");
}
if (
!endpointLink.startsWith("http://") &&
!endpointLink.startsWith("https://")
) {
throw new Error("Invalid Endpoint");
}
setRefreshLoading(true);
return platformAdapter
.commands("add_coco_server", endpointLink)
.then((res: any) => {
// console.log("add_coco_server", res);
fetchServers(false).then((r) => {
console.log("fetchServers", r);
setCurrentService(res);
});
})
.finally(() => {
setRefreshLoading(false);
return item;
});
};
}
setServerList(res);
if (res.length > 0) {
const matched = res.find((server: any) => {
return server.id === cloudSelectService?.id;
});
if (matched) {
setCloudSelectService(matched);
} else {
setCloudSelectService(res[res.length - 1]);
}
}
}, [serverList, errors, cloudSelectService]);
const refreshClick = useCallback(
(id: string) => {
async (id: string) => {
setRefreshLoading(true);
platformAdapter
.commands("refresh_coco_server_info", id)
.then((res: any) => {
console.log("refresh_coco_server_info", id, res);
fetchServers(false).then((r) => {
console.log("fetchServers", r);
});
// update currentService
setCurrentService(res);
emit("login_or_logout", true);
})
.finally(() => {
setRefreshLoading(false);
});
await platformAdapter.commands("refresh_coco_server_info", id);
await refreshServerList();
setRefreshLoading(false);
},
[fetchServers]
[refreshServerList]
);
return (
@@ -127,7 +91,6 @@ export default function Cloud() {
<ServiceInfo
refreshLoading={refreshLoading}
refreshClick={refreshClick}
fetchServers={fetchServers}
/>
<ServiceAuth
@@ -135,8 +98,8 @@ export default function Cloud() {
refreshClick={refreshClick}
/>
{currentService?.profile && currentService?.available ? (
<DataSourcesList server={currentService?.id} />
{cloudSelectService?.profile && cloudSelectService?.available ? (
<DataSourcesList server={cloudSelectService?.id} />
) : null}
</div>
) : (

View File

@@ -21,7 +21,7 @@ export function Connect({ setIsConnect, onAddServer }: ConnectServiceProps) {
};
const onAddServerClick = async (endpoint: string) => {
console.log("onAddServer", endpoint);
//console.log("onAddServer", endpoint);
await onAddServer(endpoint);
setIsConnect(true);
};

View File

@@ -20,7 +20,6 @@ export function DataSourcesList({ server }: { server: string }) {
platformAdapter
.commands("get_connectors_by_server", server)
.then((res: any) => {
// console.log("get_connectors_by_server", res);
setConnectorData(res, server);
})
.finally(() => {});
@@ -29,7 +28,6 @@ export function DataSourcesList({ server }: { server: string }) {
platformAdapter
.commands("datasource_search", { id: server })
.then((res: any) => {
// console.log("datasource_search", res);
setDatasourceData(res, server);
})
.finally(() => {

View File

@@ -14,7 +14,7 @@ import { useConnectStore } from "@/stores/connectStore";
import { useAppStore } from "@/stores/appStore";
import { copyToClipboard } from "@/utils";
import platformAdapter from "@/utils/platformAdapter";
import { handleLogout } from "@/commands/servers";
import { useServers } from "@/hooks/useServers";
interface ServiceAuthProps {
setRefreshLoading: (loading: boolean) => void;
@@ -30,7 +30,9 @@ const ServiceAuth = memo(
const addError = useAppStore((state) => state.addError);
const currentService = useConnectStore((state) => state.currentService);
const cloudSelectService = useConnectStore((state) => state.cloudSelectService);
const { logoutServer } = useServers();
const [loading, setLoading] = useState(false);
@@ -41,7 +43,7 @@ const ServiceAuth = memo(
setSSORequestID(requestID);
// Generate the login URL with the current appUid
const url = `${currentService?.auth_provider?.sso?.url}/?provider=${currentService?.id}&product=coco&request_id=${requestID}`;
const url = `${cloudSelectService?.auth_provider?.sso?.url}/?provider=${cloudSelectService?.id}&product=coco&request_id=${requestID}`;
console.log("Open SSO link, requestID:", ssoRequestID, url);
@@ -50,20 +52,17 @@ const ServiceAuth = memo(
// Start loading state
setLoading(true);
}, [ssoRequestID, loading, currentService]);
}, [ssoRequestID, loading, cloudSelectService]);
const onLogout = useCallback((id: string) => {
setRefreshLoading(true);
platformAdapter
.commands("logout_coco_server", id)
.then((res: any) => {
console.log("logout_coco_server", id, JSON.stringify(res));
handleLogout(id);
})
.finally(() => {
const onLogout = useCallback(
(id: string) => {
setRefreshLoading(true);
logoutServer(id).finally(() => {
setRefreshLoading(false);
});
}, []);
},
[logoutServer]
);
const handleOAuthCallback = useCallback(
async (code: string | null, serverId: string | null) => {
@@ -109,7 +108,7 @@ const ServiceAuth = memo(
return;
}
const serverId = currentService?.id;
const serverId = cloudSelectService?.id;
handleOAuthCallback(code, serverId);
} catch (err) {
console.error("Failed to parse URL:", err);
@@ -162,9 +161,9 @@ const ServiceAuth = memo(
useEffect(() => {
setLoading(false);
}, [currentService]);
}, [cloudSelectService]);
if (!currentService?.auth_provider?.sso?.url) {
if (!cloudSelectService?.auth_provider?.sso?.url) {
return null;
}
@@ -173,10 +172,10 @@ const ServiceAuth = memo(
<h2 className="text-lg font-medium text-gray-900 dark:text-white mb-4">
{t("cloud.accountInfo")}
</h2>
{currentService?.profile ? (
{cloudSelectService?.profile ? (
<UserProfile
server={currentService?.id}
userInfo={currentService?.profile}
server={cloudSelectService?.id}
userInfo={cloudSelectService?.profile}
onLogout={onLogout}
/>
) : (
@@ -190,7 +189,7 @@ const ServiceAuth = memo(
onCancel={() => setLoading(false)}
onCopy={() => {
copyToClipboard(
`${currentService?.auth_provider?.sso?.url}/?provider=${currentService?.id}&product=coco&request_id=${ssoRequestID}`
`${cloudSelectService?.auth_provider?.sso?.url}/?provider=${cloudSelectService?.id}&product=coco&request_id=${ssoRequestID}`
);
}}
/>
@@ -201,7 +200,7 @@ const ServiceAuth = memo(
<button
className="text-xs text-[#0096FB] dark:text-blue-400 block"
onClick={() =>
OpenURLWithBrowser(currentService?.provider?.eula)
OpenURLWithBrowser(cloudSelectService?.provider?.eula)
}
>
{t("cloud.eula")}
@@ -215,7 +214,7 @@ const ServiceAuth = memo(
<button
className="text-xs text-[#0096FB] dark:text-blue-400 block"
onClick={() =>
OpenURLWithBrowser(currentService?.provider?.privacy_policy)
OpenURLWithBrowser(cloudSelectService?.provider?.privacy_policy)
}
>
{t("cloud.privacyPolicy")}

View File

@@ -6,13 +6,13 @@ import { useConnectStore } from "@/stores/connectStore";
interface ServiceBannerProps {}
const ServiceBanner = memo(({}: ServiceBannerProps) => {
const currentService = useConnectStore((state) => state.currentService);
const cloudSelectService = useConnectStore((state) => state.cloudSelectService);
return (
<div className="w-full rounded-[4px] bg-[rgba(229,229,229,1)] dark:bg-gray-800 mb-6">
<img
width="100%"
src={currentService?.provider?.banner || bannerImg}
src={cloudSelectService?.provider?.banner || bannerImg}
alt="banner"
onError={(e) => {
const target = e.target as HTMLImageElement;

View File

@@ -1,4 +1,4 @@
import { memo, useCallback } from "react";
import { memo } from "react";
import { Globe, RefreshCcw, Trash2 } from "lucide-react";
import { useTranslation } from "react-i18next";
import clsx from "clsx";
@@ -7,90 +7,64 @@ import Tooltip from "@/components/Common/Tooltip";
import SettingsToggle from "@/components/Settings/SettingsToggle";
import { OpenURLWithBrowser } from "@/utils";
import { useConnectStore } from "@/stores/connectStore";
import platformAdapter from "@/utils/platformAdapter";
import { useServers } from "@/hooks/useServers";
interface ServiceHeaderProps {
refreshLoading?: boolean;
refreshClick: (id: string) => void;
fetchServers: (force: boolean) => Promise<void>;
}
const ServiceHeader = memo(
({ refreshLoading, refreshClick, fetchServers }: ServiceHeaderProps) => {
({ refreshLoading, refreshClick }: ServiceHeaderProps) => {
const { t } = useTranslation();
const currentService = useConnectStore((state) => state.currentService);
const setCurrentService = useConnectStore(
(state) => state.setCurrentService
);
const cloudSelectService = useConnectStore((state) => state.cloudSelectService);
const enable_coco_server = useCallback(
async (enabled: boolean) => {
if (enabled) {
await platformAdapter.commands("enable_server", currentService?.id);
} else {
await platformAdapter.commands("disable_server", currentService?.id);
}
setCurrentService({ ...currentService, enabled });
await fetchServers(false);
},
[currentService?.id]
);
const removeServer = (id: string) => {
platformAdapter.commands("remove_coco_server", id).then((res: any) => {
console.log("remove_coco_server", id, JSON.stringify(res));
fetchServers(true).then((r) => {
console.log("fetchServers", r);
});
});
};
const { enableServer, removeServer } = useServers();
return (
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-3">
<Tooltip content={currentService?.endpoint}>
<Tooltip content={cloudSelectService?.endpoint}>
<div className="flex items-center text-gray-900 dark:text-white font-medium cursor-pointer">
{currentService?.name}
{cloudSelectService?.name}
</div>
</Tooltip>
</div>
<div className="flex items-center gap-2">
<SettingsToggle
checked={currentService?.enabled}
checked={cloudSelectService?.enabled}
className={clsx({
"bg-red-600 focus:ring-red-500": !currentService?.enabled,
"bg-red-600 focus:ring-red-500": !cloudSelectService?.enabled,
})}
label={
currentService?.enabled
cloudSelectService?.enabled
? t("cloud.enable_server")
: t("cloud.disable_server")
}
onChange={enable_coco_server}
onChange={enableServer}
/>
<button
className="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 rounded-[6px] bg-white dark:bg-gray-800 border border-[rgba(228,229,239,1)] dark:border-gray-700"
onClick={() =>
OpenURLWithBrowser(currentService?.provider?.website)
OpenURLWithBrowser(cloudSelectService?.provider?.website)
}
>
<Globe className="w-3.5 h-3.5" />
</button>
<button
className="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 rounded-[6px] bg-white dark:bg-gray-800 border border-[rgba(228,229,239,1)] dark:border-gray-700"
onClick={() => refreshClick(currentService?.id)}
onClick={() => refreshClick(cloudSelectService?.id)}
>
<RefreshCcw
className={`w-3.5 h-3.5 ${refreshLoading ? "animate-spin" : ""}`}
/>
</button>
{!currentService?.builtin && (
{!cloudSelectService?.builtin && (
<button
className="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 rounded-[6px] bg-white dark:bg-gray-800 border border-[rgba(228,229,239,1)] dark:border-gray-700"
onClick={() => removeServer(currentService?.id)}
onClick={() => removeServer(cloudSelectService?.id)}
>
<Trash2 className="w-3.5 h-3.5 text-[#ff4747]" />
</button>

View File

@@ -7,11 +7,10 @@ import ServiceMetadata from "./ServiceMetadata";
interface ServiceInfoProps {
refreshLoading?: boolean;
refreshClick: (id: string) => void;
fetchServers: (force: boolean) => Promise<void>;
}
const ServiceInfo = memo(
({ refreshLoading, refreshClick, fetchServers }: ServiceInfoProps) => {
({ refreshLoading, refreshClick }: ServiceInfoProps) => {
return (
<>
<ServiceBanner />
@@ -19,7 +18,6 @@ const ServiceInfo = memo(
<ServiceHeader
refreshLoading={refreshLoading}
refreshClick={refreshClick}
fetchServers={fetchServers}
/>
<ServiceMetadata />

View File

@@ -6,25 +6,25 @@ import { useConnectStore } from "@/stores/connectStore";
interface ServiceMetadataProps {}
const ServiceMetadata = memo(({}: ServiceMetadataProps) => {
const currentService = useConnectStore((state) => state.currentService);
const cloudSelectService = useConnectStore((state) => state.cloudSelectService);
return (
<div className="mb-8">
<div className="text-sm text-gray-500 dark:text-gray-400 mb-2 flex">
<span className="flex items-center gap-1">
<PackageOpen className="w-4 h-4" /> {currentService?.provider?.name}
<PackageOpen className="w-4 h-4" /> {cloudSelectService?.provider?.name}
</span>
<span className="mx-4">|</span>
<span className="flex items-center gap-1">
<GitFork className="w-4 h-4" /> {currentService?.version?.number}
<GitFork className="w-4 h-4" /> {cloudSelectService?.version?.number}
</span>
<span className="mx-4">|</span>
<span className="flex items-center gap-1">
<CalendarSync className="w-4 h-4" /> {currentService?.updated}
<CalendarSync className="w-4 h-4" /> {cloudSelectService?.updated}
</span>
</div>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
{currentService?.provider?.description}
{cloudSelectService?.provider?.description}
</p>
</div>
);

View File

@@ -20,13 +20,15 @@ interface ServerGroups {
export const Sidebar = forwardRef<{ refreshData: () => void }, SidebarProps>(
({ setIsConnect, serverList }, _ref) => {
const { t } = useTranslation();
const currentService = useConnectStore((state) => state.currentService);
const setCurrentService = useConnectStore(
(state) => state.setCurrentService
);
const cloudSelectService = useConnectStore((state) => {
return state.cloudSelectService;
});
const setCloudSelectService = useConnectStore((state) => {
return state.setCloudSelectService;
});
const selectService = (item: Server) => {
setCurrentService(item);
setCloudSelectService(item);
setIsConnect(true);
};
@@ -41,7 +43,7 @@ export const Sidebar = forwardRef<{ refreshData: () => void }, SidebarProps>(
// Extracted server item rendering
const renderServerItem = useCallback(
(item: Server) => {
const isSelected = currentService?.id === item.id;
const isSelected = cloudSelectService?.id === item.id;
return (
<div
key={item.id}
@@ -72,7 +74,7 @@ export const Sidebar = forwardRef<{ refreshData: () => void }, SidebarProps>(
</div>
);
},
[currentService]
[cloudSelectService]
);
const { builtinServers, customServers } = useMemo(() => {

View File

@@ -12,7 +12,6 @@ interface UserProfileProps {
export function UserProfile({ server, userInfo, onLogout }: UserProfileProps) {
const handleLogout = () => {
onLogout(server);
console.log("Logout", server);
};
const [imageLoadError, setImageLoadError] = useState(false);

View File

@@ -124,7 +124,6 @@ export const DocumentList: React.FC<DocumentListProps> = ({
}
}
console.log("_docs", from, queryStrings, response);
const list = response?.hits ?? [];
const allTotal = response?.total_hits ?? 0;
// set first select hover

View File

@@ -1,6 +1,8 @@
import { useTranslation } from "react-i18next";
import { Command, RotateCcw } from "lucide-react";
import { useEffect } from "react";
import { Button } from "@headlessui/react";
import clsx from "clsx";
import { formatKey } from "@/utils/keyboardUtils";
import SettingsItem from "@/components/Settings/SettingsItem";
@@ -28,8 +30,6 @@ import { ModifierKey } from "@/types/index";
import platformAdapter from "@/utils/platformAdapter";
import { useAppStore } from "@/stores/appStore";
import SettingsInput from "@/components/Settings/SettingsInput";
import { Button } from "@headlessui/react";
import clsx from "clsx";
export const modifierKeys: ModifierKey[] = isMac
? ["meta", "ctrl"]
@@ -66,8 +66,8 @@ const Shortcuts = () => {
setNewSession,
fixedWindow,
setFixedWindow,
serviceList,
setServiceList,
serviceListShortcut,
setServiceListShortcut,
external,
setExternal,
aiOverview,
@@ -181,8 +181,8 @@ const Shortcuts = () => {
title: "settings.advanced.shortcuts.serviceList.title",
description: "settings.advanced.shortcuts.serviceList.description",
initialValue: INITIAL_SERVICE_LIST,
value: serviceList,
setValue: setServiceList,
value: serviceListShortcut,
setValue: setServiceListShortcut,
},
{
title: "settings.advanced.shortcuts.external.title",

View File

@@ -1,137 +1,164 @@
import { FC, useMemo, useState, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { isArray } from "lodash-es";
import { useAsyncEffect, useMount } from "ahooks";
import { AssistantFetcher } from "@/components/Assistant/AssistantFetcher";
import SettingsSelectPro from "@/components/Settings/SettingsSelectPro";
import { useAppStore } from "@/stores/appStore";
import platformAdapter from "@/utils/platformAdapter";
import { useAsyncEffect, useMount } from "ahooks";
import { FC, useMemo, useState } from "react";
import { ExtensionId } from "../../..";
import { useTranslation } from "react-i18next";
import { isArray } from "lodash-es";
import { ExtensionId } from "@/components/Settings/Extensions/index";
import { useConnectStore } from "@/stores/connectStore";
import type { Server } from "@/types/server";
interface Assistant {
id: string;
name?: string;
icon?: string;
description?: string;
}
interface SharedAiProps {
id: ExtensionId;
server?: any;
setServer: (server: any) => void;
assistant?: any;
setAssistant: (assistant: any) => void;
server?: Server;
setServer: (server: Server | undefined) => void;
assistant?: Assistant;
setAssistant: (assistant: Assistant | undefined) => void;
}
const SharedAi: FC<SharedAiProps> = (props) => {
const { id, server, setServer, assistant, setAssistant } = props;
const [serverList, setServerList] = useState<any[]>([server]);
const [assistantList, setAssistantList] = useState<any[]>([assistant]);
const serverList = useConnectStore((state) => state.serverList);
const [list, setList] = useState<Server[]>([]);
const [assistantList, setAssistantList] = useState<Assistant[]>([]);
const addError = useAppStore((state) => state.addError);
const { fetchAssistant } = AssistantFetcher({});
const { t } = useTranslation();
const [assistantSearchValue, setAssistantSearchValue] = useState("");
const [isLoadingAssistants, setIsLoadingAssistants] = useState(false);
const getEnabledServers = useCallback((servers: Server[]): Server[] => {
if (!isArray(servers)) return [];
return servers.filter(
(s) => s.enabled && s.available && (s.public || s.profile)
);
}, []);
useMount(async () => {
try {
const data = await platformAdapter.invokeBackend<any[]>(
"list_coco_servers"
);
const enabledServers = getEnabledServers(serverList);
setList(enabledServers);
if (isArray(data)) {
const enabledServers = data.filter(
(s) => s.enabled && s.available && (s.public || s.profile)
);
setServerList(enabledServers);
if (server) {
const matchServer = enabledServers.find((item) => {
return item.id === server.id;
});
if (matchServer) {
return setServer(matchServer);
}
}
setServer(enabledServers[0]);
if (enabledServers.length === 0) {
setServer(undefined);
return;
}
if (server) {
const matchServer = enabledServers.find((item) => item.id === server.id);
if (matchServer) {
setServer(matchServer);
return;
}
}
setServer(enabledServers[0]);
} catch (error) {
addError(String(error));
console.error('Failed to load servers:', error);
addError(`Failed to load servers: ${String(error)}`);
}
});
useAsyncEffect(async () => {
try {
if (!server) return;
if (!server) {
setAssistantList([]);
setAssistant(undefined);
return;
}
setIsLoadingAssistants(true);
try {
const data = await fetchAssistant({
current: 1,
pageSize: 1000,
pageSize: 100,
serverId: server.id,
query: assistantSearchValue,
});
const list = data.list.map((item: any) => item._source);
const assistants: Assistant[] = data.list.map((item: any) => item._source);
setAssistantList(assistants);
setAssistantList(list);
if (assistants.length === 0) {
setAssistant(undefined);
return;
}
if (assistant) {
const matched = list.find((item: any) => {
return item.id === assistant.id;
});
const matched = assistants.find((item) => item.id === assistant.id);
if (matched) {
return setAssistant(matched);
setAssistant(matched);
return;
}
}
setAssistant(list[0]);
setAssistant(assistants[0]);
} catch (error) {
addError(String(error));
console.error('Failed to fetch assistants:', error);
addError(`Failed to fetch assistants: ${String(error)}`);
setAssistantList([]);
setAssistant(undefined);
} finally {
setIsLoadingAssistants(false);
}
}, [server, assistantSearchValue]);
}, [server?.id, assistantSearchValue]);
const selectList = useMemo(() => {
return [
{
label: t(
"settings.extensions.shardAi.details.linkedAssistant.label.cocoServer"
),
value: server?.id,
icon: server?.provider?.icon,
data: serverList,
searchable: false,
onChange: (value: string) => {
const matched = serverList.find((item) => item.id === value);
setServer(matched);
},
const serverSelectConfig = {
label: t(
"settings.extensions.shardAi.details.linkedAssistant.label.cocoServer"
),
value: server?.id,
icon: server?.provider?.icon,
data: list,
searchable: false,
onChange: (value: string) => {
const matched = list.find((item) => item.id === value);
setServer(matched);
},
{
label: t(
"settings.extensions.shardAi.details.linkedAssistant.label.aiAssistant"
),
value: assistant?.id,
icon: assistant?.icon,
data: assistantList,
searchable: true,
onChange: (value: string) => {
const matched = assistantList.find((item) => item.id === value);
onSearch: undefined,
};
setAssistant(matched);
},
onSearch: (value: string) => {
setAssistantSearchValue(value);
},
const assistantSelectConfig = {
label: t(
"settings.extensions.shardAi.details.linkedAssistant.label.aiAssistant"
),
value: assistant?.id,
icon: assistant?.icon,
data: assistantList,
searchable: true,
onChange: (value: string) => {
const matched = assistantList.find((item) => item.id === value);
setAssistant(matched);
},
];
}, [serverList, assistantList, server, assistant]);
onSearch: (value: string) => {
setAssistantSearchValue(value);
},
};
const renderDescription = () => {
if (id === "QuickAIAccess") {
return t("settings.extensions.quickAiAccess.description");
}
return [serverSelectConfig, assistantSelectConfig];
}, [list, assistantList, server?.id, assistant?.id, t]);
if (id === "AIOverview") {
return t("settings.extensions.aiOverview.description");
const renderDescription = useCallback(() => {
switch (id) {
case "QuickAIAccess":
return t("settings.extensions.quickAiAccess.description");
case "AIOverview":
return t("settings.extensions.aiOverview.description");
default:
return null;
}
};
}, [id, t]);
return (
<>
@@ -154,6 +181,7 @@ const SharedAi: FC<SharedAiProps> = (props) => {
searchable={searchable}
onChange={onChange}
onSearch={onSearch}
placeholder={isLoadingAssistants && searchable ? "Loading..." : undefined}
/>
</div>
);

View File

@@ -5,3 +5,7 @@ export const HISTORY_PANEL_ID = "headlessui-popover-panel:history-panel";
export const CONTEXT_MENU_PANEL_ID = "headlessui-popover-panel:context-menu";
export const DEFAULT_COCO_SERVER_ID = "default_coco_server";
export const MAIN_WINDOW_LABEL = "main";
export const SETTINGS_WINDOW_LABEL = "settings";

View File

@@ -82,7 +82,6 @@ export function useChatActions(
const [_error, res] = await Post(`/chat/${activeChat?._id}/_close`, {});
response = res;
}
console.log("_close", response);
},
[currentService?.id, isTauri]
);
@@ -130,7 +129,6 @@ export function useChatActions(
);
response = res;
}
console.log("_cancel", response);
},
[currentService?.id, isTauri]
);
@@ -176,7 +174,6 @@ export function useChatActions(
...chat,
messages: hits,
};
console.log("id_history", updatedChat);
updatedChatRef.current = updatedChat;
setActiveChat(updatedChat);
callback && callback(updatedChat);
@@ -260,20 +257,6 @@ export function useChatActions(
`chat-stream-${clientId}-${timestamp}`,
(event) => {
const msg = event.payload as string;
try {
// console.log("msg:", JSON.parse(msg));
// console.log("user:", msg.includes(`"user"`));
// console.log("_source:", msg.includes("_source"));
// console.log("result:", msg.includes("result"));
// console.log("");
// console.log("");
// console.log("");
// console.log("");
// console.log("");
} catch (error) {
console.error("Failed to parse JSON in listener:", error);
}
handleChatCreateStreamMessage(msg);
}
);
@@ -480,7 +463,6 @@ export function useChatActions(
response = res;
}
console.log("_open", response);
return response;
},
[currentService?.id, isTauri]
@@ -509,7 +491,6 @@ export function useChatActions(
response = res;
}
console.log("_history", response);
const hits = response?.hits?.hits || [];
setChats(hits);
}, [

105
src/hooks/useServers.ts Normal file
View File

@@ -0,0 +1,105 @@
import { useEffect, useCallback } from "react";
import { useConnectStore } from "@/stores/connectStore";
import platformAdapter from "@/utils/platformAdapter";
import type { Server } from "@/types/server";
import {
getCurrentWindowService,
setCurrentWindowService,
handleLogout,
} from "@/commands/servers";
export const useServers = () => {
const setServerList = useConnectStore((state) => state.setServerList);
const currentService = useConnectStore((state) => state.currentService);
const cloudSelectService = useConnectStore((state) => {
return state.cloudSelectService;
});
const getAllServerList = async () => {
try {
const res = await platformAdapter.commands("list_coco_servers");
if (!Array.isArray(res)) {
// If res is not an array, it might be an error message or something else.
// Log it and don't proceed.
console.warn("Invalid server list response:", res);
setServerList([]); // Clear the list or handle as appropriate
return;
}
setServerList(res);
} catch (error) {
console.error("Failed to fetch server list:", error);
setServerList([]);
}
};
const addServer = useCallback(
async (endpointLink: string): Promise<Server> => {
if (!endpointLink) {
throw new Error("Endpoint is required");
}
if (
!endpointLink.startsWith("http://") &&
!endpointLink.startsWith("https://")
) {
throw new Error("Invalid Endpoint");
}
const res: Server = await platformAdapter.commands(
"add_coco_server",
endpointLink
);
await getAllServerList();
await setCurrentWindowService(res);
return res;
},
[]
);
const enableServer = useCallback(
async (enabled: boolean) => {
const service = await getCurrentWindowService();
if (!service?.id) {
throw new Error("No current service selected");
}
if (enabled) {
await platformAdapter.commands("enable_server", service.id);
} else {
await platformAdapter.commands("disable_server", service.id);
}
await setCurrentWindowService({ ...service, enabled });
await getAllServerList();
},
[currentService, cloudSelectService]
);
const removeServer = useCallback(
async (id: string) => {
await platformAdapter.commands("remove_coco_server", id);
await getAllServerList();
},
[currentService?.id, cloudSelectService?.id]
);
const logoutServer = useCallback(async (id: string) => {
await platformAdapter.commands("logout_coco_server", id);
handleLogout(id);
await getAllServerList();
}, []);
useEffect(() => {
getAllServerList();
}, [currentService?.enabled, cloudSelectService?.enabled]);
return {
getAllServerList,
refreshServerList: getAllServerList,
addServer,
enableServer,
removeServer,
logoutServer,
};
};

View File

@@ -1,3 +1,6 @@
import { isNumber } from "lodash-es";
import { useEffect } from "react";
import { useAppearanceStore } from "@/stores/appearanceStore";
import { useAppStore } from "@/stores/appStore";
import { useConnectStore } from "@/stores/connectStore";
@@ -5,8 +8,6 @@ import { useExtensionsStore } from "@/stores/extensionsStore";
import { useShortcutsStore } from "@/stores/shortcutsStore";
import { useStartupStore } from "@/stores/startupStore";
import platformAdapter from "@/utils/platformAdapter";
import { isNumber } from "lodash-es";
import { useEffect } from "react";
export const useSyncStore = () => {
const setModifierKey = useShortcutsStore((state) => {
@@ -60,8 +61,8 @@ export const useSyncStore = () => {
const setFixedWindow = useShortcutsStore((state) => {
return state.setFixedWindow;
});
const setServiceList = useShortcutsStore((state) => {
return state.setServiceList;
const setServiceListShortcut = useShortcutsStore((state) => {
return state.setServiceListShortcut;
});
const setExternal = useShortcutsStore((state) => {
return state.setExternal;
@@ -143,7 +144,7 @@ export const useSyncStore = () => {
aiAssistant,
newSession,
fixedWindow,
serviceList,
serviceListShortcut,
external,
aiOverview,
} = payload;
@@ -162,7 +163,7 @@ export const useSyncStore = () => {
setAiAssistant(aiAssistant);
setNewSession(newSession);
setFixedWindow(fixedWindow);
setServiceList(serviceList);
setServiceListShortcut(serviceListShortcut);
setExternal(external);
setAiOverview(aiOverview);
}),

View File

@@ -1,3 +1,4 @@
import type { I } from '../index.d';
import SVGWrap from "./SVGWrap";
export default function History(props: I.SVG) {

View File

@@ -1,3 +1,4 @@
import type { I } from '../index.d';
import SVGWrap from "./SVGWrap";
export default function Pin(props: I.SVG) {

View File

@@ -1,3 +1,4 @@
import type { I } from '../index.d';
import SVGWrap from "./SVGWrap";
export default function PinOff(props: I.SVG) {

View File

@@ -1,3 +1,4 @@
import type { I } from '../index.d';
import SVGWrap from "./SVGWrap";
export default function Reading(props: I.SVG) {

View File

@@ -1,3 +1,4 @@
import type { I } from '../index.d';
import SVGWrap from "./SVGWrap";
export default function Retrieve(props: I.SVG) {

View File

@@ -1,6 +1,8 @@
import React from "react";
import clsx from "clsx";
import type { I } from '../index.d';
export default function SVGWrap({
size = 18,
children,

View File

@@ -1,3 +1,4 @@
import type { I } from '../index.d';
import SVGWrap from "./SVGWrap";
export default function Selection(props: I.SVG) {

View File

@@ -1,3 +1,4 @@
import type { I } from '../index.d';
import SVGWrap from "./SVGWrap";
export default function Server(props: I.SVG) {

View File

@@ -1,3 +1,4 @@
import type { I } from '../index.d';
import SVGWrap from "./SVGWrap";
export default function Stop(props: I.SVG) {

View File

@@ -1,3 +1,4 @@
import type { I } from '../index.d';
import SVGWrap from "./SVGWrap";
export default function Understand(props: I.SVG) {

View File

@@ -1,3 +1,4 @@
import type { I } from '../index.d';
import SVGWrap from "./SVGWrap";
export default function WindowsFull(props: I.SVG) {

77
src/index.d.ts vendored
View File

@@ -1,3 +1,5 @@
import type { IConfig } from "@/utils/console-custom";
declare namespace I {
export type AppConf = {
theme: "light" | "dark" | "auto";
@@ -17,40 +19,49 @@ declare namespace I {
declare global {
interface Window {
__TAURI__: Record<string, unknown>;
__TAURI__?: {
writeText(text: string): Promise<void>;
invoke(command: string, payload?: Record<string, unknown>): Promise<any>;
dialog: {
save(options?: Record<string, unknown>): Promise<string | null>;
};
fs: {
writeBinaryFile(path: string, data: Uint8Array): Promise<void>;
writeTextFile(path: string, data: string): Promise<void>;
};
notification: {
requestPermission(): Promise<Permission>;
isPermissionGranted(): Promise<boolean>;
sendNotification(options: string | Options): void;
};
updater: {
checkUpdate(): Promise<UpdateResult>;
installUpdate(): Promise<void>;
onUpdaterEvent(
handler: (status: UpdateStatusResult) => void
): Promise<UnlistenFn>;
};
http: {
fetch<T>(
url: string,
options?: Record<string, unknown>
): Promise<Response<T>>;
};
};
__TAURI_INTERNALS__?: any;
__TAURI_OS_PLUGIN_INTERNALS__?: any;
}
}
declare interface Window {
__TAURI__?: {
writeText(text: string): Promise<void>;
invoke(command: string, payload?: Record<string, unknown>): Promise<any>;
dialog: {
save(options?: Record<string, unknown>): Promise<string | null>;
};
fs: {
writeBinaryFile(path: string, data: Uint8Array): Promise<void>;
writeTextFile(path: string, data: string): Promise<void>;
};
notification: {
requestPermission(): Promise<Permission>;
isPermissionGranted(): Promise<boolean>;
sendNotification(options: string | Options): void;
};
updater: {
checkUpdate(): Promise<UpdateResult>;
installUpdate(): Promise<void>;
onUpdaterEvent(
handler: (status: UpdateStatusResult) => void,
): Promise<UnlistenFn>;
};
http: {
fetch<T>(
url: string,
options?: Record<string, unknown>,
): Promise<Response<T>>;
};
};
bLog: (...data: any[]) => void;
infoLog: (
config?: Partial<import("./utils/console-custom").IConfig>
) => (...data: any[]) => void;
createLogger: typeof import("./utils/console-custom").default;
}
// global variable declarations
var bLog: (...data: any[]) => void;
var infoLog: (
config?: Partial<import("./utils/console-custom").IConfig>
) => (...data: any[]) => void;
var createLogger: typeof import("./utils/console-custom").default;
}

View File

@@ -3,6 +3,7 @@ import { RouterProvider } from "react-router-dom";
import { router } from "./routes/index";
import "./i18n";
import '@/utils/global-logger';
import "./main.css";

View File

@@ -20,6 +20,7 @@ import { useModifierKeyPress } from "@/hooks/useModifierKeyPress";
import { useIconfontScript } from "@/hooks/useScript";
import { Extension } from "@/components/Settings/Extensions";
import { useExtensionsStore } from "@/stores/extensionsStore";
import { useServers } from "@/hooks/useServers";
export default function Layout() {
const location = useLocation();
@@ -27,6 +28,10 @@ export default function Layout() {
const { language } = useAppStore();
const { i18n } = useTranslation();
const { activeTheme, isDark, setIsDark, setTheme } = useThemeStore();
// init servers isTauri
useServers();
const [langUpdated, setLangUpdated] = useState(false);
useAsyncEffect(async () => {

View File

@@ -3,7 +3,7 @@ import { persist, subscribeWithSelector } from "zustand/middleware";
import { produce } from "immer";
import platformAdapter from "@/utils/platformAdapter";
import { Server } from "@/types/server";
import type { Server } from "@/types/server";
const CONNECTOR_CHANGE_EVENT = "connector_data_change";
const DATASOURCE_CHANGE_EVENT = "datasourceData_change";
@@ -17,6 +17,8 @@ export type IConnectStore = {
setServerList: (servers: Server[]) => void;
currentService: Server;
setCurrentService: (service: Server) => void;
cloudSelectService: Server;
setCloudSelectService: (service: Server) => void;
connector_data: keyArrayObject;
setConnectorData: (connector_data: any[], key: string) => void;
datasourceData: keyArrayObject;
@@ -43,7 +45,6 @@ export const useConnectStore = create<IConnectStore>()(
(set) => ({
serverList: [],
setServerList: (serverList: Server[]) => {
console.log("set serverList:", serverList);
set(
produce((draft) => {
draft.serverList = serverList;
@@ -52,13 +53,20 @@ export const useConnectStore = create<IConnectStore>()(
},
currentService: {} as Server,
setCurrentService: (server: any) => {
console.log("set default server:", server);
set(
produce((draft) => {
draft.currentService = server;
})
);
},
cloudSelectService: {} as Server,
setCloudSelectService: (server: any) => {
set(
produce((draft) => {
draft.cloudSelectService = server;
})
);
},
connector_data: {},
setConnectorData: async (connector_data: any[], key: string) => {
set(
@@ -130,7 +138,9 @@ export const useConnectStore = create<IConnectStore>()(
{
name: "connect-store",
partialize: (state) => ({
serverList: state.serverList,
currentService: state.currentService,
cloudSelectService: state.cloudSelectService,
connector_data: state.connector_data,
datasourceData: state.datasourceData,
connectionTimeout: state.connectionTimeout,

View File

@@ -37,8 +37,8 @@ export type IShortcutsStore = {
setNewSession: (newSession: string) => void;
fixedWindow: string;
setFixedWindow: (fixedWindow: string) => void;
serviceList: string;
setServiceList: (serviceList: string) => void;
serviceListShortcut: string;
setServiceListShortcut: (serviceListShortcut: string) => void;
external: string;
setExternal: (external: string) => void;
resetFixedWindow: boolean;
@@ -109,8 +109,8 @@ export const useShortcutsStore = create<IShortcutsStore>()(
setNewSession: (newSession) => set({ newSession }),
fixedWindow: INITIAL_FIXED_WINDOW,
setFixedWindow: (fixedWindow) => set({ fixedWindow }),
serviceList: INITIAL_SERVICE_LIST,
setServiceList: (serviceList) => set({ serviceList }),
serviceListShortcut: INITIAL_SERVICE_LIST,
setServiceListShortcut: (serviceListShortcut) => set({ serviceListShortcut }),
external: INITIAL_EXTERNAL,
setExternal: (external) => set({ external }),
resetFixedWindow: false,
@@ -137,7 +137,7 @@ export const useShortcutsStore = create<IShortcutsStore>()(
aiAssistant: state.aiAssistant,
newSession: state.newSession,
fixedWindow: state.fixedWindow,
serviceList: state.serviceList,
serviceListShortcut: state.serviceListShortcut,
external: state.external,
resetFixedWindow: state.resetFixedWindow,
}),

View File

@@ -24,7 +24,7 @@ interface MinimalClientVersion {
export type Status = 'green' | 'yellow' | 'red';
interface Health {
services?: Record<string, Status>;
services?: Record<string, any>;
status: Status;
}

145
src/utils/console-custom.ts Normal file
View File

@@ -0,0 +1,145 @@
export interface IConfig {
logType?: string;
username?: string;
logName?: string;
usernameColor?: string;
logNameColor?: string;
padding?: number;
borderRadius?: number;
fontColor?: string;
usernameStyle?: string;
logNameStyle?: string;
}
const GourdBabyColorMap = new Map([
["1", "#FF0000"],
["2", "#FFA500"],
["3", "#FFFF00"],
["4", "#008000"],
["5", "#00FFFF"],
["6", "#0000FF"],
["7", "#800080"],
]);
const createBLog = (config: IConfig) => {
const logType = config.logType || "default";
const username = config.username || "";
const logName = config.logName || "";
const usernameColor = config.usernameColor || "#41b883";
const logNameColor = config.logNameColor || "#35495e";
const padding = config.padding || 6;
const borderRadius = config.borderRadius || 6;
const fontColor = config.fontColor || "#FFFFFF";
const usernameStyle = config.usernameStyle || "";
const logNameStyle = config.logNameStyle || "";
const logTemplate = (username = "myLog", logName = "") =>
`${username ? "%c" + username : ""} ${logName ? "%c" + logName : ""} `;
const customLog = (...data: any[]) => {
console.log(
logTemplate(username, logName),
usernameStyle
? usernameStyle
: `background: ${usernameColor}; padding: 6px; border-radius: 6px 0 0 6px; color: #fff`,
logNameStyle
? logNameStyle
: `background: ${logNameColor}; padding: 6px; border-radius: 0 6px 6px 0; color: #fff`,
...data
);
};
const defaultLog = (...data: any[]) => {
const len = data.length;
if (len > 1) {
data.map((item, index) => {
let firstStyle = `
background: ${GourdBabyColorMap.get((index % 7) + 1 + "")};
padding: ${padding}px;
border-radius: 0 0;
color: ${fontColor}
`;
let secondStyle = `
background: ${logNameColor};
padding: ${padding}px;
border-radius: 0 0;
color: ${fontColor}
`;
if (index === 0) {
firstStyle = `
background: ${GourdBabyColorMap.get((index % 7) + 1 + "")};
padding: ${padding}px;
margin-top: ${padding * 2}px;
border-radius: ${borderRadius}px 0 0 0;
color: ${fontColor}
`;
secondStyle = `
background: ${logNameColor};
padding: ${padding}px;
margin-top: ${padding * 2}px;
border-radius: 0 ${borderRadius}px 0 0;
color: ${fontColor}
`;
} else if (index === len - 1) {
firstStyle = `
background: ${GourdBabyColorMap.get((index % 7) + 1 + "")};
padding: ${padding}px;
margin-bottom: ${padding * 2}px;
border-radius: 0 0 0 ${borderRadius}px;
color: ${fontColor}
`;
secondStyle = `
background: ${logNameColor};
padding: ${padding}px;
margin-bottom: ${padding * 2}px;
border-radius: 0 0 ${borderRadius}px 0;
color: ${fontColor}
`;
}
console.log(
logTemplate(username, `data${index + 1}`),
firstStyle,
secondStyle,
item
);
});
} else {
const firstStyle = `
background: ${usernameColor};
padding: ${padding}px;
border-radius: ${borderRadius}px 0 0 ${borderRadius}px;
color: ${fontColor}
`;
const secondStyle = `
background: ${logNameColor};
padding: ${padding}px;
border-radius: 0 ${borderRadius}px ${borderRadius}px 0;
color: ${fontColor}
`;
console.log(
logTemplate(username, logName),
firstStyle,
secondStyle,
...data
);
}
};
const log = (...data: any[]) => {
switch (logType) {
case "custom":
customLog(...data);
break;
default:
defaultLog(...data);
}
};
return {
log,
};
};
export default createBLog;

View File

@@ -0,0 +1,37 @@
import createBLog, { IConfig } from './console-custom';
// default config
const defaultConfig: IConfig = {
logType: 'custom',
usernameColor: '#41b883',
logNameColor: '#35495e'
};
// create a flexible logger function
const createLogFunction = (defaultLogConfig: Partial<IConfig> = {}) => {
return (config: Partial<IConfig> = {}) => {
const finalConfig = {
...defaultConfig,
...defaultLogConfig,
...config
};
return createBLog(finalConfig).log;
};
};
// export convenient methods
export const bLog = createBLog(defaultConfig).log;
export const createLogger = createBLog;
// create a flexible logger function
export const infoLog = createLogFunction({
logName: 'Info',
logNameColor: '#28a745'
});
// register global logger
if (typeof window !== 'undefined') {
(window as any).bLog = bLog;
(window as any).infoLog = infoLog;
(window as any).createLogger = createLogger;
}

View File

@@ -25,6 +25,7 @@ export interface TauriPlatformAdapter extends BasePlatformAdapter {
) => Promise<string | string[] | null>;
metadata: typeof metadata;
error: typeof error;
getCurrentWindowLabel: () => Promise<string>;
}
// Create Tauri adapter functions
@@ -79,8 +80,8 @@ export const createTauriAdapter = (): TauriPlatformAdapter => {
const { getCurrentWindow } = await import("@tauri-apps/api/window");
const window = getCurrentWindow();
return window.setAlwaysOnTop(isPinned);
},
},
async toggleMoveToActiveSpaceAttribute() {
if (isMac) {
return toggle_move_to_active_space_attribute();
@@ -340,5 +341,10 @@ export const createTauriAdapter = (): TauriPlatformAdapter => {
queryParams,
});
},
async getCurrentWindowLabel() {
const window = await windowWrapper.getWebviewWindow();
return window.label;
},
};
};