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:
parent
c65187f656
commit
77a7918569
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
.svelte-10a60fp.svelte-10a60fp{box-sizing:border-box;margin:0;padding:0}.app.svelte-10a60fp.svelte-10a60fp{display:flex;flex-direction:column;height:100vh;background:#13131f;color:#e4e4ef;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.topbar.svelte-10a60fp.svelte-10a60fp{padding:12px 20px;border-bottom:1px solid #2a2a3c;display:flex;align-items:center;gap:12px}.logo.svelte-10a60fp.svelte-10a60fp{font-size:20px}.topbar.svelte-10a60fp h1.svelte-10a60fp{font-size:18px;font-weight:600}.version.svelte-10a60fp.svelte-10a60fp{color:#666;font-size:12px;margin-left:auto}.error-banner.svelte-10a60fp.svelte-10a60fp{background:#422;color:#f88;padding:8px 20px;font-size:13px}.main.svelte-10a60fp.svelte-10a60fp{display:flex;flex:1;overflow:hidden}.sidebar.svelte-10a60fp.svelte-10a60fp{width:240px;border-right:1px solid #2a2a3c;padding:12px;overflow-y:auto;flex-shrink:0}.sidebar-header.svelte-10a60fp.svelte-10a60fp{font-size:11px;text-transform:uppercase;color:#666;margin-bottom:8px;padding:0 12px}.sidebar-item.svelte-10a60fp.svelte-10a60fp{padding:8px 12px;border-radius:6px;cursor:pointer;margin-bottom:2px;color:#ccc}.sidebar-item.svelte-10a60fp.svelte-10a60fp:hover{background:#1e1e2e}.sidebar-item.selected.svelte-10a60fp.svelte-10a60fp{background:#2a2a3c;color:#fff}.sidebar-empty.svelte-10a60fp.svelte-10a60fp{color:#666;font-size:12px;padding:8px 12px}.content.svelte-10a60fp.svelte-10a60fp{flex:1;padding:20px;overflow-y:auto}.welcome.svelte-10a60fp.svelte-10a60fp{color:#8888a4;font-size:14px;line-height:1.6}.error-text.svelte-10a60fp.svelte-10a60fp{color:#f88}.hint.svelte-10a60fp.svelte-10a60fp{color:#666;margin-top:8px}.loading.svelte-10a60fp.svelte-10a60fp{color:#888;font-size:14px}.node-view.svelte-10a60fp h2.svelte-10a60fp{font-size:24px;margin-bottom:16px}.node-view.svelte-10a60fp p.svelte-10a60fp{color:#888;font-size:13px;margin-bottom:8px}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/wails.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<title>Wails + Svelte</title>
|
||||
<script type="module" crossorigin src="/assets/index-C37GEF9W.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D3NJ53hP.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
:root {
|
||||
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: rgba(27, 38, 54, 1);
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(""),
|
||||
url("./Inter-Medium.ttf") format("truetype");
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 3em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
margin: 0 0 0 20px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.result {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #e80000aa);
|
||||
}
|
||||
|
||||
.logo.vanilla:hover {
|
||||
filter: drop-shadow(0 0 2em #f7df1eaa);
|
||||
}
|
||||
|
||||
.result {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin: 1.5rem auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 1rem;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.input-box .btn:hover {
|
||||
background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.input-box .input {
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
color: black;
|
||||
background-color: rgba(240, 240, 240, 1);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.input-box .input:hover {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.input-box .input:focus {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="26.6" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 308"><path fill="#FF3E00" d="M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244"></path><path fill="#FFF" d="M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
File diff suppressed because it is too large
Load Diff
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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">⚒ Верстак</h1>
|
||||
<p style="color:#8888a4">Wails desktop app — under construction</p>
|
||||
<div class="app">
|
||||
<!-- Top bar -->
|
||||
<div class="topbar">
|
||||
<span class="logo">⚒</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>
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue