@anmol-srv/sigil 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,178 @@
1
+ /* ============================================================
2
+ SIGIL DESIGN SYSTEM — colors_and_type.css
3
+ Dark, sharp-edged console. Blue brand mark, monospace data,
4
+ restrained status color. Import this file to inherit all tokens.
5
+ ============================================================ */
6
+
7
+ /* ---- Webfonts ---------------------------------------------- */
8
+ @import url('https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600&display=swap');
9
+
10
+ :root {
11
+ /* ========================================================
12
+ COLOR — cool-neutral dark ramp
13
+ Surfaces are layered near-black; text is a desaturated
14
+ white ramp. Chroma is kept extremely low (cool grey).
15
+ ======================================================== */
16
+
17
+ /* Backgrounds / surfaces (low → high elevation) */
18
+ --bg-0: #08090b; /* app background, deepest */
19
+ --bg-1: #0b0c0e; /* page canvas */
20
+ --surface-1: #101113; /* card / panel */
21
+ --surface-2: #151618; /* row hover, inset, raised */
22
+ --surface-3: #1b1d20; /* active / pressed surface */
23
+
24
+ /* Borders / hairlines */
25
+ --border-1: #1d1f23; /* default hairline divider */
26
+ --border-2: #292c31; /* stronger border / card edge */
27
+ --border-strong: #3a3e44; /* focus-adjacent, input border hover */
28
+
29
+ /* Foreground text ramp */
30
+ --fg-1: #f4f5f6; /* primary text, headings, key values */
31
+ --fg-2: #a2a5ab; /* body, secondary text */
32
+ --fg-3: #74777d; /* labels, captions, footer */
33
+ --fg-4: #50535a; /* disabled, faint meta */
34
+
35
+ /* ---- Brand (from the Sigil mark gradient) ---- */
36
+ --brand: #0084ff; /* primary blue — active, links, focus */
37
+ --brand-deep: #004ce6; /* gradient anchor / pressed */
38
+ --brand-light: #99d5ff; /* gradient highlight / on-dark accents */
39
+ --brand-tint: rgba(0, 132, 255, 0.12); /* selected/active wash */
40
+ --brand-ring: rgba(0, 132, 255, 0.45); /* focus ring */
41
+
42
+ /* ---- Status (semantic, used sparingly) ---- */
43
+ --ok: #3ddc84; /* connected / healthy */
44
+ --ok-tint: rgba(61, 220, 132, 0.12);
45
+ --warn: #f5b544; /* degraded / attention */
46
+ --warn-tint: rgba(245, 181, 68, 0.12);
47
+ --danger: #ff5a52; /* error / disconnected */
48
+ --danger-tint: rgba(255, 90, 82, 0.12);
49
+ --info: var(--brand);
50
+
51
+ /* ========================================================
52
+ TYPE — Geist (UI) + Geist Mono (data / identifiers)
53
+ UI copy is grotesk; anything machine-derived (pids,
54
+ versions, hashes, ids, durations) is monospace.
55
+ ======================================================== */
56
+ --font-sans: 'Geist', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
57
+ --font-mono: 'Geist Mono', ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, monospace;
58
+
59
+ /* Type scale (px → rem at 16px base) */
60
+ --text-display: 2.25rem; /* 36 — page hero (rare) */
61
+ --text-h1: 1.625rem; /* 26 — page title */
62
+ --text-h2: 1.125rem; /* 18 — section / card value */
63
+ --text-h3: 1rem; /* 16 — sub-section */
64
+ --text-body: 0.9375rem;/* 15 — body copy */
65
+ --text-sm: 0.8125rem;/* 13 — table cells, meta */
66
+ --text-label: 0.6875rem;/* 11 — UPPERCASE eyebrow labels */
67
+ --text-xs: 0.625rem; /* 10 — micro meta */
68
+
69
+ /* Weights */
70
+ --w-regular: 400;
71
+ --w-medium: 500;
72
+ --w-semibold:600;
73
+ --w-bold: 700;
74
+
75
+ /* Tracking */
76
+ --track-label: 0.12em; /* uppercase eyebrows */
77
+ --track-tight: -0.01em; /* large headings */
78
+ --track-normal: 0;
79
+
80
+ /* Line heights */
81
+ --lh-tight: 1.15;
82
+ --lh-snug: 1.35;
83
+ --lh-body: 1.55;
84
+
85
+ /* ========================================================
86
+ GEOMETRY — sharp edges. Radius is near-zero by design.
87
+ ======================================================== */
88
+ --radius-0: 0px; /* default — sharp */
89
+ --radius-1: 2px; /* inputs, badges (barely softened) */
90
+ --radius-2: 3px; /* the largest we ever go */
91
+
92
+ /* Borders */
93
+ --bw: 1px;
94
+
95
+ /* ========================================================
96
+ SPACING — 4px base grid
97
+ ======================================================== */
98
+ --sp-1: 4px;
99
+ --sp-2: 8px;
100
+ --sp-3: 12px;
101
+ --sp-4: 16px;
102
+ --sp-5: 24px;
103
+ --sp-6: 32px;
104
+ --sp-7: 48px;
105
+ --sp-8: 64px;
106
+ --sp-9: 96px;
107
+
108
+ /* Layout */
109
+ --maxw: 1680px; /* content max width */
110
+ --gutter: 32px; /* page horizontal padding */
111
+ --nav-h: 64px; /* top bar height */
112
+
113
+ /* ========================================================
114
+ ELEVATION — shadows are flat & subtle (console, not card UI)
115
+ ======================================================== */
116
+ --shadow-0: none;
117
+ --shadow-1: 0 1px 0 rgba(0,0,0,0.4);
118
+ --shadow-2: 0 8px 24px rgba(0,0,0,0.45);
119
+ --shadow-pop: 0 12px 40px rgba(0,0,0,0.6);
120
+
121
+ /* Focus */
122
+ --focus-ring: 0 0 0 1px var(--brand), 0 0 0 4px var(--brand-ring);
123
+
124
+ /* Motion — minimal, quick, no bounce */
125
+ --ease: cubic-bezier(0.2, 0, 0, 1);
126
+ --dur-1: 90ms;
127
+ --dur-2: 150ms;
128
+ }
129
+
130
+ /* ============================================================
131
+ SEMANTIC ELEMENT STYLES — opt-in helpers
132
+ ============================================================ */
133
+
134
+ .sig-display {
135
+ font-family: var(--font-sans);
136
+ font-size: var(--text-display);
137
+ font-weight: var(--w-bold);
138
+ letter-spacing: var(--track-tight);
139
+ line-height: var(--lh-tight);
140
+ color: var(--fg-1);
141
+ }
142
+ .sig-h1 {
143
+ font-family: var(--font-sans);
144
+ font-size: var(--text-h1);
145
+ font-weight: var(--w-bold);
146
+ letter-spacing: var(--track-tight);
147
+ line-height: var(--lh-tight);
148
+ color: var(--fg-1);
149
+ }
150
+ .sig-h2 {
151
+ font-family: var(--font-sans);
152
+ font-size: var(--text-h2);
153
+ font-weight: var(--w-semibold);
154
+ line-height: var(--lh-snug);
155
+ color: var(--fg-1);
156
+ }
157
+ .sig-label {
158
+ font-family: var(--font-sans);
159
+ font-size: var(--text-label);
160
+ font-weight: var(--w-semibold);
161
+ letter-spacing: var(--track-label);
162
+ text-transform: uppercase;
163
+ color: var(--fg-3);
164
+ }
165
+ .sig-body {
166
+ font-family: var(--font-sans);
167
+ font-size: var(--text-body);
168
+ font-weight: var(--w-regular);
169
+ line-height: var(--lh-body);
170
+ color: var(--fg-2);
171
+ }
172
+ .sig-mono {
173
+ font-family: var(--font-mono);
174
+ font-size: var(--text-sm);
175
+ font-weight: var(--w-medium);
176
+ color: var(--fg-1);
177
+ font-feature-settings: 'ss01' on, 'zero' on;
178
+ }
@@ -0,0 +1,8 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
2
+ <g fill="currentColor">
3
+ <path d="m54.7 21.7v8.9h3.3l19.9-9.7 1.4-0.1 0.1 1.8-9.7 19.9v3.1h9.6l16.2-12.7 2.7-2.8v-27.9h-28.6l-14.9 19.5z"></path>
4
+ <path d="m2.2 2.2v27.9l2.7 2.2 15.8 13.4h9.5v-3.2l-9.8-20 0.1-1.8 1.6 0.1 19.9 9.8h3.2v-8.9l-14.9-19.5z"></path>
5
+ <path d="m20.6 54.4-17.2 13.2-1.2 1.6v28.7h28.1l14.9-19.4v-9.7h-3.3l-19.9 10.5-1.5 0.1v-1.8l9.7-19.8v-3.4z"></path>
6
+ <path d="m54.7 68.8v9.7l14.9 19.4h28.7v-28.7l-19.1-14.8h-9.5v3.3l9.7 19.9-0.1 1.7h-1.4l-19.7-10.5z"></path>
7
+ </g>
8
+ </svg>
@@ -0,0 +1,26 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
2
+ <linearGradient id="sig1" x1="54.74" x2="98.29" y1="23.07" y2="10.4" gradientUnits="userSpaceOnUse">
3
+ <stop stop-color="#99D5FF" offset="0"></stop>
4
+ <stop stop-color="#0084FF" offset=".3602"></stop>
5
+ <stop stop-color="#004CE6" offset="1"></stop>
6
+ </linearGradient>
7
+ <linearGradient id="sig2" x1="1.691" x2="45.24" y1="10.02" y2="32.95" gradientUnits="userSpaceOnUse">
8
+ <stop stop-color="#005CE6" offset=".3602"></stop>
9
+ <stop stop-color="#0084FF" offset=".7432"></stop>
10
+ <stop stop-color="#C9E7FF" offset="1"></stop>
11
+ </linearGradient>
12
+ <linearGradient id="sig3" x1="2.223" x2="45.24" y1="89.56" y2="67.06" gradientUnits="userSpaceOnUse">
13
+ <stop stop-color="#005CE6" offset=".3602"></stop>
14
+ <stop stop-color="#0084FF" offset=".7432"></stop>
15
+ <stop stop-color="#C9E7FF" offset="1"></stop>
16
+ </linearGradient>
17
+ <linearGradient id="sig4" x1="54.74" x2="98.29" y1="78.41" y2="89.91" gradientUnits="userSpaceOnUse">
18
+ <stop stop-color="#99D5FF" offset="0"></stop>
19
+ <stop stop-color="#0084FF" offset=".3602"></stop>
20
+ <stop stop-color="#004CE6" offset="1"></stop>
21
+ </linearGradient>
22
+ <path fill="url(#sig1)" d="m54.7 21.7v8.9h3.3l19.9-9.7 1.4-0.1 0.1 1.8-9.7 19.9v3.1h9.6l16.2-12.7 2.7-2.8v-27.9h-28.6l-14.9 19.5z"></path>
23
+ <path fill="url(#sig2)" d="m2.2 2.2v27.9l2.7 2.2 15.8 13.4h9.5v-3.2l-9.8-20 0.1-1.8 1.6 0.1 19.9 9.8h3.2v-8.9l-14.9-19.5z"></path>
24
+ <path fill="url(#sig3)" d="m20.6 54.4-17.2 13.2-1.2 1.6v28.7h28.1l14.9-19.4v-9.7h-3.3l-19.9 10.5-1.5 0.1v-1.8l9.7-19.8v-3.4z"></path>
25
+ <path fill="url(#sig4)" d="m54.7 68.8v9.7l14.9 19.4h28.7v-28.7l-19.1-14.8h-9.5v3.3l9.7 19.9-0.1 1.7h-1.4l-19.7-10.5z"></path>
26
+ </svg>
@@ -4,55 +4,61 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Sigil</title>
7
- <link rel="icon" type="image/svg+xml" href="/static/sigil.svg">
7
+ <link rel="icon" type="image/svg+xml" href="/static/design/sigil-mark.svg">
8
+ <!-- Sigil design system tokens (color, type, spacing, geometry) — loaded
9
+ BEFORE app.css so the dashboard re-skins onto the brand. -->
10
+ <link rel="stylesheet" href="/static/design/colors_and_type.css">
8
11
  <link rel="stylesheet" href="/static/app.css">
