Chip-based variable insertion in title cells (T425887, MR-1)

Summary

  • Adds a chip-based template editor for the Title cell (T425887, MR-1 of a multi-MR rollout). The user types text AND inserts "chips" — typed references to row values (filename, date, depicts label, category label, location-of-creation label, author) that render in place as the resolved string. Each chip displays as the text it will publish to (so the user always sees the totality of the visible string and the system can guard length limits).
  • Click a chip to change its format (date: 7 formats), language (depicts/locOfCreation: per-chip BCP-47 picker for multilingual labels via wbgetentities), or hide individual whitespace-separated words via toggle (struck-through in display, omitted in publish).
  • Chip data persists alongside the rendered title in Metadata.json under a new titleSegments draft field, so chip-built titles round-trip across reloads and devices. The rendered string in item.title stays as the authoritative cell value for everything downstream (publish path, validation, wikitext preview) — the chip editor is purely an alternate input method.
  • 2026-05-16 update: rebased onto main (was v0.28.0; main is v0.37.0). Auto-sequence (T425984) is integrated into the chip editor as the maintainer directed in comment 11927794 — the collision-detection and "Convert to sequence" affordance now operate on the chip editor's rendered string and surface inside the editor footer.

Phabricator

  • T425887 — advanced title and caption generation
  • T425984 — auto-sequence suggestion for duplicate titles (integrated into this MR per maintainer directive, replacing v0.33.0's standalone implementation in the Title editor)

Maintainer brief that drives this MR

Direct quote from comment 11926139 (2026-05-15T16:05:46), which overrides the Round-1 design proposal that lived earlier in the task:

the design i am looking for is that the user can write text like usual but they can add "values" from elsewhere as chips inbetween the text. these chips might have some options like how to structure the date and time and what language to use. these options are available by clicking on the chip. chip suggestions can be displayed under the text. all values elsewhere in the row should be available as they might be relevant. for wikidata items and categories their label is relevant. the categorie prefix should be removed. for chips that are several words, when clicking on it the user should be able to click words to toggle them. untoggled words should appear as strikethrough. A chip should display as the text it will be. helping the user to understand what the totality of the text will be and help the system to gard the string length limit. when clicking the chip to edit the text, the suer should se the current version, the possible variations in the templates of the values should be listed below (also as the result not the template design) and above should be what variable is in the chip (column name, Qid Label, category, ...) language should also be abel to be selected for example of wikidata items.

And comment 11926154:

dont use the { } use the chips

And comment 11927794 on the T425984 rebase strategy:

auto-sequence concept becomes "just another chip kind" inside the new editor. Hash tag (#) at end of string still triggers it; numbering can be added, or added from chip suggestions list. ... Should work on same new editor with chips system—same code style, same mechanics. ... Recommends integrating T425984 code into this code, then publish whilst overwriting previous T425984 code.

This MR implements every clause of the brief above (length-limit guard is implemented as warn-but-allow at the SDC label cap of 250 chars; coordinates → multi-level admin labels are deferred — see "Deferred" below).

What's in MR-1

Editor primitive. React state + a regular <input> for the active typing zone, with chips rendered as inline pill <span>s alongside (NOT contenteditable). Two child components: <ChipPill> for each inserted chip, <ChipPopover> for the click-to-edit popover, <ChipPicker> for the "+ chip" insertion flyout.

Variables shipped. filename (basename / full / extension), date taken (7 formats), depicts (per QID, multilingual), location-of-creation (per QID, multilingual), categories (with "Category:" prefix stripped), author. Picker auto-hides empty-data kinds for the row.

Click-a-chip popover. Header = describes what the chip refers to (column name / QID + label / category name). Below = the current rendered value. Then the format options listed AS RENDERED VALUES (not template syntax — per the brief). Then the language picker (date / depicts / locOfCreation). Then the per-word toggle (when the chip's render is multi-word). Then a Delete chip button.

Multilingual. Depicts and location-of-creation chips fetch their label per chosen language via the existing window.fetchWikidataEntity(qid, lang) bridge (cached in the existing 5-min apiCache). The chip render updates async when the label arrives — chip-editor subscribes via a small pub-sub in cell-templates.js.

Persistence.

  • DRAFT_FIELDS in user-store.js adds titleSegments.
  • pickDraftFields and setDraft now honor a null sentinel as "explicitly clear this field" — needed so a chip-built title that the user later replaces with literal text drops the titleSegments entry rather than leaving it stale forever. Backward-compatible.
  • Stale-segments detection: on TitleEditor open, if renderSegments(initialSegments, item) !== item.title, the segments are dropped (the user edited the title in the detail panel after building chips → text wins).

Auto-sequence integration (T425984). Collision detection (sibling stash rows + own-Commons files), the "Convert to sequence" affordance, and the info popout listing existing <basename> N files all now operate on the chip editor's rendered string (post-chip-resolution) instead of the raw <input> value. Accepting collapses any chip segments into the literal <basename> # placeholder (single text-only segment, no chips) — the publish-time resolver (src/api/sequence.js, unchanged) substitutes the next integer in the user's owned <basename> N series. The Enter-accepts-sequence shortcut is dropped: the chip editor's Enter commits the typed value as-is, and sequence acceptance is now click-only. A future iteration may add a typeable "#" sequence-number chip kind (per the maintainer's directive); for MR-1 the placeholder stays in literal text since the resolver still expects that form.

Coupling with parallel MRs. This MR ships a chip-based editor for Title only. The title↔️caption link (T426424) landed on main as v0.34.0 — its {title} template variable in the caption is independent of the chip editor (which only operates on Title in MR-1). MR-2 of this rollout (Caption support + library + recent panel) will revisit whether the {title} variable becomes a chip kind in the caption editor.

Deferred to follow-up MRs

  • Caption cell support (MR-2)
  • Save/load template library in a separate modal (MR-2)
  • Recent-templates panel (MR-2)
  • Coordinates → multi-level admin labels via Wikidata SPARQL P131 chain — needs maintainer's policy clearance on Nominatim use (open Q5 from Round 1, never explicitly answered). Until then, the brief's "coordinates should show their labels from different levels" stays unimplemented; the existing locationOfCreation label IS shippable as a single-label chip and is included in MR-1.
  • Per-template default-language pin (later MR alongside save/load library)
  • Typeable # sequence-number chip kind (could replace the literal <basename> # placeholder with a chip; deferred since the publish-time resolver still expects the literal form)

Test plan

  • Open the preview, click the title cell of any stash row that has a date and at least one depicts entry (e.g. SAMPLE_UPLOADS row 0 — Maastricht_Vrijthof_dawn).
  • Type a literal prefix (e.g. "Photo of "), click "+ chip", pick the "Vrijthof" depicts label → see "Photo of Vrijthof" with Vrijthof as a blue pill.
  • Click the Vrijthof chip → popover opens with the variable name header, current value, language dropdown. Switch to nl → after a moment the chip's rendered text re-renders with the Dutch label (or stays English if nl isn't on Wikidata for that QID).
  • Click "+ chip" again, pick a date format → see the chip insert with the chosen format (e.g. "April 29, 2026"). Click the date chip → format options appear AS the rendered values (not "iso", "long" labels but actual "2026-04-29", "April 29, 2026"). Click a different one → chip's text updates in place.
  • Insert a multi-word chip (location-of-creation "Maastricht" → only one word, so try a category like "Panoramas of Limburg (Netherlands)" first). Click the chip → see the per-word toggle row. Click a word → it strikes through in both the popover and the inline pill. Press Enter to commit → reload the preview → the rendered title in the cell should reflect the toggled-out word(s) being omitted.
  • Type a literal title (no chips), commit → check that titleSegments does NOT appear in the user-store draft (should fall back to text-only shape).
  • Build a chip title, commit, then open the detail panel and overwrite the title with literal text. Re-open the table cell editor → it should drop the chips and start fresh as literal text (stale-segments detection).
  • Build a chip title that is over 250 chars rendered → should see the orange warn-but-allow message ("Rendered text is X characters (over the 250-char cap)"). Commit should still work; this is intentional.
  • Auto-sequence: build (or type) a chip title whose rendered string collides with another stash row's title OR with one of your own previously-published Commons files. The "Add #" sequence-suggestion strip should appear below the validation status row. Click the info icon → see the list of existing files (or "sequence will start at 1" message). Click "Add #" → the cell commits as <basename> # (literal text, chips collapsed). Reload → the placeholder persists, and publish picks the next integer.
  • Run npm run build in the worktree → must pass cleanly (the undefined-refs scanner runs as part of prebuild).

Notes for the merger

  • Per-branch CI vs merge-result regressions. Per CLAUDE.md the merger must run npm run build locally against the merge result before pushing the release commit. The merge was done on this branch (commit 7a1d8ad) so the on-branch CI did exercise the integration, but a fresh npm install && npm run build is still the rule.
  • Multi-MR rollout. This is MR-1; the changelog lists what's deferred. Now that auto-sequence is integrated, the bump should be MINOR (backwards-compat new feature: 0.37.0 → 0.38.0). T425984's separate Done-state from v0.33.0 should be acknowledged in the release notes — see the CHANGELOG [Unreleased] Changed entry that explains the integration.
  • Lockfile. No dep changes — the new modules are in-tree (src/cell-templates.js, src/ui/chip-editor.jsx).

🤖 Generated with Claude Code

Edited by Daanvr

Merge request reports

Loading