New tooltip.js module with styled tooltips (data-tip) and multi-line
inline help (data-help). Migrates all ~70 native title attributes
across index.php, JS modules, and all plugins. Adds inline help on
9 strategic UI elements (split, superview, zen, extract, assets, etc).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Orphaned items now use mediaTile() instead of a hand-written triple-
ternary, so audio and PDF files render with the same icon/player tile
as linked items. Eliminates the code duplication that caused audio
files to fall through to the <img> path in the orphaned section.
* applyFilter() tracks visibleCount after toggling item display. When
the active filter hides every item (e.g. "Audio" tab with no audio
files) but total assets exist, a type-specific placeholder is shown:
matching icon (ph-image / ph-music-note / ph-file-pdf) + "no {type}
assets" text. Hidden again on the next filter switch that has results.
* Section header hide logic narrowed to [data-section]:not(.gallery-item)
so it only targets the header divs, not the items themselves.
chore: bump version to 0.9.19
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PDF upload support:
* index.php: application/pdf added to $mimeMap + $extMap in the
/api/upload handler. PDFs are stored in uploads/ like images/audio.
* editor.js uploadFile(): detects PDF via MIME or .pdf extension.
Inserts a markdown link [📄 filename](uploads/...) instead of an
image embed. Paste + drop handlers updated to accept PDFs.
* index.php: new toolbar button #md-upload-pdf with ph-file-pdf icon
next to the extract button. Triggers a hidden file input
(accept=".pdf"). Wired in editor.js.
Gallery → Assets rename:
* index.php footer button: "Images" → "Assets", icon ph-folder-open.
* index.php gallery overlay header: "images" → "assets", icon
ph-folder-open, upload input accept extended to include .pdf.
* Empty state text: "no images" → "no assets".
File-type tabs in the assets overlay:
* index.php: new #gallery-tabs div with 4 buttons (All / Images /
Audio / PDF), each with data-filter attribute.
* gallery.js: fileType(url) helper returns 'image'|'audio'|'pdf'|
'other'. Tab clicks toggle .active class and filter gallery-items
by data-url + fileType. activeFilter persists across re-opens.
* CSS: .gallery-tab styling with hover + .active accent fill.
PDF rendering in the assets grid:
* gallery.js mediaTile(): PDFs render as ph-file-pdf icon + filename
instead of a thumbnail. Orphaned PDFs show the same icon tile.
* Click on an orphaned PDF opens it in a new tab (window.open).
* "New card" from an orphaned PDF inserts a markdown link (not
image embed).
Orphan detection fix (gallery.js):
* Root cause: loadCards() in app.js uses two-phase lazy loading —
first /api/cards/meta (body=null on all cards), then batches of 25
with full bodies. Opening the gallery before the last batch
completes meant most cards had body=null, getReferencedUrls()
found no URLs, and every file appeared orphaned.
* Fix: openGallery() now fetches its own card list with full bodies
via api('api/cards') in parallel with api('api/uploads'), stored in
a module-local galleryCards array. Both getReferencedUrls() and
findCardForUrl() read from galleryCards instead of state.cards.
* Bonus: added a fallback regex uploads/[hex]{32}.[ext] that catches
references in any format the specific patterns might miss (HTML
attributes, custom blocks, inline text).
chore: bump version to 0.9.18
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>