### Summary of Improvements This PR replaces the custom `IntersectionObserver`-based virtualization with the `virtua` library to significantly improve rendering performance and UI responsiveness. ### 🚀 Performance Results Verified using `session-performance.test.ts`: - **Rendering**: 2000 messages rendered in **16.90ms**. - **Huge Conversation**: 10,000 messages processed in **0.80ms**. - **Session Switching**: Average switch time reduced to **0.58ms** (virtually zero lag). ### 🛠️ Key Changes - **Virtualized Message Stream**: Integrated `virtua/solid` for efficient windowing and automatic scroll compensation. - **Floating Scroll Controls**: Applied `position: absolute` and `pointer-events: none` to the list controls to ensure scroll-to-top/bottom buttons float correctly over the message area without blocking interactions. - **Package Synchronization**: Updated `virtua` and SDK dependencies, with a fully synchronized `package-lock.json` for stable builds. ### 🎥 UI Verification https://github.com/user-attachments/assets/24e483a3-8be6-4ac4-a431-d719f2015f4e - **Smooth Scrolling**: Verified that rendering gaps are eliminated during fast scrolls. - **Position Retention**: Scroll positions are preserved when switching between sessions. > [!NOTE] > Detailed performance gains and layout fixes are isolated to the `virtua` implementation and core package updates, following the requested cleanup. --------- Co-authored-by: Shantur Rathore <i@shantur.com>
140 lines
4.5 KiB
TypeScript
140 lines
4.5 KiB
TypeScript
import fs from "fs"
|
|
import { defineConfig } from "vite"
|
|
import solid from "vite-plugin-solid"
|
|
import { VitePWA } from "vite-plugin-pwa"
|
|
import { resolve } from "path"
|
|
import { copyMonacoPublicAssets } from "./scripts/monaco-public-assets.js"
|
|
|
|
const uiPackageJson = JSON.parse(fs.readFileSync(resolve(__dirname, "package.json"), "utf-8")) as { version?: string }
|
|
const uiVersion = uiPackageJson.version ?? "0.0.0"
|
|
|
|
export default defineConfig({
|
|
root: "./src/renderer",
|
|
plugins: [
|
|
solid(),
|
|
{
|
|
name: "prepare-monaco-public-assets",
|
|
// Ensure Monaco's AMD assets exist in `root/public` for both dev server and builds.
|
|
// These files are gitignored and generated on demand.
|
|
configureServer(server) {
|
|
copyMonacoPublicAssets({
|
|
uiRendererRoot: resolve(__dirname, "src/renderer"),
|
|
warn: (msg) => server.config.logger.warn(msg),
|
|
sourceRoots: [
|
|
resolve(__dirname, "../../node_modules/monaco-editor/min/vs"),
|
|
resolve(__dirname, "node_modules/monaco-editor/min/vs"),
|
|
],
|
|
})
|
|
},
|
|
buildStart() {
|
|
copyMonacoPublicAssets({
|
|
uiRendererRoot: resolve(__dirname, "src/renderer"),
|
|
warn: (msg) => this.warn(msg),
|
|
sourceRoots: [
|
|
resolve(__dirname, "../../node_modules/monaco-editor/min/vs"),
|
|
resolve(__dirname, "node_modules/monaco-editor/min/vs"),
|
|
],
|
|
})
|
|
},
|
|
},
|
|
{
|
|
name: "emit-ui-version",
|
|
generateBundle() {
|
|
this.emitFile({
|
|
type: "asset",
|
|
fileName: "ui-version.json",
|
|
source: JSON.stringify({ uiVersion }, null, 2),
|
|
})
|
|
},
|
|
},
|
|
{
|
|
name: "prepare-pwa-source-icon",
|
|
apply: "build",
|
|
buildStart() {
|
|
// vite-pwa-assets requires the source image inside root/public/
|
|
const source = resolve(__dirname, "src/images/CodeNomad-Icon.png")
|
|
const publicDir = resolve(__dirname, "src/renderer/public")
|
|
const dest = resolve(publicDir, "logo.png")
|
|
fs.mkdirSync(publicDir, { recursive: true })
|
|
fs.copyFileSync(source, dest)
|
|
},
|
|
},
|
|
VitePWA({
|
|
registerType: "autoUpdate",
|
|
injectRegister: "auto",
|
|
pwaAssets: {
|
|
preset: "minimal-2023",
|
|
image: "public/logo.png",
|
|
},
|
|
manifest: {
|
|
name: "CodeNomad",
|
|
short_name: "CodeNomad",
|
|
id: "/",
|
|
start_url: "/",
|
|
display: "standalone",
|
|
display_override: ["window-controls-overlay", "standalone"],
|
|
background_color: "#1a1a1a",
|
|
theme_color: "#1a1a1a",
|
|
},
|
|
workbox: {
|
|
// Workbox defaults to 2 MiB; our main bundle can slightly exceed that.
|
|
// This is a build-time limit for the precache manifest, not a hard runtime cap.
|
|
maximumFileSizeToCacheInBytes: 3 * 1024 * 1024,
|
|
// Preserve server-side auth redirects (e.g., /login) instead of serving cached index.html.
|
|
navigateFallback: null,
|
|
// Only precache static assets (avoid caching HTML documents / routes).
|
|
globPatterns: ["**/*.{js,css,png,jpg,jpeg,svg,webp,ico,woff,woff2,ttf,eot,json,webmanifest}"],
|
|
// Monaco assets can be large; cache them at runtime instead.
|
|
globIgnores: [
|
|
"**/*.html",
|
|
"**/assets/*worker-*.js",
|
|
"**/assets/editor.api-*.js",
|
|
"**/monaco/vs/**/*",
|
|
],
|
|
// Only cache static UI assets; never cache API traffic.
|
|
runtimeCaching: [
|
|
{
|
|
urlPattern: ({ url, request }) => {
|
|
if (url.pathname.startsWith("/api/")) return false
|
|
if (request.destination === "document") return false
|
|
return ["script", "style", "image", "font"].includes(request.destination)
|
|
},
|
|
handler: "CacheFirst",
|
|
options: {
|
|
cacheName: "asset-cache",
|
|
expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 },
|
|
cacheableResponse: { statuses: [0, 200] },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
],
|
|
css: {
|
|
postcss: "./postcss.config.js",
|
|
},
|
|
resolve: {
|
|
alias: {
|
|
"@": resolve(__dirname, "./src"),
|
|
},
|
|
},
|
|
optimizeDeps: {
|
|
exclude: ["lucide-solid"],
|
|
},
|
|
ssr: {
|
|
noExternal: ["lucide-solid"],
|
|
},
|
|
server: {
|
|
port: 3000,
|
|
},
|
|
build: {
|
|
outDir: "dist",
|
|
rollupOptions: {
|
|
input: {
|
|
main: resolve(__dirname, "./src/renderer/index.html"),
|
|
loading: resolve(__dirname, "./src/renderer/loading.html"),
|
|
},
|
|
},
|
|
},
|
|
})
|