mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-03 05:29:12 +00:00
- Document is first converted in the chatinputarea, then sent to the chat component. From there, it's sent in the chat API body and then processed by the backend - We couldn't directly use a UploadFile type in the backend API because we'd have to convert the api type to a multipart form. This would require other client side migrations without uniform benefit, which is why we do it in this two-phase process. This also gives us capacity to repurpose the moe generic interface down the road.
99 lines
2.7 KiB
TypeScript
99 lines
2.7 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import useSWR from "swr";
|
|
|
|
export interface LocationData {
|
|
city?: string;
|
|
region?: string;
|
|
country?: string;
|
|
countryCode?: string;
|
|
timezone: string;
|
|
}
|
|
|
|
const locationFetcher = () =>
|
|
window
|
|
.fetch("https://ipapi.co/json")
|
|
.then((res) => res.json())
|
|
.catch((err) => console.log(err));
|
|
|
|
export const toTitleCase = (str: string) =>
|
|
str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase());
|
|
|
|
export function welcomeConsole() {
|
|
console.log(
|
|
`%c %s`,
|
|
"font-family:monospace",
|
|
`
|
|
__ __ __ __ ______ __ _____ __
|
|
/\\ \\/ / /\\ \\_\\ \\ /\\ __ \\ /\\ \\ /\\ __ \\ /\\ \\
|
|
\\ \\ _"-. \\ \\ __ \\ \\ \\ \\/\\ \\ _\\_\\ \\ \\ \\ __ \\ \\ \\ \\
|
|
\\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\ \\_____\\ /\\_____\\ \\ \\_\\ \\_\\ \\ \\_\\
|
|
\\/_/\\/_/ \\/_/\\/_/ \\/_____/ \\/_____/ \\/_/\\/_/ \\/_/
|
|
|
|
|
|
Greetings traveller,
|
|
|
|
I am ✨Khoj✨, your open-source, personal AI copilot.
|
|
|
|
See my source code at https://github.com/khoj-ai/khoj
|
|
Read my operating manual at https://docs.khoj.dev
|
|
`,
|
|
);
|
|
}
|
|
|
|
export function useIPLocationData() {
|
|
const { data: locationData, error: locationDataError } = useSWR<LocationData>(
|
|
"/api/ip",
|
|
locationFetcher,
|
|
{ revalidateOnFocus: false },
|
|
);
|
|
|
|
if (locationDataError || !locationData) return;
|
|
return locationData;
|
|
}
|
|
|
|
export function useIsMobileWidth() {
|
|
const [isMobileWidth, setIsMobileWidth] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const handleResize = () => {
|
|
if (window.innerWidth <= 768) {
|
|
setIsMobileWidth(true);
|
|
} else {
|
|
setIsMobileWidth(false);
|
|
}
|
|
};
|
|
|
|
handleResize();
|
|
window.addEventListener("resize", handleResize);
|
|
return () => window.removeEventListener("resize", handleResize);
|
|
}, []);
|
|
|
|
return isMobileWidth;
|
|
}
|
|
|
|
export const convertBytesToText = (fileSize: number) => {
|
|
if (fileSize < 1024) {
|
|
return `${fileSize} B`;
|
|
} else if (fileSize < 1024 * 1024) {
|
|
return `${(fileSize / 1024).toFixed(2)} KB`;
|
|
} else {
|
|
return `${(fileSize / (1024 * 1024)).toFixed(2)} MB`;
|
|
}
|
|
};
|
|
|
|
export function useDebounce<T>(value: T, delay: number): T {
|
|
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
|
|
useEffect(() => {
|
|
const handler = setTimeout(() => {
|
|
setDebouncedValue(value);
|
|
}, delay);
|
|
|
|
return () => {
|
|
clearTimeout(handler);
|
|
};
|
|
}, [value, delay]);
|
|
|
|
return debouncedValue;
|
|
}
|