Skip to content

Move note title and URL from separate inputs into the note body (UI only) #83

Description

@thermcampos

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 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.
  • 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 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).
  • Existing tag-footer behavior (parsing, autocomplete, chips) unaffected.
  • NoteAdd.test.tsx updated for all of the above; bash tools/check-frontend.sh passes.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions