From 6edd1fa091dc53bc9f21cdc134f9890aa8ae5bcc Mon Sep 17 00:00:00 2001 From: vegard Date: Sun, 15 Mar 2026 15:46:34 +0100 Subject: [PATCH] Frontend: entiteter, graf-visualisering og #-autocomplete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EntitiesBlock: liste med søk/filter, opprett, rediger, slett, relasjonsvisning med navigering mellom entiteter - GraphBlock: SVG force-directed layout via traverse API, pan/zoom, drag noder, dobbeltklikk for å utforske - EntityAutocomplete: #-mention med debounced søk, tastaturnavigering, dropdown med typefarger og aliaser - Registrert entities block-type + kunnskapsgraf-side i seed Co-Authored-By: Claude Opus 4.6 --- migrations/seed_dev.sql | 12 +- web/src/lib/blocks/EntitiesBlock.svelte | 535 ++++++++++++++++++ web/src/lib/blocks/GraphBlock.svelte | 385 ++++++++++++- web/src/lib/blocks/registry.ts | 5 + .../lib/components/EntityAutocomplete.svelte | 235 ++++++++ 5 files changed, 1160 insertions(+), 12 deletions(-) create mode 100644 web/src/lib/blocks/EntitiesBlock.svelte create mode 100644 web/src/lib/components/EntityAutocomplete.svelte diff --git a/migrations/seed_dev.sql b/migrations/seed_dev.sql index 56c27f1..16bb4ed 100644 --- a/migrations/seed_dev.sql +++ b/migrations/seed_dev.sql @@ -192,6 +192,16 @@ UPDATE workspaces SET settings = jsonb_set( {"id": "notes-1", "type": "notes", "title": "Show notes", "props": {"noteId": "a0000000-0000-0000-0000-000000000040"}} ] }, + { + "slug": "kunnskapsgraf", + "title": "Kunnskapsgraf", + "icon": "🕸️", + "layout": "2-1", + "blocks": [ + {"id": "graph-1", "type": "graph", "title": "Grafvisning"}, + {"id": "entities-1", "type": "entities", "title": "Entiteter"} + ] + }, { "slug": "research", "title": "Research", @@ -199,7 +209,7 @@ UPDATE workspaces SET settings = jsonb_set( "layout": "2-col", "blocks": [ {"id": "research-1", "type": "research", "title": "Research-klipp"}, - {"id": "graph-1", "type": "graph", "title": "Kunnskapsgraf"} + {"id": "entities-2", "type": "entities", "title": "Entiteter"} ] } ]'::jsonb diff --git a/web/src/lib/blocks/EntitiesBlock.svelte b/web/src/lib/blocks/EntitiesBlock.svelte new file mode 100644 index 0000000..88493b3 --- /dev/null +++ b/web/src/lib/blocks/EntitiesBlock.svelte @@ -0,0 +1,535 @@ + + +{#if selected} + +
+ + + {#if editing} +
+ + + +
+ + +
+
+ {:else} +
+ + {selected.type} + +

{selected.name}

+ {#if selected.aliases.length > 0} +
+ {#each selected.aliases as alias} + {alias} + {/each} +
+ {/if} +
+ + +
+
+ +
+

Relasjoner

+ {#if loadingEdges} +

Laster...

+ {:else if selectedEdges.length === 0} +

Ingen relasjoner

+ {:else} + {#each selectedEdges as edge (edge.edge_id)} +
+ {edge.relation_label} + + + {edge.connected_type} + + +
+ {/each} + {/if} +
+ {/if} +
+{:else} + +
+
+ + + +
+ + {#if showCreate} +
+ + + +
+ + +
+
+ {/if} + + {#if loading} +

Laster...

+ {:else if entities.length === 0} +

{query ? 'Ingen treff' : 'Ingen entiteter ennå'}

+ {:else} +
+ {#each entities as entity (entity.id)} + + {/each} +
+ {/if} +
+{/if} + +{#if error} +
{error}
+{/if} + + diff --git a/web/src/lib/blocks/GraphBlock.svelte b/web/src/lib/blocks/GraphBlock.svelte index b0cb20d..21fee99 100644 --- a/web/src/lib/blocks/GraphBlock.svelte +++ b/web/src/lib/blocks/GraphBlock.svelte @@ -1,25 +1,388 @@ -
- 🕸️ -

Graph

-

Kommer snart

+
+
+ { if (e.key === 'Enter') searchAndLoad(); }} + class="input" + /> + +
+ + {#if loading} +
Laster graf...
+ {:else if error} +
{error}
+ {:else if nodes.length === 0} +
Søk etter en entitet for å visualisere grafen
+ {:else} + + + {#each edges as edge (edge.id)} + {@const source = nodes.find(n => n.id === edge.source_id)} + {@const target = nodes.find(n => n.id === edge.target_id)} + {#if source && target} + + {edge.relation_label} + {/if} + {/each} + + + {#each nodes as node (node.id)} + handleMouseDown(e, node)} + ondblclick={() => handleNodeClick(node)} + > + + {node.name ?? node.id.slice(0, 8)} + + {/each} + + {/if}
diff --git a/web/src/lib/blocks/registry.ts b/web/src/lib/blocks/registry.ts index fcc417e..1089ce5 100644 --- a/web/src/lib/blocks/registry.ts +++ b/web/src/lib/blocks/registry.ts @@ -27,6 +27,11 @@ export const blockRegistry: Record = { icon: '🕸️', component: () => import('./GraphBlock.svelte') }, + entities: { + label: 'Entiteter', + icon: '#️⃣', + component: () => import('./EntitiesBlock.svelte') + }, research: { label: 'Research', icon: '🔍', diff --git a/web/src/lib/components/EntityAutocomplete.svelte b/web/src/lib/components/EntityAutocomplete.svelte new file mode 100644 index 0000000..dcbf58d --- /dev/null +++ b/web/src/lib/components/EntityAutocomplete.svelte @@ -0,0 +1,235 @@ + + +
+ + + {#if showDropdown && suggestions.length > 0} + + {/if} +
+ +