Commit Graph

23340 Commits

Author SHA1 Message Date
scheinriese
c2cf10e35d refactor: unify page-icon vertical alignment across all icon types
The page-title icon container used `.flex.self-start` and relied on each
icon type's own intrinsic baseline (font glyph for emoji, SVG bottom
anchor for tabler, etc.), with `margin-top: 8px` and `pb-[1.5px]`
band-aids compensating for one type at a time. Replaced with a single
flex-center 38×38 button rule, scoped to `.ls-page-title .ls-page-icon`,
so every type centers on the same visual axis. Drops the per-type pixel
overrides, the mobile-specific `:size 28` (mobile font is unchanged from
desktop, so the override was a band-aid), and the inline `1lh` style.
Bumps the icon-to-title gap from `gap-2` to `gap-3` (12px) for breathing
room.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 23:33:21 +02:00
scheinriese
154a0865c3 fix: restore icon-picker focus after color pick when no icon highlighted
The color-picker's :after-close! callback was gated on @*highlighted-index,
so opening the color picker without first navigating to an icon left focus
nowhere when the popover closed. The capture-phase keydown listener only
fires for keys whose target is inside the icon-picker container, so the
picker would appear open but reject every keystroke until the user clicked
the search input. Fall back to focusing the search input (and resetting
:focus-region to :search) when there's no highlighted tile to return to.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 20:49:38 +02:00
scheinriese
0763eeef71 feat: full keyboard navigation across color picker regions
Tab/Shift+Tab cycles three lanes (swatches → hex input → recents) and
arrow keys cross between them: hex input ↑ jumps to the active swatch,
↓ jumps to the first recent; the swatches grid's bottom row hops into
the hex input when the pane is open. The recents lane gets roving
tabindex with 2D nav across its two flex-wrapped rows, and react-
colorful's pad/hue sliders are stripped from the Tab order (mouse/
touch only) so keyboard users never land on the pad. The collapsed
pane is marked `inert` to keep its hidden controls out of focus.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 20:12:39 +02:00
scheinriese
4669d24821 feat: custom color picker with recents, contrast, and named-color autocomplete
Adds a hex/HSV picker pane to the icon-picker color popover, plus a
per-graph recents lane and cross-theme contrast adjustment. Hex input
supports named-color autocomplete (148 CSS + 949 XKCD) with a ghost
suffix that Tab/Right-arrow accepts. Picked colors that fail WCAG 3:1
against either light or dark background are auto-shifted in OKLCH per
mode; recents and the trigger swatch render as half-pie splits when the
two modes diverge.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 19:39:38 +02:00
scheinriese
5d80633068 fix: icon-picker keyboard nav, color-commit focus, per-tab search
Multiple keyboard-navigation fixes for the unified icon-picker:

- After committing a color, return DOM focus to the highlighted tile via
  Radix's onCloseAutoFocus instead of trying to focus during the swatch
  click (Radix's FocusScope traps focus while the popover is mounted, so
  any synchronous .focus() is silently undone). Routed through a new
  :after-close! kwarg on color-picker.
- focus-grid! now moves DOM focus to the active tile alongside updating
  *highlighted-index, so activeElement and .is-highlighted always
  coincide (WAI-APG roving-focus pattern, no double focus ring).
- Stable refs in icon-search and asset-picker: created once in
  :will-mount, stored in component state. rum/create-ref produces a new
  RefObject every render in defcs, which left popover-captured closures
  holding orphaned refs whose .current was nil after later renders.
- Search results now filter by tab content type while keeping the query
  active across tabs: :all shows both, :emoji only emoji matches, :icon
  only icon matches. Mirrors the gate in compute-flat-items so the
  visible grid and keyboard-nav flat-items list stay in sync.
- emojis-cp / icons-cp now forward the full opts to pane-section so
  highlight, ghost-highlight, focus-region, and wave propagate on the
  no-search :emoji / :icon tab paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 01:45:07 +02:00
