diff --git a/app.js b/app.js
index 297daaf..8681413 100644
--- a/app.js
+++ b/app.js
@@ -14,8 +14,9 @@ let sortCol = 'name';
let sortDir = 'asc';
// editing state
-let editingTuneId = null; // null = add mode
-let editingEntryId = null; // null = no existing instrument entry
+let editingTuneId = null; // null = add mode
+let editingEntryId = null; // null = no existing instrument entry
+let editingOriginalRefIds = []; // ref IDs present when edit modal opened
// notes modal state
let notesTuneId = null;
@@ -499,8 +500,9 @@ function pillStatus(status, extraClass = '') {
btnAddTune.addEventListener('click', openAdd);
function openAdd() {
- editingTuneId = null;
- editingEntryId = null;
+ editingTuneId = null;
+ editingEntryId = null;
+ editingOriginalRefIds = [];
$('modal-title').textContent = 'Add tune';
tuneForm.reset();
refsList.innerHTML = '';
@@ -552,6 +554,7 @@ async function openEdit(tuneId, entryId) {
tuneForm.elements.inst_difficulty.value = entry.difficulty ?? '';
}
+ editingOriginalRefIds = (fullTune.references ?? []).map(r => r.id);
(fullTune.references ?? []).forEach(r => addRefRow(r.link, r.site?.name ?? '', r.id, r.site_id, r.musicians ?? []));
}
@@ -716,6 +719,35 @@ async function saveTune() {
});
}
}
+
+ // Sync references
+ const formRows = [...refsList.querySelectorAll('.ref-row')];
+ const formRefIds = new Set(formRows.map(r => r.dataset.refId).filter(Boolean));
+
+ // Delete refs removed from the form
+ for (const refId of editingOriginalRefIds) {
+ if (!formRefIds.has(String(refId))) {
+ await apiFetch(`/tunes/${tuneId}/references/${refId}`, { method: 'DELETE' });
+ }
+ }
+
+ // Create new refs; patch existing ones (link + site)
+ for (const row of formRows) {
+ const refId = row.dataset.refId;
+ const link = row.querySelector('.ref-link').value.trim() || null;
+ const siteId = row.querySelector('.ref-site').value ? parseInt(row.querySelector('.ref-site').value) : null;
+ if (!link && !siteId) continue;
+ if (refId) {
+ await apiFetch(`/tunes/${tuneId}/references/${refId}`, {
+ method: 'PATCH', body: JSON.stringify({ link, site_id: siteId }),
+ });
+ } else {
+ const musicians = row._getMusicians ? row._getMusicians() : [];
+ await apiFetch(`/tunes/${tuneId}/references`, {
+ method: 'POST', body: JSON.stringify({ link, site_id: siteId, musicians }),
+ });
+ }
+ }
}
tuneModal.close();
@@ -901,12 +933,17 @@ async function openRefsModal(tuneId) {
refs.forEach(r => {
const item = document.createElement('div');
item.className = 'refs-modal-item';
- const musicians = r.musicians?.map(m => m.name).join(', ') ?? '';
- const siteName = r.site?.name ?? '';
- const meta = [siteName, musicians].filter(Boolean).join(' · ');
+ const musicians = r.musicians?.map(m => m.name).join(', ') ?? '';
+ const siteName = r.site?.name ?? '';
+ const meta = [siteName, musicians].filter(Boolean).join(' · ');
+ // Use meta as link text; fall back to hostname so raw URL is never shown
+ let displayText = meta;
+ if (!displayText && r.link) {
+ try { displayText = new URL(r.link).hostname.replace(/^www\./, ''); } catch { displayText = 'link'; }
+ }
+ if (!displayText) displayText = 'link';
item.innerHTML = `
- ${esc(r.link ?? r.site?.name ?? 'link')}
- ${meta ? `${esc(meta)}` : ''}`;
+ ${esc(displayText)}`;
refsModalList.appendChild(item);
});
}
diff --git a/style.css b/style.css
index 38a12c6..e96f8e8 100644
--- a/style.css
+++ b/style.css
@@ -787,7 +787,6 @@ fieldset.active .fs-body { display: contents; }
.ref-link-anchor {
color: var(--accent);
font-size: .875rem;
- word-break: break-all;
text-decoration: none;
}
.ref-link-anchor:hover { text-decoration: underline; }