- Align section headers across asset-picker and icon-picker at 12px
from the edge by dropping .pane-section's px-2. Grid padding becomes
px-3 pt-1 pb-3 so headers sit closer to their grids.
- Hide "Available assets · 0" when no assets exist; the zero-state
rows speak for themselves. Header reappears during search so "· 0"
still conveys "no matches".
- Drop row-title to text-sm, bump row-chevron to gray-10 (no opacity),
and move the empty-row separator from gray-04 to gray-06 for dark-
mode legibility.
- Pin the web-images info icon to the right edge (margin-left: auto +
margin-right: 12px) and remove its opacity layer.
- Let the web-image external-badge float past the circular avatar clip
by dropping overflow-hidden on .web-image-item (img clips itself).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reorder to Paste clipboard first, then Upload. Drop the standalone URL
row now that the clipboard row accepts pasted URL text. Switch the
shortcut badge to combo style and trigger its press animation on both
keystroke and click. CSS uses :has() to cancel ancestor padding so hover
backgrounds reach the picker edges without a horizontal scrollbar.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Avatar/image rendering checks that the referenced asset entity still
exists before calling into the async image component. Without the
guard, the picker crashed inside avatar-image-cp while loading a ghost
uuid. Add a self-heal effect that rewrites dangling icon values on
mount: :avatar degrades to text-only, :image clears entirely.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Replace "No images yet" placeholder with three tappable rows when the
asset list is empty: Upload from computer, Paste image URL, and
(when supported) Paste from clipboard with a ⌘V hint. Floating
action bar hides while rows are shown.
- Accept Finder / Explorer file pastes. A sync ClipboardEvent reader
inspects clipboardData.files/items, since navigator.clipboard.read()
cannot see OS file references. Global paste listener attached to
the picker root.
- Route URL downloads through Electron IPC (:httpRequest) in desktop
builds to bypass renderer CORS. Add opt-in :structured flag to the
IPC handler so it returns {:status :ok :headers :data} without
breaking existing body-only callers. Sniff magic bytes to detect
image vs HTML vs unknown when server Content-Type is unreliable.
- Classify download failures by HTTP status and error kind. New
url-save-error-copy maps :html-page, :not-image, :too-large,
:unknown, :http-status (with 401/402/403/404/429 specifics),
:empty, :cors, and :network to actionable user copy. Browser-build
CORS rejections now get an honest "browser blocked cross-origin"
message instead of "check your connection".
- Log failures via js/console.error in both IPC and fetch paths for
future debuggability.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Live tab control at the top of the asset-picker flips between circular
avatar previews and uncropped image previews. Switching modes persists
an in-place `:type` flip on an existing asset-backed icon, and future
picks are saved with the active mode's `:type`.
Extracted the icon-picker's tab-bar markup into a reusable `ui/tab-items`
helper and unscoped `.tabs-section` / `.tab-item` CSS so both pickers
share the underline style. Web-image thumbnails now default to
`object-fit: contain` in Image mode (was always `cover`).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The old (memoize (fn [] (debounce …))) built a fresh debouncer on
every render, so each keystroke got its own timer and debouncing
never actually happened — partial-prefix queries all fired, producing
the race condition that request-id guards. Move the debounce into
component state via :will-mount (not :init, because rum/local only
installs the underlying atom during :will-mount) so a single timer
persists across renders.
Two related bugs around the web-images section:
1. Fast typing produced overlapping fetches where a late 'do' response
could overwrite results for 'donald trump'. Stamp each request with
a generation id in a component-local atom and drop responses whose
id no longer matches.
2. The section unmounted during transition states — while the parent's
debounced query hadn't caught up yet, or after the user typed but
before loading? flipped to true — so the layout below jumped up,
then back down when skeletons appeared. Compute a pending? flag
from both conditions, keep the section mounted whenever loading? or
pending?, and mirror avatar-mode on skeletons so circle-mode loads
land in the exact spot the circular skeletons occupied. CSS mirror
the same geometry (transparent 2px border + avatar-mode 50% radius)
on .web-image-placeholder.
'inherit' is a CSS-layer fallback written into --ls-color-icon-preset
so icons without an explicit color still render with something. It's
not a real color and must not leak into React state or the persisted
preset — otherwise the swatches show a phantom selection ring and the
value round-trips through storage. Filter it at both entry points:
color-picker's use-state initializer and icon-search's :init when
reading icon-value color + stored preset.
Also initializes color from the current icon's color first (falling
back to stored preset) so opening the picker against an already-
colored icon highlights the correct swatch.
The global .ui__icon svg { filter: brightness(.8) } dark-theme clamp
was desaturating icons rendered with an explicit color (e.g. cyan-10
looked visibly muted versus the swatch it was picked from). Add an
icon-colored class anywhere an effective color is present (and not the
'inherit' CSS sentinel), and use it to opt out of the filter where
color IS the affordance — inline icons, picker previews, and the live
color-picker preview on the picker root. Sidebar opacity-70 is left
intact since it's intentional visual hierarchy.
Deleting an icon writes a :none sentinel rather than removing the
property, so del-btn? checks that only tested for truthy icon values
kept the delete button visible on empty slots. Teach both the block
call site (icon picker trigger) and icon-search itself to treat :none
as absent. icon-search additionally recomputes del-btn? reactively
from the live entity via db-mixins/query so keep-popup? flows (e.g.
picking a color that writes the icon for the first time) update the
button in place instead of staying stale.
Stored icon values can fall out of sync with what the picker can
render — e.g. data saved against a phantom tabler utility export
before the picker added a filter. Add a renderable-icon? predicate
and use it to (1) drop dead entries from recently-used so the grid
doesn't render empty tiles, and (2) fall back to the "Set icon"
button on page titles whose stored icon can't resolve.
@tabler/icons-react exports utility helpers (e.g. createReactComponent)
alongside the IconFoo components. The picker keyed off every export, so
these utilities showed up as phantom entries in search, rendered empty,
and — if picked — were written back as unresolvable icon values.
The 48x48 preview boxes had a lot of empty margin around the 24px
icons — especially after dropping the background/border. Bumping to
32px fills the tiles in a way that feels consistent with the densely-
packed icon/emoji grids. Image placeholder scales with it: 28x28→32x32
box, 16→20 camera glyph.
Matches the hover pattern used by icon/emoji tiles: background-color
change instead of a border outline, and no opacity fade on the whole
tile. Drops the default background/border on the preview so unselected
tiles read as plain icons rather than framed controls. Label color
steps up to gray-12 on hover so the tile feels active.
The narrower transition list (background-color, outline-color) replaces
transition-all — the outline-color channel is what lets the
is-highlighted ring fade in without a discrete style jump.
Wire the Custom tab into the picker's arrow-key navigation: Text, Avatar,
and Image tiles now gain a blue ring when highlighted by keyboard and
commit on Enter, matching Icons/Emojis.
custom-tab-cp destructures the already-plumbed :highlighted-id from opts
and emits data-item-id + conditional .is-highlighted on each button. CSS
neutralizes the generic button.is-highlighted (which would paint the
label column) and moves the ring onto the 48x48 preview child. A
baseline transparent outline avoids the currentColor flash when the ring
appears.
Hovering a color swatch live-previews that color on both the picker grid
icons and the page-title icon, letting the user imagine the choice before
committing. Click commits as before; mouse-leave or popup close reverts.
- color-picker now tracks local hover state and splits its effect: a
display-only effect (deps [effective-color]) updates the CSS var on
hover and commit; a commit effect (deps [color]) persists to storage.
An unmount cleanup clears external preview state.
- New :on-hover! / :on-hover-end! kwargs are opt-in; threaded through
icon-picker -> icon-search via :preview-target-db-id from block.cljs.
- get-node-icon-cp subscribes to :ui/icon-hover-preview so sidebar /
inline icons update reactively.
- icon-picker-trigger-icon is a small reactive sub-component so the
page-title trigger updates without forcing the hook-using parent into
a class component. It pre-normalizes icon-value before applying the
preview color so normalize-icon's early-exit doesn't strip :data:value.
- :focus-visible outline on swatches gives keyboard users the same
preview affordance as mouse hover.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The visibility check only handled nil icons, not the {:type :none} value
written when the user explicitly deletes an icon. Treat both as "no icon".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace rectangular shui/button color picker with round swatch circles:
- Trigger button shows filled circle (with color) or slashed empty circle
- Popup renders swatches as 24px circles with hover scale and active press
- Selected swatch gets accent-colored ring via box-shadow
- "No color" swatch uses CSS-only diagonal slash (bottom-left to top-right)
- Remove duplicate .color-picker styles from tab-bar and text-picker-actions
- Move "none" to first position in color array for natural scanning order
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pass the icon-search *color atom value as :selected-color to text-picker,
so the text-picker's color initializes from the tab-bar selection when no
existing icon color is saved.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add derive-abbreviated, normalize-word-boundaries, and abbreviated-stop-words
for smart title abbreviation (e.g. "Software Engineer" → "Soft Eng")
- Replace single preview with gallery row of 3 selectable style tiles
- Add ::mode and ::deleted? state atoms; persist mode in icon data
- Live-persist on gallery click, alignment change, and text input (300ms debounce)
- Guard will-unmount with ::deleted? to prevent re-persisting after delete
- Add gallery CSS (.text-picker-gallery, -item, -preview, -label) and
color-picker nested styles in .text-picker-actions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Merge load-image-url!, try-load-image-with-extensions!, and duplicate
<load-asset-url! into a single unified <load-asset-url! with retry logic,
extension guessing, and per-component stale guard
- Fix concurrent asset loads dropping by replacing global *load-id-counter
with per-component ::load-id atoms
- Add on-error handler to image-icon-cp <img> tag for graceful fallback
- Wire up collapsible sections in asset-picker using rum/react on
*section-states (fixes reactivity with @ plain deref)
- Restore correct } placement in cond-> forms (emoji-cp, text-cp, avatar-cp)
that clj-paren-repair had corrupted by absorbing test/expr pairs into maps
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Asset queries now require checksum (filtering orphaned entries) and
deduplicate by checksum to prevent showing duplicate thumbnails.
Changed async query to transact-db? false so deleted assets don't get
re-transacted from worker DB back into frontend. Added debug logging
across asset URL resolution, picker mounting, and view lifecycle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ghost assets now retry URL resolution 3 times with 1s delay, show a
clickable refresh icon for manual retry, and handle invalidated blob
URLs via img on-error. Orphaned asset properties (type without
checksum+size) are auto-retracted on save, and block rendering requires
all 3 asset properties to prevent false asset treatment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Context-aware empty states: "No images yet" (first use) vs "No matching
images" (search) with appropriate icons (photo vs search-off)
- Add .asset-picker-empty CSS class with grid-column: 1/-1 to fix text
wrapping bug where empty state was confined to a single 65px grid column
- Refine drag overlay: frosted glass backdrop, corner bracket indicators,
file type subtitle (PNG, JPG, SVG, GIF, WebP), shared CSS between pickers
- Lock scroll on drag-active to prevent content shifting under overlay
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow users to drag image files directly onto the icon picker popup to set
as icon, bypassing the Custom > Image tab navigation. Features a frosted
glass overlay with backdrop blur, radial edge glow using theme-aware accent
colors, decorative corner brackets, and scroll locking during drag.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When search filters results to a single category, the chevron toggle
and keyboard shortcut hints are unnecessary — collapse the only visible
section makes no sense. Uses simple header mode instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Show section shortcut hints (⌥⌘1/2/3) only when focus is on grid/tabs,
hidden when typing in search
- Restore ⌥⌘1/2/3 to toggle section collapse (was incorrectly switching
tabs); remove tab-switching shortcuts
- Scope keyboard highlight to active section only, preventing duplicate
highlights when same emoji appears in Recently used and Emojis
- Unify hover shape from circle to rounded-rect (8px) matching keyboard
focus, with consistent visual hierarchy (hover < focus < ghost)
- Add "No results found" empty state with search-off icon and subtitle
- Show ghost asset placeholder for missing image files instead of hiding
them entirely
- Unify Emojis/Icons virtual list height (358px) to match All tab,
eliminating height jump when switching between browsing tabs
- Bump picker max-height to 442px to accommodate taller virtual lists
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Temporary debug logging to trace how :logseq.property.asset/type "jpg"
leaks onto regular text blocks during icon picker avatar→tabler-icon
switching. Logs at: save-image-asset!, wrap-parse-block, save-block-inner!,
api-insert-new-block!, and icon on-chosen callback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enhance unified icon picker: refine asset-picker section navigation,
improve avatar/image icon handling in on-chosen callbacks, add asset
alignment support for images, and update icon rendering for block and
page contexts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bump sidebar icon sizes to 20px, increase avatar font-size in sidebar,
fix page title avatar dimensions, refine icon picker tabs and layout,
add keyboard focus styles for tab items, and prevent bold layout shift.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces a comprehensive icon picker system supporting 5 icon types:
emoji, tabler icons, text (SVG viewBox), avatar, and image assets.
Key features:
- Text icons rendered as crisp SVG with smart text splitting
- Image asset picker with grid view, drag-and-drop upload
- Wikipedia Commons image search with license confirmation
- URL-based asset import with validation
- Recently used assets tracking
- Unified icon format with normalize-icon for consistent storage
- DB migrations for default-icon, wikidata-id, property-key-width properties
- Context menu "Set icon" / "Change icon" entry points
- Bordered tooltip arrows for web image info
- Photo-icon sizing in CMD+K and page title contexts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cross-handler prefix overlaps don't cause chord dormancy because each
Closure KeyboardShortcutHandler instance has its own independent key
tree and state machine. Only same-handler prefix conflicts (where
Closure's tree can't have a node be both leaf and branch) cause
actual chord dormancy.
- Change binding-match? to use same-handler? instead of handler-match?
for prefix overlaps (exact matches still use handler-match?)
- Update test to verify cross-handler prefixes are NOT detected
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show the amber "Deactivates" banner only after the user confirms
Reassign, not alongside the red "Used by" prompt. Reduces visual
noise during the decision step and shows consequences after the action.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>