9
12
  </head>
10
13
  <body>
14
+ <!-- Toast stack — central status/error feedback (see toast.js) -->
15
+ <div id="toasts" class="toast-stack" aria-live="polite"></div>
16
+
11
17
  <!-- ════════════════════════════════════════════════════════════════════
12
18
  ONBOARDING WIZARD (shown until SETUP_COMPLETE)
13
19
  ════════════════════════════════════════════════════════════════════ -->
14
20
  <div class="onboarding" id="onboarding" hidden>
15
21
  <aside class="onboarding-sidebar">
16
22
  <div class="brand">
17
- <img class="brand-mark" src="/static/sigil.svg" alt="Sigil" width="20" height="20">
23
+ <img class="brand-mark" src="/static/design/sigil-mark.svg" alt="Sigil" width="20" height="20">
18
24
  <div class="brand-name">Sigil</div>
19
25
  <div class="brand-badge">setup</div>
20
26
  </div>
21
27
 
22
28
  <ol class="onboarding-steps" id="ob-steps">
23
- <li class="onboarding-step active" data-ob-step="welcome">
29
+ <li class="onboarding-step active" data-ob-step="connectors">
24
30
  <div class="num">1</div>
25
31
  <div>
26
- <div class="label">Welcome</div>
27
- <div class="desc">What Sigil is</div>
32
+ <div class="label">Connectors</div>
33
+ <div class="desc">Editors &amp; tools</div>
28
34
  </div>
