fix: mouse & keyDown (#122)

* feat: impl Coco server related APIs

* chore: remove unused method

* fix: invoke Rust interfaces in tauri::run()

* chore: add invoke

* feat: add add_coco_server

* fix: trim the tailing forward slash

* feat: interface get_user_profiles

* chore: add

* fix: store the servers in add interface

* chore: ass

* fix: skip non-publich servers with no token

* feat: add

* feat: get datasources and connectors

* fix: invoke interfaces in tauri::run()

* chore: add SidebarRef

* refactor: refactoring coco-app

* refactor: refactoring coco app

* refactor: refactoring project layout

* refactor: refactoring server management

* chore: cleanup code

* chore: display error when connect failed

* refactor: refactoring refresh server's info

* refactor: refactoring how to connect the coco serverg

* chore: rename to cloud

* refactor: refactoring remove coco server

* fix: refresh current selected server

* fix: reset server selection

* chore: update login status

* feat: add error message tips

* fix: fix login and logout

* refactor: refactoring http client

* fix: fix the datasources

* chore: minor fix

* refactor: refactoring code

* fix: fix search api

* chore: optimize part of icons

* chore: fix build

* refactor: search list icon

* refactor: search list icon

* chore: lib

* feat: add plugin-os

* feat: add data-dark

* fix: mouse & keyDown

* fix: mouse & keyDown

* fix: mouse & keyDown

---------

Co-authored-by: Steve Lau <stevelauc@outlook.com>
Co-authored-by: medcl <m@medcl.net>
This commit is contained in:
BiggerRain
2025-02-07 20:26:45 +08:00
committed by GitHub
parent 1b1d9bfc40
commit 81a02890d6
9 changed files with 582 additions and 585 deletions

View File

@@ -19,27 +19,23 @@ import { invoke } from "@tauri-apps/api/core";
import { UserProfile } from "./UserProfile";
import { DataSourcesList } from "./DataSourcesList";
import { Sidebar } from "./Sidebar";
import { Connect } from "./Connect.tsx";
import { Connect } from "./Connect";
import { OpenURLWithBrowser } from "@/utils";
import { useAppStore } from "@/stores/appStore";
import { useConnectStore } from "@/stores/connectStore";
import bannerImg from "@/assets/images/coco-cloud-banner.jpeg";
export default function Cloud() {
const SidebarRef = useRef<{ refreshData: () => void; }>(null);
const SidebarRef = useRef<{ refreshData: () => void }>(null);
// const [error, setError] = useState<string | null>(null);
const error = useAppStore((state) => state.error);
const setError = useAppStore((state) => state.setError);
const [isConnect, setIsConnect] = useState(true);
// const [ssoRequestID, setSSORequestID] = useState("");
const ssoRequestID = useAppStore((state) => state.ssoRequestID);
const setSSORequestID = useAppStore((state) => state.setSSORequestID);
// const ssoServerID = useAppStore((state) => state.ssoServerID);
// const setSSOServerID = useAppStore((state) => state.setSSOServerID);
const endpoint = useAppStore((state) => state.endpoint);
const currentService = useConnectStore((state) => state.currentService);
@@ -50,12 +46,8 @@ export default function Cloud() {
const [loading, setLoading] = useState(false);
const [refreshLoading, setRefreshLoading] = useState(false);
// const [profiles, setProfiles] = useState<any>({});
// const [userInfo, setUserInfo] = useState<any>({});
//fetch the servers
// fetch the servers
useEffect(() => {
fetchServers(true);
}, []);
@@ -65,28 +57,9 @@ export default function Cloud() {
setLoading(false);
setRefreshLoading(false);
setError("");
// setEndpoint(currentService.endpoint);
setIsConnect(true);
// setUserInfo(profiles[endpoint] || {})
}, [JSON.stringify(currentService)]);
// const get_user_profiles = useCallback(() => {
// invoke("get_user_profiles")
// .then((res: any) => {
// console.log("get_user_profiles", res);
// setProfiles(res);
// console.log("setUserInfo", res[endpoint]);
// setUserInfo(res[endpoint] || {})
// })
// .catch((err: any) => {
// console.error(err);
// });
// }, [endpoint]);
useEffect(() => {
// get_user_profiles()
}, [])
const fetchServers = async (resetSelection: boolean) => {
invoke("list_coco_servers")
.then((res: any) => {
@@ -107,10 +80,13 @@ export default function Cloud() {
const add_coco_server = (endpointLink: string) => {
if (!endpointLink) {
throw new Error('Endpoint is required');
throw new Error("Endpoint is required");
}
if (!endpointLink.startsWith("http://") && !endpointLink.startsWith("https://")) {
throw new Error('Invalid Endpoint');
if (
!endpointLink.startsWith("http://") &&
!endpointLink.startsWith("https://")
) {
throw new Error("Invalid Endpoint");
}
setRefreshLoading(true);
@@ -142,7 +118,6 @@ export default function Cloud() {
const handleOAuthCallback = useCallback(
async (code: string | null, serverId: string | null) => {
if (!code) {
setError("No authorization code received");
return;
@@ -153,7 +128,7 @@ export default function Cloud() {
await invoke("handle_sso_callback", {
serverId: serverId, // Make sure 'server_id' is the correct argument
requestId: ssoRequestID, // Make sure 'request_id' is the correct argument
code: code
code: code,
});
if (serverId != null) {
@@ -165,11 +140,9 @@ export default function Cloud() {
.catch((err) => {
setError(err);
});
} catch (e) {
console.error("Sign in failed:", e);
setError("SSO login failed: " + e);
// setAuth(undefined, endpoint);
throw error;
} finally {
setLoading(false);
@@ -180,11 +153,10 @@ export default function Cloud() {
const handleUrl = (url: string) => {
try {
// url = "coco://oauth_callback?code=cuhhi8o2sdbbbcoe0g10ktmht6aky3jmd4xkwsgvzf748i4zdgr898bfeu3kze7ffdusdtbgtnpke8ng3fe6&provider=coco-cloud/"
const urlObject = new URL(url);
console.log("handle urlObject:", urlObject);
//TODO, pass request_id and check with local, if the request_id are same, then continue
// TODO, pass request_id and check with local, if the request_id are same, then continue
const reqId = urlObject.searchParams.get("request_id");
const code = urlObject.searchParams.get("code");
@@ -196,27 +168,19 @@ export default function Cloud() {
const serverId = currentService?.id;
handleOAuthCallback(code, serverId);
// switch (urlObject.hostname) {
// case "/oauth_callback":
// break;
// default:
// console.log("Unhandled deep link path:", urlObject.pathname);
// }
} catch (err) {
console.error("Failed to parse URL:", err);
setError("Invalid URL format: " + err);
}
};
// Fetch the initial deep link intent
useEffect(() => {
// Test the handleUrl function
// handleUrl("coco://oauth_callback?code=cui88lg2sdb4dnu97jpgypcugrskkt1i3venntth7gk52exnq8hxufxvqn8hhegoaw369s394bcyb6ehtnhz&request_id=642a985c-6baa-4ec8-be41-d8c6ddbc0e60&provider=coco-cloud/");
// Function to handle pasted URL
const handlePaste = (event: any) => {
const pastedText = event.clipboardData.getData('text');
const pastedText = event.clipboardData.getData("text");
console.log("handle paste text:", pastedText);
if (isValidCallbackUrl(pastedText)) {
// Handle the URL as if it's a deep link
@@ -227,11 +191,11 @@ export default function Cloud() {
// Function to check if the pasted URL is valid for our deep link scheme
const isValidCallbackUrl = (url: string) => {
return url && url.startsWith('coco://oauth_callback');
return url && url.startsWith("coco://oauth_callback");
};
// Adding event listener for paste events
document.addEventListener('paste', handlePaste);
document.addEventListener("paste", handlePaste);
getCurrentDeepLinkUrls()
.then((urls) => {
@@ -251,27 +215,15 @@ export default function Cloud() {
return () => {
unlisten.then((fn) => fn());
document.removeEventListener('paste', handlePaste);
document.removeEventListener("paste", handlePaste);
};
}, [ssoRequestID]);
// const generateLogin = () => {
// const requestID = uuidv4();
// setSSORequestID(requestID);
// setSSOServerID(currentService?.id); // Set server ID
//
// // The URL is now updated when ssoRequestID and ssoServerID are both set
// };
const LoginClick = useCallback(() => {
if (loading) return; // Prevent multiple clicks if already loading
// If the appUid doesn't exist, generate one
// if (!ssoRequestID) {
let requestID = uuidv4();
setSSORequestID(requestID);
// setSSOServerID(currentService?.id);
// }
// Generate the login URL with the current appUid
const url = `${currentService?.auth_provider?.sso?.url}/?provider=${currentService?.id}&product=coco&request_id=${requestID}`;
@@ -283,7 +235,6 @@ export default function Cloud() {
// Start loading state
setLoading(true);
}, [ssoRequestID, loading, currentService]);
const refreshClick = (id: string) => {
@@ -291,10 +242,10 @@ export default function Cloud() {
invoke("refresh_coco_server_info", { id })
.then((res: any) => {
console.log("refresh_coco_server_info", id, JSON.stringify(res));
fetchServers(false).then(r => {
fetchServers(false).then((r) => {
console.log("fetchServers", r);
});
//update currentService
// update currentService
setCurrentService(res);
})
.catch((err: any) => {
@@ -320,7 +271,8 @@ export default function Cloud() {
.catch((err: any) => {
setError(err);
console.error(err);
}).finally(() => {
})
.finally(() => {
setRefreshLoading(false);
});
}
@@ -329,12 +281,12 @@ export default function Cloud() {
invoke("remove_coco_server", { id })
.then((res: any) => {
console.log("remove_coco_server", id, JSON.stringify(res));
fetchServers(true).then(r => {
fetchServers(true).then((r) => {
console.log("fetchServers", r);
})
});
})
.catch((err: any) => {
//TODO display the error message
// TODO display the error message
setError(err);
console.error(err);
});
@@ -342,10 +294,13 @@ export default function Cloud() {
return (
<div className="flex bg-gray-50 dark:bg-gray-900">
<Sidebar ref={SidebarRef} onAddServer={onAddServer} serverList={serverList} />
<Sidebar
ref={SidebarRef}
onAddServer={onAddServer}
serverList={serverList}
/>
<main className="flex-1 p-4 py-8">
{isConnect ? (
<div className="max-w-4xl mx-auto">
<div className="w-full rounded-[4px] bg-[rgba(229,229,229,1)] dark:bg-gray-800 mb-6">
@@ -364,7 +319,9 @@ export default function Cloud() {
<div className="flex gap-2">
<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)}
onClick={() =>
OpenURLWithBrowser(currentService?.provider?.website)
}
>
<Globe className="w-3.5 h-3.5" />
</button>
@@ -373,7 +330,8 @@ export default function Cloud() {
onClick={() => refreshClick(currentService?.id)}
>
<RefreshCcw
className={`w-3.5 h-3.5 ${refreshLoading ? "animate-spin" : ""
className={`w-3.5 h-3.5 ${
refreshLoading ? "animate-spin" : ""
}`}
/>
</button>
@@ -416,7 +374,11 @@ export default function Cloud() {
Account Information
</h2>
{currentService?.profile ? (
<UserProfile server={currentService?.id} userInfo={currentService?.profile} onLogout={onLogout} />
<UserProfile
server={currentService?.id}
userInfo={currentService?.profile}
onLogout={onLogout}
/>
) : (
<div>
{/* Login Button (conditionally rendered when not loading) */}
@@ -446,7 +408,8 @@ export default function Cloud() {
}}
className="text-xl text-blue-500 hover:text-blue-600"
>
<Copy className="inline mr-2" /> {/* Lucide Copy Icon */}
<Copy className="inline mr-2" />{" "}
{/* Lucide Copy Icon */}
</button>
</div>
)}
@@ -455,7 +418,9 @@ export default function Cloud() {
<button
className="text-xs text-[#0096FB] dark:text-blue-400 block"
onClick={() =>
OpenURLWithBrowser(currentService?.provider?.privacy_policy)
OpenURLWithBrowser(
currentService?.provider?.privacy_policy
)
}
>
EULA | Privacy Policy
@@ -465,7 +430,9 @@ export default function Cloud() {
</div>
) : null}
{currentService?.profile ? <DataSourcesList server={currentService?.id} /> : null}
{currentService?.profile ? (
<DataSourcesList server={currentService?.id} />
) : null}
</div>
) : (
<Connect setIsConnect={setIsConnect} onAddServer={add_coco_server} />

View File

@@ -107,7 +107,7 @@ export default function ChatInput({
useEffect(() => {
const setupListener = async () => {
const unlisten = await listen("tauri://focus", () => {
console.log("Window focused!");
// console.log("Window focused!");
if (isChatMode) {
textareaRef.current?.focus();
} else {

View File

@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from "react";
import React, { useState, useRef, useEffect, useCallback } from "react";
import { useInfiniteScroll } from "ahooks";
import { isTauri, invoke } from "@tauri-apps/api/core";
import { open } from "@tauri-apps/plugin-shell";
@@ -29,10 +29,11 @@ export const DocumentList: React.FC<DocumentListProps> = ({
const [total, setTotal] = useState(0);
const containerRef = useRef<HTMLDivElement>(null);
const itemRefs = useRef<(HTMLDivElement | null)[]>([]);
const [isKeyboardMode, setIsKeyboardMode] = useState(false);
const { data, loading } = useInfiniteScroll(
async (d) => {
const from = d?.list.length || 0;
const from = d?.list?.length || 0;
let queryStrings: any = {
query: input,
@@ -55,40 +56,26 @@ export const DocumentList: React.FC<DocumentListProps> = ({
const list = response?.hits || [];
const total = response?.total_hits || 0;
// console.log("docs:", list, total);
setTotal(total);
getDocDetail(list[0] || {});
return {
list,
hasMore: from + list.length < total,
list: list,
hasMore: list.length === PAGE_SIZE,
};
} catch (error) {
console.error("Failed to fetch documents:", error);
return {
list: [],
list: d?.list || [],
hasMore: false,
};
}
},
{
target: containerRef,
isNoMore: (d) => (d?.list.length || 0) >= total,
isNoMore: (d) => !d?.hasMore,
reloadDeps: [input, JSON.stringify(sourceData)],
onBefore: () => {
setTimeout(() => {
const parentRef = containerRef.current;
if (parentRef && parentRef.childElementCount > 10) {
const itemHeight =
(parentRef.firstChild as HTMLElement)?.offsetHeight || 80;
parentRef.scrollTo({
top: (parentRef.lastChild as HTMLElement)?.offsetTop - itemHeight,
behavior: "instant",
});
}
});
},
onFinally: (data) => onFinally(data, containerRef),
}
);
@@ -96,22 +83,31 @@ export const DocumentList: React.FC<DocumentListProps> = ({
const onFinally = (data: any, ref: any) => {
if (data?.page === 1) return;
const parentRef = ref.current;
if (!parentRef) return;
const itemHeight = parentRef.firstChild?.offsetHeight || 80;
parentRef.scrollTo({
top:
parentRef.lastChild?.offsetTop - (data?.list?.length + 1) * itemHeight,
if (!parentRef || selectedItem === null) return;
const targetElement = itemRefs.current[selectedItem];
if (!targetElement) return;
requestAnimationFrame(() => {
targetElement.scrollIntoView({
behavior: "instant",
block: "nearest",
});
});
};
function onMouseEnter(index: number, item: any) {
const onMouseEnter = useCallback(
(index: number, item: any) => {
if (isKeyboardMode) return;
getDocDetail(item);
setSelectedItem(index);
}
},
[isKeyboardMode, getDocDetail]
);
useEffect(() => {
setSelectedItem(null);
setIsKeyboardMode(false);
}, [isChatMode, input]);
const handleOpenURL = async (url: string) => {
@@ -126,17 +122,32 @@ export const DocumentList: React.FC<DocumentListProps> = ({
}
};
const handleKeyDown = (e: KeyboardEvent) => {
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
if (!data?.list?.length) return;
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
e.preventDefault();
setIsKeyboardMode(true);
if (e.key === "ArrowUp") {
e.preventDefault();
setSelectedItem((prev) => (prev === null || prev === 0 ? 0 : prev - 1));
} else if (e.key === "ArrowDown") {
e.preventDefault();
setSelectedItem((prev) =>
prev === null ? 0 : prev === data?.list?.length - 1 ? prev : prev + 1
);
setSelectedItem((prev) => {
const newIndex = prev === null || prev === 0 ? 0 : prev - 1;
getDocDetail(data.list[newIndex]?.document);
return newIndex;
});
} else {
setSelectedItem((prev) => {
const newIndex =
prev === null
? 0
: prev === data.list.length - 1
? prev
: prev + 1;
getDocDetail(data.list[newIndex]?.document);
return newIndex;
});
}
} else if (e.key === "Meta") {
e.preventDefault();
}
@@ -147,22 +158,39 @@ export const DocumentList: React.FC<DocumentListProps> = ({
handleOpenURL(item?.url);
}
}
},
[data, selectedItem, getDocDetail]
);
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
if (e.movementX !== 0 || e.movementY !== 0) {
setIsKeyboardMode(false);
}
};
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}, []);
useEffect(() => {
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [selectedItem]);
}, [handleKeyDown]);
useEffect(() => {
if (selectedItem !== null && itemRefs.current[selectedItem]) {
requestAnimationFrame(() => {
itemRefs.current[selectedItem]?.scrollIntoView({
behavior: "smooth",
behavior: "instant",
block: "nearest",
});
});
}
}, [selectedItem]);
@@ -196,13 +224,8 @@ export const DocumentList: React.FC<DocumentListProps> = ({
}`}
>
<div className="flex gap-2 items-center flex-1 min-w-0">
<ItemIcon item={item} />
<span
className={`text-sm truncate`}
>
{item?.title}
</span>
<span className={`text-sm truncate`}>{item?.title}</span>
</div>
</div>
);

View File

@@ -91,16 +91,16 @@ export default function Footer({ }: FooterProps) {
<div className="flex items-center gap-3">
<div className="gap-1 flex items-center text-[#666] dark:text-[#666] text-sm">
<span className="mr-1.5 ">Quick open</span>
<kbd className="docsearch-modal-footer-commands-key pr-1">
<kbd className="coco-modal-footer-commands-key pr-1">
<Command className="w-3 h-3" />
</kbd>
<kbd className="docsearch-modal-footer-commands-key pr-1">
<kbd className="coco-modal-footer-commands-key pr-1">
<ArrowDown01 className="w-3 h-3" />
</kbd>
</div>
<div className="flex items-center text-[#666] dark:text-[#666] text-sm">
<span className="mr-1.5 ">Open</span>
<kbd className="docsearch-modal-footer-commands-key pr-1">
<kbd className="coco-modal-footer-commands-key pr-1">
<CornerDownLeft className="w-3 h-3" />
</kbd>
</div>

View File

@@ -152,7 +152,7 @@ export default function ChatInput({
if (!isTauri()) return;
const setupListener = async () => {
const unlisten = await listen("tauri://focus", () => {
console.log("Window focused!");
// console.log("Window focused!");
if (isChatMode) {
textareaRef.current?.focus();
} else {

View File

@@ -21,17 +21,14 @@ import { ShortcutItem } from "./ShortcutItem";
import { Shortcut } from "./shortcut";
import { useShortcutEditor } from "@/hooks/useShortcutEditor";
import { useAppStore } from "@/stores/appStore";
import {AppTheme} from "@/utils/tauri.ts";
import {useTheme} from "@/contexts/ThemeContext.tsx";
// import { useAuthStore } from "@/stores/authStore";
// import { useConnectStore } from "@/stores/connectStore";
import { AppTheme } from "@/utils/tauri";
import { useTheme } from "@/contexts/ThemeContext";
export function ThemeOption({
icon: Icon,
title,
theme,
}: {
}: {
icon: any;
title: string;
theme: AppTheme;

View File

@@ -7,7 +7,7 @@ import React, {
} from "react";
import { isTauri, invoke } from "@tauri-apps/api/core";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { listen } from "@tauri-apps/api/event";
import { listen, emit } from "@tauri-apps/api/event";
import { AppTheme, WindowTheme } from "../utils/tauri";
import { useThemeStore } from "../stores/themeStore";
@@ -45,6 +45,8 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
unlisten = await currentWindow.onThemeChanged(({ payload: w_theme }) => {
console.log("window New theme:", w_theme);
setWindowTheme(w_theme);
// Update tray icon
switchTrayIcon(w_theme);
if (theme === "auto") applyTheme(w_theme);
});
};
@@ -75,6 +77,8 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
const root = window.document.documentElement;
root.classList.remove("light", "dark");
root.classList.add(displayTheme);
//
root.setAttribute("data-theme", displayTheme);
}
// Apply theme to UI and sync with Tauri
@@ -91,16 +95,13 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
console.error("Failed to update window theme:", err);
}
// Update tray icon
await switchTrayIcon(displayTheme);
// Notify other windows to update the theme
// try {
try {
// console.log("theme-changed", displayTheme);
// await emit("theme-changed", { theme: displayTheme });
// } catch (err) {
// console.error("Failed to emit theme-changed event:", err);
// }
await emit("theme-changed", { theme: displayTheme });
} catch (err) {
console.error("Failed to emit theme-changed event:", err);
}
}
};
@@ -126,19 +127,18 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
// Handle theme changes from user interaction
const changeTheme = async (newTheme: AppTheme) => {
console.log("Theme changed to:", newTheme);
setTheme(newTheme);
const displayTheme = getDisplayTheme(newTheme);
await applyTheme(displayTheme);
};
useEffect(() => {
if (!isTauri()) return;
let unlisten: () => void;
const setupListener = async () => {
unlisten = await listen("theme-changed", (event: any) => {
console.log("Theme updated to:", event.payload);
// console.log("Theme updated to:", event.payload);
changeClassTheme(event.payload.theme)
});
};

View File

@@ -2,67 +2,70 @@
@tailwind components;
@tailwind utilities;
@layer {
:root {
/* Base variables */
:root {
--spacing-base: 12px;
--modal-width: 560px;
--modal-height: 600px;
--searchbox-height: 56px;
--hit-height: 56px;
--footer-height: 44px;
--icon-stroke-width: 1.4;
--background: #ffffff;
--foreground: #09090b;
--border: #e3e3e7;
--docsearch-primary-color: rgb(149, 5, 153);
--docsearch-text-color: rgb(28, 30, 33);
--docsearch-spacing: 12px;
--docsearch-icon-stroke-width: 1.4;
--docsearch-highlight-color: var(--docsearch-primary-color);
--docsearch-muted-color: rgb(150, 159, 175);
--docsearch-modal-container-background: rgba(101, 108, 133, .8);
--docsearch-modal-width: 560px;
--docsearch-modal-height: 600px;
--docsearch-modal-background: rgb(245, 246, 247);
--docsearch-modal-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, .5), 0 3px 8px 0 rgba(85, 90, 100, 1);
--docsearch-searchbox-height: 56px;
--docsearch-searchbox-background: rgb(235, 237, 240);
--docsearch-searchbox-focus-background: #fff;
--docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);
--docsearch-hit-height: 56px;
--docsearch-hit-color: rgb(68, 73, 80);
--docsearch-hit-active-color: #fff;
--docsearch-hit-background: #fff;
--docsearch-hit-shadow: 0 1px 3px 0 rgb(212, 217, 225);
--docsearch-key-gradient: linear-gradient(-225deg, rgb(213, 219, 228) 0%, rgb(248, 248, 248) 100%);
--docsearch-key-shadow: inset 0 -2px 0 0 rgb(205, 205, 230), inset 0 0 1px 1px #fff, 0 1px 2px 1px rgba(30, 35, 90, .4);
--docsearch-footer-height: 44px;
--docsearch-footer-background: #fff;
--docsearch-footer-shadow: 0 -1px 0 0 rgb(224, 227, 232), 0 -3px 6px 0 rgba(69, 98, 155, .12);
--docsearch-icon-color: rgb(21, 21, 21);
}
}
.dark {
/* Light theme */
[data-theme="light"] {
--coco-primary-color: rgb(149, 5, 153);
--coco-text-color: rgb(28, 30, 33);
--coco-muted-color: rgb(150, 159, 175);
--coco-modal-container-background: rgba(101, 108, 133, .8);
--coco-modal-background: rgb(245, 246, 247);
--coco-modal-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, .5), 0 3px 8px 0 rgba(85, 90, 100, 1);
--coco-searchbox-background: rgb(235, 237, 240);
--coco-searchbox-focus-background: #fff;
--coco-hit-color: rgb(68, 73, 80);
--coco-hit-active-color: #fff;
--coco-hit-background: #fff;
--coco-hit-shadow: 0 1px 3px 0 rgb(212, 217, 225);
--coco-key-gradient: linear-gradient(-225deg, rgb(213, 219, 228) 0%, rgb(248, 248, 248) 100%);
--coco-key-shadow: inset 0 -2px 0 0 rgb(205, 205, 230), inset 0 0 1px 1px #fff, 0 1px 2px 1px rgba(30, 35, 90, .4);
--coco-footer-background: #fff;
--coco-footer-shadow: 0 -1px 0 0 rgb(224, 227, 232), 0 -3px 6px 0 rgba(69, 98, 155, .12);
--coco-icon-color: rgb(21, 21, 21);
}
/* Dark theme */
[data-theme="dark"] {
--background: #09090b;
--foreground: #f9f9f9;
--border: #27272a;
--docsearch-text-color: rgb(245, 246, 247);
--docsearch-modal-container-background: rgba(9, 10, 17, .8);
--docsearch-modal-background: rgb(21, 23, 42);
--docsearch-modal-shadow: inset 1px 1px 0 0 rgb(44, 46, 64), 0 3px 8px 0 rgb(0, 3, 9);
--docsearch-searchbox-background: rgb(9, 10, 17);
--docsearch-searchbox-focus-background: #000;
--docsearch-hit-color: rgb(190, 195, 201);
--docsearch-hit-shadow: none;
--docsearch-hit-background: rgb(9, 10, 17);
--docsearch-key-gradient: linear-gradient(-26.5deg, rgb(86, 88, 114) 0%, rgb(49, 53, 91) 100%);
--docsearch-key-shadow: inset 0 -2px 0 0 rgb(40, 45, 85), inset 0 0 1px 1px rgb(81, 87, 125), 0 2px 2px 0 rgba(3, 4, 9, .3);
--docsearch-footer-background: rgb(30, 33, 54);
--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2);
--docsearch-muted-color: rgb(127, 132, 151);
--docsearch-icon-color: rgb(255, 255, 255);
}
--coco-text-color: rgb(245, 246, 247);
--coco-modal-container-background: rgba(9, 10, 17, .8);
--coco-modal-background: rgb(21, 23, 42);
--coco-modal-shadow: inset 1px 1px 0 0 rgb(44, 46, 64), 0 3px 8px 0 rgb(0, 3, 9);
--coco-searchbox-background: rgb(9, 10, 17);
--coco-searchbox-focus-background: #000;
--coco-hit-color: rgb(190, 195, 201);
--coco-hit-shadow: none;
--coco-hit-background: rgb(9, 10, 17);
--coco-key-gradient: linear-gradient(-26.5deg, rgb(86, 88, 114) 0%, rgb(49, 53, 91) 100%);
--coco-key-shadow: inset 0 -2px 0 0 rgb(40, 45, 85), inset 0 0 1px 1px rgb(81, 87, 125), 0 2px 2px 0 rgba(3, 4, 9, .3);
--coco-footer-background: rgb(30, 33, 54);
--coco-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2);
--coco-muted-color: rgb(127, 132, 151);
--coco-icon-color: rgb(255, 255, 255);
}
/* Base styles */
@layer base {
* {
@apply box-border border-[--border];
}
html{
html {
@apply h-full;
}
@@ -81,6 +84,7 @@
}
}
/* Component styles */
@layer components {
.settings-input {
@apply block w-full rounded-md border-gray-300 dark:border-gray-600
@@ -99,7 +103,9 @@
}
}
/* Utility styles */
@layer utilities {
/* Scrollbar styles */
.custom-scrollbar {
scrollbar-width: thin;
scrollbar-color: #cbd5e1 transparent;
@@ -126,10 +132,12 @@
background-color: #475569;
}
/* Background styles */
.bg-100 {
background-size: 100% 100%;
}
/* Error page styles */
#error-page {
display: flex;
justify-content: center;
@@ -187,19 +195,21 @@
background-color: #f79c42;
}
.docsearch-modal-footer-commands-key {
/* coco styles */
.coco-modal-footer-commands-key {
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
border: 0px;
border: 0;
padding: 2px;
background: var(--docsearch-key-gradient);
box-shadow: var(--docsearch-key-shadow);
color: var(--docsearch-muted-color);
background: var(--coco-key-gradient);
box-shadow: var(--coco-key-shadow);
color: var(--coco-muted-color);
}
.user-select{
/* User selection styles */
.user-select {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;

View File

@@ -42,5 +42,5 @@ export default {
},
plugins: [],
mode: "jit",
darkMode: "class",
darkMode: ["class", '[data-theme="dark"]'],
};