Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bbd36c79b | ||
|
|
54359f8d68 | ||
|
|
f400705bb7 |
@@ -72,13 +72,44 @@ jobs:
|
|||||||
#
|
#
|
||||||
# Den sidste linje er et link til udgivelsen selv, og den tages af: en
|
# Den sidste linje er et link til udgivelsen selv, og den tages af: en
|
||||||
# side, der linker til sig selv, hjælper ingen.
|
# side, der linker til sig selv, hjælper ingen.
|
||||||
|
#
|
||||||
|
# Og `fetch-depth: 0` er ikke nok, hvilket kostede to udgivelser at opdage.
|
||||||
|
# actions/checkout henter først alle tags korrekt — og overskriver derefter
|
||||||
|
# netop den ene, der udløste kørslen:
|
||||||
|
#
|
||||||
|
# git rev-parse refs/tags/v0.46.0
|
||||||
|
# git fetch --no-tags origin +0dc4c2f9b8…:refs/tags/v0.46.0
|
||||||
|
#
|
||||||
|
# Den henter commit-SHA'en ind i tag-refen, så den annoterede tag bliver til
|
||||||
|
# en letvægtstag, der peger direkte på commit'en. `%(contents)` giver så
|
||||||
|
# commitbeskeden, og det var flettecommittens overskrift, der stod som
|
||||||
|
# udgivelsestekst på v0.45.0 og v0.46.0 — uden at noget fejlede.
|
||||||
|
#
|
||||||
|
# Derfor hentes tagget som *ref* igen her, før det læses.
|
||||||
- id: notes
|
- id: notes
|
||||||
run: |
|
run: |
|
||||||
|
git fetch --force origin \
|
||||||
|
"refs/tags/${GITHUB_REF_NAME}:refs/tags/${GITHUB_REF_NAME}"
|
||||||
|
|
||||||
|
body=$(git tag -l --format='%(contents)' "${GITHUB_REF_NAME}" \
|
||||||
|
| sed '\#^https://github.com/.*/releases/tag/#d' \
|
||||||
|
| sed -e :a -e '/^\n*$/{$d;N;ba' -e '}')
|
||||||
|
|
||||||
|
# Hver tagbesked begynder med sin egen version. Holder det ikke, læser vi
|
||||||
|
# noget andet end tagget — og en forkert udgivelsestekst skal fejle her
|
||||||
|
# frem for at blive opdaget på siden bagefter.
|
||||||
|
case "$body" in
|
||||||
|
"${GITHUB_REF_NAME}"*) ;;
|
||||||
|
*)
|
||||||
|
echo "::error::Tagbeskeden begynder ikke med ${GITHUB_REF_NAME}." \
|
||||||
|
"Læste vi en letvægtstag? Første linje: $(printf '%s' "$body" | head -1)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
{
|
{
|
||||||
echo 'body<<VERDANDE_EOF'
|
echo 'body<<VERDANDE_EOF'
|
||||||
git tag -l --format='%(contents)' "${GITHUB_REF_NAME}" \
|
printf '%s\n' "$body"
|
||||||
| sed '\#^https://github.com/.*/releases/tag/#d' \
|
|
||||||
| sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'
|
|
||||||
echo VERDANDE_EOF
|
echo VERDANDE_EOF
|
||||||
} >> "$GITHUB_OUTPUT"
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
|||||||
@@ -4635,3 +4635,57 @@ test('et projekt kan deles med en, der allerede har en konto', async ({ browser,
|
|||||||
|
|
||||||
expect(trouble).toEqual([]);
|
expect(trouble).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* En søgning i ⌘K efterlader resultaterne, den fandt.
|
||||||
|
*
|
||||||
|
* Paletten viste de noter, der passede, i sin egen liste — og når man valgte en,
|
||||||
|
* åbnede den noten på en side, hvis liste stod helt uændret. De andre træffere var
|
||||||
|
* væk, og det eneste sted, de nogensinde havde været, var en popup, der lige var
|
||||||
|
* lukket. Rapporteret sådan her: "jeg får kun præsenteret en popup liste med
|
||||||
|
* resultater, men hvis jeg trykker retur, kommer jeg tilbage til den normale liste
|
||||||
|
* — ikke resultater."
|
||||||
|
*
|
||||||
|
* Prøven laver sine egne noter med et ord, ingen anden note i basen har. Et ord
|
||||||
|
* lånt fra en tidligere prøve ville gøre den her afhængig af rækkefølgen, og den
|
||||||
|
* kobling har allerede narret mig én gang i den her fil.
|
||||||
|
*/
|
||||||
|
test('en søgning i ⌘K efterlader de træffere, den fandt', async ({ page }) => {
|
||||||
|
const trouble = watchForTrouble(page);
|
||||||
|
await page.goto('/noter');
|
||||||
|
|
||||||
|
// Gemt ved at forlade feltet, som resten af fladen gør det — der er ingen
|
||||||
|
// Gem-knap at huske.
|
||||||
|
for (const title of ['Fyrreskov nord', 'Fyrreskov syd', 'Egeskov']) {
|
||||||
|
await page.getByRole('button', { name: 'Ny note' }).click();
|
||||||
|
const body = page.getByLabel('Notens tekst');
|
||||||
|
await body.fill(`${title}\n\nnoget om den`);
|
||||||
|
await body.blur();
|
||||||
|
await expect(page.locator('.list').getByText(title, { exact: true })).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.goto('/');
|
||||||
|
await page.getByRole('button', { name: /Søg/ }).click();
|
||||||
|
const palette = page.getByRole('dialog');
|
||||||
|
await palette.getByRole('textbox').fill('Fyrreskov');
|
||||||
|
await expect(palette.locator('li button', { hasText: 'Fyrreskov nord' })).toBeVisible();
|
||||||
|
await palette.locator('li button', { hasText: 'Fyrreskov nord' }).click();
|
||||||
|
|
||||||
|
// Noten er åben …
|
||||||
|
await expect(page).toHaveURL(/\/noter\?note=[^&]+&q=Fyrreskov/);
|
||||||
|
await expect(page.getByLabel('Notens tekst')).toContainText('Fyrreskov nord');
|
||||||
|
|
||||||
|
// … og listen ved siden af den er de andre træffere, ikke den normale liste.
|
||||||
|
await expect(page.getByPlaceholder('Søg i noter')).toHaveValue('Fyrreskov');
|
||||||
|
const list = page.locator(".list");
|
||||||
|
await expect(list.getByText('Fyrreskov syd')).toBeVisible();
|
||||||
|
await expect(list.getByText('Egeskov')).toHaveCount(0);
|
||||||
|
|
||||||
|
// Og adressen lyver ikke, når feltet tømmes: uden det ville et genbesøg hente
|
||||||
|
// en søgning tilbage, man selv havde ryddet væk.
|
||||||
|
await page.getByPlaceholder('Søg i noter').fill('');
|
||||||
|
await expect(page).not.toHaveURL(/[?&]q=/);
|
||||||
|
await expect(list.getByText('Egeskov')).toBeVisible();
|
||||||
|
|
||||||
|
expect(trouble).toEqual([]);
|
||||||
|
});
|
||||||
|
|||||||
@@ -125,7 +125,13 @@
|
|||||||
// En indstilling bærer sin egen adresse i sit id — der er ingen række at slå
|
// En indstilling bærer sin egen adresse i sit id — der er ingen række at slå
|
||||||
// op, den ER stedet.
|
// op, den ER stedet.
|
||||||
if (item.kind === 'setting') return goto(item.id);
|
if (item.kind === 'setting') return goto(item.id);
|
||||||
if (item.kind === 'note') return goto(`/noter?note=${item.id}`);
|
// Søgningen følger med noten. Uden den åbner man én træffer på en side, hvis
|
||||||
|
// liste står uændret — og de andre træffere var kun at se i den popup, man
|
||||||
|
// lige lukkede.
|
||||||
|
if (item.kind === 'note') {
|
||||||
|
const q = query.trim();
|
||||||
|
return goto(`/noter?note=${item.id}` + (q ? `&q=${encodeURIComponent(q)}` : ''));
|
||||||
|
}
|
||||||
goto(item.kind === 'project' ? `/projekt/${item.id}` : `/projekt/${item.project}`);
|
goto(item.kind === 'project' ? `/projekt/${item.id}` : `/projekt/${item.project}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
import { api, humanMessage } from '$lib/api.js';
|
import { api, humanMessage } from '$lib/api.js';
|
||||||
import { app } from '$lib/stores.svelte.js';
|
import { app } from '$lib/stores.svelte.js';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { goto } from '$app/navigation';
|
import { untrack } from 'svelte';
|
||||||
|
import { goto, replaceState } from '$app/navigation';
|
||||||
import { t, tag } from '$lib/i18n.svelte.js';
|
import { t, tag } from '$lib/i18n.svelte.js';
|
||||||
import { NOTE, startDrag } from '$lib/dnd.js';
|
import { NOTE, startDrag } from '$lib/dnd.js';
|
||||||
import { colorVar } from '$lib/colors.js';
|
import { colorVar } from '$lib/colors.js';
|
||||||
@@ -483,6 +484,49 @@
|
|||||||
load(query);
|
load(query);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* En søgning, man kommer med, og en søgning, man kan gå videre med.
|
||||||
|
*
|
||||||
|
* ⌘K viste noter, der passede, i sin egen liste — og når man valgte en, åbnede
|
||||||
|
* den noten på en side, hvis liste stod uændret. Så var de andre træffere væk,
|
||||||
|
* og det eneste sted, de havde været, var en popup, der lige var lukket. "Jeg
|
||||||
|
* får kun præsenteret en popup med resultater, og trykker jeg retur, kommer jeg
|
||||||
|
* tilbage til den normale liste."
|
||||||
|
*
|
||||||
|
* Nu bærer adressen søgningen, så listen ved siden af den åbne note er de andre
|
||||||
|
* træffere. Og den skrives tilbage, når feltet ændres, så adressen kan gemmes og
|
||||||
|
* deles — og frem for alt ikke lyver, når man har tømt feltet.
|
||||||
|
*
|
||||||
|
* `settled` er det, de to retninger er blevet enige om — og den læses med
|
||||||
|
* `untrack`, hvilket er hele forskellen på at virke og ikke.
|
||||||
|
*
|
||||||
|
* Uden den: den nederste effekt sætter `settled`, det vækker den øverste, og
|
||||||
|
* `replaceState` har endnu ikke opdateret `$page.url`. Den øverste læser derfor
|
||||||
|
* den *gamle* adresse, finder en søgning, der ikke er enig med `settled`, og
|
||||||
|
* skriver den tilbage i feltet. Man tømmer søgefeltet, og ordet kommer igen.
|
||||||
|
* Hver effekt skal reagere på sin egen kilde — adressen og feltet — og aldrig
|
||||||
|
* på den fælles markør.
|
||||||
|
*/
|
||||||
|
let settled = $state(null);
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
const inURL = $page.url.searchParams.get('q') ?? '';
|
||||||
|
if (inURL === untrack(() => settled)) return;
|
||||||
|
settled = inURL;
|
||||||
|
draftQuery = inURL;
|
||||||
|
query = inURL;
|
||||||
|
});
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
const now = query;
|
||||||
|
if (now === untrack(() => settled)) return;
|
||||||
|
settled = now;
|
||||||
|
const url = new URL(untrack(() => $page.url));
|
||||||
|
if (now) url.searchParams.set('q', now);
|
||||||
|
else url.searchParams.delete('q');
|
||||||
|
replaceState(url, {});
|
||||||
|
});
|
||||||
|
|
||||||
// Arriving from somewhere that names a note — a task's panel, a project's page —
|
// Arriving from somewhere that names a note — a task's panel, a project's page —
|
||||||
// opens it rather than dropping the person on a list to find it again.
|
// opens it rather than dropping the person on a list to find it again.
|
||||||
let asked = $derived($page.url.searchParams.get('note'));
|
let asked = $derived($page.url.searchParams.get('note'));
|
||||||
|
|||||||
Reference in New Issue
Block a user