scheinriese
a153d10963 feat: diagonal color wave across icon-picker grid
When the user hovers or commits a swatch, the icons in the picker grid
transition to the new color in a staggered wave from top-left to
bottom-right rather than snapping in lockstep — borrowed from Linear's
icon picker.

Mechanically: each cell renderer (icon-cp / emoji-cp / text-cp /
avatar-cp) stamps its grid position as inline `--r` / `--c` custom
properties, sourced from `pane-section`'s render loop. The existing
`--ls-color-icon-preset` cascade already carries the chosen color
down to the cells via currentColor inheritance; we add `color
320ms cubic-bezier(.4,0,.2,1)` to the per-button transition with
`transition-delay: calc(var(--c) * 22ms + var(--r) * 36ms)`, so
each cell starts its glide on its own clock.

Two design decisions worth flagging:

- **Hover preview drives the wave too**, not just commit. CSS
  transitions hold the old value during their `delay` window and
  retarget cleanly when the property changes mid-flight, so rapid
  cursor sweeps gracefully chase without flicker — far cells just
  hold steady until the user settles.
- **Material's "standard" easing** (`cubic-bezier(0.4, 0, 0.2, 1)`)
  over `ease-out`: each cell pauses, glides through the middle, and
  settles. The pause makes the stagger legible — neighbors are
  visibly "starting" while predecessors are mid-glide, which is
  what reads as a wave rather than a snap-and-fade.

`prefers-reduced-motion: reduce` strips the color transition so the
grid snaps instantly for users who opt out of animation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 16:42:31 +02:00
scheinriese
bb1dd9f029 fix: avatar color preview + commit in icon picker
The color picker had two bugs on avatar icons:

**Hover preview only updated the text, not the circle background.**
`get-node-icon-cp` propagates the preview via the wrapper's CSS
`color`, which inherits down to the avatar's text but not its inline
`background-color`. Same shape inside `icon-picker-trigger-icon`,
which mutated only `[:data :color]`. Both now also mutate
`[:data :backgroundColor]` for `:avatar` icons so the circle previews
together with the text.

