merge: engelske skærmbilleder, og otte steder hvor fladen blev ved med at være dansk
Release / Build and publish image (push) Failing after 9m54s

Skærmbillederne er engelske, fjorten i alt, og der er to mennesker i dem. Det
sidste er ikke pynt: halvdelen af det, programmet er til, findes ikke med én
konto — en ansvarlig, et navn i historikken, en medlemsliste, og "Venter på
andre" som andet end en tom side.

Sproget kommer fra kontoens locale og ikke fra strenge skrevet i hånden, hvilket
er dét, der fandt fejlene: otte steder, hvor grænsefladen blev ved med at være
dansk, uanset hvad kontoen sagde. "Forsinket" og "I dag" skrevet ind i markup,
"Uden sektion" som boardets første kolonne, gentagelsesreglerne halvt oversat,
alle nitten navne på hændelser i historikken, og et `da-DK` hårdkodet i Kommende.
Gentagelsesreglerne er flyttet til `when.js`, så menuen og mærket ikke kan blive
uenige igen — det er præcis, hvad den fil er til.

README's billeder havde været brudte siden `37605ba`, som lavede PNG'erne om til
WebP og slettede dem, uden at teksten fulgte med: forsidebilledet og alle seks i
tabellen var 404 på GitHub. Og noterne — hele funktionen — stod ikke nævnt hverken
i README, på forsiden eller i dokumentationen.

Nu skrives PNG til docs og WebP til site, altså præcis dét, de to steder læser.
Før efterlod hver kørsel tolv usporede PNG'er i site, som intet pegede på.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kristian Wind
2026-08-20 07:34:16 +02:00
co-authored by Claude Opus 5
45 changed files with 597 additions and 142 deletions
+1 -1
View File
@@ -64,7 +64,7 @@
// The unsectioned column comes first, as it does in the list view: it is where
// quick add puts things, so it is where new work appears.
let columns = $derived([
{ id: '', name: 'Uden sektion' },
{ id: '', name: t('project.noSection') },
...sections.map((s) => ({ id: s.id, name: s.name }))
]);
+3 -13
View File
@@ -14,6 +14,7 @@
import { app, projectName } from '$lib/stores.svelte.js';
import { focusOnMount } from '$lib/focus.js';
import { t } from '$lib/i18n.svelte.js';
import { REPEATS } from '$lib/when.js';
let { task, onclose } = $props();
@@ -29,17 +30,6 @@
/** Who the task can be given to. Only ever more than one on a shared project. */
let members = $state([]);
// The rules worth a menu entry. Anything more specific is typed into the
// title, where the parser understands far more than a list ever could.
const REPEATS = [
{ rule: '', label: 'Aldrig' },
{ rule: 'FREQ=DAILY', label: 'Hver dag' },
{ rule: 'FREQ=WEEKLY', label: 'Hver uge' },
{ rule: 'FREQ=WEEKLY;INTERVAL=2', label: 'Hver anden uge' },
{ rule: 'FREQ=MONTHLY', label: t('detail.monthly') },
{ rule: 'FREQ=YEARLY', label: t('detail.yearly') }
];
let subtasks = $state([]);
let comments = $state([]);
let attachments = $state([]);
@@ -452,13 +442,13 @@
<label for="repeat">{t('detail.repeats')}</label>
<select id="repeat" bind:value={recurrence} onchange={saveRecurrence}>
{#each REPEATS as option (option.rule)}
<option value={option.rule}>{option.label}</option>
<option value={option.rule}>{t(option.label)}</option>
{/each}
<!-- A rule typed as "hver anden tirsdag" has no entry in the list;
showing it rather than snapping to the nearest preset is the
difference between a menu and a menu that quietly edits. -->
{#if recurrence && !REPEATS.some((o) => o.rule === recurrence)}
<option value={recurrence}>{task.recurrence_text ?? 'Som skrevet'}</option>
<option value={recurrence}>{task.recurrence_text ?? t('detail.asWritten')}</option>
{/if}
</select>
</div>
+7 -4
View File
@@ -10,9 +10,12 @@
*/
import { app } from '$lib/stores.svelte.js';
import { t, tag } from '$lib/i18n.svelte.js';
import { repeatLabel } from '$lib/when.js';
let { task, onedit } = $props();
let repeats = $derived(repeatLabel(task.recurrence_rule, task.recurrence_text));
let leaving = $state(false);
const priorityLabel = (p) => (p === 4 ? t('task.noPriority') : t('task.priority', { n: p }));
@@ -84,7 +87,7 @@
<span class="description">{task.description}</span>
{/if}
{#if assignee || due || task.labels?.length || task.recurrence_text || task.subtask_count || task.attachment_count}
{#if assignee || due || task.labels?.length || repeats || task.subtask_count || task.attachment_count}
<span class="meta">
{#if assignee}
<!-- First in the row, because "who" changes what the rest of the line
@@ -102,15 +105,15 @@
{due.text}{#if time}&nbsp;{time}{/if}
</span>
{/if}
{#if task.recurrence_text}
<span class="repeat" title={t('task.repeats', { rule: task.recurrence_text })}>
{#if repeats}
<span class="repeat" title={t('task.repeats', { rule: repeats })}>
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M17 2l4 4-4 4" />
<path d="M3 11V9a4 4 0 014-4h14" />
<path d="M7 22l-4-4 4-4" />
<path d="M21 13v2a4 4 0 01-4 4H3" />
</svg>
{task.recurrence_text}
{repeats}
</span>
{/if}
<!-- What is underneath, said as a fraction rather than a total: "3" tells