chore: use async loading for main window

This commit is contained in:
haritabh-z01 2025-06-29 21:17:02 +05:30
parent 469b290a53
commit 71040d079c
5 changed files with 157 additions and 151 deletions

View file

@ -15,38 +15,6 @@
</head>
<body style="margin: 0; font-family: system-ui, -apple-system, sans-serif;">
<div id="root"></div>
<!-- Loading splash that adapts to system theme -->
<div id="loading-splash" style="
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: light-dark(#ffffff, #0a0a0a);
color: light-dark(#0a0a0a, #fafafa);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 16px;
z-index: 9999;
">
<div style="
width: 48px;
height: 48px;
border: 3px solid light-dark(#e4e4e7, #27272a);
border-top: 3px solid light-dark(#0a0a0a, #fafafa);
border-radius: 50%;
animation: spin 1s linear infinite;
"></div>
<div style="font-size: 14px; opacity: 0.7;">Loading Amical...</div>
</div>
<style>
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<script type="module" src="src/renderer/main/index.tsx"></script>
</body>
</html>

View file

@ -90,6 +90,7 @@ export class AppManager {
private async setupWindows(): Promise<void> {
this.windowManager.createWidgetWindow();
this.windowManager.createOrShowMainWindow();
this.setupTRPCHandler();
if (process.platform === "darwin" && app.dock) {

View file

@ -0,0 +1,122 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useState, useEffect } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ipcLink } from "electron-trpc-experimental/renderer";
import superjson from "superjson";
import {
SidebarProvider,
SidebarInset,
SidebarTrigger,
} from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/app-sidebar";
import { TranscriptionsPage } from "./pages/transcriptions";
import { VocabularyPage } from "./pages/vocabulary";
import { ModelsPage } from "./pages/models";
import { SettingsPage } from "./pages/settings";
import { SiteHeader } from "@/components/site-header";
import { api } from "@/trpc/react";
// import { Waveform } from '../components/Waveform'; // Waveform might not be needed if hook is removed
// import { useRecording } from '../hooks/useRecording'; // Remove hook import
const NUM_WAVEFORM_BARS = 10; // This might be unused now
// Create a client
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
refetchOnWindowFocus: false,
},
},
});
// Create tRPC client
const trpcClient = api.createClient({
links: [ipcLink({ transformer: superjson })],
});
const App: React.FC = () => {
const [currentView, setCurrentView] = useState(() => {
// Try to restore the view from localStorage, fallback to default
if (typeof window !== "undefined") {
return localStorage.getItem("amical-current-view") || "Voice Recording";
}
return "Voice Recording";
});
const handleNavigation = (item: any) => {
setCurrentView(item.title);
// Save to localStorage to preserve during HMR
localStorage.setItem("amical-current-view", item.title);
};
const renderContent = () => {
switch (currentView) {
case "Transcriptions":
return <TranscriptionsPage />;
case "Vocabulary":
return <VocabularyPage />;
case "Models":
return <ModelsPage />;
case "Settings":
return <SettingsPage />;
default:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold">Welcome to Amical</h2>
<p>Select an option from the sidebar to get started.</p>
</div>
);
}
};
return (
<api.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<SidebarProvider
style={
{
"--sidebar-width": "calc(var(--spacing) * 72)",
"--header-height": "calc(var(--spacing) * 12)",
} as React.CSSProperties
}
>
<div className="flex h-screen w-screen flex-col">
{/* Header spans full width with traffic light spacing */}
<SiteHeader currentView={currentView} />
<div className="flex flex-1 min-h-0">
<AppSidebar
variant="inset"
onNavigate={handleNavigation}
currentView={currentView}
/>
<SidebarInset>
<div className="flex flex-1 flex-col min-h-0">
<div className="@container/main flex flex-1 flex-col min-h-0 overflow-hidden">
<div className="flex-1 overflow-y-auto">
<div
className="mx-auto w-full flex flex-col gap-4 md:gap-6"
style={{
maxWidth: "var(--content-max-width)",
padding: "var(--content-padding)",
}}
>
{renderContent()}
</div>
</div>
</div>
</div>
</SidebarInset>
</div>
</div>
</SidebarProvider>
</QueryClientProvider>
</api.Provider>
);
};
// Export the App component as default for lazy loading
export default App;

View file

