Use OpenCode v2 file APIs for browsing and Monaco DiffEditor for session snapshot diffs, with local baseline language metadata and optional CDN language loading.
186 lines
6.2 KiB
TypeScript
186 lines
6.2 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"
|
|
|
|
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",
|
|
buildStart() {
|
|
const publicDir = resolve(__dirname, "src/renderer/public")
|
|
const destRoot = resolve(publicDir, "monaco/vs")
|
|
|
|
const candidates = [
|
|
resolve(__dirname, "../../node_modules/monaco-editor/min/vs"),
|
|
resolve(__dirname, "node_modules/monaco-editor/min/vs"),
|
|
]
|
|
const sourceRoot = candidates.find((p) => fs.existsSync(resolve(p, "loader.js")))
|
|
if (!sourceRoot) {
|
|
this.warn("Monaco source directory not found; skipping copy")
|
|
return
|
|
}
|
|
|
|
fs.mkdirSync(destRoot, { recursive: true })
|
|
|
|
const copyRecursive = (src: string, dest: string) => {
|
|
const stat = fs.statSync(src)
|
|
if (stat.isDirectory()) {
|
|
fs.mkdirSync(dest, { recursive: true })
|
|
for (const entry of fs.readdirSync(src)) {
|
|
copyRecursive(resolve(src, entry), resolve(dest, entry))
|
|
}
|
|
return
|
|
}
|
|
fs.copyFileSync(src, dest)
|
|
}
|
|
|
|
// Keep the working tree clean; these assets are generated.
|
|
try {
|
|
fs.rmSync(destRoot, { recursive: true, force: true })
|
|
} catch {
|
|
// ignore
|
|
}
|
|
fs.mkdirSync(destRoot, { recursive: true })
|
|
|
|
// Copy core Monaco runtime.
|
|
for (const dir of ["base", "editor", "platform"] as const) {
|
|
const src = resolve(sourceRoot, dir)
|
|
if (fs.existsSync(src)) {
|
|
copyRecursive(src, resolve(destRoot, dir))
|
|
}
|
|
}
|
|
// loader.js is required.
|
|
copyRecursive(resolve(sourceRoot, "loader.js"), resolve(destRoot, "loader.js"))
|
|
|
|
// Copy baseline rich language packages + workers.
|
|
for (const lang of ["typescript", "html", "json", "css"] as const) {
|
|
const src = resolve(sourceRoot, "language", lang)
|
|
if (fs.existsSync(src)) {
|
|
copyRecursive(src, resolve(destRoot, "language", lang))
|
|
}
|
|
}
|
|
|
|
// Copy baseline basic tokenizers.
|
|
for (const lang of ["python", "markdown", "cpp", "kotlin"] as const) {
|
|
const src = resolve(sourceRoot, "basic-languages", lang)
|
|
if (fs.existsSync(src)) {
|
|
copyRecursive(src, resolve(destRoot, "basic-languages", lang))
|
|
}
|
|
}
|
|
|
|
// Copy monaco.contribution.js entrypoints (needed by some loads).
|
|
const monacoContribution = resolve(sourceRoot, "basic-languages", "monaco.contribution.js")
|
|
if (fs.existsSync(monacoContribution)) {
|
|
copyRecursive(monacoContribution, resolve(destRoot, "basic-languages", "monaco.contribution.js"))
|
|
}
|
|
const underscoreContribution = resolve(sourceRoot, "basic-languages", "_.contribution.js")
|
|
if (fs.existsSync(underscoreContribution)) {
|
|
copyRecursive(underscoreContribution, resolve(destRoot, "basic-languages", "_.contribution.js"))
|
|
}
|
|
},
|
|
},
|
|
{
|
|
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: {
|
|
// 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"),
|
|
},
|
|
},
|
|
},
|
|
})
|