* feat(property): add :asset property type with thumbnail grid picker
Introduces a new user-facing "Asset" property type, modeled on :node but
restricted to blocks tagged with :logseq.class/Asset. Unlike :node's text
search dropdown, the :asset picker opens a 4-column thumbnail grid popup
populated from db-async/<get-tag-objects, filtered to image assets via
common-config/img-formats.
Once a value is set, the property cell renders an inline asset-cp preview
alongside "View" and "Swap" action buttons. View dispatches a new
:asset/show-preview event that opens the image lightbox for images, the
PDF viewer for PDFs (same path as the inline open-pdf-file handler), and
falls back to redirect-to-page! for other asset types. Swap reopens the
grid popup so the user can pick a different asset.
- Register :asset in user-built-in-property-types, user-ref-property-types,
and property-types-with-db; add asset-entity? validator that requires
:block/title and :logseq.class/Asset in :block/tags.
- Label the type as "Asset" in property-type-label so it appears in the
property type sub-pane.
- Add asset-grid-popup-content and asset-value-picker components in
property/value.cljs, dispatch :asset ahead of the generic select branch
in property-scalar-value-aux, and include :asset in the reference
display set used by select-item.
- Wire :asset/show-preview in handler/events/ui.cljs using the existing
lightbox, pdf-assets, and assets-handler plumbing.
* fix(property): refresh asset thumbnail after Swap
asset-cp captures the asset block in :will-mount, so React reconciling
the same component in place across re-renders left the old thumbnail
rendered even though the underlying property value had been updated
(visible because the View button opened the newly chosen asset).
Keying asset-cp on (:block/uuid value) forces React to remount it on
swap so :will-mount re-runs with the new block.
* fix(property): let image clicks select the asset in picker grid
asset-cp renders an <img> with its own click handler that opens the
image preview lightbox, which swallowed clicks before they could reach
the grid cell's select-this-asset handler. Users had to click the
padding around an image to pick it. Disabling pointer events on the
thumbnail wrapper lets the click bubble straight to the parent button.
* fix(property): fit asset thumbnails without cropping any aspect ratio
asset-cp emits the <img> with explicit width/height attributes (default
250px) wrapped in a div.asset-container that has no size constraint of
its own, so neither square nor tall images respected the thumbnail
bounding box: max-h-full on the img resolved against an auto-sized
parent and tall images overflowed the property row.
Constrain every descendant with !important max-w-full / max-h-full and
force the img to w-auto h-auto object-contain, and give the block
property thumbnail a fixed 80x80 bounding box instead of the previous
120x80 max that let the asset-container stretch. Same override is
applied to grid picker cells so they stay uniform regardless of source
aspect ratio.
* fix(property): stop tall images cropping in asset picker grid
The previous fix applied max-w/max-h-full to all descendants, but
div.asset-container's own height stayed auto, so max-height: 100% on
the img resolved against an auto-sized parent and tall images still
overflowed their grid cell.
Force div.asset-container itself to w-full h-full (and flex-center) so
the img's max-h-full resolves against a real bounded height. Same fix
is applied to the block property thumbnail for consistency.
* feat(property): show all asset types in picker grid with title bar
Drop the image-only filter so the asset picker grid lists every block
tagged :logseq.class/Asset, not just images. Non-image assets render as
a centered tabler icon + uppercase extension label chosen by a new
asset-icon-for-type helper (photo / music / movie / file-type-pdf /
file fallback); image assets keep the existing asset-cp thumbnail.
Each grid cell is now a vertical flex with a small title strip at the
top showing :block/title (truncated) and the thumbnail body filling
the rest of the square. min-height: 0 on the body lets the flex child
shrink inside the aspect-ratio box so square images still fit without
pushing the title off screen.
* feat(property): skip image sizing constraints for non-image asset values
The filled asset-value-picker used to force every asset-cp render into
a fixed 80x80 box with img overrides meant for bitmap thumbnails. That
mangled PDF link icons, audio <audio> controls and video <video>
players because those elements have their own intrinsic layout.
Branch on (:logseq.property.asset/type value): image assets keep the
80x80 constrained box so square/tall/wide bitmaps still fit without
cropping, while every other asset type renders asset-cp in a plain
flex-shrink-0 wrapper at its natural size.
* feat(property): preview video assets in a modal instead of inline
Inline asset-cp for videos rendered a full <video> player in the
property row, which was oversized and awkward next to the View/Swap
buttons. Replace it with a compact shui button showing a movie icon
plus the truncated asset title; clicking the button dispatches the
existing :asset/show-preview event.
The :asset/show-preview handler grows a video branch that resolves the
asset URL via <make-asset-url and opens a centered shui dialog with an
autoplaying <video controls>, capped at 80vh so it never exceeds the
viewport. Image lightbox and PDF viewer paths are untouched.
* refactor(property): drop redundant View button on asset property values
Every asset type already has a native click-to-preview path: image
thumbnails open the lightbox via resizable-image's handler, PDF links
open the PDF viewer via asset-link, audio/video players are clickable
in place, and the video-asset fallback button itself dispatches
:asset/show-preview. The separate View button just duplicated those
actions, so remove it and leave only Swap next to the thumbnail.
* fix(property): let video asset button grow to fit title
The video preview button capped itself at max-w-[240px] with a
truncated title span, cropping any video whose name was longer than
the cap. Drop the cap and truncation so the button sizes to its
content, and add h-auto whitespace-normal text-left so titles that
wrap onto multiple lines stay visible instead of being clipped by
shui's fixed button height.
* refactor(property): review cleanups for :asset property type
Addresses review feedback from the branch self-review:
- Extract the asset-cp-fit CSS escape hatch into an asset-thumb-fit-class
def with a docstring explaining why .asset-container needs forced
sizing, and reuse it in both the grid cell and the filled-state
thumbnail instead of duplicating the 60-char tailwind arbitrary
variant soup in two places.
- Simplify the two-clause cond in asset-value-picker's filled branch
to an if now that the :else is the only fallthrough.
- Wrap set-block-property! in the grid picker on-click with p/catch so
transact failures log and surface a notification toast instead of
being silently swallowed after the popup hides.
- Replace the bespoke :content-props {:class "max-w-[min(90vw,1200px)]"}
on the video preview dialog with shui's supported :auto-width? true
option, which applies max-w-[90vw] w-max sm:max-w-[960px] via the
data-auto-width CSS hook.
- Add a new logseq.db.frontend.property.type-test ns covering both the
:asset property type registration (present in user-built-in /
user-ref / all-ref / property-types-with-db, absent from
cardinality / closed-value) and the asset-entity? validator against
a real datascript db built via db-test/create-conn-with-blocks.
- Add a comment above asset-entity? documenting that :logseq.class/Asset
lives in ldb/private-tags, so the class is used programmatically for
scoping rather than via #Asset inline tagging.
* fix(property): satisfy CI lint checks for :asset property type
Address two lint failures on PR #12506:
- Drop the unused datascript.core require in property/type_test.cljs
(clj-kondo unused-namespace warning failed the deps/db lint job).
- Replace four hardcoded user-facing strings in the asset grid picker
with i18n keys, adding :asset/picker-empty, :asset/picker-fallback-type,
and :asset/picker-set-failed to the English dictionary; reuse
:ui/loading for the loading state.
* fix(property): address upstream review on :asset property type
Two follow-ups from the copilot-pull-request-reviewer review on PR #12506:
- Guard :asset picker against edits in publishing mode. show-grid!
short-circuits when config/publishing? is true so the empty-state click
and the Swap button can no longer open the picker, and the
Backspace/Delete branch of the keydown handler no longer fires
delete-block-property! Mirrors how single-value-select gates its
popup. Read-only paths (image lightbox, video preview dialog) are
left intact so published readers can still view embedded assets.
- Localize the Swap button label. Adds :asset/swap "Swap" to en.edn
and replaces the hard-coded literal with (t :asset/swap), matching
the other strings in asset-value-picker (picker-empty,
picker-set-failed, picker-fallback-type).
* fix(property): use letter-a icon for :asset property type
Without an entry in property-icon's case form, :asset properties fell
through to the default "letter-t" glyph, which collides visually with
the Text type. Map :asset to "letter-a" so the column header reads as
"A".
* fix: improve asset property picker
---------
Co-authored-by: Victor239 <12621257+Victor239@users.noreply.github.com>
Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Description
This library provides an API to the frontend(datascript) and backend(SQLite) databases from the Logseq app and the CLI. This library defines the core schema and fns for DB graphs. There are a few namespaces that also support file graphs for the graph-parser. This library is compatible with ClojureScript and with nbb-logseq to respectively provide frontend and commandline functionality.
API
This library is under the parent namespace logseq.db. It provides the following namespaces:
logseq.db- main public ns. Most often used by frontendlogseq.db.frontend.*- frontend namespaces for DB graphslogseq.db.sqlite.*- backend/sqlite namespaces for DB graphslogseq.db.common.*- frontend and backend namespaces for DB graphs
The following namespaces are used with file graphs via the graph-parser: logseq.db.common.order, logseq.db.frontend.entity-util and logseq.db.common.entity-plus.
Usage
See the frontend for example usage.
Dev
This follows the practices that the Logseq frontend follows. Most of the same linters are used, with configurations that are specific to this library. See this library's CI file for linting examples.
Setup
To run linters and tests, you'll want to install pnpm dependencies once:
pnpm install
This step is not needed if you're just running the application.
Testing
Testing is done with nbb-logseq and nbb-test-runner. Some basic usage:
# Run all tests
$ pnpm test
# List available options
$ pnpm test -H
# Run tests with :focus metadata flag
$ pnpm test -i focus
Datalog linting
Datalog rules for the client are linted through a script that also uses the datalog-parser. To run this linter:
bb lint:rules
Managing dependencies
The package.json dependencies are just for testing and should be updated if there is new behavior to test.
The deps.edn dependencies are used by both ClojureScript and nbb-logseq. Their versions should be backwards compatible with each other with priority given to the frontend. No new dependency should be introduced to this library without an understanding of the tradeoffs of adding this to nbb-logseq.