chore: use async loading for main window
This commit is contained in:
parent
469b290a53
commit
71040d079c
5 changed files with 157 additions and 151 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
122
apps/desktop/src/renderer/main/content.tsx
Normal file
122
apps/desktop/src/renderer/main/content.tsx
Normal 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;
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue