mirror of
https://github.com/logseq/logseq.git
synced 2026-05-19 10:22:37 +00:00
- Row highlight on shortcut trigger now uses a horizontal gradient sweep (background-position animation) instead of a static background flash, providing a distinct visual language from the focus ring - Shimmer uses theme-aware accent color via color-mix on keymap page, with a neutral fallback in shui.css base styles - Guard against animation spam: clearTimeout+reset pattern prevents stale timeout accumulation during rapid key repeat; reflow only forced on first trigger, class stays applied until last trigger ends - Fix combo key press animation: animate the container (the keycap) instead of individual kbd elements, so translateY and box-shadow follow the container's border-radius correctly - Scope row shimmer to .shui-shortcut-row/.shortcut-row elements to prevent accidental shimmer on standalone badge containers - Respect prefers-reduced-motion for all new animations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
582 lines
14 KiB
CSS
582 lines
14 KiB
CSS
.cp__shortcut {
|
|
&-table-wrap {
|
|
@apply relative;
|
|
|
|
a.fold {
|
|
@apply absolute right-0 top-0 w-full pt-3 pr-3
|
|
pb-3 flex items-center justify-end select-none;
|
|
|
|
&:active {
|
|
@apply bg-white/50 opacity-60;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Reset button styles for interactive elements changed from <a> to <button> */
|
|
.cp__shortcut-page-x button.shortcut-binding-remove,
|
|
.cp__shortcut-page-x button.shortcut-toolbar-action,
|
|
.cp__shortcut-page-x button.shortcut-keystroke-clear,
|
|
.cp__shortcut-page-x button.icon-link,
|
|
.cp__shortcut-page-x button.x,
|
|
.shortcut-popover button.shortcut-binding-remove,
|
|
.shortcut-popover button.shortcut-toolbar-action,
|
|
.shortcut-filter-popover button.shortcut-binding-remove,
|
|
.shortcut-filter-popover button.shortcut-toolbar-action,
|
|
button.shortcut-feedback-action {
|
|
all: unset;
|
|
cursor: pointer;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.cp__shortcut-page-x {
|
|
@apply relative w-full max-w-full;
|
|
|
|
&-pane-controls {
|
|
@apply flex flex-col gap-2;
|
|
|
|
.shortcut-toolbar-row {
|
|
@apply flex flex-wrap gap-3 items-center;
|
|
}
|
|
|
|
.search-input-wrap {
|
|
@apply relative flex-1 min-w-0;
|
|
|
|
.search-icon {
|
|
@apply absolute left-2 flex items-center opacity-40 pointer-events-none;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
}
|
|
|
|
input.form-input {
|
|
@apply w-full pl-7 pr-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(--lx-gray-01, var(--ls-primary-background-color, var(--rx-gray-01)));
|
|
}
|
|
}
|
|
|
|
.x {
|
|
@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));
|
|
}
|
|
}
|
|
}
|
|
|
|
input.form-input {
|
|
@apply py-1;
|
|
}
|
|
|
|
a.icon-link {
|
|
@apply hover:opacity-80 active:opacity-40 select-none;
|
|
|
|
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;
|
|
height: 30px;
|
|
padding: 0 10px;
|
|
min-width: 9rem;
|
|
border-radius: 6px;
|
|
border: 1px solid var(--lx-gray-07, var(--ls-quaternary-background-color, var(--rx-gray-07)));
|
|
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 flex flex-col gap-2 sticky top-0 z-10;
|
|
padding-top: 20px;
|
|
padding-bottom: 20px;
|
|
background-color: hsl(var(--background));
|
|
}
|
|
|
|
.shortcut-empty-state {
|
|
@apply flex flex-col items-center justify-center gap-2 select-none;
|
|
min-height: calc(60dvh - var(--shortcut-header-h, 120px));
|
|
color: var(--lx-gray-09, var(--rx-gray-09));
|
|
}
|
|
|
|
> article {
|
|
@apply relative pb-4 w-full;
|
|
|
|
> ul {
|
|
@apply px-4 m-0 py-0;
|
|
|
|
li {
|
|
@apply text-[15px] px-1;
|
|
|
|
&.shortcut-row {
|
|
@apply rounded-md cursor-pointer select-none py-1 px-2 -mx-1 flex-wrap min-w-0;
|
|
transition: background-color 100ms ease, opacity 100ms ease, box-shadow 100ms ease;
|
|
|
|
&:hover {
|
|
background-color: var(--lx-gray-04-alpha, var(--rx-gray-04-alpha));
|
|
}
|
|
|
|
&:active {
|
|
@apply opacity-80;
|
|
}
|
|
|
|
/* Active row (popover open) — stronger than hover so it stays visible */
|
|
&.active {
|
|
background-color: var(--lx-gray-05-alpha, var(--rx-gray-05-alpha));
|
|
}
|
|
|
|
/* Shortcut keypress shimmer — accent band sweeps left-to-right across the row */
|
|
&.shui-shortcut-row--pressed {
|
|
background-image: linear-gradient(
|
|
90deg,
|
|
transparent 0%,
|
|
transparent 35%,
|
|
color-mix(in srgb, var(--lx-accent-09, hsl(var(--primary))) 10%, transparent) 50%,
|
|
transparent 65%,
|
|
transparent 100%
|
|
);
|
|
background-size: 300% 100%;
|
|
background-repeat: no-repeat;
|
|
animation: shortcut-row-sweep 700ms ease-out forwards;
|
|
}
|
|
|
|
/* Keyboard navigation focus ring — two levels above hover for clear distinction */
|
|
&:focus-visible {
|
|
background-color: var(--lx-gray-06-alpha, var(--rx-gray-06-alpha));
|
|
box-shadow: inset 0 0 0 2px var(--lx-accent-09, hsl(var(--primary)));
|
|
outline: none;
|
|
}
|
|
}
|
|
|
|
&.th {
|
|
@apply rounded mb-2 sticky cursor-pointer
|
|
select-none active:opacity-80 px-2 py-1 z-[1];
|
|
top: var(--shortcut-header-h, 0px);
|
|
outline: none;
|
|
|
|
&:focus-visible {
|
|
box-shadow: inset 0 0 0 2px var(--lx-accent-09, hsl(var(--primary)));
|
|
}
|
|
|
|
background-color: var(--lx-gray-03, var(--ls-tertiary-background-color, var(--rx-gray-03)));
|
|
}
|
|
|
|
.label-wrap {
|
|
@apply flex flex-1;
|
|
min-width: 60px;
|
|
}
|
|
|
|
.action-wrap {
|
|
@apply flex items-center flex-wrap justify-end
|
|
select-none;
|
|
column-gap: 16px;
|
|
row-gap: 6px;
|
|
max-width: 55%;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
|
|
&.disabled {
|
|
@apply opacity-60 cursor-default;
|
|
}
|
|
|
|
.shortcut-status-label {
|
|
@apply text-xs whitespace-nowrap;
|
|
opacity: 0.5;
|
|
padding-top: 2px;
|
|
|
|
&:not(:only-child) {
|
|
margin-right: -8px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* CSS-only hover dimming: dim all rows except hovered, active, or keyboard-focused */
|
|
&:hover li.shortcut-row:not(:hover):not(.active):not(:focus-visible) {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* When popover is open: dim non-active rows, but restore on hover or keyboard focus */
|
|
&:has(.active) li.shortcut-row:not(.active):not(:focus-visible) {
|
|
opacity: 0.5;
|
|
|
|
&:hover {
|
|
opacity: 0.85;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
@keyframes shortcut-row-sweep {
|
|
from { background-position: -50% 0; }
|
|
to { background-position: 150% 0; }
|
|
}
|
|
|
|
/* === V3 SHORTCUT POPOVER === */
|
|
.shortcut-popover {
|
|
@apply flex flex-col;
|
|
width: 340px;
|
|
|
|
&:focus, &:focus-within { outline: none; }
|
|
|
|
> [data-orientation] {
|
|
@apply mt-0;
|
|
}
|
|
}
|
|
|
|
/* === KEYSTROKE FILTER POPOVER === */
|
|
.shortcut-filter-popover {
|
|
@apply flex flex-col;
|
|
width: 288px;
|
|
|
|
&:focus, &:focus-within { outline: none; }
|
|
|
|
> [data-orientation] {
|
|
@apply mt-0;
|
|
}
|
|
}
|
|
|
|
.shortcut-popover-title {
|
|
@apply px-4 font-medium text-xs select-none;
|
|
padding-top: 16px;
|
|
padding-bottom: 2px;
|
|
color: var(--lx-gray-11, var(--rx-gray-11));
|
|
}
|
|
|
|
/* Input field — borderless, content sits directly on popover surface */
|
|
.shortcut-input-field {
|
|
@apply px-4 pt-2 pb-3 flex flex-wrap items-center;
|
|
column-gap: 12px;
|
|
row-gap: 6px;
|
|
min-height: 40px;
|
|
}
|
|
|
|
.shortcut-input-placeholder {
|
|
@apply text-sm select-none;
|
|
color: var(--lx-gray-11, var(--rx-gray-11));
|
|
opacity: 0.75;
|
|
background: linear-gradient(
|
|
90deg,
|
|
currentColor 0%,
|
|
currentColor 40%,
|
|
var(--lx-gray-12, var(--rx-gray-12)) 50%,
|
|
currentColor 60%,
|
|
currentColor 100%
|
|
);
|
|
background-size: 250% 100%;
|
|
background-position: 100% 0;
|
|
-webkit-background-clip: text;
|
|
background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
animation: shortcut-shimmer 4s ease-in-out infinite;
|
|
animation-delay: 1s;
|
|
}
|
|
|
|
/* Each binding grouped in a subtle container */
|
|
.shortcut-input-binding {
|
|
@apply inline-flex items-center flex-wrap rounded-md p-1;
|
|
gap: 4px;
|
|
background-color: var(--lx-gray-04-alpha, var(--rx-gray-04-alpha));
|
|
max-width: 100%;
|
|
|
|
.shortcut-binding-remove {
|
|
@apply flex items-center cursor-pointer select-none;
|
|
color: var(--lx-gray-10, var(--rx-gray-10));
|
|
|
|
&:hover {
|
|
color: var(--lx-gray-12, var(--rx-gray-12));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Allow long separate-key sequences to wrap inside the binding */
|
|
.shortcut-input-binding .shui-shortcut-separate {
|
|
flex-wrap: wrap;
|
|
white-space: normal;
|
|
}
|
|
|
|
/* Uncommitted binding: dashed key borders */
|
|
.shortcut-input-binding--pending .shui-shortcut-key {
|
|
border-style: dashed;
|
|
}
|
|
|
|
/* Conflict state: tint the binding container red, keys stay normal */
|
|
.shortcut-input-field.conflict .shortcut-input-binding--pending {
|
|
background-color: var(--rx-red-06-alpha, rgb(239 68 68 / 0.2));
|
|
|
|
.shortcut-binding-remove {
|
|
color: var(--rx-red-11);
|
|
|
|
&:hover {
|
|
color: var(--rx-red-12);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Keep combo overflow hidden in conflict */
|
|
.shortcut-input-field.conflict .shortcut-input-binding--pending .shui-shortcut-combo {
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Feedback banner */
|
|
.shortcut-feedback {
|
|
@apply flex items-center justify-between gap-2 px-4 py-2 text-xs;
|
|
animation: shortcut-fade-in 200ms ease-out;
|
|
|
|
&--error {
|
|
color: var(--rx-red-11, #dc2626);
|
|
background-color: var(--rx-red-03-alpha, rgb(239 68 68 / 0.08));
|
|
}
|
|
&--success {
|
|
color: var(--rx-green-11, #16a34a);
|
|
background-color: var(--rx-green-03-alpha, rgb(34 197 94 / 0.08));
|
|
}
|
|
&--warning {
|
|
color: var(--rx-amber-11, #b45309);
|
|
background-color: var(--rx-amber-03-alpha, hsla(44, 100%, 50%, 0.1));
|
|
}
|
|
&--muted {
|
|
color: var(--lx-gray-09, var(--rx-gray-09));
|
|
background-color: var(--lx-gray-03-alpha, var(--rx-gray-03-alpha));
|
|
}
|
|
}
|
|
|
|
.shortcut-feedback-name {
|
|
@apply font-medium;
|
|
color: var(--rx-red-12, #991b1b);
|
|
|
|
.shortcut-feedback--success & {
|
|
color: var(--rx-green-12, #166534);
|
|
}
|
|
|
|
.shortcut-feedback--warning & {
|
|
color: var(--rx-amber-12, #451a03);
|
|
}
|
|
}
|
|
|
|
.shortcut-feedback-action {
|
|
@apply cursor-pointer font-medium whitespace-nowrap;
|
|
color: var(--rx-red-11, #dc2626);
|
|
&:hover { text-decoration: underline; }
|
|
|
|
.shortcut-feedback--muted & {
|
|
color: var(--lx-gray-11, var(--rx-gray-11));
|
|
}
|
|
|
|
.shortcut-feedback--success & {
|
|
color: var(--rx-green-11, #16a34a);
|
|
}
|
|
}
|
|
|
|
/* Toolbar */
|
|
.shortcut-toolbar {
|
|
@apply flex items-center justify-between px-4 py-1.5 text-xs select-none;
|
|
color: var(--lx-gray-08, var(--rx-gray-08));
|
|
margin-top: auto;
|
|
}
|
|
|
|
.shortcut-toolbar button.shortcut-toolbar-action {
|
|
gap: 4px;
|
|
&:hover {
|
|
color: var(--lx-gray-12, var(--rx-gray-12));
|
|
text-decoration: underline;
|
|
}
|
|
}
|
|
|
|
.shortcut-toolbar button.shortcut-toolbar-reset {
|
|
color: var(--lx-accent-11, var(--ls-link-text-color, hsl(var(--primary) / 0.8)));
|
|
&:hover {
|
|
color: var(--lx-accent-12, var(--ls-link-text-hover-color, hsl(var(--primary))));
|
|
}
|
|
}
|
|
|
|
.shortcut-toolbar-hint {
|
|
@apply ml-3;
|
|
color: var(--lx-gray-11, var(--rx-gray-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); }
|
|
}
|
|
|
|
@keyframes shortcut-fade-out {
|
|
from { opacity: 1; max-height: 40px; padding-top: 8px; padding-bottom: 8px; }
|
|
to { opacity: 0; max-height: 0; padding-top: 0; padding-bottom: 0; }
|
|
}
|
|
|
|
.shortcut-feedback.is-dismissing {
|
|
animation: shortcut-fade-out 200ms ease-in forwards;
|
|
overflow: hidden;
|
|
}
|
|
|
|
@keyframes shortcut-auto-fade {
|
|
0% { opacity: 1; }
|
|
70% { opacity: 1; }
|
|
100% { opacity: 0.3; }
|
|
}
|
|
|
|
@keyframes shortcut-shimmer {
|
|
0%, 30% { background-position: 100% 0; }
|
|
70%, 100% { background-position: -50% 0; }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
@keyframes shortcut-badge-in {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
|
|
@keyframes shortcut-fade-in {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
|
|
@keyframes shortcut-fade-out {
|
|
from { opacity: 1; }
|
|
to { opacity: 0; }
|
|
}
|
|
|
|
@keyframes shortcut-auto-fade {
|
|
from { opacity: 1; }
|
|
to { opacity: 0.3; }
|
|
}
|
|
|
|
.shortcut-input-placeholder {
|
|
animation: none;
|
|
-webkit-text-fill-color: unset;
|
|
background: none;
|
|
}
|
|
}
|
|
|
|
.cp__shortcut-conflicts-list {
|
|
&-wrap {
|
|
> section {
|
|
@apply bg-gray-03 border-[2px] mb-3 dark:bg-transparent;
|
|
|
|
> ul {
|
|
@apply px-2 pb-2 m-0 list-none;
|
|
}
|
|
|
|
> h2 {
|
|
@apply flex items-center p-2 text-red-600 text-sm space-x-1 font-extrabold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.sidebar-item .cp__shortcut-page-x {
|
|
padding: 12px 0 0 0;
|
|
background-color: var(--color-level-2, var(--lx-gray-02, var(--rx-gray-02)));
|
|
}
|
|
|
|
.sidebar-item article {
|
|
max-height: unset;
|
|
}
|
|
|
|
.keyboard-shortcut {
|
|
@apply inline-flex;
|
|
|
|
.ui__button {
|
|
@apply cursor-default;
|
|
}
|
|
}
|