Repository navigation
Closed
Labels
Description
Summary
Follow-up to #75 (tags moved into the note body, PR #82). Move title and URL entry for notes from dedicated inputs into the note document itself, using smarter semantics: the first line is the title (no prefix), and an optional url: line declares the URL. UI only: no changes to the backend, tasks, Home view, or shared-note views. The payload contract ({ title, description, url, tags } on POST /rest/notes / PATCH /rest/notes/{id}) is unchanged — the client derives title and url from the document.
Scope: client/src/views/NoteAdd/index.tsx (plus i18n strings and tests).
Current state (findings)
- Note add/edit lives in
client/src/views/NoteAdd/index.tsx(single component, add + edit modes). - Tags already live in the body as a strict footer line
tags: a, b(last non-empty line, case-insensitive), parsed per keystroke, stripped fromdescriptionon save, re-synthesized on load (feat: move tags to note document. Issue 75 #82):TAGS_FOOTER_PATTERN,parseTagsFooter,stripTagsFooter,synthesizeTagsFooter. - Title input:
FormInputatindex.tsx:500-513(required,name="note_title"). URL input:FormInputatindex.tsx:517-530(name="url"). Neither has max-length or format validation client-side; the backend validates title size. - Save guard at
index.tsx:317-320blocks save when title or content is blank ("Please fill in all the fields"). - Form card header:
Card.Titleatindex.tsx:468renders i18nnote_form_title("Add note"). Page hero (ContentHeader,index.tsx:456-462) stays static. - Helper text below the textarea (
index.tsx:610-612, hardcoded English): "Add a final line `tags: a, b` to tag this note." - Markdown preview (
ModalMarkdown) already receives the body with the tags footer stripped. - Draft autosave to localStorage stores
{ title, content, noteUrl }underdraft:note:new/draft:note:edit:{id}(debounced 1.5s). - i18n via
react-i18next; strings inclient/src/constants/{english,spanish,portuguese,russian}.ts. - Tests:
client/src/__test__/views/NoteAdd.test.tsx(Vitest + testing-library).
Design decisions (grilled and settled)
Model
- Line 1 of the document is the title. No prefix. Even a long paragraph on line 1 is the title; if it exceeds the backend's title size limit, the backend rejects it and the user must shorten it (existing error surfacing via
AlertError). - URL is declared on a line matching
/^url:\s*(.*)$/i, positioned after line 1 and before thetags:footer line.
Parse rules
- Title normalization: strip leading markdown heading markers (
#+space sequences) and trim, both for the storedtitleand the live header display.# My Title→My Title. - Blank first line blocks save: strictly line 1, no "first non-empty line" fallback. Shows the existing "Please fill in all the fields" error.
- URL line: case-insensitive prefix; if multiple
url:lines exist, the first wins and the rest stay as body text. The value is the first token after the prefix — split on any delimiter (whitespace, comma, etc.), sourl: https://a https://b→https://a. No URL format validation (backend owns validation). Emptyurl:→ no URL.
Save
- Title and URL are stripped from
descriptionbefore saving (same as the tags footer), so they never leak into Home previews, the public shared-note page, markdown rendering, or search.titleandurlgo in their dedicated payload fields.
Load (edit mode)
- Re-synthesize the document as: title on line 1,
url: <url>on line 2 (deterministic position, only when a URL exists), then the body, then the tags footer. - Existing draft-restore behavior is preserved: a stored draft body wins over synthesis.
Live UI
- Remove the Title and URL
FormInputs entirely (and their state). - Live card title: the
Card.Titlereflects the normalized line 1 per keystroke, reinforcing that line 1 is the title. Fallback when line 1 is blank: "Untitled note" (new i18n key added to all 4 locale files). The static page hero (ContentHeader) is unchanged. - Helper text updated to: "The first line is the note title. To link a URL to this note, add a line `url: ` before the tags. Add a final line `tags: a, b` to tag this note."
Markdown preview
ModalMarkdownrenders the body with the title line,url:line, and tags footer all stripped.
Drafts
- Draft now stores
contentonly (title/URL derive from it). - Old-shaped drafts (with separate
title/noteUrlkeys) are merged into the content once on load, then treated as new-shaped.
Acceptance criteria
- First line of the document is sent as
title(leading#s stripped, trimmed); it is stripped fromdescriptionon save. - Blank first line blocks save with the existing "Please fill in all the fields" error.
- A line
url: <url>between line 1 and the tags footer is parsed case-insensitively and sent asurl; firsturl:line wins; value stops at the first whitespace/comma delimiter; the line is stripped fromdescriptionon save. - Edit mode on a legacy note (title/url only in fields) shows title on line 1 and
url:on line 2; saving round-trips the same title, URL, body, and tags. - Title and URL inputs fully removed from
NoteAdd. -
Card.Titleupdates live per keystroke with the normalized line 1; blank line 1 shows "Untitled note"; new i18n key present in all 4 locales. - Helper text mentions the title line, the
url:line, and the tags footer. - Markdown preview hides title,
url:, and tags lines. - Draft autosave/restore works with content-only drafts; old-shaped drafts migrate (title/url merged into content once).
- Existing tag-footer behavior (parsing, autocomplete, chips) unaffected.
-
NoteAdd.test.tsxupdated for all of the above;bash tools/check-frontend.shpasses.
Activity