29
35
  </li>
30
- <li class="onboarding-step future" data-ob-step="database">
36
+ <li class="onboarding-step future" data-ob-step="llm">
31
37
  <div class="num">2</div>
32
38
  <div>
33
- <div class="label">Database</div>
34
- <div class="desc">Where memory lives</div>
39
+ <div class="label">Provider</div>
40
+ <div class="desc">Fact extraction + reasoning</div>
35
41
  </div>
36
42
  </li>
37
- <li class="onboarding-step future" data-ob-step="llm">
43
+ <li class="onboarding-step future" data-ob-step="embedding">
38
44
  <div class="num">3</div>
39
45
  <div>
40
- <div class="label">LLM provider</div>
41
- <div class="desc">Fact extraction + reasoning</div>
46
+ <div class="label">Embeddings</div>
47
+ <div class="desc">Semantic search</div>
42
48
  </div>
43
49
  </li>
44
- <li class="onboarding-step future" data-ob-step="embedding">
50
+ <li class="onboarding-step future" data-ob-step="database">
45
51
  <div class="num">4</div>
46
52
  <div>
47
- <div class="label">Embeddings</div>
48
- <div class="desc">Semantic search</div>
53
+ <div class="label">Database</div>
54
+ <div class="desc">Where memory lives</div>
49
55
  </div>
