From 157c4e659b1ac9be37b53e98bff6d34645f3b2a3 Mon Sep 17 00:00:00 2001 From: mAi Date: Wed, 27 May 2026 14:30:23 +0200 Subject: [PATCH] feat(new): auto-suggest kebab slug from title MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit m's request: typing "Mallorca 2026" into the new-item Title should suggest "mallorca-2026" in the Slug field. Surface-only — server still validates per itemwrite (^[a-z0-9][a-z0-9-]{0,62}$). Inline ~25-line vanilla-JS handler on /new: - normalize('NFD') + strip combining diacritics → ä→a, ñ→n, São→sao - ß → ss (German sharp-s) - non-alphanum run → single hyphen - trim leading/trailing hyphens, collapse runs of hyphens - slice(0, 63) to match the validator's length cap Behavioural contract per m's brief: - Slug syncs from Title on every Title input event UNTIL the user edits the slug manually. After that the slug field is locked in (`slug.dataset.userEdited === '1'`). - A pre-filled slug counts as user-edited too — defensive against any future flow that lands on /new with a slug already populated. Scoped to /new only — the detail-page edit form intentionally keeps manual slug control because auto-sync there would silently rename existing items. Template additions: - Added `id="new-item-form"`, `id="new-title"`, `id="new-slug"` to the form + inputs so the script can grab them by id rather than name (name="slug" exists on the detail page too and we don't want to cross-bind). Test (web/new_form_test.go): - TestNewFormHasSlugSuggestScript — asserts the inline script's signature fragments (`normalize('NFD')`, `replace(/ß/g, 'ss')`, `slice(0, 63)`, `dataset.userEdited`, the input ids) all render on /new. Guards against a "harmless cleanup" pass silently stripping the script. Manual verification: typing "Mallorca 2026" updates slug to "mallorca-2026"; typing in the slug field locks further sync. Full web suite green. --- web/new_form_test.go | 27 +++++++++++++++++++++++++++ web/templates/new.tmpl | 40 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/web/new_form_test.go b/web/new_form_test.go index fbfc854..bfdb9ef 100644 --- a/web/new_form_test.go +++ b/web/new_form_test.go @@ -62,6 +62,33 @@ func TestNewFormPreselectsParent(t *testing.T) { } } +// TestNewFormHasSlugSuggestScript pins the Phase 5k slug auto-suggest: +// the new-item template ships an inline {{end}}