diff --git a/app.js b/app.js
index b2c54ca..91fa837 100644
--- a/app.js
+++ b/app.js
@@ -10,19 +10,16 @@ let allMusicians = [];
let allInstruments = [];
let allTunings = [];
let allReferenceSites = [];
-let allTerms = [];
let sortCol = 'name';
let sortDir = 'asc';
// editing state
-let editingTuneId = null; // null = add mode
-let editingEntryId = null; // null = no existing instrument entry
-let editingOriginalRefIds = []; // ref IDs present when edit modal opened
+let editingTuneId = null; // null = add mode
+let editingEntryId = null; // null = no existing instrument entry
// notes modal state
-let notesTuneId = null;
-let notesEntryId = null;
-let noteEditorState = null; // { type: 'tune'|'inst', noteId: null|number }
+let notesTuneId = null;
+let notesEntryId = null;
let unlocked = false;
@@ -44,7 +41,6 @@ const sourceDatalist = $('source-list');
const musicianDatalist = $('musician-list');
const instrumentSelect = $('instrument-select');
const tuningDatalist = $('tuning-list');
-const termDatalist = $('term-list');
const btnDeleteTune = $('btn-delete-tune');
const btnEditNotes = $('btn-edit-notes');
const btnBulkEdit = $('btn-bulk-edit');
@@ -118,20 +114,18 @@ function setStatus(msg, isError = false) {
async function loadAll() {
setStatus('Loading…');
try {
- [allTunes, allSources, allMusicians, allInstruments, allTunings, allReferenceSites, allTerms] = await Promise.all([
+ [allTunes, allSources, allMusicians, allInstruments, allTunings, allReferenceSites] = await Promise.all([
apiFetch('/tunes/'),
apiFetch('/sources/'),
apiFetch('/musicians/'),
apiFetch('/instruments'),
apiFetch('/tunings/'),
apiFetch('/reference_sites/'),
- apiFetch('/terms/'),
]);
populateFilterOptions();
populateDatalist(sourceDatalist, allSources);
populateDatalist(musicianDatalist, allMusicians);
populateDatalist(tuningDatalist, allTunings);
- populateDatalist(termDatalist, allTerms);
populateDatalist(refSiteDatalist, allReferenceSites);
populateInstrumentSelect();
renderTable();
@@ -208,12 +202,6 @@ $('btn-quick-add-tuning').addEventListener('click', async () => {
});
});
-$('btn-quick-add-term').addEventListener('click', async () => {
- await quickAdd('/terms/', 'New term name:', allTerms, termDatalist, item => {
- tuneForm.elements.inst_term.value = item.name;
- });
-});
-
$('btn-quick-add-instrument').addEventListener('click', async () => {
const item = await quickAdd('/instruments', 'New instrument name:', allInstruments, null);
if (item) {
@@ -241,11 +229,6 @@ function resolveTuningId(name) {
return allTunings.find(x => x.name.toLowerCase() === name.toLowerCase())?.id ?? null;
}
-function resolveTermId(name) {
- if (!name) return null;
- return allTerms.find(x => x.name.toLowerCase() === name.toLowerCase())?.id ?? null;
-}
-
// ── Rows: one per instrument entry ────────────────────────────
// For tunes with no instrument entries, emit one row with empty instrument cols
function buildRows(tunes) {
@@ -433,7 +416,7 @@ function renderTable() {
emptyMsg.hidden = rows.length > 0;
rows.forEach(({ tune, entry }) => {
- const hasNotes = tune.has_notes ?? false;
+ const hasNotes = (tune.notes?.length > 0) || (entry?.notes?.length > 0);
// ── Mobile 2-line card data ──────────────────────────────────
const nameRaw = tune.name ?? '';
@@ -448,7 +431,7 @@ function renderTable() {
const mobPills = entry ? pillStatus(entry.status, 'mob-pill') : '';
const mobActions = [
hasNotes ? `` : '',
- (tune.reference_count ?? 0) > 0 ? `` : '',
+ tune.references?.length > 0 ? `` : '',
``,
].join('');
@@ -457,13 +440,13 @@ function renderTable() {
${esc(nameRaw)} |
${esc(sourceRaw)} |
${esc(keyStr)} |
- ${esc(entry?.instrument_name ?? '')} |
- ${esc(entry?.tuning ?? '')} |
+ ${esc(entry?.instrument_name ?? '')} |
+ ${esc(entry?.tuning ?? '')} |
${pillStatus(entry?.status)} |
${hasNotes
? ``
: ''} |
- ${(tune.reference_count ?? 0) > 0
+ | ${tune.references?.length > 0
? ``
: ''} |
-
Delete tune
📝 Notes
@@ -182,7 +171,7 @@
-
+
-
-
-
-
-
-
diff --git a/style.css b/style.css
index c9ebe44..249d662 100644
--- a/style.css
+++ b/style.css
@@ -29,7 +29,6 @@ header {
padding: .75rem 1.25rem;
display: flex;
align-items: center;
- flex-wrap: wrap;
gap: .75rem;
}
@@ -40,11 +39,8 @@ header h1 { font-size: 1.4rem; white-space: nowrap; }
gap: .6rem;
align-items: center;
flex-shrink: 0;
- margin-left: auto; /* right-aligns on same line; left-fills when alone on a row */
}
-
-
#filter-search {
flex: 1;
background: var(--bg);
@@ -232,10 +228,8 @@ th.sort-desc .sort-indicator::after { content: '▼'; }
text-overflow: ellipsis;
white-space: nowrap;
}
-.cell-name-content { max-width: min(14vw, 200px); }
-.cell-source-content { max-width: min(11vw, 160px); }
-.cell-inst-content { max-width: min(11vw, 130px); }
-.cell-tuning-content { max-width: min(9vw, 110px); }
+.cell-name-content { max-width: min(22vw, 260px); }
+.cell-source-content { max-width: min(26vw, 200px); }
.pill {
display: inline-block;
@@ -261,9 +255,6 @@ th.sort-desc .sort-indicator::after { content: '▼'; }
/* ── Modal ────────────────────────────────────────────────── */
dialog {
- position: fixed;
- inset: 0;
- margin: auto;
background: var(--surface);
color: var(--text);
border: 1px solid var(--border);
@@ -305,8 +296,6 @@ fieldset label {
font-size: .85rem;
}
-fieldset input[type="checkbox"] { justify-self: start; }
-
fieldset input[type="text"],
fieldset input[type="date"],
fieldset select {
@@ -359,8 +348,6 @@ fieldset.active .fs-body { display: contents; }
.btn-danger { background: var(--accent); }
-.modal-error { color: #eb5757; font-size: .82rem; margin: .3rem 0 0; }
-
.note-row {
display: flex;
align-items: center;
@@ -369,7 +356,7 @@ fieldset.active .fs-body { display: contents; }
border-bottom: 1px solid var(--border);
}
.note-row:last-child { border-bottom: none; }
-.note-text { flex: 1; font-size: .875rem; white-space: pre-wrap; word-break: break-word; }
+.note-text { flex: 1; font-size: .875rem; }
.note-edit-controls { display: flex; gap: .25rem; }
.btn-edit-row { background: transparent; border: none; font-size: 1rem; padding: .2rem .3rem; }
@@ -405,43 +392,6 @@ fieldset.active .fs-body { display: contents; }
#btn-save-tune { background: var(--accent); }
#btn-cancel { background: transparent; border: 1px solid var(--border); }
-#notes-modal {
- max-width: 700px;
- height: 70vh;
- max-height: 85vh;
-}
-
-#note-editor-modal {
- max-width: 700px;
- height: 90vh;
- max-height: 95vh;
-}
-#note-editor-modal[open] {
- display: flex;
- flex-direction: column;
-}
-
-#note-editor-ta {
- flex: 1;
- min-height: 0;
- display: block;
- width: 100%;
- resize: vertical;
- background: var(--bg);
- border: 1px solid var(--border);
- color: var(--text);
- border-radius: var(--radius);
- padding: .5rem .65rem;
- font-size: .875rem;
- font-family: var(--font);
- line-height: 1.5;
- margin-bottom: .5rem;
-}
-#note-editor-ta:focus {
- outline: 2px solid var(--accent2);
- outline-offset: -1px;
-}
-
/* ── Detail modal ─────────────────────────────────────────── */
#detail-content h2 { margin-bottom: .75rem; }
#detail-content table { width: 100%; border-collapse: collapse; font-size: .85rem; }
@@ -457,16 +407,11 @@ fieldset.active .fs-body { display: contents; }
/* ── Responsive ───────────────────────────────────────────── */
@media (max-width: 600px) {
- /* Fixed viewport so header and footer stay put while main scrolls */
- body { height: 100dvh; overflow: hidden; }
- main { overflow-y: auto; }
-
- /* Search drops to its own full-width row */
- #filter-search { order: 3; flex-basis: 100%; }
- /* Actions fill remaining space; each button gets an equal share; ☰ stays natural size */
- #header-actions { flex: 1; margin-left: 0; }
- #header-actions > button { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; }
- #btn-bulk-edit { flex: none; }
+ /* Header: h1 + actions on line 1, search full-width on line 2 */
+ header { flex-wrap: wrap; }
+ header h1 { order: 1; flex: 1; }
+ #header-actions { order: 2; flex-shrink: 0; }
+ #filter-search { order: 3; flex-basis: 100%; }
/* Table: block layout so the card has a concrete width to truncate against */
#tune-table,
@@ -531,17 +476,17 @@ fieldset.active .fs-body { display: contents; }
}
.mob-actions { display: flex; gap: .2rem; flex-shrink: 0; }
- /* Modals: fullscreen — !important beats any ID-specific desktop size rules */
+ /* Modals: fullscreen */
dialog {
- position: fixed !important;
- inset: 0 !important;
- width: 100% !important;
- max-width: 100% !important;
- height: 100% !important;
- max-height: 100% !important;
- border-radius: 0 !important;
- border: none !important;
- margin: 0 !important;
+ position: fixed;
+ inset: 0;
+ width: 100%;
+ max-width: 100%;
+ height: 100%;
+ max-height: 100%;
+ border-radius: 0;
+ border: none;
+ margin: 0;
}
}
@@ -574,8 +519,7 @@ fieldset.active .fs-body { display: contents; }
#btn-bulk-edit { background: transparent; border: 1px solid var(--border); color: var(--text); font-size: 1rem; padding: .3rem .65rem; }
#btn-call { background: #1b4332; color: #74c69d; border: 1px solid #2d6a4f; font-weight: 600; }
-#btn-review { background: #3d2e10; color: #f2c94c; border: 1px solid #4d3a1e; font-weight: 600; }
-#btn-learn { background: #2a1a4e; color: #c4b5fd; border: 1px solid #3d2562; font-weight: 600; }
+#btn-learn { background: #3d2e10; color: #f2c94c; border: 1px solid #4d3a1e; font-weight: 600; }
/* ── Call modal ───────────────────────────────────────────── */
#call-modal {
@@ -839,6 +783,7 @@ 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; }
|