Add --lx/--rx color fallbacks for OG theme compatibility

Ensure shortcut component and shui shortcut badges work across both
Radix and legacy Logseq color themes by adding proper fallback chains:

- shortcut.css: Add --lx-* → --rx-* fallbacks for gray scale (08-12),
  --lx-* → --ls-* → --rx-* for background steps (01-04, 06), use
  opacity-based row dimming instead of color-based for theme-agnostic
  muting, and use --color-level-6 for icon link color
- shui.css: Add --rx-* fallbacks to bare --lx-* variables on shortcut
  key badges (background, border, text color, separator)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
scheinriese
2026-03-10 10:09:59 +01:00
committed by Tienson Qin
parent 1594a122cc
commit ef215dccfc
3 changed files with 303 additions and 171 deletions

View File

@@ -274,8 +274,8 @@ div[data-radix-popper-content-wrapper] {
/* Combo Keys - simultaneous key combinations with separator */
.shui-shortcut-combo {
@apply flex items-start relative rounded;
background-color: var(--lx-gray-06-alpha);
border: 1px solid rgba(0, 0, 0, 0.1);
background-color: var(--lx-gray-06-alpha, var(--rx-gray-06-alpha));
border: 1px solid var(--lx-gray-06-alpha, var(--rx-gray-06-alpha));
box-sizing: border-box;
white-space: nowrap;
}
@@ -301,7 +301,7 @@ div[data-radix-popper-content-wrapper] {
}
.shui-shortcut-separator {
background-color: var(--lx-gray-07-alpha);
background-color: var(--lx-gray-07-alpha, var(--rx-gray-07-alpha));
align-self: stretch;
flex-shrink: 0;
width: 1px;
@@ -316,8 +316,8 @@ div[data-radix-popper-content-wrapper] {
.shui-shortcut-separate kbd.shui-shortcut-key {
@apply flex flex-col items-center justify-center px-1 py-0.5 relative rounded shrink-0;
background-color: var(--lx-gray-06-alpha);
border: 1px solid rgba(0, 0, 0, 0.1);
background-color: var(--lx-gray-06-alpha, var(--rx-gray-06-alpha));
border: 1px solid var(--lx-gray-06-alpha, var(--rx-gray-06-alpha));
box-sizing: border-box;
min-width: 20px;
}
@@ -329,7 +329,7 @@ div[data-radix-popper-content-wrapper] {
font-weight: normal;
line-height: 16px;
font-style: normal;
color: var(--lx-gray-12);
color: var(--lx-gray-12, var(--rx-gray-12));
font-size: 12px;
text-align: center;
letter-spacing: -0.5px;
@@ -342,7 +342,7 @@ kbd.shui-shortcut-key,
.shui-shortcut-key {
@apply text-xs font-normal h-5 flex items-center justify-center;
font-family: 'Inter', sans-serif;
color: var(--lx-gray-12);
color: var(--lx-gray-12, var(--rx-gray-12));
font-size: 12px;
text-align: center;
letter-spacing: -0.5px;
@@ -356,8 +356,8 @@ kbd.shui-shortcut-key,
/* Keys in separate containers get their own styling */
.shui-shortcut-separate kbd.shui-shortcut-key {
@apply rounded;
background-color: var(--lx-gray-06-alpha);
border: 1px solid rgba(0, 0, 0, 0.1);
background-color: var(--lx-gray-06-alpha, var(--rx-gray-06-alpha));
border: 1px solid var(--lx-gray-06-alpha, var(--rx-gray-06-alpha));
box-sizing: border-box;
}

View File

@@ -143,10 +143,13 @@
(start-commit-timer! kn-trimmed))
;; During accumulation, append
(let [cur (rum/deref *keystroke-ref)
new-ks (util/trim-safe (str cur kn))]
(set-accumulating! true)
(set-keystroke! new-ks)
(start-commit-timer! new-ks))))))))))
parts (string/split (string/trim cur) #" ")
at-limit? (and (seq (first parts)) (>= (count parts) 5))]
(when-not at-limit?
(let [new-ks (util/trim-safe (str cur kn))]
(set-accumulating! true)
(set-keystroke! new-ks)
(start-commit-timer! new-ks))))))))))))
(js/setTimeout #(.focus el) 128)
@@ -196,91 +199,99 @@
"Close " (shui/shortcut "escape" {:style :compact})]]]]))
(rum/defc pane-controls
[q set-q! filters set-filters! keystroke set-keystroke! toggle-categories-fn]
(let [*search-ref (rum/use-ref nil)]
[q set-q! filter-key set-filter-key! keystroke set-keystroke! toggle-categories-fn pill-counts]
(let [*search-ref (rum/use-ref nil)
in-keystroke? (not (string/blank? keystroke))]
[:div.cp__shortcut-page-x-pane-controls
;; search input — first and widest element
[:span.search-input-wrap
[:span.search-icon (ui/icon "search" {:size 15})]
[:input.form-input.is-small
{:placeholder "Search shortcuts..."
:ref *search-ref
:value (or q "")
:auto-focus true
:on-key-down #(when (= 27 (.-keyCode %))
(util/stop %)
(if (string/blank? q)
(some-> (rum/deref *search-ref) (.blur))
(set-q! "")))
:on-change #(let [v (util/evalue %)]
(set-q! v))}]
;; Row 1: search + keystroke button
[:div.shortcut-toolbar-row
[:span.search-input-wrap
[:span.search-icon (ui/icon "search" {:size 15})]
[:input.form-input.is-small
{:placeholder "Search shortcuts..."
:ref *search-ref
:value (or q "")
:auto-focus true
:on-key-down #(when (= 27 (.-keyCode %))
(util/stop %)
(if (string/blank? q)
(some-> (rum/deref *search-ref) (.blur))
(set-q! "")))
:on-change #(let [v (util/evalue %)]
(when-not (string/blank? v)
(set-keystroke! ""))
(set-q! v))}]
(when-not (string/blank? q)
[:a.x
{:on-click (fn []
(set-q! "")
(js/setTimeout #(some-> (rum/deref *search-ref) (.focus)) 50))}
(ui/icon "x" {:size 14})])]
(when-not (string/blank? q)
[:a.x
{:on-click (fn []
(set-q! "")
(js/setTimeout #(some-> (rum/deref *search-ref) (.focus)) 50))}
(ui/icon "x" {:size 12})])]
;; toggle fold/unfold categories
[:a.flex.items-center.icon-link
{:on-click toggle-categories-fn
:title "Toggle categories pane"}
(ui/icon "fold")]
;; keystroke filter button
(let [filter-popup-id :shortcut-keystroke-filter
open-filter! (fn [^js e]
(set-q! "")
(shui/popup-show!
(.-currentTarget e)
(fn [_]
(keyboard-filter-record-inner
keystroke set-keystroke!
#(shui/popup-hide! filter-popup-id)))
{:id filter-popup-id
:force-popover? true
:align "end"
:content-props
{:class "shortcut-filter-popover-content p-0 w-auto"
:collision-padding 12
:onOpenAutoFocus #(.preventDefault %)
:onCloseAutoFocus #(.preventDefault %)
:onEscapeKeyDown (fn [_] false)
:onPointerDownOutside (fn [_] nil)}}))]
(if in-keystroke?
[:button.shortcut-keystroke-active
{:on-click open-filter!}
[:span.shortcut-keystroke-keys
(ui/icon "keyboard" {:size 14})
(shui/shortcut keystroke)]
[:a.shortcut-keystroke-clear
{:on-click (fn [^js e]
(.stopPropagation e)
(set-keystroke! ""))}
(ui/icon "x" {:size 12})]]
[:button.shortcut-keystroke-inactive
{:on-click open-filter!}
(ui/icon "keyboard" {:size 14})
[:span "Search by keys"]]))]
;; refresh
[:a.flex.items-center.icon-link
{:on-click refresh-shortcuts-list!
:title "Refresh all"}
(ui/icon "refresh")]
;; Row 2: filter pills + fold + refresh
[:div.shortcut-pills-row
[:div.shortcut-filter-pills
(for [k [:All :Custom :Unset :Disabled]
:let [active? (or (and (= k :All) (nil? filter-key))
(= filter-key k))
cnt (get pill-counts k 0)
title (if (= k :All) "All" (name k))]]
[:button.shortcut-filter-pill
{:key (name k)
:class (when active? "shortcut-filter-pill--active")
:on-click #(set-filter-key! (when-not (or (= k :All) (= filter-key k)) k))}
[:span.shortcut-filter-pill-title title]
[:span.shortcut-filter-pill-count (str " \u00B7 " cnt)]])]
;; keyboard filter
(let [filter-popup-id :shortcut-keystroke-filter
open-filter! (fn [^js e]
(shui/popup-show!
(.-currentTarget e)
(fn [_]
(keyboard-filter-record-inner
keystroke set-keystroke!
#(shui/popup-hide! filter-popup-id)))
{:id filter-popup-id
:force-popover? true
:align "end"
:content-props
{:class "shortcut-filter-popover-content p-0 w-auto"
:collision-padding 12
:onOpenAutoFocus #(.preventDefault %)
:onCloseAutoFocus #(.preventDefault %)
:onEscapeKeyDown (fn [_] false)
:onPointerDownOutside (fn [_] nil)}}))]
[:a.flex.items-center.icon-link.relative
{:on-click open-filter!
:title "Filter by keystroke"}
(ui/icon "keyboard")
(when-not (string/blank? keystroke)
(ui/point "bg-red-600.absolute" 4 {:style {:right -2 :top -2}}))])
(when (string/blank? q)
[:div.flex.items-center.gap-2
[:a.flex.items-center.icon-link
{:on-click toggle-categories-fn
:title "Toggle categories pane"}
(ui/icon "fold")]
;; category filter
(ui/dropdown-with-links
(fn [{:keys [toggle-fn]}]
[:a.flex.items-center.icon-link.relative
{:on-click toggle-fn
:title "Filter by status"}
(ui/icon "filter")
(when (seq filters)
(ui/point "bg-red-600.absolute" 4 {:style {:right -2 :top -2}}))])
(for [k [:All :Disabled :Unset :Custom]
:let [all? (= k :All)
checked? (or (contains? filters k) (and all? (nil? (seq filters))))]]
{:title (if all? (t :keymap/all) (t (keyword :keymap (string/lower-case (name k)))))
:icon (ui/icon (if checked? "checkbox" "square"))
:options {:on-click #(set-filters! (if all? #{} (let [f (if checked? disj conj)] (f filters k))))}})
nil)]))
[:a.flex.items-center.icon-link
{:on-click refresh-shortcuts-list!
:title "Refresh all"}
(ui/icon "refresh")]])]]))
(rum/defc shortcut-desc-label
[id binding-map]
@@ -759,27 +770,55 @@
(if (= :recording rec-state) "Cancel " "Close ")
(shui/shortcut "escape" {:style :compact})]]]]))
(defn- classify-shortcut
"Return a set of category keywords (:Custom, :Disabled, :Unset) for a shortcut."
[{:keys [binding user-binding]}]
(let [binding (to-vector binding)
user-binding (and user-binding (to-vector user-binding))
custom? (not (nil? user-binding))
disabled? (or (false? user-binding)
(false? (first binding)))
unset? (and (not disabled?)
(or (= user-binding [])
(and (nil? binding) (nil? user-binding))
(and (= binding [])
(nil? user-binding))))]
(cond-> #{}
custom? (conj :Custom)
disabled? (conj :Disabled)
unset? (conj :Unset))))
(defn- count-shortcuts-by-filter
"Count shortcuts per filter category in result-list-map.
Returns {:All n :Custom n :Unset n :Disabled n}."
[result-list-map]
(let [all-bindings (mapcat (fn [[_c bm]] (vals bm)) result-list-map)]
(reduce (fn [acc m]
(let [cats (classify-shortcut m)]
(-> acc
(update :All inc)
(cond->
(contains? cats :Custom) (update :Custom inc)
(contains? cats :Disabled) (update :Disabled inc)
(contains? cats :Unset) (update :Unset inc)))))
{:All 0 :Custom 0 :Unset 0 :Disabled 0}
all-bindings)))
(defn- count-visible-shortcuts
"Count shortcuts visible after applying category filters and keystroke filter."
[result-list-map filters in-keystroke? keystroke]
"Count shortcuts visible after applying category filter and keystroke filter."
[result-list-map filter-key in-keystroke? keystroke]
(->> result-list-map
(mapcat
(fn [[_c binding-map]]
(for [[id {:keys [binding user-binding]}] binding-map
:let [binding (to-vector binding)
user-binding (and user-binding (to-vector user-binding))
custom? (not (nil? user-binding))
disabled? (or (false? user-binding)
(false? (first binding)))
unset? (and (not disabled?)
(or (= user-binding [])
(and (nil? binding) (nil? user-binding))
(and (= binding [])
(nil? user-binding))))]
:when (or (nil? (seq filters))
(when (contains? filters :Custom) custom?)
(when (contains? filters :Disabled) disabled?)
(when (contains? filters :Unset) unset?))
(for [[id m] binding-map
:let [cats (classify-shortcut m)
binding (to-vector (:binding m))
user-binding (and (:user-binding m) (to-vector (:user-binding m)))
disabled? (contains? cats :Disabled)
unset? (contains? cats :Unset)]
:when (or (= filter-key :All)
(nil? filter-key)
(contains? cats filter-key))
:when (or (not in-keystroke?)
(and (not disabled?) (not unset?)
(let [binding' (or user-binding binding)
@@ -803,13 +842,13 @@
_ (r/use-atom shortcut-config/*category)
_ (r/use-atom *refresh-sentry)
[ready?, set-ready!] (rum/use-state false)
[filters, set-filters!] (rum/use-state #{})
[filter-key, set-filter-key!] (rum/use-state nil)
[keystroke, set-keystroke!] (rum/use-state "")
[q set-q!] (rum/use-state nil)
categories-list-map (build-categories-map)
all-categories (into #{} (map first categories-list-map))
in-filters? (boolean (seq filters))
in-filter? (some? filter-key)
in-query? (not (string/blank? (util/trim-safe q)))
in-keystroke? (not (string/blank? keystroke))
@@ -830,23 +869,11 @@
(set-folded-categories! #{})
(set-folded-categories! all-categories))
total-count (apply + (map #(count (second %)) categories-list-map))
visible-count (if (or in-filters? in-keystroke?)
(count-visible-shortcuts result-list-map filters in-keystroke? keystroke)
pill-counts (count-shortcuts-by-filter result-list-map)
visible-count (if (or in-filter? in-keystroke?)
(count-visible-shortcuts result-list-map filter-key in-keystroke? keystroke)
(apply + (map #(count (second %)) result-list-map)))
shortcuts-word (fn [n] (if (= n 1) "shortcut" "shortcuts"))
filter-qualifier (cond
(and in-filters? (contains? filters :Custom)) "custom "
(and in-filters? (contains? filters :Disabled)) "disabled "
(and in-filters? (contains? filters :Unset)) "unset "
:else "")
status-text (cond
(not ready?) "..."
(zero? visible-count) "No matching shortcuts"
(or in-query? in-keystroke? in-filters?)
(str visible-count " " filter-qualifier (shortcuts-word visible-count))
:else
(str total-count " " (shortcuts-word total-count)))]
no-results? (and ready? (zero? visible-count))]
(hooks/use-effect!
(fn []
@@ -868,21 +895,25 @@
(let [h (.-offsetHeight header)]
(.setProperty (.-style el) "--shortcut-header-h" (str h "px"))))))}
[:header
(pane-controls q set-q! filters set-filters! keystroke set-keystroke! toggle-categories!)
[:div.shortcut-status-line status-text]]
(pane-controls q set-q! filter-key set-filter-key! keystroke set-keystroke! toggle-categories! pill-counts)]
[:article
(when-not ready?
[:p.py-8.flex.justify-center (ui/loading "")])
(when ready?
(when (and ready? no-results?)
[:div.shortcut-empty-state
(ui/icon "search" {:size 24})
[:span "No matching shortcuts"]])
(when (and ready? (not no-results?))
[:ul.list-none.m-0.py-3
(for [[c binding-map] result-list-map
:let [folded? (contains? folded-categories c)]]
[:<>
;; category row
(when (and (not in-query?)
(not in-filters?)
(not in-filter?)
(not in-keystroke?))
[:li.flex.justify-between.th
{:key (str c)
@@ -892,25 +923,19 @@
[:i.flex.items-center
(ui/icon (if folded? "chevron-left" "chevron-down"))]])
;; binding row
(when (or in-query? in-filters? (not folded?))
;; binding rows
(when (or in-query? in-filter? (not folded?))
(for [[id {:keys [binding user-binding] :as m}] binding-map
:let [binding (to-vector binding)
user-binding (and user-binding (to-vector user-binding))
label (shortcut-desc-label id m)
custom? (not (nil? user-binding))
disabled? (or (false? user-binding)
(false? (first binding)))
unset? (and (not disabled?)
(or (= user-binding [])
(and (nil? binding) (nil? user-binding))
(and (= binding [])
(nil? user-binding))))]]
cats (classify-shortcut m)
custom? (contains? cats :Custom)
disabled? (contains? cats :Disabled)
unset? (contains? cats :Unset)]]
(when (or (nil? (seq filters))
(when (contains? filters :Custom) custom?)
(when (contains? filters :Disabled) disabled?)
(when (contains? filters :Unset) unset?))
(when (or (nil? filter-key)
(contains? cats filter-key))
;; keystrokes filter
(when (or (not in-keystroke?)

View File

@@ -17,7 +17,11 @@
@apply relative;
&-pane-controls {
@apply flex gap-3 items-center;
@apply flex flex-col gap-2;
.shortcut-toolbar-row {
@apply flex gap-3 items-center;
}
.search-input-wrap {
@apply relative flex-1 min-w-0;
@@ -30,18 +34,23 @@
input.form-input {
@apply w-full pl-7 text-sm mt-0;
border-radius: 6px;
&:focus {
@apply outline-none border-gray-04 ring-2 ring-ring ring-offset-2;
--tw-ring-offset-color: var(--ls-primary-background-color, hsl(var(--background)));
--tw-ring-offset-color: var(--lx-gray-01, var(--ls-primary-background-color, var(--rx-gray-01)));
}
}
a.x {
@apply flex items-center absolute right-1 px-1 opacity-60
hover:opacity-90;
@apply flex items-center absolute right-1 px-1 cursor-pointer;
top: 50%;
transform: translateY(-50%);
color: var(--lx-gray-09, var(--rx-gray-09));
&:hover {
color: var(--lx-gray-12, var(--rx-gray-12));
}
}
}
@@ -50,22 +59,115 @@
}
a.icon-link {
@apply opacity-80 hover:opacity-100 active:opacity-40 select-none;
@apply hover:opacity-80 active:opacity-40 select-none;
color: var(--ls-secondary-text-color);
color: var(--lx-gray-09, var(--color-level-6, var(--rx-gray-09)));
}
.shortcut-pills-row {
@apply flex items-center justify-between gap-2;
}
.shortcut-filter-pills {
@apply flex items-center gap-1 flex-wrap;
}
.shortcut-filter-pill {
@apply text-xs px-2 py-0.5 rounded-full cursor-pointer select-none
border;
color: var(--lx-gray-09, var(--rx-gray-09));
background-color: transparent;
border-color: var(--lx-gray-06, var(--ls-quaternary-background-color, var(--rx-gray-06)));
transition: all 100ms ease;
&:hover {
background-color: var(--lx-gray-05-alpha, var(--rx-gray-05-alpha));
color: var(--lx-gray-12, var(--rx-gray-12));
}
&-title {
font-weight: 400;
}
&-count {
font-weight: 400;
opacity: 0.6;
}
&--active {
background-color: var(--lx-gray-06-alpha, var(--rx-gray-06-alpha));
border-color: transparent;
color: var(--lx-gray-12, var(--rx-gray-12));
.shortcut-filter-pill-title {
font-weight: 500;
}
}
}
.shortcut-keystroke-inactive,
.shortcut-keystroke-active {
@apply flex items-center gap-1.5 text-sm cursor-pointer
select-none flex-shrink-0;
height: 30px;
padding: 0 10px;
min-width: 140px;
border-radius: 6px;
border: 1px solid var(--lx-gray-06, var(--ls-quaternary-background-color, var(--rx-gray-06)));
background-color: var(--lx-gray-02, var(--ls-secondary-background-color, var(--rx-gray-02)));
color: var(--lx-gray-12, var(--rx-gray-12));
transition: background-color 150ms ease;
outline: none;
&:hover {
background-color: var(--lx-gray-04, var(--ls-quaternary-background-color, var(--rx-gray-04)));
}
&:focus-visible {
@apply ring-2 ring-ring ring-offset-2;
--tw-ring-offset-color: var(--lx-gray-01, var(--ls-primary-background-color, var(--rx-gray-01)));
}
}
.shortcut-keystroke-active {
max-width: 50%;
.shui-shortcut-key {
animation: shortcut-badge-in 150ms ease-out;
}
}
.shortcut-keystroke-keys {
@apply flex items-center gap-1.5;
flex: 1;
min-width: 0;
overflow: hidden;
mask-image: linear-gradient(to right, black 80%, transparent);
-webkit-mask-image: linear-gradient(to right, black 80%, transparent);
}
.shortcut-keystroke-clear {
@apply flex items-center cursor-pointer;
color: var(--lx-gray-09, var(--rx-gray-09));
margin-left: auto;
&:hover {
color: var(--lx-gray-12, var(--rx-gray-12));
}
}
}
> header {
@apply px-4 pb-2 flex flex-col gap-2 sticky top-0 z-10;
@apply px-4 flex flex-col gap-2 sticky top-0 z-10;
padding-top: 20px;
padding-bottom: 20px;
background-color: hsl(var(--background));
}
.shortcut-status-line {
@apply text-xs select-none;
color: var(--rx-gray-09, var(--ls-secondary-text-color));
.shortcut-empty-state {
@apply flex flex-col items-center justify-center gap-2 py-16 select-none;
color: var(--lx-gray-09, var(--rx-gray-09));
}
> article {
@@ -79,10 +181,10 @@
&.shortcut-row {
@apply rounded-md cursor-pointer select-none py-1 px-2 -mx-1;
transition: background-color 100ms ease, color 100ms ease;
transition: background-color 100ms ease, opacity 100ms ease;
&:hover {
background-color: var(--rx-gray-04, rgba(255, 255, 255, 0.06));
background-color: var(--lx-gray-04-alpha, var(--rx-gray-04-alpha));
}
&:active {
@@ -91,7 +193,7 @@
/* Active row (popover open) — stronger than hover so it stays visible */
&.active {
background-color: var(--rx-gray-05, rgba(255, 255, 255, 0.1));
background-color: var(--lx-gray-05-alpha, var(--rx-gray-05-alpha));
}
}
@@ -100,7 +202,7 @@
select-none active:opacity-80 px-2 py-1 z-[1];
top: var(--shortcut-header-h, 0px);
background-color: var(--ls-tertiary-background-color);
background-color: var(--lx-gray-03, var(--ls-tertiary-background-color, var(--rx-gray-03)));
}
.label-wrap {
@@ -135,15 +237,15 @@
/* CSS-only hover dimming: dim all rows except hovered */
&:hover li.shortcut-row:not(:hover):not(.active) {
color: var(--rx-gray-11);
opacity: 0.5;
}
/* When popover is open: dim non-active rows, but restore on hover */
&:has(.active) li.shortcut-row:not(.active) {
color: var(--rx-gray-11);
opacity: 0.5;
&:hover {
color: var(--rx-gray-12);
opacity: 0.85;
}
}
}
@@ -179,7 +281,7 @@
@apply px-4 font-medium text-xs select-none;
padding-top: 16px;
padding-bottom: 2px;
color: var(--rx-gray-11, var(--ls-secondary-text-color));
color: var(--lx-gray-11, var(--rx-gray-11));
}
/* Input field — borderless, content sits directly on popover surface */
@@ -214,15 +316,15 @@
/* Each binding grouped in a subtle container */
.shortcut-input-binding {
@apply inline-flex items-center flex-wrap rounded-md p-1;
background-color: var(--rx-gray-04-alpha, rgba(0, 0, 0, 0.07));
background-color: var(--lx-gray-04-alpha, var(--rx-gray-04-alpha));
max-width: 100%;
.shortcut-binding-remove {
@apply flex items-center ml-1 cursor-pointer select-none;
color: var(--rx-gray-10);
color: var(--lx-gray-10, var(--rx-gray-10));
&:hover {
color: var(--rx-gray-12);
color: var(--lx-gray-12, var(--rx-gray-12));
}
}
}
@@ -274,8 +376,8 @@
background-color: var(--rx-amber-03-alpha, hsla(44, 100%, 50%, 0.1));
}
&--muted {
color: var(--rx-gray-09, var(--ls-secondary-text-color));
background-color: var(--rx-gray-03-alpha, rgba(0, 0, 0, 0.04));
color: var(--lx-gray-09, var(--rx-gray-09));
background-color: var(--lx-gray-03-alpha, var(--rx-gray-03-alpha));
}
}
@@ -298,7 +400,7 @@
&:hover { text-decoration: underline; }
.shortcut-feedback--muted & {
color: var(--rx-gray-11, var(--ls-secondary-text-color));
color: var(--lx-gray-11, var(--rx-gray-11));
}
.shortcut-feedback--success & {
@@ -309,13 +411,13 @@
/* Toolbar */
.shortcut-toolbar {
@apply flex items-center justify-between px-4 py-1.5 text-xs select-none;
color: var(--rx-gray-08, rgba(0, 0, 0, 0.4));
color: var(--lx-gray-08, var(--rx-gray-08));
margin-top: auto;
}
.shortcut-toolbar-action {
@apply cursor-pointer flex items-center gap-1;
&:hover { color: var(--rx-gray-12, var(--ls-primary-text-color)); }
&:hover { color: var(--lx-gray-12, var(--rx-gray-12)); }
}
.shortcut-toolbar-hint {
@@ -324,6 +426,11 @@
}
/* Animations */
@keyframes shortcut-badge-in {
from { opacity: 0; transform: scale(0.85); }
to { opacity: 1; transform: scale(1); }
}
@keyframes shortcut-fade-in {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
@@ -376,7 +483,7 @@
.sidebar-item .cp__shortcut-page-x {
padding: 12px 0 0 0;
background-color: var(--color-level-2);
background-color: var(--color-level-2, var(--lx-gray-02, var(--rx-gray-02)));
}
.sidebar-item article {