Update fonts, upadte logging, and auth.

This commit is contained in:
LukeGus
2025-08-08 00:40:32 -05:00
parent d0b139e388
commit 08f9143993
7 changed files with 160 additions and 63 deletions

View File

@@ -35,8 +35,8 @@ function App() {
} }
return ( return (
<div className="flex"> <div className="flex min-h-svh w-full">
<main> <main className="flex-1 w-full">
{renderActiveView()} {renderActiveView()}
</main> </main>
</div> </div>

View File

@@ -68,16 +68,14 @@ export function Homepage({onSelectView}: HomepageProps): React.ReactElement {
}, []); }, []);
return ( return (
<div className="flex min-h-screen">
<HomepageSidebar <HomepageSidebar
onSelectView={onSelectView} onSelectView={onSelectView}
disabled={!loggedIn || authLoading} disabled={!loggedIn || authLoading}
isAdmin={isAdmin} isAdmin={isAdmin}
username={loggedIn ? username : null} username={loggedIn ? username : null}
/> >
<div className="flex-1 bg-background grid grid-cols-3 items-center"> <div className="w-full min-h-svh grid place-items-center">
<div className="col-span-1"></div> <div className="flex flex-row items-center justify-center gap-8">
<div className="col-span-1 flex justify-center">
<HomepageAuth <HomepageAuth
setLoggedIn={setLoggedIn} setLoggedIn={setLoggedIn}
setIsAdmin={setIsAdmin} setIsAdmin={setIsAdmin}
@@ -87,13 +85,11 @@ export function Homepage({onSelectView}: HomepageProps): React.ReactElement {
dbError={dbError} dbError={dbError}
setDbError={setDbError} setDbError={setDbError}
/> />
</div>
<div className="col-span-1 flex justify-center">
<HomepageUpdateLog <HomepageUpdateLog
loggedIn={loggedIn} loggedIn={loggedIn}
/> />
</div> </div>
</div> </div>
</div> </HomepageSidebar>
); );
} }

View File