50
56
  </li>
51
57
  <li class="onboarding-step future" data-ob-step="finish">
52
58
  <div class="num">5</div>
53
59
  <div>
54
60
  <div class="label">All set</div>
55
- <div class="desc">Wire up your editor</div>
61
+ <div class="desc">Open the dashboard</div>
56
62
  </div>
57
63
  </li>
58
64
  </ol>
@@ -62,31 +68,31 @@
62
68
  </aside>
63
69
 
64
70
  <main class="onboarding-content">
65
- <!-- ── Step 1: Welcome ─────────────────────────────────────────── -->
66
- <section class="wizard-step active" data-step="welcome">
67
- <h1>Welcome to Sigil.</h1>
68
- <p class="lede">Persistent memory for AI coding agents. One brain shared across Claude Code, Cursor, Codex, and any MCP client stored in your own Postgres.</p>
69
-
70
- <h2>What we'll set up</h2>
71
- <ol class="kv">
72
- <div class="row"><div class="k">1. Database</div><div class="v" style="font-family: var(--font-ui);">Connect Sigil to a Postgres database — Neon, Supabase, RDS, or local install. We'll install pgvector and run migrations for you.</div></div>
73
- <div class="row"><div class="k">2. LLM provider</div><div class="v" style="font-family: var(--font-ui);">For fact extraction, AUDM dedup, and query synthesis. Claude Code subscription works for free.</div></div>
74
- <div class="row"><div class="k">3. Embeddings</div><div class="v" style="font-family: var(--font-ui);">Vector model for semantic search.</div></div>
75
- <div class="row"><div class="k">4. Editor wiring</div><div class="v" style="font-family: var(--font-ui);">Register Sigil with the AI coding tools you have installed.</div></div>
76
- </ol>
71
+ <!-- ── Step 1: Connectors ──────────────────────────────────────── -->
72
+ <section class="wizard-step active" data-step="connectors">
73
+ <h1>Connect your tools.</h1>
74
+ <p class="lede">Sigil installs memory hooks into the AI coding tools you already use — one shared brain across Claude Code, Cursor, Codex, Kiro, and any MCP client. Click to connect; you can change this any time.</p>
75
+
76
+ <div class="connector-grid" id="ob-connectors"><div class="muted">detecting installed tools…</div></div>
77
77
 
