verstak/frontend/src/lib/FirstRun.svelte

212 lines
5.0 KiB
Svelte

<script>
import { t } from './i18n'
export let onComplete = null
export let onQuit = null
let vaultPath = ''
let defaultPath = ''
let loading = false
let errorMsg = ''
let pathInfo = ''
let pathCheck = null
function wailsCall(method, ...args) {
try {
if (window['go'] && window['go']['main'] && window['go']['main']['App']) {
const fn = window['go']['main']['App'][method]
if (typeof fn === 'function') return fn(...args)
}
} catch (e) { console.error('Wails error:', method, e) }
return Promise.reject(new Error('Wails not connected: ' + method))
}
async function init() {
try {
const defaultVaultPath = await wailsCall('GetDefaultVaultPath')
defaultPath = defaultVaultPath || ''
vaultPath = defaultPath || ''
if (vaultPath) await checkPath()
} catch (e) { defaultPath = '' }
}
init()
async function browseFolder() {
try {
const chosen = await wailsCall('PickDirectory')
if (chosen) {
vaultPath = chosen
await checkPath()
}
} catch (e) { errorMsg = String(e) }
}
async function checkPath() {
if (!vaultPath || !vaultPath.trim()) {
pathInfo = ''
pathCheck = null
return
}
try {
pathCheck = await wailsCall('CheckVaultPath', vaultPath.trim())
if (pathCheck) {
pathInfo = pathCheck.description
}
} catch (e) { pathInfo = ''; pathCheck = null }
}
async function createWorkspace() {
if (!vaultPath || !vaultPath.trim()) return
loading = true
errorMsg = ''
if (pathCheck && !pathCheck.writable) {
errorMsg = t('firstrun.errorNoWrite')
loading = false
return
}
try {
const status = await wailsCall('CreateVault', vaultPath.trim())
if (status && status.status === 'ready' && onComplete) {
onComplete(status)
}
} catch (e) {
errorMsg = String(e)
}
loading = false
}
function handleQuit() {
if (onQuit) onQuit()
}
function handleKeydown(e) {
if (e.key === 'Enter' && !loading && vaultPath.trim()) createWorkspace()
}
</script>
<div class="first-run-screen">
<div class="first-run-card">
<img class="first-run-logo" src="/assets/app-icons/icon_64x64.png" width="64" height="64" alt="" />
<h1>{t('firstrun.title')}</h1>
<p class="first-run-desc">{t('firstrun.desc')}</p>
<div class="form-group">
<label class="form-label" for="vault-path">{t('firstrun.pathLabel')}</label>
<div class="input-row">
<input
id="vault-path"
type="text"
bind:value={vaultPath}
on:input={checkPath}
on:keydown={handleKeydown}
placeholder={defaultPath || t('firstrun.defaultPath')}
disabled={loading}
/>
<button class="btn" on:click={browseFolder} disabled={loading}>{t('firstrun.browse')}</button>
</div>
</div>
{#if pathInfo}
<div class="path-info">{pathInfo}</div>
{/if}
{#if errorMsg}
<div class="error-msg">{errorMsg}</div>
{/if}
<div class="first-run-actions">
<button class="btn btn-primary btn-lg" on:click={createWorkspace}
disabled={!vaultPath.trim() || loading}>
{loading ? t('firstrun.creating') : t('firstrun.create')}
</button>
<button class="btn btn-lg" on:click={handleQuit}>{t('firstrun.quit')}</button>
</div>
</div>
</div>
<style>
.first-run-screen {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 2rem;
background: var(--bg, #13131f);
}
.first-run-card {
max-width: 520px;
width: 100%;
padding: 2.5rem;
background: var(--surface, #1e1e2e);
border-radius: 12px;
border: 1px solid var(--border, #2a2a3e);
text-align: center;
}
.first-run-logo {
display: block;
width: 64px;
height: 64px;
margin: 0 auto 0.5rem auto;
}
.first-run-card h1 {
margin: 0 0 0.75rem 0;
font-size: 1.5rem;
color: var(--text, #e0e0e0);
}
.first-run-desc {
color: var(--text-dim, #888);
margin-bottom: 1.5rem;
font-size: 0.9rem;
line-height: 1.5;
}
.input-row {
display: flex;
gap: 0.5rem;
}
.input-row input {
flex: 1;
}
.path-info {
text-align: left;
font-size: 0.85rem;
padding: 0.5rem 0.75rem;
margin-top: 0.5rem;
background: var(--surface-alt, #252538);
border-radius: 6px;
color: var(--text-dim, #888);
}
.error-msg {
text-align: left;
font-size: 0.85rem;
padding: 0.5rem 0.75rem;
margin-top: 0.5rem;
background: rgba(255, 107, 107, 0.1);
border: 1px solid rgba(255, 107, 107, 0.3);
border-radius: 6px;
color: #ff6b6b;
}
.first-run-actions {
display: flex;
gap: 0.75rem;
justify-content: center;
margin-top: 1.5rem;
}
.btn-lg {
padding: 0.65rem 1.5rem;
font-size: 1rem;
}
.form-group {
margin-bottom: 1rem;
text-align: left;
}
.form-label {
display: block;
font-size: 0.85rem;
color: var(--text-dim, #888);
margin-bottom: 0.4rem;
}
</style>