diff --git a/e2e/canvas.spec.ts b/e2e/canvas.spec.ts
index 92a4b34..6032014 100644
--- a/e2e/canvas.spec.ts
+++ b/e2e/canvas.spec.ts
@@ -18,6 +18,13 @@ test('palette click creates a dashed draft and Apply posts the intent', async ({
await expect(page.locator('.resource-node.draft')).toHaveCount(0)
})
+test('clicking a node shows its origin in the inspector source row', async ({ page }) => {
+ await page.goto('/')
+ await page.getByText('web', { exact: true }).click()
+ await expect(page.locator('.source-row')).toBeVisible()
+ await expect(page.locator('.source-row')).toContainText('terraform')
+})
+
test('context menu marks a node for removal', async ({ page }) => {
await page.goto('/')
await page.getByText('web', { exact: true }).click({ button: 'right' })
diff --git a/packages/ui/src/Inspector.tsx b/packages/ui/src/Inspector.tsx
index d1cc692..b3c9e1b 100644
--- a/packages/ui/src/Inspector.tsx
+++ b/packages/ui/src/Inspector.tsx
@@ -1,4 +1,5 @@
import { useStore } from './store.js'
+import { SourceIcon } from './icons.js'
function Value({ v }: { v: unknown }) {
if (v === undefined) return —
@@ -56,6 +57,12 @@ export function Inspector() {
{node.status}
{node.type}
+ {node.origin && (
+
+
+ {node.origin}
+
+ )}
{!(node.id in modifies) && (
diff --git a/packages/ui/src/icons.tsx b/packages/ui/src/icons.tsx
index e43974a..53efc25 100644
--- a/packages/ui/src/icons.tsx
+++ b/packages/ui/src/icons.tsx
@@ -27,3 +27,24 @@ export function ResourceIcon({ type }: { type: string }) {
)
}
+
+// Provider/origin glyph — distinct from ResourceIcon (which keys off the
+// resource *type*). 'terraform' gets a layered-squares glyph evoking stacked
+// state layers; any other provider id (e.g. a future live-scan origin) falls
+// back to a generic database/cloud glyph. SVG only, never emoji.
+export function SourceIcon({ origin }: { origin: string }) {
+ const isTerraform = origin === 'terraform'
+ return (
+
+ )
+}
diff --git a/packages/ui/src/styles.css b/packages/ui/src/styles.css
index 5d2e9f9..551d20d 100644
--- a/packages/ui/src/styles.css
+++ b/packages/ui/src/styles.css
@@ -39,6 +39,8 @@ body { margin: 0; font-family: ui-sans-serif, system-ui, sans-serif; }
.status-pill.status-update { background: #fef9c3; }
.status-pill.status-delete { background: #fee2e2; }
.status-pill.status-replace { background: #f3e8ff; }
+.source-row { display: flex; align-items: center; gap: 6px; margin: 6px 0;
+ font-size: 11px; color: #64748b; }
.inspector-actions { display: flex; gap: 6px; margin: 8px 0; }
.inspector-actions button { font-size: 12px; padding: 4px 8px; border: 1px solid #e2e8f0;
border-radius: 6px; background: #fff; cursor: pointer; }