From 6729a35435eb80a175377888b452c6215f9acda9 Mon Sep 17 00:00:00 2001 From: vegard Date: Wed, 18 Mar 2026 22:36:08 +0000 Subject: [PATCH] Lokasjon-input: del posisjon i chat med kartvisning (oppgave 29.9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ny «Del posisjon»-knapp i ChatInput ved siden av tale/video-knappene. Bruker Geolocation API for å hente brukerens posisjon, oppretter en content-node med metadata.location { lat, lon, address }. Reverse geocoding via Nominatim (best-effort) gir adresse i metadata. Kartvisning i chat via Leaflet/OpenStreetMap viser posisjonen inline. Komponenter: - LocationShare.svelte: knapp + geolocation + geocoding + node-opprettelse - LocationMap.svelte: Leaflet-kart med markør og adresse-popup - Leaflet lastes via CDN (unpkg) i app.html --- frontend/src/app.html | 2 + frontend/src/lib/components/ChatInput.svelte | 7 ++ .../src/lib/components/LocationMap.svelte | 57 +++++++++ .../src/lib/components/LocationShare.svelte | 117 ++++++++++++++++++ frontend/src/routes/chat/[id]/+page.svelte | 37 +++++- tasks.md | 3 +- 6 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 frontend/src/lib/components/LocationMap.svelte create mode 100644 frontend/src/lib/components/LocationShare.svelte diff --git a/frontend/src/app.html b/frontend/src/app.html index 99ea7b0..a69058b 100644 --- a/frontend/src/app.html +++ b/frontend/src/app.html @@ -7,6 +7,8 @@ + + %sveltekit.head% diff --git a/frontend/src/lib/components/ChatInput.svelte b/frontend/src/lib/components/ChatInput.svelte index dedbd2b..697ac5e 100644 --- a/frontend/src/lib/components/ChatInput.svelte +++ b/frontend/src/lib/components/ChatInput.svelte @@ -1,6 +1,7 @@ + +
+
+ {#if address} +

{address}

+ {:else} +

{lat}, {lon}

+ {/if} +
diff --git a/frontend/src/lib/components/LocationShare.svelte b/frontend/src/lib/components/LocationShare.svelte new file mode 100644 index 0000000..8c3ccbc --- /dev/null +++ b/frontend/src/lib/components/LocationShare.svelte @@ -0,0 +1,117 @@ + + +{#if locState === 'idle'} + +{:else} +
+ + + + + + {#if locState === 'requesting'}Henter posisjon…{:else if locState === 'geocoding'}Finner adresse…{:else}Lagrer…{/if} + +
+{/if} diff --git a/frontend/src/routes/chat/[id]/+page.svelte b/frontend/src/routes/chat/[id]/+page.svelte index a4008dd..0300c26 100644 --- a/frontend/src/routes/chat/[id]/+page.svelte +++ b/frontend/src/routes/chat/[id]/+page.svelte @@ -5,6 +5,7 @@ import { createNode, casUrl } from '$lib/api'; import ChatInput from '$lib/components/ChatInput.svelte'; import AudioPlayer from '$lib/components/AudioPlayer.svelte'; + import LocationMap from '$lib/components/LocationMap.svelte'; import { tick } from 'svelte'; const session = $derived($page.data.session as Record | undefined); @@ -203,6 +204,25 @@ return (meta.transcription?.segment_count ?? 0) > 0; } catch { return false; } } + + /** Check if this node is a location node */ + function isLocationNode(node: Node): boolean { + try { + const meta = JSON.parse(node.metadata ?? '{}'); + return meta.location && typeof meta.location.lat === 'number' && typeof meta.location.lon === 'number'; + } catch { return false; } + } + + /** Get location data from a node */ + function getLocation(node: Node): { lat: number; lon: number; address?: string } | undefined { + try { + const meta = JSON.parse(node.metadata ?? '{}'); + if (meta.location?.lat != null && meta.location?.lon != null) { + return meta.location; + } + } catch {} + return undefined; + }
@@ -260,9 +280,10 @@ {@const own = isOwnMessage(msg)} {@const audio = isAudioNode(msg)} {@const image = isImageNode(msg)} + {@const location = isLocationNode(msg)} {@const bot = isAgentMessage(msg)}
-
+
{#if !own}

{#if bot}🤖 {/if}{senderName(msg)} @@ -296,12 +317,24 @@

{imageDescription(msg)}

{/if}
+ {:else if location} + {@const loc = getLocation(msg)} + {#if loc} +
+ + + + + Posisjon +
+ + {/if} {:else}

{msg.content || ''}

{/if} -

+

{formatTime(msg)}

diff --git a/tasks.md b/tasks.md index 2499952..3b46f60 100644 --- a/tasks.md +++ b/tasks.md @@ -406,8 +406,7 @@ noden er det som lever videre. - [x] 29.8 Video-prosessering: `synops-video` CLI for transcode (H.264), thumbnail-generering, og varighet-uttrekk. Input: `--cas-hash `. Output: ny CAS-hash (trancodet) + thumbnail CAS-hash. ### Geolokasjon -- [~] 29.9 Lokasjon-input: "Del posisjon"-knapp i input-komponenten → Geolocation API → node med `metadata.location: { "lat": 59.91, "lon": 10.75 }`. Kart-visning i node-detaljer (Leaflet/OpenStreetMap). Valgfritt: reverse geocoding via Nominatim for adresse. - > Påbegynt: 2026-03-18T22:30 +- [x] 29.9 Lokasjon-input: "Del posisjon"-knapp i input-komponenten → Geolocation API → node med `metadata.location: { "lat": 59.91, "lon": 10.75 }`. Kart-visning i node-detaljer (Leaflet/OpenStreetMap). Valgfritt: reverse geocoding via Nominatim for adresse. ### Håndskrift/tegning - [ ] 29.10 Tegne-input: enkel canvas-basert tegneflate i input-komponenten. Eksporter som PNG → CAS → media-node. Ikke whiteboard (det er et eget verktøy) — dette er "rask skisse som input", som en post-it.