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:
2026-03-22 11:45:50 +00:00
parent d3df99a8f0
commit 4813a65a53
8 changed files with 116 additions and 42 deletions

View File

@@ -526,27 +526,27 @@ app.get('/api/pdfs', (req, res) => {
app.post('/api/pdfs', pdf_upload.single('file'), (req, res) => {
if (!req.file) return fail(res, 'no file uploaded');
const display_name = (req.body.display_name?.trim() || req.file.originalname).trim();
if (list_pdfs().some(p => p.display_name === display_name)) {
const display_name = req.body.display_name?.trim() || req.file.originalname;
const filename = sanitize_pdf_filename(req.body.filename?.trim() || req.file.originalname);
const all = list_pdfs();
if (all.some(p => p.display_name === display_name)) {
try { unlinkSync(join('./data/pdfs', req.file.filename)); } catch {}
return fail(res, 'a file with that name already exists');
return fail(res, 'a file with that display name already exists');
}
if (all.some(p => p.filename === filename)) {
try { unlinkSync(join('./data/pdfs', req.file.filename)); } catch {}
return fail(res, 'a file with that filename already exists');
}
const id = generate_id();
const filename = sanitize_pdf_filename(display_name);
const temp_path = join('./data/pdfs', req.file.filename);
const final_path = join('./data/pdfs', filename);
if (!rename_no_replace(temp_path, final_path)) return fail(res, 'a file with that name already exists on disk');
if (!rename_no_replace(temp_path, final_path)) {
try { unlinkSync(temp_path); } catch {}
return fail(res, 'a file with that filename already exists on disk');
}
const thumb_prefix = join('./data/pdfs', id + '-thumb');
const thumb_file = generate_pdf_thumb(final_path, thumb_prefix);
const pdf = {
id,
filename,
display_name,
original_name: req.file.originalname,
size: req.file.size,
thumb_filename: thumb_file,
uploaded_at: Date.now(),
};
const pdf = { id, filename, display_name, original_name: req.file.originalname, size: req.file.size, thumb_filename: thumb_file, uploaded_at: Date.now() };
set_pdf(pdf);
ok(res, { pdf });
});
@@ -555,16 +555,19 @@ app.put('/api/pdfs/:id', (req, res) => {
const pdf = get_pdf(req.params.id);
if (!pdf) return fail(res, 'not found', 404);
const display_name = req.body.display_name?.trim();
const filename = req.body.filename?.trim() ? sanitize_pdf_filename(req.body.filename.trim()) : pdf.filename;
if (!display_name) return fail(res, 'display_name is required');
if (list_pdfs().some(p => p.display_name === display_name && p.id !== pdf.id)) {
return fail(res, 'a file with that name already exists');
const all = list_pdfs();
if (all.some(p => p.display_name === display_name && p.id !== pdf.id))
return fail(res, 'a file with that display name already exists');
if (all.some(p => p.filename === filename && p.id !== pdf.id))
return fail(res, 'a file with that filename already exists');
if (filename !== pdf.filename) {
const new_path = join('./data/pdfs', filename);
if (!rename_no_replace(join('./data/pdfs', pdf.filename), new_path))
return fail(res, 'a file with that filename already exists on disk');
}
const new_filename = sanitize_pdf_filename(display_name);
if (new_filename !== pdf.filename) {
const new_path = join('./data/pdfs', new_filename);
if (!rename_no_replace(join('./data/pdfs', pdf.filename), new_path)) return fail(res, 'a file with that name already exists on disk');
}
const updated = { ...pdf, display_name, filename: new_filename };
const updated = { ...pdf, display_name, filename };
set_pdf(updated);
ok(res, { pdf: updated });
});