You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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).
Tags already live in the body as a strict footer line tags: a, b (last non-empty line, case-insensitive), parsed per keystroke, stripped from description on save, re-synthesized on load (feat: move tags to note document. Issue 75 #82): TAGS_FOOTER_PATTERN, parseTagsFooter, stripTagsFooter, synthesizeTagsFooter.
Title input: FormInput at index.tsx:500-513 (required, name="note_title"). URL input: FormInput at index.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-320 blocks save when title or content is blank ("Please fill in all the fields").
Form card header: Card.Title at index.tsx:468 renders i18n note_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 } under draft:note:new / draft:note:edit:{id} (debounced 1.5s).
i18n via react-i18next; strings in client/src/constants/{english,spanish,portuguese,russian}.ts.
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 the tags: footer line.
Parse rules
Title normalization: strip leading markdown heading markers (#+space sequences) and trim, both for the stored title and 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.), so url: https://a https://b → https://a. No URL format validation (backend owns validation). Empty url: → no URL.
Save
Title and URL are stripped from description before saving (same as the tags footer), so they never leak into Home previews, the public shared-note page, markdown rendering, or search. title and url go 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.Title reflects 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
ModalMarkdown renders the body with the title line, url: line, and tags footer all stripped.
Drafts
Draft now stores content only (title/URL derive from it).
Old-shaped drafts (with separate title/noteUrl keys) 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 from description on 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 as url; first url: line wins; value stops at the first whitespace/comma delimiter; the line is stripped from description on 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.Title updates 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).
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 }onPOST /rest/notes/PATCH /rest/notes/{id}) is unchanged — the client derivestitleandurlfrom the document.Scope:
client/src/views/NoteAdd/index.tsx(plus i18n strings and tests).Current state (findings)
client/src/views/NoteAdd/index.tsx(single component, add + edit modes).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.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.index.tsx:317-320blocks save when title or content is blank ("Please fill in all the fields").Card.Titleatindex.tsx:468renders i18nnote_form_title("Add note"). Page hero (ContentHeader,index.tsx:456-462) stays static.index.tsx:610-612, hardcoded English): "Add a final line `tags: a, b` to tag this note."ModalMarkdown) already receives the body with the tags footer stripped.{ title, content, noteUrl }underdraft:note:new/draft:note:edit:{id}(debounced 1.5s).react-i18next; strings inclient/src/constants/{english,spanish,portuguese,russian}.ts.client/src/__test__/views/NoteAdd.test.tsx(Vitest + testing-library).Design decisions (grilled and settled)
Model
AlertError)./^url:\s*(.*)$/i, positioned after line 1 and before thetags:footer line.Parse rules
#+space sequences) and trim, both for the storedtitleand the live header display.# My Title→My Title.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
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)
url: <url>on line 2 (deterministic position, only when a URL exists), then the body, then the tags footer.Live UI
FormInputs entirely (and their state).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.Markdown preview
ModalMarkdownrenders the body with the title line,url:line, and tags footer all stripped.Drafts
contentonly (title/URL derive from it).title/noteUrlkeys) are merged into the content once on load, then treated as new-shaped.Acceptance criteria
title(leading#s stripped, trimmed); it is stripped fromdescriptionon save.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.url:on line 2; saving round-trips the same title, URL, body, and tags.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.url:line, and the tags footer.url:, and tags lines.NoteAdd.test.tsxupdated for all of the above;bash tools/check-frontend.shpasses.