78
78
  <div class="wizard-actions">
79
79
  <div class="spacer"></div>
80
- <button class="btn primary large" data-ob-next="database">Get started</button>
80
+ <button class="btn ghost" data-ob-next="llm" id="ob-connectors-skip">Skip for now</button>
81
+ <button class="btn primary large" data-ob-next="llm">Continue</button>
81
82
  </div>
82
83
  </section>
83
84
 
84
- <!-- ── Step 2: Database ────────────────────────────────────────── -->
85
+ <!-- ── Step 4: Database (linear guided flow) ───────────────────── -->
85
86
  <section class="wizard-step" data-step="database">
86
- <h1>Connect your database.</h1>
87
- <p class="lede">Sigil stores every fact, document, and embedding in a Postgres database you control. Pick a managed provider or point at a local install.</p>
87
+ <h1>Set up your database.</h1>
88
+ <p class="lede">Sigil stores every fact, document, and embedding in Postgres with pgvector. Let Sigil create a local one for you, or point it at a database you control.</p>
88
89
 
89
90
  <div class="provider-card-grid" id="db-mode-cards">
91
+ <label class="provider-card" data-db-mode="docker" id="ob-db-mode-docker" hidden>
92
+ <input type="radio" name="db-mode" value="docker" hidden>
93
+ <span class="name">Local (automatic) <span class="badge info" style="margin-left:8px;">RECOMMENDED</span></span>
94
+ <span class="hint">Sigil runs a pgvector Postgres in Docker for you. Zero config.</span>
95
+ </label>
90
96
  <label class="provider-card" data-db-mode="url">
91
97
  <input type="radio" name="db-mode" value="url" hidden checked>
92
98
  <span class="name">Connection URL</span>
@@ -95,10 +101,12 @@
95
101
  <label class="provider-card" data-db-mode="fields">
96
102
  <input type="radio" name="db-mode" value="fields" hidden>
97
103
  <span class="name">Local Postgres install</span>
