Files
oikos/web/vite.config.ts
dtoro aed068de12
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
ci / web (push) Has been cancelled
Desktop App / Build Linux (amd64) (push) Has been cancelled
Desktop App / Attach to Release (push) Has been cancelled
feat(web): redesign UI as an OS-style desktop shell
Replace the sidebar + hash-routed page shell with a desktop metaphor:
draggable app icons, apps opening as floating wmkit windows, a centered
"What should Nomos do?" task launcher, and a bottom taskbar showing all
open windows plus a system tray.

- New app registry ($lib/apps.ts) — adding an app is one entry, nothing
  else to touch.
- New desktop shell components (Desktop, WindowLayer, DesktopIcon,
  Taskbar, TaskLauncher) under $lib/components/desktop-shell/.
- Icon positions are a persisted, collision-avoiding grid ($lib/stores/icons.ts).
- Window layout persists across reloads (wmkit persist), with
  drag-to-maximize, F6 window cycling, and now a right-click desktop menu
  (cascade/tile/show desktop/reset icons) plus Cmd/Ctrl+Z undo/redo for
  window moves, resizes, and closes.
- Taskbar buttons get a hover-close and self-correct their title once a
  new task's real goal is known.
- New task windows (desktop launcher and the Tasks app's "New task"
  button) open as a window, not a dialog, and hand off to the real
  session window once the backend assigns an id.
- Fixed a real gap along the way: GET /sessions/{id} couldn't tell
  "session deleted" from "session has no messages yet" (both returned
  200 with an empty list) — cmd/nomos/main.go now checks existence and
  404s, so a stale/persisted task window shows "Task not found" instead
  of a misleadingly empty, live-looking chat.
- Test coverage for the new pure logic (icon placement/collision
  avoidance, app registry id helpers) plus a vitest matchMedia polyfill
  needed to import anything touching the theme store.

Deletes the now-superseded sidebar shell, MinimizedWindowsBar, and the
standalone Chat/EntityDetail pages (folded into the window layer).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 21:34:15 +02:00

70 lines
2.5 KiB
TypeScript

/// <reference types="vitest/config" />
import type { ProxyOptions } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import tailwindcss from '@tailwindcss/vite'
import { readFileSync, existsSync } from 'fs'
import { defineConfig } from 'vite'
// In Docker, VERSION is copied into the build WORKDIR (/build/web/VERSION).
// In local dev, the cwd is web/ and VERSION is two dirs up (../../VERSION
// from vite.config.ts location in web/). Try both.
let version: string
if (existsSync('../VERSION')) {
version = readFileSync('../VERSION', 'utf-8').trim()
} else {
version = readFileSync('VERSION', 'utf-8').trim()
}
// Injects OIKOS_API_TOKEN into proxied /api requests in dev — the API no
// longer has a dev-open bypass (plans/2026-07-12-wails-desktop-app.md 0.4),
// so `OIKOS_API_TOKEN=dev-token npm run dev` needs this to reach it.
function authProxy(target: string, rewrite?: (path: string) => string): ProxyOptions {
return {
target,
...(rewrite ? { rewrite } : {}),
configure: (proxy) => {
proxy.on('proxyReq', (proxyReq) => {
const token = process.env.OIKOS_API_TOKEN
if (token) proxyReq.setHeader('Authorization', `Bearer ${token}`)
})
}
}
}
export default defineConfig({
plugins: [tailwindcss(), svelte()],
base: '/',
define: {
__OIKOS_VERSION__: JSON.stringify(`v${version}`),
// Lets the dev server auto-configure the SPA with the same token it
// already injects into proxied requests (see authProxy above), so `npm
// run dev` skips the "Connect to Oikos" prompt instead of re-asking for
// a token every time localStorage gets cleared. Empty string (never a
// real prod secret — see main.ts, only consulted in import.meta.env.DEV)
// when OIKOS_API_TOKEN isn't set, so the prompt still shows if unconfigured.
__OIKOS_DEV_TOKEN__: JSON.stringify(process.env.OIKOS_API_TOKEN ?? ''),
},
resolve: {
alias: { $lib: '/src/lib' }
},
build: {
outDir: 'dist',
emptyOutDir: true
},
server: {
proxy: {
'/api': authProxy('http://localhost:8090'),
// Production Caddy strips /agent before forwarding to nomos
// (compose/caddy/Caddyfile.oikos handle_path /agent/*); match that
// here so dev and prod agree on nomos's actual route paths.
'/agent': authProxy('http://localhost:8092', (path) => path.replace(/^\/agent/, ''))
}
},
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['src/test-setup.ts'],
include: ['src/**/*.{test,spec}.{ts,js}']
}
})