PDF: separate display name and filename; show filename in picker; fix rename
- Upload dialog now has distinct display name + filename fields, both pre-filled from the uploaded file but independently editable - Rename in file picker shows and edits both display name and filename separately - Filename conflict checked against both KV store and disk (via rename_no_replace) - Display name and filename are fully independent — no longer derived from each other - Add find_pdf_references() helper in storage.mjs for future use - CSS: fp-name-wrap shows display name + dim monospace filename below it; rename mode stacks two inputs; fp-field-label for upload form labels Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1479,32 +1479,49 @@ function render_file_picker_list() {
|
||||
row.appendChild(thumb);
|
||||
}
|
||||
|
||||
const name_wrap = document.createElement('div');
|
||||
name_wrap.className = 'fp-name-wrap';
|
||||
const name_el = document.createElement('span');
|
||||
name_el.className = 'fp-name';
|
||||
name_el.textContent = pdf.display_name;
|
||||
const filename_el = document.createElement('span');
|
||||
filename_el.className = 'fp-filename';
|
||||
filename_el.textContent = pdf.filename;
|
||||
name_wrap.append(name_el, filename_el);
|
||||
|
||||
const rename_btn = document.createElement('button');
|
||||
rename_btn.type = 'button';
|
||||
rename_btn.className = 'btn btn-secondary btn-sm';
|
||||
rename_btn.textContent = 'Rename';
|
||||
rename_btn.addEventListener('click', () => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.className = 'fp-rename-input';
|
||||
input.value = pdf.display_name;
|
||||
name_el.replaceWith(input);
|
||||
const name_input = document.createElement('input');
|
||||
name_input.type = 'text';
|
||||
name_input.className = 'fp-rename-input';
|
||||
name_input.value = pdf.display_name;
|
||||
name_input.placeholder = 'Display name';
|
||||
|
||||
const filename_input = document.createElement('input');
|
||||
filename_input.type = 'text';
|
||||
filename_input.className = 'fp-rename-input fp-rename-filename';
|
||||
filename_input.value = pdf.filename;
|
||||
filename_input.placeholder = 'filename.pdf';
|
||||
filename_input.spellcheck = false;
|
||||
|
||||
name_wrap.replaceChildren(name_input, filename_input);
|
||||
rename_btn.textContent = 'Save';
|
||||
input.focus();
|
||||
input.select();
|
||||
name_input.focus();
|
||||
name_input.select();
|
||||
|
||||
const do_rename = async () => {
|
||||
const new_name = input.value.trim();
|
||||
if (!new_name || new_name === pdf.display_name) {
|
||||
const new_display = name_input.value.trim();
|
||||
const new_filename = filename_input.value.trim();
|
||||
if (!new_display || !new_filename) { render_file_picker_list(); return; }
|
||||
if (new_display === pdf.display_name && new_filename === pdf.filename) {
|
||||
render_file_picker_list();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = await api.rename_pdf(pdf.id, new_name);
|
||||
const result = await api.rename_pdf(pdf.id, new_display, new_filename);
|
||||
const idx = all_pdfs.findIndex(p => p.id === pdf.id);
|
||||
if (idx !== -1) all_pdfs[idx] = result.pdf;
|
||||
all_pdfs.sort((a, b) => a.display_name.localeCompare(b.display_name));
|
||||
@@ -1515,10 +1532,10 @@ function render_file_picker_list() {
|
||||
};
|
||||
|
||||
rename_btn.onclick = do_rename;
|
||||
input.addEventListener('keydown', (e) => {
|
||||
[name_input, filename_input].forEach(inp => inp.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') { do_rename(); }
|
||||
if (e.key === 'Escape') { render_file_picker_list(); }
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
const select_btn = document.createElement('button');
|
||||
@@ -1542,7 +1559,7 @@ function render_file_picker_list() {
|
||||
render_file_picker_list();
|
||||
});
|
||||
|
||||
row.append(name_el, rename_btn, select_btn, del_btn);
|
||||
row.append(name_wrap, rename_btn, select_btn, del_btn);
|
||||
return row;
|
||||
}));
|
||||
|
||||
@@ -2062,22 +2079,32 @@ async function init() {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
const name_input = document.getElementById('fp-upload-name');
|
||||
const filename_input = document.getElementById('fp-upload-filename');
|
||||
if (!name_input.value.trim()) {
|
||||
name_input.value = file.name.replace(/\.pdf$/i, '');
|
||||
}
|
||||
if (!filename_input.value.trim()) {
|
||||
filename_input.value = file.name;
|
||||
}
|
||||
});
|
||||
|
||||
qs(document.getElementById('dialog-file-picker'), '#fp-upload-btn').addEventListener('click', async () => {
|
||||
const file_input = document.getElementById('fp-file-input');
|
||||
const name_input = document.getElementById('fp-upload-name');
|
||||
const filename_input = document.getElementById('fp-upload-filename');
|
||||
const file = file_input.files[0];
|
||||
if (!file) return;
|
||||
const display_name = name_input.value.trim();
|
||||
const filename = filename_input.value.trim();
|
||||
if (!display_name) { alert('Display name is required.'); return; }
|
||||
if (!filename) { alert('Filename is required.'); return; }
|
||||
try {
|
||||
const result = await api.upload_pdf(file, name_input.value.trim() || null);
|
||||
const result = await api.upload_pdf(file, display_name, filename);
|
||||
all_pdfs.push(result.pdf);
|
||||
all_pdfs.sort((a, b) => a.display_name.localeCompare(b.display_name));
|
||||
file_input.value = '';
|
||||
name_input.value = '';
|
||||
filename_input.value = '';
|
||||
render_file_picker_list();
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
|
||||
Reference in New Issue
Block a user