Universal link picker (TYPO3-style): page, element, media file, external targets with per-link new-tab toggle #65
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#65
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Motivation
Authoring links in deckle is currently bare-bones:
link-typed element params render as a plain text input, and the RTE has an ad-hoc popover with a URL field and a flat page list. There is no way to link to a specific content element, no media-file linking, and no way to open a link in a new tab. This issue introduces a TYPO3-style universal link picker used everywhere a link is authored.Agreed design (from grilling session)
Storage. Internal links are stored as resolved pretty URLs in vanilla markdown (
/blog/post/, element targets as/services/#c-<uid>). No tokens, no render-time resolution — content files keep building with stock Hugo. On page rename/move, a media-ops-style scan rewrites all stored links to the new canonical URL; Hugo aliases remain as the redirect safety net.Link kinds. Four tabs in the picker: internal page, element-on-page, media file (reusing the existing media picker), external URL. No dedicated email/phone tabs —
mailto:/tel:can be typed into the external tab.target="_blank". A per-link "open in new tab" toggle:
<a href="…" target="_blank" rel="noopener">HTML (the theme already renders withunsafe=true). The RTE markdown bridge must round-trip inline<a>HTML back into a link mark with a target attribute.type: linkparam gets an implicit reserved companion param<name>Target(same mechanism as the reserveduidparam) — written only when set, accepted by schema validation automatically, rendered by the scaffold shortcode templates.Element anchors. The shared content-element attrs partial emits
id="c-<uid>"on every block-level element in all builds (not just dev preview), so any element is always linkable.Picker UX (internal tab). Two-pane dialog: page tree on the left, pure navigation only. The larger right pane shows the selected page's direct child pages and direct child block elements (labeled with element type + schema-declared summary), plus a "link to this page" header row. Single click on any right-pane item immediately takes it and closes the dialog — no confirm step. Top-level elements only; container internals are out of scope for v1 (anchors exist, so it can be added later without a format change).
Delete protection. The reference scan extends to page URLs and element anchors: deleting a linked page or element shows the media-style warning listing every referencing page, but the author may proceed.
RTE unification. The ad-hoc link popover is removed; the same picker dialog serves inline links, pre-filled when editing an existing link.
Widget. The
linkfield widget mirrors the image field: a friendly chip (page title / element summary / filename / URL, plus a new-tab indicator) with edit and clear actions, instead of a raw text input. The picker's tree shows the language currently being edited.Agent Brief
Category: enhancement
Summary: Replace deckle's bare link inputs with a universal TYPO3-style link picker (page / element-on-page / media file / external) with a per-link "open in new tab" toggle, plus the supporting anchor emission, schema companion param, reference warnings, and rename-rewrite.
Current behavior:
linkis already a first-class field type in the element/page-type schema vocabulary, but its editor widget is the same plain text input astext.id; thedata-ce-*markers exist only under the dev preview server.Desired behavior:
Picker dialog (one shared component used by both the
linkfield widget and the RTE):mailto:/tel:work here too).Storage format:
/pretty/url/. Element link:/pretty/url/#c-<uid>where<uid>is the element's reserved uid param value. Content files remain vanilla markdown that builds with stock Hugo.[text](url); links with the toggle serialize as inline HTML<a href="…" target="_blank" rel="noopener">text</a>. The RTE markdown bridge must round-trip that inline<a>form back into a link mark carrying a target attribute (lossless: plain links must never gain the HTML form).type: linkparam named e.g.hrefgets an implicit reserved companionhrefTarget, written only when the toggle is on. Schema validation must accept the companion without sidecar declaration (same mechanism as the reserveduidparam), and it must round-trip losslessly through the content engine. Scaffold shortcode templates that render link params (e.g. the button element) rendertarget+rel="noopener"when the companion is present.Anchor emission:
id="c-<uid>"on every block element in all builds (production included), not only under the dev preview.Reference safety:
<a href>HTML, and inlink-typed params — to the new canonical URL, preserving any#c-<uid>fragment. Aliases stay as the fallback for anything missed.Widget:
linkfield widget becomes a chip-style control mirroring the image field: shows a friendly label (page title, element summary, filename, or the external URL) plus a new-tab indicator, with edit (reopens picker) and clear actions.Key interfaces:
FieldTypevocabulary already containslink— no new type constant; the change is widget + implicit companion param.uid— extend the same concept so<linkParamName>Targetis accepted/preserved for everytype: linkparam.Summarydeclaration — the source of element row labels in the picker's right pane.targetattribute and serializer/parser support in the markdown bridge.Acceptance criteria:
linkfield widget opens the picker; chosen page/element/file/external targets are stored as described and re-open pre-filled for editing./page/url/#c-<uid>and the published site's HTML contains a matchingid="c-<uid>"on that element, so the browser scrolls to it.<a … target="_blank" rel="noopener">form → reopen editor → link mark shows toggle on; toggling off returns it to plain markdown syntax.<name>Target="_blank"in the shortcode call without any sidecar change, passes validation, survives an unrelated edit of the same element (lossless round-trip), and the scaffold button element renderstarget="_blank" rel="noopener".<a>hrefs, link params) across the site to the new URL, preserving#c-<uid>fragments; the alias is still written.<a>round-trip, rewrite-on-rename, reference listing).Out of scope:
target=_blankfor external links via a render hook, and anylayouts/_default/_markup/render-link.html.page://…) or blocking deletes of referenced pages/elements.