98
- <span class="hint">Docker, brew, apt — host + port + user + password.</span>
104
+ <span class="hint">Existing host + port + user + password.</span>
99
105
  </label>
100
106
  </div>
101
107
 
108
+ <p class="muted text-sm" id="ob-db-docker-note" style="margin-top: var(--s-2);" hidden></p>
109
+
102
110
  <div id="ob-db-url" style="margin-top: var(--s-5);">
103
111
  <label class="field">
104
112
  <span class="label">Postgres connection URL</span>
@@ -119,18 +127,17 @@
119
127
  <label class="field"><span class="label">Password</span><input type="password" id="ob-db-pass" autocomplete="off"></label>
120
128
  </div>
121
129
 
122
- <div class="flex-row" style="margin-top: var(--s-3);">
123
- <button class="btn primary" id="ob-db-test">Test connection</button>
124
- <button class="btn" id="ob-db-install-pgv" hidden>Install pgvector</button>
125
- <button class="btn" id="ob-db-migrate" hidden>Run migrations</button>
130
+ <div class="flex-row" style="margin-top: var(--s-4);">
131
+ <button class="btn primary" id="ob-db-setup">Set up database</button>
126
132
  </div>
127
133
 
128
- <pre id="ob-db-result" class="result" hidden></pre>
134
+ <!-- Linear flow rows (filled by JS) — replaces the old toggling buttons -->
135
+ <div class="db-flow" id="ob-db-flow" hidden></div>
129
136
 
130
137
  <div class="wizard-actions">
131
- <button class="btn ghost" data-ob-back="welcome">Back</button>
138
+ <button class="btn ghost" data-ob-back="embedding">Back</button>
132
139
  <div class="spacer"></div>
133
- <button class="btn primary" data-ob-next="llm" id="ob-db-next" disabled>Continue</button>
140
+ <button class="btn primary" data-ob-next="finish" id="ob-db-next" disabled>Continue</button>
134
141
  </div>
135
142
  </section>
136
143
 
@@ -149,7 +156,7 @@
149
156
  <pre id="ob-llm-result" class="result" hidden></pre>
150
157
 
151
158
  <div class="wizard-actions">
152
- <button class="btn ghost" data-ob-back="database">Back</button>
159
+ <button class="btn ghost" data-ob-back="connectors">Back</button>
153
160
  <div class="spacer"></div>
154
161
  <button class="btn primary" data-ob-next="embedding" id="ob-llm-next" disabled>Continue</button>
155
162
  </div>
@@ -171,7 +178,7 @@
171
178
  <div class="wizard-actions">
172
179
  <button class="btn ghost" data-ob-back="llm">Back</button>
173
180
  <div class="spacer"></div>
174
- <button class="btn primary" data-ob-next="finish" id="ob-emb-next" disabled>Continue</button>
181
+ <button class="btn primary" data-ob-next="database" id="ob-emb-next" disabled>Continue</button>
175
182
  </div>
176
183
  </section>
177
184
 
@@ -194,6 +201,11 @@
194
201
  <div class="row"><div class="k">Other devices</div><div class="v" style="font-family: var(--font-ui);">From the Devices tab in the dashboard, create a pairing code and run <code>sigil join …</code> on the joining device.</div></div>
195
202
  </ul>
196
203
 
204
+ <label class="field" style="flex-direction:row; align-items:center; gap: var(--s-2); margin-top: var(--s-5);">
205
+ <input type="checkbox" id="ob-always-up" checked>
206
+ <span class="label" style="text-transform:none; letter-spacing:0;">Keep Sigil always running — start on login, auto-restart on crash or reboot (recommended)</span>
207
+ </label>
208
+
197
209
  <div class="wizard-actions">
198
210
  <div class="spacer"></div>
199
211
  <button class="btn primary large" id="ob-complete">Open the dashboard →</button>
@@ -208,7 +220,7 @@
208
220
  <header>
209
221
  <div class="header-inner">
210
222
  <div class="brand">
