Compare commits

...
1 Commits
Author SHA1 Message Date
Kristian WindandClaude Opus 5 ce1d27f8ca fix(notes): én note, der ikke kunne tegnes, låste hele ruden
Release / Build and publish image (push) Failing after 9m53s
Meldt ind som "jeg kan ikke skifte mellem noter — uanset hvilken jeg trykker på,
åbner de ikke".

Editoren indlæste med `note.body.trim()` uden værn. `body` er en streng fra
serveren hver eneste gang — indtil den ikke er, og så kaster den her inde i et
`$effect`. En effekt, der kaster, tager komponentens opdateringer med sig: den
forrige note bliver stående, og *ingen* note kan åbnes bagefter. Klikket kan ikke
engang lande, hvilket er derfor det ikke ligner en fejl i noten, man klikkede på,
men i hele ruden.

To ting, ikke én. Værnet, så en manglende krop er en tom note. Og en ramme om
tegningen, så det næste, der en dag ikke kan tegnes, heller ikke bliver til det
her: en note, der fejler, viser sin egen tekst uden formatering, og resten af
ruden virker videre.

`loadedId` sættes nu før tegningen frem for efter. Ellers ville effekten prøve
igen ved hver eneste ændring bagefter og kaste hver gang.

`textContent` og ikke `innerHTML` i faldbakken: teksten er ikke tegnet, og at
lægge den ind som markup ville være at køre præcis det, tegningen nægtede.
Gemningen er ikke i fare — den skriver `draft`, som kommer fra svaret og ikke fra
det, der står i editoren, indtil nogen taster i den.

Prøven tvinger formen frem i stedet for at vente på den nyttelast, der gjorde
det. Hvad der end fik `body` til at mangle, er svaret det samme: én note må ikke
kunne låse de tolv hundrede andre ude. Den er set fejle uden rettelsen — med
netop den fejl, konsollen viste.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 15:32:36 +02:00
2 changed files with 94 additions and 7 deletions
+54
View File
@@ -3038,3 +3038,57 @@ test('flere noter kan markeres, arkiveres og hentes frem igen', async ({ page })
expect(trouble).toEqual([]);
});
/**
* Én note, der ikke kan tegnes, må ikke tage ruden med sig.
*
* Meldt ind som "jeg kan ikke skifte mellem noter — uanset hvilken jeg trykker
* på, åbner de ikke". Editoren indlæste med `note.body.trim()` uden værn, og
* `body` er en streng fra serveren hver eneste gang — indtil den ikke er. Så
* kaster den inde i et `$effect`, og en effekt, der kaster, tager komponentens
* opdateringer med sig: den forrige note bliver stående, og ingen note kan åbnes
* bagefter. Klikket kan ikke engang lande.
*
* Formen fremtvinges her frem for at vente på den nyttelast, der gjorde det:
* hvad der end fik `body` til at mangle, er svaret det samme — én note må ikke
* kunne låse de tolv hundrede andre ude.
*/
test('en note uden krop må ikke låse ruden', async ({ page }) => {
const errors = [];
page.on('pageerror', (e) => errors.push(e.message));
await page.goto('/noter');
for (const title of ['Alfa', 'Beta']) {
await page.getByRole('button', { name: 'Ny note' }).click();
const ed = page.getByRole('textbox', { name: 'Notens tekst' });
await ed.click();
await page.keyboard.type(title);
await expect(ed.locator('h1')).toHaveText(title);
await page.keyboard.press('Enter');
await page.keyboard.type('Brødtekst i ' + title);
await expect(ed.locator('p').filter({ hasText: 'Brødtekst i ' + title })).toBeVisible();
await page.waitForTimeout(900);
}
await page.reload();
await expect(page.locator('li .row strong').filter({ hasText: 'Beta' })).toBeVisible();
await page.route('**/api/v1/notes/*', async (route) => {
if (route.request().method() !== 'GET') return route.continue();
const res = await route.fetch();
const note = await res.json();
delete note.body;
await route.fulfill({ response: res, json: note });
});
await page.locator('li').filter({ hasText: 'Alfa' }).first().click();
await page.waitForTimeout(600);
expect(errors, 'noten kastede under indlæsningen').toEqual([]);
await page.unroute('**/api/v1/notes/*');
await page.locator('li').filter({ hasText: 'Beta' }).first().click();
await expect(
page.getByRole('textbox', { name: 'Notens tekst' }).locator('h1'),
'ruden er låst: den næste note åbner ikke'
).toHaveText('Beta', { timeout: 5000 });
expect(errors, 'ruden efterlod en fejl bag sig').toEqual([]);
});
+40 -7
View File
@@ -35,20 +35,53 @@
let loadedId = $state(null);
$effect(() => {
if (!editor || !note || note.id === loadedId) return;
// An empty note opens on its title. That is what Apple Notes does, and it is
// also what the list needs: the first line becomes the name of the note, so a
// note whose first line is body text is a note called by its first sentence.
// Sat *før* tegningen, ikke efter.
//
// Tested on the text and not on the rendered result: an empty body renders as
// one empty paragraph, not as nothing, so a falsy check here never fired.
editor.innerHTML = note.body.trim() ? markdownToHtml(note.body) : '<h1><br></h1>';
// Kaster noget herunder, er noten stadig den, der er forsøgt indlæst — og
// uden det her ville effekten prøve igen ved hver eneste ændring bagefter og
// kaste hver gang.
loadedId = note.id;
active = {};
// Kildevisningen hører til den note, den blev slået til på. Skifter man note,
// er det den rige flade, man skal møde — ellers står den næste note som kode,
// uden at nogen har bedt om det.
asSource = false;
colourCode();
// Én note, der ikke kan tegnes, må ikke tage ruden med sig.
//
// Det var `note.body.trim()` uden værn, og `body` er en streng fra serveren
// hver eneste gang — indtil den ikke er. Så kaster den her inde i et
// `$effect`, og en effekt, der kaster, tager komponentens opdateringer med
// sig: den forrige note bliver stående, og *ingen* note kan åbnes bagefter.
// Klikket kan ikke engang lande. Meldt ind som "jeg kan ikke skifte mellem
// noter, uanset hvilken jeg trykker på", hvilket er præcis, hvad det ser ud
// som fra den anden side.
//
// Fejlen står i konsollen frem for i en toast: den hører til den, der kan
// rette den, og en note, der viser sin egen tekst uden formatering, siger
// allerede til den, der læser den, at noget er galt.
//
// `textContent` og ikke `innerHTML` i faldbakken — teksten er ikke tegnet, og
// at lægge den ind som markup ville være at køre det, tegningen lige nægtede.
// Gemningen er ikke i fare: den skriver `draft`, som kommer fra svaret og
// ikke fra det, der står i editoren, indtil nogen taster i den.
const body = note.body ?? '';
try {
// An empty note opens on its title. That is what Apple Notes does, and it
// is also what the list needs: the first line becomes the name of the
// note, so a note whose first line is body text is a note called by its
// first sentence.
//
// Tested on the text and not on the rendered result: an empty body renders
// as one empty paragraph, not as nothing, so a falsy check here never
// fired.
editor.innerHTML = body.trim() ? markdownToHtml(body) : '<h1><br></h1>';
colourCode();
} catch (e) {
editor.textContent = body;
console.error('verdande: noten kunne ikke tegnes', note.id, e);
}
});
const SYNTAX = [