diff --git a/web/e2e/smoke.spec.js b/web/e2e/smoke.spec.js index 39e6bb4..3bfaf02 100644 --- a/web/e2e/smoke.spec.js +++ b/web/e2e/smoke.spec.js @@ -2651,6 +2651,10 @@ test('[[ foreslår en note, og linket lander i noten', async ({ page }) => { await ed.blur(); await expect(page.locator('.links')).toContainText('Rejseplan til Berlin'); + // And it is a link you can follow: clicking the chip opens the note it names. + await page.locator('.links .link', { hasText: 'Rejseplan til Berlin' }).click(); + await expect(ed.locator('h1')).toHaveText('Rejseplan til Berlin'); + expect(trouble).toEqual([]); }); diff --git a/web/src/lib/locales/da.js b/web/src/lib/locales/da.js index 8ce9897..41d1b34 100644 --- a/web/src/lib/locales/da.js +++ b/web/src/lib/locales/da.js @@ -22,6 +22,8 @@ export const da = { 'notes.unshared': 'Noten er din igen.', // Deling med enkeltpersoner, ved siden af projektet. 'notes.groupSharedWithMe': 'Delt med mig', + 'notes.linkOpen': 'Åbn', + 'notes.linkMissing': 'Noten blev ikke fundet.', 'notes.sharedBy': 'Delt af {name}', 'notes.sharePeople': 'Del med personer', 'notes.sharePick': 'Vælg en person …', diff --git a/web/src/lib/locales/en.js b/web/src/lib/locales/en.js index dab402b..f7ad590 100644 --- a/web/src/lib/locales/en.js +++ b/web/src/lib/locales/en.js @@ -18,6 +18,8 @@ export const en = { 'notes.unshared': 'The note is yours again.', // Sharing with individual people, alongside the project. 'notes.groupSharedWithMe': 'Shared with me', + 'notes.linkOpen': 'Open', + 'notes.linkMissing': 'That note could not be found.', 'notes.sharedBy': 'Shared by {name}', 'notes.sharePeople': 'Share with people', 'notes.sharePick': 'Choose someone …', diff --git a/web/src/routes/noter/+page.svelte b/web/src/routes/noter/+page.svelte index 5f38220..b6dc2bb 100644 --- a/web/src/routes/noter/+page.svelte +++ b/web/src/routes/noter/+page.svelte @@ -14,6 +14,7 @@ import { api, humanMessage } from '$lib/api.js'; import { app } from '$lib/stores.svelte.js'; import { page } from '$app/stores'; + import { goto } from '$app/navigation'; import { t, tag } from '$lib/i18n.svelte.js'; import { NOTE, startDrag } from '$lib/dnd.js'; import { colorVar } from '$lib/colors.js'; @@ -653,6 +654,44 @@ // What the note points at, for the panel under the editor. let links = $derived(selected?.links ?? []); + /** + * Follows a link from the panel to the thing it names. + * + * A [[note]] resolves by its title to a note — the one on the list if it is + * there, otherwise looked up — and opens it in place. A #project and a task go + * to their own pages. Until now these were text and nothing else: the note said + * it pointed somewhere and there was no way to go. + */ + async function openLink(link) { + if (link.kind === 'note') { + const wanted = link.target_id.toLowerCase(); + const here = notes.find((n) => (n.title ?? '').toLowerCase() === wanted); + if (here) { + open(here); + return; + } + try { + const found = (await api.notes({ q: link.target_id })).notes ?? []; + const hit = found.find((n) => (n.title ?? '').toLowerCase() === wanted) ?? found[0]; + if (hit) open(hit); + else app.toast(t('notes.linkMissing')); + } catch (e) { + app.toast(humanMessage(e)); + } + return; + } + if (link.kind === 'project') { + const project = app.projects.find( + (p) => p.name.toLowerCase() === link.target_id.toLowerCase() + ); + if (project) goto(`/projekt/${project.id}`); + return; + } + if (link.kind === 'task') { + goto(`/opgave/${link.target_id}`); + } + } + @@ -889,7 +928,9 @@ {#if links.length} {#each links as link} - {label(link)} + {/each} {/if} @@ -1615,9 +1656,17 @@ .link { color: var(--ink-muted); background: var(--surface); + border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 0 var(--s1); font-family: var(--mono, ui-monospace, monospace); + font-size: inherit; + cursor: pointer; + } + .link:hover { + color: var(--ink); + background: var(--surface-raised); + border-color: var(--line-strong); } /* Sharing lives in a popover above the Del button, so the footer stays a status