211
- <img class="brand-mark" src="/static/sigil.svg" alt="Sigil" width="20" height="20">
223
+ <img class="brand-mark" src="/static/design/sigil-mark.svg" alt="Sigil" width="20" height="20">
212
224
  <div class="brand-name">Sigil</div>
213
225
  <div class="brand-badge" id="brand-badge">local</div>
214
226
  </div>
@@ -383,6 +395,16 @@
383
395
  </div>
384
396
  </div>
385
397
 
398
+ <!-- Coding agents — single-click install of Sigil hooks into Claude Code,
399
+ Cursor, Codex, Kiro, Hermes. Cards reuse the wizard's connectorCard. -->
400
+ <div class="panel" style="padding: var(--s-5); margin-bottom: var(--s-4);">
401
+ <div class="title-block" style="margin-bottom: var(--s-4);">
402
+ <h3 style="margin: 0;">Coding agents</h3>
403
+ <p class="muted text-sm" style="margin: 4px 0 0;">One shared memory across every AI coding tool you use. Sigil writes a small CLAUDE.md/AGENTS.md import + hook config — fully reversible.</p>
404
+ </div>
405
+ <div class="connector-grid" id="settings-connectors"><div class="muted">detecting installed tools…</div></div>
406
+ </div>
407
+
386
408
  <!-- Current config + switch controls -->
387
409
  <div class="panel" style="padding: var(--s-5); margin-bottom: var(--s-4);">
388
410
  <div class="row"><div class="k">Database</div><div class="v" id="cfg-db">…</div></div>
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Central toast feedback — the only channel for transient status/errors.
3
+ * Design-system styled: a sharp surface-1 panel with a hairline border, a 7px
4
+ * status square (red/green/amber/brand) and an optional mono error code.
5
+ * Errors are sticky (no auto-dismiss); info/success fade. Replaces every
6
+ * inline `out.textContent = …` / `alert()` in the old GUI.
7
+ */
8
+ function host() {
9
+ return document.getElementById('toasts');
10
+ }
11
+
12
+ export function toast({ variant = 'info', message = '', hint, code, timeout } = {}) {
13
+ const stack = host();
14
+ if (!stack) return () => {};
15
+
16
+ const el = document.createElement('div');
17
+ el.className = `toast toast-${variant}`;
18
+ el.setAttribute('role', variant === 'error' ? 'alert' : 'status');
19
+
20
+ const sq = document.createElement('span');
21
+ sq.className = 'toast-sq';
22
+
23
+ const body = document.createElement('div');
24
+ body.className = 'toast-body';
25
+
26
+ const msg = document.createElement('div');
27
+ msg.className = 'toast-msg';
28
+ msg.textContent = message;
29
+ body.appendChild(msg);
30
+
31
+ if (hint) {
32
+ const h = document.createElement('div');
33
+ h.className = 'toast-hint';
34
+ h.textContent = hint;
35
+ body.appendChild(h);
36
+ }
37
+ if (code) {
38
+ const c = document.createElement('span');
39
+ c.className = 'toast-code';
40
+ c.textContent = code;
41
+ body.appendChild(c);
42
+ }
43
+
44
+ const x = document.createElement('button');
45
+ x.className = 'toast-x';
46
+ x.type = 'button';
47
+ x.textContent = '×';
48
+ x.setAttribute('aria-label', 'dismiss');
49
+ const remove = () => { el.remove(); };
50
+ x.onclick = remove;
51
+
52
+ el.append(sq, body, x);
53
+ stack.appendChild(el);
54
+
55
+ const ttl = timeout != null ? timeout : (variant === 'error' ? 0 : 4000);
56
+ if (ttl > 0) setTimeout(remove, ttl);
57
+ return remove;
58
+ }
59
+
60
+ export const toastError = (o) => toast({ ...o, variant: 'error' });
61
+ export const toastOk = (o) => toast({ ...o, variant: 'success' });
62
+ export const toastInfo = (o) => toast({ ...o, variant: 'info' });