@@ -14,7 +14,7 @@ import {
SidebarGroupLabel, SidebarGroupLabel,
SidebarMenu, SidebarMenu,
SidebarMenuButton, SidebarMenuButton,
SidebarMenuItem, SidebarProvider, SidebarMenuItem, SidebarProvider, SidebarInset,
} from "@/components/ui/sidebar.tsx" } from "@/components/ui/sidebar.tsx"
import { import {
@@ -44,6 +44,7 @@ interface SidebarProps {
disabled?: boolean; disabled?: boolean;
isAdmin?: boolean; isAdmin?: boolean;
username?: string | null; username?: string | null;
children?: React.ReactNode;
} }
function handleLogout() { function handleLogout() {
@@ -72,7 +73,8 @@ export function HomepageSidebar({
getView, getView,
disabled, disabled,
isAdmin, isAdmin,
username username,
children,
}: SidebarProps): React.ReactElement { }: SidebarProps): React.ReactElement {
const [adminSheetOpen, setAdminSheetOpen] = React.useState(false); const [adminSheetOpen, setAdminSheetOpen] = React.useState(false);
const [allowRegistration, setAllowRegistration] = React.useState(true); const [allowRegistration, setAllowRegistration] = React.useState(true);
@@ -160,7 +162,7 @@ export function HomepageSidebar({
}; };
return ( return (
<div> <div className="min-h-svh">
<SidebarProvider> <SidebarProvider>
<Sidebar> <Sidebar>
<SidebarContent> <SidebarContent>
@@ -430,6 +432,9 @@ export function HomepageSidebar({
</Sheet> </Sheet>
)} )}
</Sidebar> </Sidebar>
<SidebarInset>
{children}
</SidebarInset>
</SidebarProvider> </SidebarProvider>
</div> </div>
) )

View File

@@ -98,7 +98,7 @@ export function HomepageUpdateLog({loggedIn}: HomepageUpdateLogProps) {
}; };
return ( return (
<div className="w-[400px] h-[600px] mr-8 flex flex-col border border-border rounded-lg bg-card p-4"> <div className="w-[400px] h-[600px] flex flex-col border border-border rounded-lg bg-card p-4">
<div> <div>
<h3 className="text-lg font-semibold mb-3">Updates & Releases</h3> <h3 className="text-lg font-semibold mb-3">Updates & Releases</h3>

View File

@@ -38,6 +38,7 @@ import {
import {ScrollArea} from "@/components/ui/scroll-area.tsx"; import {ScrollArea} from "@/components/ui/scroll-area.tsx";
import {Input} from "@/components/ui/input.tsx"; import {Input} from "@/components/ui/input.tsx";
import {getSSHHosts} from "@/apps/SSH/ssh-axios"; import {getSSHHosts} from "@/apps/SSH/ssh-axios";
import {Checkbox} from "@/components/ui/checkbox.tsx";
interface SSHHost { interface SSHHost {
id: number; id: number;
@@ -186,6 +187,17 @@ export function SSHSidebar({onSelectView, onHostConnect, allTabs, runCommandOnTa
} }
}; };
function getCookie(name: string) {
return document.cookie.split('; ').reduce((r, v) => {
const parts = v.split('=');
return parts[0] === name ? decodeURIComponent(parts[1]) : r;
}, "");
}
const updateRightClickCopyPaste = (checked) => {
document.cookie = `rightClickCopyPaste=${checked}; expires=2147483647; path=/`;
}
return ( return (
<SidebarProvider> <SidebarProvider>
<Sidebar className="h-full flex flex-col overflow-hidden"> <Sidebar className="h-full flex flex-col overflow-hidden">
@@ -332,6 +344,19 @@ export function SSHSidebar({onSelectView, onHostConnect, allTabs, runCommandOnTa
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
</Accordion> </Accordion>
<Separator className="p-0.25"/>
<div className="flex items-center space-x-2 mt-5">
<Checkbox id="enable-copy-paste" onCheckedChange={updateRightClickCopyPaste}
defaultChecked={getCookie("rightClickCopyPaste") === "true"}/>
<label
htmlFor="enable-paste"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Enable rightclick copy/paste
</label>
</div>
</div> </div>
</SheetContent> </SheetContent>
</Sheet> </Sheet>

View File

@@ -52,6 +52,49 @@ export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTermina
fitAddonRef.current?.fit(); fitAddonRef.current?.fit();
} }
function getCookie(name: string) {
return document.cookie.split('; ').reduce((r, v) => {
const parts = v.split('=');
return parts[0] === name ? decodeURIComponent(parts[1]) : r;
}, "");
}
function getUseRightClickCopyPaste() {
return getCookie("rightClickCopyPaste") === "true"
}
async function writeTextToClipboard(text: string): Promise<void> {
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(text);
return;
}
} catch (_) {
}
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
try {
document.execCommand('copy');
} finally {
document.body.removeChild(textarea);
}
}
async function readTextFromClipboard(): Promise<string> {
try {
if (navigator.clipboard && navigator.clipboard.readText) {
return await navigator.clipboard.readText();
}
} catch (_) {
}
return '';
}
useEffect(() => { useEffect(() => {
if (!terminal || !xtermRef.current || !hostConfig) return; if (!terminal || !xtermRef.current || !hostConfig) return;
@@ -59,7 +102,7 @@ export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTermina
cursorBlink: true, cursorBlink: true,
cursorStyle: 'bar', cursorStyle: 'bar',
scrollback: 10000, scrollback: 10000,
fontSize: 15, fontSize: 14,
fontFamily: '"JetBrains Mono Nerd Font", "MesloLGS NF", "FiraCode Nerd Font", "Cascadia Code", "JetBrains Mono", Consolas, "Courier New", monospace', fontFamily: '"JetBrains Mono Nerd Font", "MesloLGS NF", "FiraCode Nerd Font", "Cascadia Code", "JetBrains Mono", Consolas, "Courier New", monospace',
theme: { theme: {
background: '#09090b', background: '#09090b',
@@ -88,6 +131,31 @@ export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTermina
terminal.loadAddon(webLinksAddon); terminal.loadAddon(webLinksAddon);
terminal.open(xtermRef.current); terminal.open(xtermRef.current);
const element = xtermRef.current;
const handleContextMenu = async (e: MouseEvent) => {
if (!getUseRightClickCopyPaste()) return;
e.preventDefault();
e.stopPropagation();
try {
if (terminal.hasSelection()) {
const selection = terminal.getSelection();
if (selection) {
await writeTextToClipboard(selection);
terminal.clearSelection();
}
} else {
const pasteText = await readTextFromClipboard();
if (pasteText) {
terminal.paste(pasteText);
}
}
} catch (_) {
}
};
if (element) {
element.addEventListener('contextmenu', handleContextMenu);
}
const resizeObserver = new ResizeObserver(() => { const resizeObserver = new ResizeObserver(() => {
if (resizeTimeout.current) clearTimeout(resizeTimeout.current); if (resizeTimeout.current) clearTimeout(resizeTimeout.current);
resizeTimeout.current = setTimeout(() => { resizeTimeout.current = setTimeout(() => {
@@ -158,6 +226,9 @@ export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTermina
return () => { return () => {
resizeObserver.disconnect(); resizeObserver.disconnect();
if (element) {
element.removeEventListener('contextmenu', handleContextMenu);
}
if (resizeTimeout.current) clearTimeout(resizeTimeout.current); if (resizeTimeout.current) clearTimeout(resizeTimeout.current);
if (pingIntervalRef.current) { if (pingIntervalRef.current) {
clearInterval(pingIntervalRef.current); clearInterval(pingIntervalRef.current);