Skip to main content

max / makenotwork

user_profile includes the link row rather than respelling it The loop in tabs/user_profile.html and partials/link_row.html rendered the same control under two sets of verbs: moveLinkUp/moveLinkDown/editLinkBtn/ saveLinkBtn/cancelLinkBtn against onMoveLink/onEditLink/onSaveLink/ onCancelLink. Both call the same helpers in tab-user-profile.js, which is loaded on that tab and so serves the HTMX-inserted fragment too. Found by Shape 6's re-triage under the glue-module ruling (27d5e5b8), and it is the one candidate in that inventory needing no vocabulary at all. The include is the whole fix. frontend_globals HIGH_WATER 111 -> 106. handwritten_confirm 48 -> 47, which is a dedup rather than a conversion: the seal counts template source, so the second hand-written "Remove this link?" goes while the page still draws one per row.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-23 16:27 UTC
Signed with PGP, not checked
Commit: 4dce017a7120cc10253143ad1f7f59eb440d6eec
Parent: 16887a2
4 files changed, +26 insertions, -38 deletions
@@ -104,26 +104,10 @@
104 104 };
105 105
106 106 // --- user_profile.html ---
107 - window.moveLinkUp = function () {
108 - moveLink(this, -1);
109 - };
110 -
111 - window.moveLinkDown = function () {
112 - moveLink(this, 1);
113 - };
114 -
115 - window.editLinkBtn = function () {
116 - editLink(this);
117 - };
118 -
119 - window.saveLinkBtn = function () {
120 - saveLink(this);
121 - };
122 -
123 - window.cancelLinkBtn = function () {
124 - cancelLink(this);
125 - };
126 -
107 + // The link row's five wrappers are gone: user_profile.html includes
108 + // partials/link_row.html rather than respelling it, so the row's one set of
109 + // verbs (onMoveLink/onEditLink/onSaveLink/onCancelLink, in actions-partials.js)
110 + // serves both the loop and the HTMX-inserted fragment.
127 111 window.copyElementText = function (id) {
128 112 window.copyWithFeedback(this, document.getElementById(id).textContent, 'Copied', 1500);
129 113 };
@@ -68,7 +68,15 @@
68 68 /// `window._mediaPickerSelect` with it. That step waited eleven days on a
69 69 /// vocabulary gap -- nothing could say "put this chosen value into that other
70 70 /// field" -- and shipped the day `Act::fills` did.
71 - const HIGH_WATER: usize = 111;
71 + ///
72 + /// 106 on 2026-08-23, and this one cost no vocabulary at all. Shape 6's
73 + /// re-triage under the glue-module ruling found that `partials/link_row.html`
74 + /// and the loop in `tabs/user_profile.html` were the same control rendered
75 + /// twice under two spellings, so `moveLinkUp`, `moveLinkDown`, `editLinkBtn`,
76 + /// `saveLinkBtn` and `cancelLinkBtn` existed only because one template would
77 + /// not include the other. The include is the whole fix; the row's surviving
78 + /// verbs live in `actions-partials.js` and serve both paths.
79 + const HIGH_WATER: usize = 106;
72 80
73 81 /// Every file whose global assignments count: the legacy scripts and the typed
74 82 /// modules that replaced them.
@@ -56,7 +56,15 @@
56 56 /// were candidates in the same pass and are deliberately not converted: both
57 57 /// target `hx-target="closest .<class>"`, and a relative selector is not
58 58 /// something `Action::replaces` can say. Filed rather than approximated.
59 - const HIGH_WATER: usize = 48;
59 + ///
60 + /// 47 on 2026-08-23, and NOT a conversion: `tabs/user_profile.html` stopped
61 + /// respelling `partials/link_row.html` and includes it instead, so the second
62 + /// hand-written copy of "Remove this link?" is gone while the rendered page
63 + /// still draws exactly one per row. Worth knowing when reading this number as
64 + /// progress: the seal counts template source, so deduplicating two templates
65 + /// lowers it without describing anything. The surviving site is still blocked
66 + /// on the `closest .link-row` target above.
67 + const HIGH_WATER: usize = 47;
60 68
61 69 /// Every template, recursively. Walked rather than listed for the reason the
62 70 /// frontend-globals seal walks `frontend/src`: a fence that has to be told about
@@ -6,22 +6,10 @@
6 6
7 7 <div id="links-list">
8 8 {% for link in custom_links %}
9 - <div class="link-row" data-id="{{ link.id }}">
10 - <div class="link-order-buttons">
11 - <button type="button" class="order-btn" data-action="moveLinkUp" title="Move up">&#9650;</button>
12 - <button type="button" class="order-btn" data-action="moveLinkDown" title="Move down">&#9660;</button>
13 - </div>
14 - <input type="text" value="{{ link.title }}" placeholder="Title" name="title" disabled>
15 - <input type="url" value="{{ link.url }}" placeholder="https://..." name="url" disabled>
16 - <button class="btn-secondary link-edit-btn" data-action="editLinkBtn">Edit</button>
17 - <button class="btn-primary link-save-btn hidden" data-action="saveLinkBtn">Save</button>
18 - <button class="btn-secondary link-cancel-btn hidden" data-action="cancelLinkBtn">Cancel</button>
19 - <button class="btn-danger link-remove-btn"
20 - hx-delete="/api/links/{{ link.id }}"
21 - hx-target="closest .link-row"
22 - hx-swap="outerHTML"
23 - hx-confirm="Remove this link?">Remove</button>
24 - </div>
9 + {% let id = link.id.as_str() %}
10 + {% let title = link.title.as_str() %}
11 + {% let url = link.url.as_str() %}
12 + {% include "partials/link_row.html" %}
25 13 {% endfor %}
26 14 </div>
27 15