Initial scaffold from M1 spike (tiletopia)
Tauri 2 + Svelte 5 + xterm.js + portable-pty. Single full-window WSL terminal pane with clickable distro picker. M1 verified manually on Windows: window opens, xterm.js renders, claude TUI works, resize reflows cleanly. Graduated from ~/claude/ideas/wsl-mux/ per the approved plan at ~/.claude/plans/imperative-coalescing-feigenbaum.md. See memory.md for decisions, open TODOs, and the M2-M5 roadmap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
b352f8f049
36 changed files with 11534 additions and 0 deletions
112
src/App.svelte
Normal file
112
src/App.svelte
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import XtermPane from "./components/XtermPane.svelte";
|
||||
import { listDistros } from "./ipc";
|
||||
|
||||
let distros = $state<string[]>([]);
|
||||
let selected = $state<string | undefined>(undefined);
|
||||
let status = $state("starting…");
|
||||
let statusOk = $state(true);
|
||||
let loadError = $state<string | null>(null);
|
||||
|
||||
function isInteractiveDistro(name: string): boolean {
|
||||
return !name.toLowerCase().startsWith("docker-desktop");
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const d = await listDistros();
|
||||
console.log("listDistros() returned:", d);
|
||||
distros = d;
|
||||
// Pick fresh every mount (HMR can preserve $state across reloads).
|
||||
selected = d.find(isInteractiveDistro) ?? d[0] ?? undefined;
|
||||
console.log("default selected:", selected);
|
||||
} catch (e) {
|
||||
console.warn("list_distros failed:", e);
|
||||
loadError = String(e);
|
||||
}
|
||||
});
|
||||
|
||||
function pick(d: string) {
|
||||
console.log("user picked distro:", d);
|
||||
selected = d;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="app">
|
||||
<header class="titlebar">
|
||||
<span class="label">tiletopia</span>
|
||||
|
||||
<span class="distros">
|
||||
{#if distros.length === 0}
|
||||
<span class="muted">no distros enumerated</span>
|
||||
{:else}
|
||||
{#each distros as d}
|
||||
<button
|
||||
class="distro-btn"
|
||||
class:active={d === selected}
|
||||
onclick={() => pick(d)}
|
||||
>
|
||||
{d}
|
||||
</button>
|
||||
{/each}
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<span class="status {statusOk ? 'ok' : 'err'}">{status}</span>
|
||||
</header>
|
||||
|
||||
<div class="pane-wrap">
|
||||
{#if loadError}
|
||||
<pre class="err-pre">listDistros failed: {loadError}</pre>
|
||||
{:else if selected !== undefined || distros.length === 0}
|
||||
{#key selected}
|
||||
<XtermPane
|
||||
distro={selected}
|
||||
onStatus={(msg, ok) => {
|
||||
status = msg;
|
||||
statusOk = ok;
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.distros {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
.distro-btn {
|
||||
font: inherit;
|
||||
font-family: "Cascadia Mono", "JetBrains Mono", "Consolas", monospace;
|
||||
font-size: 11px;
|
||||
background: #222;
|
||||
color: #aaa;
|
||||
border: 1px solid #333;
|
||||
border-radius: 3px;
|
||||
padding: 2px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.distro-btn:hover {
|
||||
background: #2a2a2a;
|
||||
color: #ddd;
|
||||
}
|
||||
.distro-btn.active {
|
||||
background: #1a3a5c;
|
||||
color: #cce6ff;
|
||||
border-color: #2a5a8c;
|
||||
}
|
||||
.muted {
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
.err-pre {
|
||||
color: #d66;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue