gui: port frontend to Wails v2

Frontend build failure root cause:
- Vite 8 uses rolldown with Wails v3 typed-events plugin
- @wailsio/runtime (Wails v3) in frontend dependencies
- vite.config.js had wails('./bindings') plugin from Wails v3 template
- main.js used Svelte 5 mount() API but Svelte 4 required

Fixes:
- Remove @wailsio/runtime dependency
- Remove wails('./bindings') plugin from vite.config.js
- Replace Vite 8 with Vite 5.4.21 + Rollup (stable)
- Downgrade Svelte 5 to Svelte 4.2.19
- Downgrade @sveltejs/vite-plugin-svelte to v3.1.2
- Fix main.js: mount() -> new App({ target })
- Rewrite App.svelte with Wails v2 binding calls (window.go.main.App.*)
- UI: sidebar with sections, nodes, basic navigation

Build: cd frontend && npm run build -> dist/ (476ms)
Build GUI: go build -tags 'gui production webkit2_41' -o verstak-gui ./cmd/verstak-gui
Run: ./verstak-gui (window opens, no SIGSEGV)
This commit is contained in:
2026-05-31 19:39:49 +08:00
parent c65187f656
commit 77a7918569
12 changed files with 1162 additions and 717 deletions
+850 -699
View File
File diff suppressed because it is too large Load Diff
+4 -7
View File
@@ -5,16 +5,13 @@
"type": "module",
"scripts": {
"dev": "vite",
"build:dev": "vite build --minify false --mode development",
"build": "vite build --mode production",
"preview": "vite preview"
},
"dependencies": {
"@wailsio/runtime": "latest"
},
"dependencies": {},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"svelte": "^5.46.4",
"vite": "^8.0.5"
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"svelte": "^4.2.19",
"vite": "^5.4.21"
}
}
+124 -5
View File
@@ -1,10 +1,129 @@
<script>
// Stub — will be replaced with actual UI
import { onMount } from 'svelte'
let sections = []
let nodes = []
let version = ''
let error = ''
let selectedSection = ''
let selectedNode = null
// Wails v2 bindings — generated by wails
// For now, use window.wails or import from wailsjs
async function callGo(method, ...args) {
if (window.go && window.go.main && window.go.main.App) {
return window.go.main.App[method](...args)
}
throw new Error('Wails bindings not loaded')
}
onMount(async () => {
try {
version = 'verstak-gui'
// Try Wails bindings
if (window.go && window.go.main && window.go.main.App) {
version = await window.go.main.App.VerstakVersion()
sections = await window.go.main.App.ListSections()
nodes = await window.go.main.App.ListRootNodes()
}
} catch (e) {
error = String(e)
}
})
function selectSection(id) {
selectedSection = id
// TODO: load nodes for section
}
function selectNode(node) {
selectedNode = node
}
</script>
<div style="display:flex;align-items:center;justify-content:center;height:100vh;background:#13131f;color:#e4e4ef;font-family:sans-serif">
<div style="text-align:center">
<h1 style="font-size:28px;margin-bottom:8px">&#9874; Верстак</h1>
<p style="color:#8888a4">Wails desktop app — under construction</p>
<div class="app">
<!-- Top bar -->
<div class="topbar">
<span class="logo">&#9874;</span>
<h1>Верстак</h1>
<span class="version">{version}</span>
</div>
<!-- Error banner -->
{#if error}
<div class="error-banner">
Error: {error}
</div>
{/if}
<!-- Main content -->
<div class="main">
<!-- Sidebar -->
<div class="sidebar">
<div class="sidebar-header">Разделы</div>
{#each sections as section}
<div class="sidebar-item {selectedSection === section.id ? 'selected' : ''}"
on:click={() => selectSection(section.id)}>
{section.label}
</div>
{/each}
<div class="sidebar-header" style="margin-top:16px">Корневые дела</div>
{#each nodes as node}
<div class="sidebar-item {selectedNode && selectedNode.id === node.id ? 'selected' : ''}"
on:click={() => selectNode(node)}>
{node.title}
</div>
{/each}
{#if nodes.length === 0 && sections.length > 0}
<div class="sidebar-empty">Нет дел. Создайте первое дело.</div>
{/if}
</div>
<!-- Content panel -->
<div class="content">
{#if selectedNode}
<div class="node-view">
<h2>{selectedNode.title}</h2>
<p>ID: {selectedNode.id}</p>
<p>Type: {selectedNode.type}</p>
</div>
{:else if sections.length > 0}
<div class="welcome">
<p>Wails v2 Desktop GUI работает.</p>
<p>Sections: {sections.length}, Root nodes: {nodes.length}</p>
{#if error}
<p class="error-text">Wails bindings error: {error}</p>
<p class="hint">Window opens but Go bindings not connected yet.</p>
{/if}
</div>
{:else}
<div class="loading">Загрузка...</div>
{/if}
</div>
</div>
</div>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
.app { display: flex; flex-direction: column; height: 100vh; background: #13131f; color: #e4e4ef; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
.topbar { padding: 12px 20px; border-bottom: 1px solid #2a2a3c; display: flex; align-items: center; gap: 12px; }
.logo { font-size: 20px; }
.topbar h1 { font-size: 18px; font-weight: 600; }
.version { color: #666; font-size: 12px; margin-left: auto; }
.error-banner { background: #442222; color: #ff8888; padding: 8px 20px; font-size: 13px; }
.main { display: flex; flex: 1; overflow: hidden; }
.sidebar { width: 240px; border-right: 1px solid #2a2a3c; padding: 12px; overflow-y: auto; flex-shrink: 0; }
.sidebar-header { font-size: 11px; text-transform: uppercase; color: #666; margin-bottom: 8px; padding: 0 12px; }
.sidebar-item { padding: 8px 12px; border-radius: 6px; cursor: pointer; margin-bottom: 2px; color: #ccc; }
.sidebar-item:hover { background: #1e1e2e; }
.sidebar-item.selected { background: #2a2a3c; color: #fff; }
.sidebar-empty { color: #666; font-size: 12px; padding: 8px 12px; }
.content { flex: 1; padding: 20px; overflow-y: auto; }
.welcome { color: #8888a4; font-size: 14px; line-height: 1.6; }
.error-text { color: #ff8888; }
.hint { color: #666; margin-top: 8px; }
.loading { color: #888; font-size: 14px; }
.node-view h2 { font-size: 24px; margin-bottom: 16px; }
.node-view p { color: #888; font-size: 13px; margin-bottom: 8px; }
</style>
+3 -2
View File
@@ -1,4 +1,5 @@
import { mount } from 'svelte'
import App from './App.svelte'
mount(App, { target: document.getElementById('app') })
new App({
target: document.getElementById('app')
})
+6 -4
View File
@@ -1,13 +1,15 @@
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import wails from "@wailsio/runtime/plugins/vite";
// https://vitejs.dev/config/
export default defineConfig({
server: {
host: "127.0.0.1",
port: Number(process.env.WAILS_VITE_PORT) || 9245,
port: 3001,
strictPort: true,
},
plugins: [svelte(), wails("./bindings")],
plugins: [svelte()],
build: {
outDir: "dist",
emptyOutDir: true,
},
});