From 871957b930203c019e631c4606cfdf8266d222fa Mon Sep 17 00:00:00 2001
From: Adam Malczewski <github@tradam.dev>
Date: Wed, 10 Jun 2026 16:29:01 +0900
Subject: feat(views,surface-host): Extensions sidebar view — auto-expanded
 surfaces + tables
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

views (new feature):
- pure panel-stack reducer + thin generic ViewSidebar (dropdown picker + add/remove),
  switches on view KIND, never a surface id

Extensions view (composition root):
- folds frontend modules + backend surfaces into one "Extensions" view
- frontend module list AGGREGATED from each feature's public `manifest` export
  (can't drift); no per-module version (FE features are internal to dispatch-web)
- surfaces are AUTO-SUBSCRIBED on catalog + rendered expanded (no catalog buttons)

surface-host:
- consecutive `stat` fields coalesce into one aligned label/value table (StatTable)
- generic custom-field renderer: dispatch on rendererId === "table" → SurfaceTable
  (pure parseTablePayload), so a backend `custom`/table field renders generically
- shared presentational components/Table.svelte (used by both, neither feature
  depends on the other)

store:
- auto-subscribe every catalog entry, unsubscribe vanished ones, re-subscribe all
  on reconnect; expose all received specs via `surfaces` (drops single-selection)

backend-handoff: CR-1 — emit Loaded Extensions as a custom/table field; notes
what's already covered FE-side (renderer shipped, stat-table fallback works).
---
 src/components/Table.svelte | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 src/components/Table.svelte

(limited to 'src/components/Table.svelte')

diff --git a/src/components/Table.svelte b/src/components/Table.svelte
new file mode 100644
index 0000000..7c56e69
--- /dev/null
+++ b/src/components/Table.svelte
@@ -0,0 +1,42 @@
+<script lang="ts">
+	// Generic, purely presentational table. Props in → markup out; zero logic,
+	// zero data-fetching. Shared by the surface custom-field "table" renderer and
+	// the frontend "Loaded Modules" view, so neither feature depends on the other.
+	let {
+		columns,
+		rows,
+		empty = "No data",
+	}: {
+		readonly columns: readonly string[];
+		readonly rows: readonly (readonly string[])[];
+		/** Text shown when there are no rows. */
+		readonly empty?: string;
+	} = $props();
+</script>
+
+<div class="overflow-x-auto">
+	<table class="table table-sm">
+		<thead>
+			<tr>
+				{#each columns as col, i (i)}
+					<th>{col}</th>
+				{/each}
+			</tr>
+		</thead>
+		<tbody>
+			{#if rows.length === 0}
+				<tr>
+					<td colspan={Math.max(columns.length, 1)} class="opacity-60">{empty}</td>
+				</tr>
+			{:else}
+				{#each rows as row, r (r)}
+					<tr>
+						{#each row as cell, c (c)}
+							<td>{cell}</td>
+						{/each}
+					</tr>
+				{/each}
+			{/if}
+		</tbody>
+	</table>
+</div>
-- 
cgit v1.2.3