@ -1,24 +1,15 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useState, useEffect } from "react";
import React, { Suspense } from "react";
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ipcLink } from "electron-trpc-experimental/renderer";
import superjson from "superjson";
import {
SidebarProvider,
SidebarInset,
SidebarTrigger,
} from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/app-sidebar";
import { ThemeProvider } from "@/components/theme-provider";
import { TranscriptionsPage } from "./pages/transcriptions";
import { VocabularyPage } from "./pages/vocabulary";
import { ModelsPage } from "./pages/models";
import { SettingsPage } from "./pages/settings";
import "@/styles/globals.css";
import { SiteHeader } from "@/components/site-header";
import { api } from "@/trpc/react";
import { ThemeProvider } from "@/components/theme-provider";
// Lazy import the main content
const Content = React.lazy(
() =>
import("./content.js") as unknown as Promise<{
default: React.ComponentType<any>;
}>,
);
// Extend Console interface to include original methods
declare global {
@ -62,109 +53,33 @@ console.debug = (...args: any[]) => {
// Keep original methods available if needed
console.original = originalConsole;
// import { Waveform } from '../components/Waveform'; // Waveform might not be needed if hook is removed
// import { useRecording } from '../hooks/useRecording'; // Remove hook import
const NUM_WAVEFORM_BARS = 10; // This might be unused now
// Create a client
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
refetchOnWindowFocus: false,
},
},
});
// Create tRPC client
const trpcClient = api.createClient({
links: [ipcLink({ transformer: superjson })],
});
const App: React.FC = () => {
const [currentView, setCurrentView] = useState(() => {
// Try to restore the view from localStorage, fallback to default
if (typeof window !== "undefined") {
return localStorage.getItem("amical-current-view") || "Voice Recording";
}
return "Voice Recording";
});
const handleNavigation = (item: any) => {
setCurrentView(item.title);
// Save to localStorage to preserve during HMR
localStorage.setItem("amical-current-view", item.title);
};
const renderContent = () => {
switch (currentView) {
case "Transcriptions":
return <TranscriptionsPage />;
case "Vocabulary":
return <VocabularyPage />;
case "Models":
return <ModelsPage />;
case "Settings":
return <SettingsPage />;
default:
return (
<div className="space-y-4">
<h2 className="text-2xl font-bold">Welcome to Amical</h2>
<p>Select an option from the sidebar to get started.</p>
</div>
);
}
};
// Loading spinner component
const LoadingSpinner: React.FC = () => {
return (
<api.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<ThemeProvider>
<SidebarProvider
style={
{
"--sidebar-width": "calc(var(--spacing) * 72)",
"--header-height": "calc(var(--spacing) * 12)",
} as React.CSSProperties
}
>
<div className="flex h-screen w-screen flex-col">
{/* Header spans full width with traffic light spacing */}
<SiteHeader currentView={currentView} />
<div className="flex flex-1 min-h-0">
<AppSidebar
variant="inset"
onNavigate={handleNavigation}
currentView={currentView}
/>
<SidebarInset>
<div className="flex flex-1 flex-col min-h-0">
<div className="@container/main flex flex-1 flex-col min-h-0 overflow-hidden">
<div className="flex-1 overflow-y-auto">
<div
className="mx-auto w-full flex flex-col gap-4 md:gap-6"
style={{
maxWidth: "var(--content-max-width)",
padding: "var(--content-padding)",
}}
>
{renderContent()}
</div>
</div>
</div>
</div>
</SidebarInset>
</div>
</div>
</SidebarProvider>
</ThemeProvider>
</QueryClientProvider>
</api.Provider>
<div className="flex items-center justify-center min-h-screen bg-background">
<div className="flex flex-col items-center gap-4">
<div className="relative">
<div className="w-12 h-12 border-4 border-muted rounded-full"></div>
<div className="w-12 h-12 border-4 border-foreground border-t-transparent rounded-full animate-spin absolute top-0 left-0"></div>
</div>
<p className="text-sm text-muted-foreground">Loading Amical...</p>
</div>
</div>
);
};
// Main App component with Suspense
const App: React.FC = () => {
return (
<ThemeProvider>
<Suspense fallback={<LoadingSpinner />}>
<Content />
</Suspense>
</ThemeProvider>
);
};
// Render the app
const container = document.getElementById("root");
if (container) {
const root = createRoot(container);

View file

@ -2,7 +2,7 @@
<html>
<head>
<meta charset="UTF-8" />
<title>Floating Button</title>
<title>Widget</title>
<!-- Favicon links -->
<link rel="icon" type="image/x-icon" href="/assets/logo.ico" />
<link rel="icon" type="image/png" sizes="16x16" href="/assets/icon-16x16.png" />