**Clicking a swatch did not apply the new color.** color-picker's
`set-color!` is React state; the useEffect that propagates to the
external `*color` atom runs after the synchronous `on-select!`
callback. The icon-picker's `on-chosen` wrapper re-applies `@*color`
to `m`, so the freshly-picked color was overwritten with the previous
one (avatars defaulted to gray-09, so it looked like clicks were
ignored entirely). Sync the atom with `(reset! *color c)` before
calling on-chosen so the wrapper sees the new value. Same fix
incidentally addresses the same bug for already-colored regular
icons.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 16:12:20 +02:00
scheinriese
f731037692 feat: color-picker popover keyboard nav, muting, and tooltips
The color swatches popover now behaves like a proper WAI-APG radio
group: the popover lands focus on the currently-selected swatch on
open (deferred a tick past Radix's onOpenAutoFocus so we override it),
ArrowLeft/Right/Up/Down + Home/End rove with wrap-around, and the
trigger keeps roving tabindex so Tab in/out walks one stop instead of
nine.

While the user is engaging with the palette (mouse-hovering anywhere
in the group OR any swatch focused), non-active siblings drop to 0.55
opacity. Three stay bright: hovered, focused, and currently-selected.
At rest the whole row reads at full saturation — engagement-only
muting matches macOS/Figma/Linear and avoids hurting first-impression
discoverability.

Focus halo gets a styling pass: the system blue outline is replaced by
a colored ring with the same anatomy as the selected ring (inset edge
+ bg gap + outer halo) but thinner (2.5px vs 3.5px) and in the global
accent so focus is never the swatch's own hue — keeps focused vs.
selected legibly distinct on, e.g., the orange swatch where the ring
would otherwise blend.

Each swatch carries a tooltip (hover + keyboard focus, 300ms delay,
matching the asset-picker's web-image-item) with its color name. The
"no color" swatch gets a two-tier tooltip — "Default" + "Inherits the
surrounding text color" — to communicate why someone might want it
(theme-aware behavior in custom themes).

Note on the tooltip wiring: rum/with-key on a shui lsui-wrap React
element strips children to nil, so keys are passed in the props map
where React.createElement extracts them natively.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 15:55:08 +02:00
scheinriese
77dbc2cc66 feat: extend icon-picker topbar arrow-nav and unify focus styling
The icon-picker topbar now ropes the color swatch and trash button into
the same arrow-key rove as the tabs (data-topbar-stop + the controller's
:topbar region). Tabs auto-activate when arrow-roved over (e.detail = 0
discriminates programmatic clicks from real ones, so the on-change
handler keeps focus inside the topbar instead of bouncing to search).

Topbar focus styling is unified across both pickers so back / tabs /
segments / color / trash all paint the same blue ring (the trio of
browser-default outline, custom --lx-accent-09 outline, and shui's
--ring box-shadow no longer disagrees). Trash and inactive tabs need
opacity:1 on :focus-visible so the base opacity-60 doesn't dim the
ring into a muted glow.

Tile-to-tile transition no longer animates outline-color: animating it
from `currentColor` (e.g. red on a tinted icon) briefly tinted the
focus ring red before the accent color landed. Width and offset still
glide so the ring grows/shrinks smoothly; color snaps instantly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 15:15:23 +02:00
scheinriese
c8c1f7cb59 refactor: use segmented control for asset-picker Avatar/Image mode
The Avatar/Image selector isn't a tab bar — both modes show the same
content (the asset grid); only the resulting icon's :type/shape
differs. Switch to a pilled segmented control with radiogroup ARIA
semantics so the affordance reads as "pick one value" rather than
"navigate between sections".

Adds a generic `ui/segmented-control` helper alongside `tab-items`,
plus shared `.segmented-control` styling. Topbar gets a fixed 40px
height to match the icon-picker.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 15:13:43 +02:00
scheinriese
b10a3c6253 feat: keyboard navigation in asset-picker
Mirrors the icon-picker's grid model: a flat-items list + per-section
column descriptors flow into a shared keyboard-nav controller. The
controller is generalized to an options map so callers pass their own
container selector, escape behavior, and (optionally) a topbar selector
for heterogeneous toolbars. Asset-picker wires it for back/tabs/trash
rove via data-topbar-stop, search-input shortcuts (Up/Down/Tab/Esc/
Enter-for-ghost), and tile/row highlight + ghost-highlight props on
recently-used, web images, available assets, and zero-state action rows.

Hoists .is-highlighted / .is-ghost-highlighted CSS out of the
icon-picker scope so both pickers share it, and adds an inset-ring
treatment for the full-width zero-state rows. The mid-load "Loading
assets" spinner is dropped — the web-image skeletons cover the gap.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 15:12:37 +02:00
scheinriese
75c1cbbf2a refactor: polish asset-picker section alignment and details
- 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>
2026-04-24 15:27:31 +02:00
scheinriese
4a907f3bf1 feat: polish asset-picker zero-state rows
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>
2026-04-24 14:47:32 +02:00
scheinriese
7ad881f26e fix: guard icon against deleted asset references
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>
2026-04-24 14:47:24 +02:00
scheinriese
f099d6d492 chore: gitignore .claude/plans/ local planning artifacts
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 13:23:37 +02:00
scheinriese
0d60acab6f feat: zero-state rows + paste/URL UX in asset picker
- 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>
2026-04-24 13:22:48 +02:00
scheinriese
6f7416bb45 feat: add Avatar/Image tab switch to asset picker
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>
2026-04-23 15:40:54 +02:00
scheinriese
63df8047cf fix: stabilize debounced web-query setter across renders
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.
2026-04-23 14:49:59 +02:00
scheinriese
d66af6ec6f fix: guard stale web-image responses and avoid layout jump
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.
2026-04-23 14:49:43 +02:00
scheinriese
605226fc6e fix: drop 'inherit' sentinel before it reaches color-picker state
'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.
2026-04-23 14:49:17 +02:00
scheinriese
964f890f8d fix: preserve color picker saturation via icon-colored class
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.
2026-04-23 14:48:58 +02:00
scheinriese
45e767dee6 fix: treat :none sentinel as 'no icon' in delete-button state
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.
2026-04-23 14:48:35 +02:00
scheinriese
c0a3fb73c5 fix: recover gracefully from unresolvable stored icons
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.
2026-04-23 14:48:10 +02:00
scheinriese
6b9c688284 fix: drop non-Icon* entries from tabler-icons map
@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.
2026-04-23 14:47:44 +02:00
scheinriese
41a7f08408 feat: enlarge Custom tab tile icons from 24 to 32
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.
2026-04-23 14:43:17 +02:00
scheinriese
a7c2fc0e75 feat: align Custom tab tile interaction with icon picker
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.
2026-04-23 14:42:50 +02:00
scheinriese
7ec9fcbafc feat: keyboard-navigate Custom tab tiles
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.
2026-04-23 14:42:05 +02:00
scheinriese
407308fe64 feat: preview color on icons when hovering color picker swatches
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>
2026-04-16 14:52:15 +02:00
scheinriese
d2415cf595 fix: show Set icon button when icon is deleted via :none sentinel
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>
2026-04-16 14:52:02 +02:00
scheinriese
16d77abef2 feat: redesign color picker as round swatches with selection rings
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>
2026-03-23 13:50:44 +01:00
scheinriese
7cfc196bf6 fix: inherit tab-bar selected color into text-picker on mount
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>
2026-03-23 13:10:00 +01:00
scheinriese
c2e5f34a32 feat: add text-picker gallery with Initials/Abbreviated/Custom modes and live persistence
- 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>
2026-03-23 12:57:05 +01:00
scheinriese
3b780f56da fix: unify asset URL loading, add collapsible sections, fix cond-> corruption
- 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>
2026-03-23 12:38:52 +01:00
scheinriese
25457481c4 fix: use neutral grey for drag overlay corner brackets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 12:01:15 +01:00
scheinriese
b7dd0a3bc0 fix: deduplicate assets by checksum and prevent zombie re-transaction
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>
2026-03-18 00:59:40 +01:00
scheinriese
1b839f00d3 fix: add asset URL retry mechanism and prevent orphaned asset property leaks
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>
2026-03-18 00:29:27 +01:00
scheinriese
13da2d3415 feat: improve empty state copy and drag overlay for asset/icon picker
- 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>
2026-03-18 00:10:40 +01:00
scheinriese
8079717f85 fix: match overscroll background color to lx-gray-02 on icon picker root
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 00:02:59 +01:00
scheinriese
a6cb734251 feat: add drag-and-drop image upload to icon picker with frosted glass overlay
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>
2026-03-17 23:59:52 +01:00
scheinriese
2e65a0c190 feat: hide section collapse controls when only one search category visible
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>
2026-03-17 21:20:19 +01:00
scheinriese
c1a054fcb4 feat: improve icon picker UX — keyboard hints, highlight scoping, empty states
- 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>
2026-03-17 20:27:18 +01:00
scheinriese
493bae1dca debug: add console logs to diagnose asset/type ghost block bug
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>
2026-03-17 19:27:23 +01:00
scheinriese
6d97c97a20 feat: improve icon picker with asset management and avatar rendering
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>
2026-03-17 19:25:26 +01:00
scheinriese
cca45c4f7f style: adjust icon sizes and picker UI styling
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>
2026-03-17 19:25:16 +01:00
scheinriese
e782310e57 feat: add unified icon picker with emoji, text SVG, and image icon support
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>
2026-03-17 13:38:14 +01:00
Rustum Zia
5fe47fb69f fix(left_sidebar): don't activate resizer when sidebar is closed (#12455) 2026-03-17 13:43:09 +08:00
Tienson Qin
b27272828d code cleanup round 2 2026-03-17 00:07:33 +08:00
Tienson Qin
db6fee669a sync client code cleanup 2026-03-17 00:07:33 +08:00
Tienson Qin
917309113f feat: sync checksum 2026-03-17 00:07:33 +08:00
Tienson Qin
6183ba2375 fix(rtc): recover legacy rebased tx tempids 2026-03-17 00:07:33 +08:00