Files
Will Miaoandsmhc 8269cc7b66 feat(autocomplete): turn on listed LoRAs when picked from the dropdown
The "already added" cue was a single check, so a listed-but-disabled
LoRA looked identical to an enabled one, and picking either inserted a
duplicate tag that left the disabled entry off.

Show two states — a green "Added" pill and an amber "Off" pill — and
make the pick act on them: an added LoRA is enabled in place (reusing
the shared add-or-enable helper), an active one is left alone, and the
typed query is removed either way so no duplicate tag or stray search
text is left in the prompt.

Co-authored-by: smhc <6404304+smhc@users.noreply.github.com>
2026-11-07 20:20:17 +08:00

1055 lines
25 KiB
CSS

/* Shared styling for the LoRA Manager frontend widgets */
.lm-tooltip {
position: fixed;
z-index: 10001;
background: rgba(0, 0, 0, 0.85);
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
display: none;
overflow: hidden;
max-width: 300px;
pointer-events: none;
transition: opacity 0.15s ease;
}
.lm-tooltip__media-container {
position: relative;
max-width: 300px;
max-height: 300px;
}
.lm-tooltip__media {
max-width: 100%;
max-height: 300px;
object-fit: contain;
display: block;
}
.lm-tooltip__label {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 8px;
color: white;
font-size: 13px;
font-family: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
text-align: center;
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
line-height: 1.4;
overflow-wrap: break-word;
word-break: break-word;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.lm-tooltip__license-overlay {
position: absolute;
top: 8px;
left: 8px;
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 4px 8px;
border-radius: 999px;
background: rgba(10, 10, 14, 0.78);
border: 1px solid rgba(255, 255, 255, 0.12);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.45);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
max-width: calc(100% - 16px);
}
.lm-tooltip__license-icon {
width: 18px;
height: 18px;
display: inline-block;
background-color: rgba(255, 255, 255, 0.9);
-webkit-mask: var(--license-icon-image) center/contain no-repeat;
mask: var(--license-icon-image) center/contain no-repeat;
}
/* Set 2 — new style license overlay */
.lm-tooltip__license-overlay-new {
position: absolute;
top: 8px;
left: 8px;
display: flex;
flex-wrap: wrap;
gap: 4px;
padding: 5px 8px;
border-radius: 999px;
background: rgba(10, 10, 14, 0.78);
border: 1px solid rgba(255, 255, 255, 0.12);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
max-width: calc(100% - 16px);
}
.lm-tooltip__license-icon-new {
width: 16px;
height: 16px;
display: inline-block;
border-radius: 3px;
-webkit-mask: var(--license-icon-image) center/contain no-repeat;
mask: var(--license-icon-image) center/contain no-repeat;
}
.lm-tooltip__license-icon-new.allowed {
background-color: #40c057;
}
.lm-tooltip__license-icon-new.denied {
background-color: #fa5252;
}
.lm-loras-container {
display: flex;
flex-direction: column;
gap: 5px;
padding: 6px;
background-color: var(--lm-surface);
border-radius: 6px;
width: 100%;
box-sizing: border-box;
overflow: auto;
outline: none;
}
/* Vue node mode: prevent content from pushing node size via ResizeObserver.
contain:size breaks the feedback loop — the container's intrinsic size
is determined solely by CSS, not by how many LoRAs are inside. */
.lm-loras-container.lm-vue-node {
height: 100%;
min-height: var(--comfy-widget-min-height, 200px);
contain: layout size;
}
.lm-loras-container:focus {
outline: none;
}
/* Trigger-word widget container (tags_widget.js) */
.comfy-tags-container {
background-color: var(--lm-surface);
}
/* Vue node mode: prevent content from pushing node size via ResizeObserver.
Same technique as .lm-loras-container.lm-vue-node above. */
.comfy-tags-container.lm-vue-node {
height: 100%;
min-height: var(--comfy-widget-min-height, 150px);
contain: layout size;
}
/* Empty-state copy. The font size must be explicit: in canvas mode these
elements inherit ComfyUI's body size (16px) instead of the smaller widget
size used by the Vue node renderer, which made the placeholder look bold
and oversized — most visibly on light palettes. */
.lm-lora-empty-state {
text-align: center;
padding: 16px 0;
color: var(--lm-fg-subtle);
font-size: 12px;
font-style: italic;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
width: 100%;
}
.lm-loras-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4px 8px;
border-bottom: 1px solid var(--lm-border-soft);
margin-bottom: 5px;
position: relative;
cursor: ew-resize;
touch-action: none;
}
.lm-toggle-container {
display: flex;
align-items: center;
}
.lm-toggle-label,
.lm-strength-label {
color: var(--lm-fg-muted);
font-size: 13px;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
.lm-toggle-label {
margin-left: 8px;
}
.lm-strength-label {
margin-right: 8px;
display: flex;
align-items: center;
}
.lm-drag-hint {
margin-left: 5px;
font-size: 11px;
opacity: 0.6;
transition: opacity 0.2s ease;
}
.lm-loras-header:hover .lm-drag-hint {
opacity: 1;
}
.lm-lora-entry {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px;
border-radius: 6px;
transition: all 0.2s ease;
margin-bottom: 4px;
border: 1px solid transparent;
background-color: var(--lm-surface-raised);
touch-action: none;
cursor: pointer !important;
}
.lm-lora-entry[data-active="false"] {
background-color: var(--lm-surface);
}
.lm-lora-entry:hover {
background-color: var(--lm-surface-hover);
}
.lm-lora-entry[data-active="false"]:hover {
background-color: var(--lm-surface-hover);
}
.lm-lora-entry[data-selected="true"] {
background-color: var(--lm-surface-selected) !important;
border: 1px solid var(--lm-accent-border) !important;
box-shadow: 0 0 0 1px var(--lm-accent-soft) !important;
}
.lm-lora-entry[data-selected="true"][data-locked="true"] {
background-color: var(--lm-surface-selected) !important;
border-left: 3px solid var(--lm-warning-border) !important;
border-right: 1px solid var(--lm-accent-border) !important;
border-top: 1px solid var(--lm-accent-border) !important;
border-bottom: 1px solid var(--lm-accent-border) !important;
box-shadow: 0 0 0 1px var(--lm-accent-soft), inset 0 0 20px color-mix(in srgb, var(--lm-warning) 6%, transparent) !important;
}
.lm-lora-entry[data-selected="true"][data-locked="true"][data-active="false"] {
background-color: color-mix(in srgb, var(--lm-warning) 10%, var(--lm-surface)) !important;
border-left: 3px solid var(--lm-warning-border) !important;
}
.lm-lora-entry[data-missing="true"] {
background-color: var(--lm-danger-soft);
border: 1px solid var(--lm-danger-border);
}
.lm-lora-entry[data-missing="true"] .lm-lora-name {
color: var(--lm-danger-text);
}
.lm-lora-clip-entry[data-missing="true"] {
background-color: var(--lm-danger-soft);
border: 1px solid var(--lm-danger-border);
border-left: 2px solid var(--lm-danger-border);
}
.lm-lora-clip-entry[data-missing="true"] .lm-lora-name {
color: var(--lm-danger-text);
}
.lm-lora-name {
margin-left: 4px;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--lm-fg);
font-size: 13px;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
.lm-lora-entry[data-active="false"] .lm-lora-name {
color: var(--lm-fg-subtle);
}
.lm-lora-strength-control {
display: flex;
align-items: center;
gap: 8px;
}
.lm-lora-strength-input {
width: 50px;
min-width: 50px;
padding: 2px 4px;
border-radius: 4px;
border: 1px solid var(--lm-border-soft);
background: var(--lm-surface);
color: var(--lm-fg);
text-align: center;
font-size: 13px;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.lm-lora-strength-input:focus {
outline: none;
border-color: var(--lm-accent);
box-shadow: 0 0 0 2px var(--lm-accent-soft);
}
/* Advisory cue: strength is outside the usage-tips recommended range.
Amber to distinguish it from the red "missing LoRA" error state. */
.lm-lora-strength-input.lm-strength-out-of-range {
border-color: var(--lm-warning);
color: var(--lm-warning);
}
.lm-lora-strength-input.lm-strength-out-of-range:focus {
border-color: var(--lm-warning-border);
box-shadow: 0 0 0 2px var(--lm-warning-soft);
}
.lm-lora-strength-buttons {
display: flex;
flex-direction: column;
gap: 2px;
}
.lm-lora-entry[data-active="false"] .lm-lora-strength-input {
color: var(--lm-fg-subtle);
}
.lm-lora-arrow {
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
user-select: none;
color: var(--lm-fg-muted);
transition: all 0.2s ease;
}
.lm-lora-arrow:hover {
color: var(--lm-fg);
transform: scale(1.2);
}
.lm-lora-arrow svg {
width: 12px;
height: 12px;
}
.lm-lora-expand-button {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
user-select: none;
color: var(--lm-fg-muted);
background-color: var(--lm-surface-hover);
border: 1px solid var(--lm-border-soft);
border-radius: 3px;
transition: all 0.2s ease;
flex-shrink: 0;
padding: 0;
box-sizing: border-box;
}
.lm-lora-expand-button:hover {
background-color: var(--lm-accent-soft);
border-color: var(--lm-accent-border);
color: var(--lm-fg);
transform: scale(1.05);
}
.lm-lora-expand-button:active {
transform: scale(0.95);
background-color: var(--lm-accent-soft);
}
.lm-lora-expand-button:focus {
outline: none;
}
.lm-lora-expand-button:focus-visible {
box-shadow: 0 0 0 2px var(--lm-accent-border);
}
.lm-lora-toggle {
width: 18px;
height: 18px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
background-color: var(--lm-surface-raised);
border: 1px solid var(--lm-border-soft);
}
.lm-lora-toggle--active {
background-color: var(--lm-accent);
border-color: var(--lm-accent);
}
.lm-lora-toggle:hover {
transform: scale(1.05);
box-shadow: var(--lm-shadow);
}
.lm-lora-drag-handle {
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
cursor: grab;
user-select: none;
color: var(--lm-fg-subtle);
transition: all 0.2s ease;
margin-right: 8px;
flex-shrink: 0;
}
.lm-lora-drag-handle svg {
width: 14px;
height: 14px;
}
.lm-lora-drag-handle:hover {
color: var(--lm-fg);
transform: scale(1.1);
}
.lm-lora-drag-handle:active {
cursor: grabbing;
}
.lm-lora-lock-button {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
user-select: none;
background-color: var(--lm-surface-hover);
border: 1px solid var(--lm-border-soft);
border-radius: 3px;
transition: all 0.2s ease;
flex-shrink: 0;
padding: 0;
box-sizing: border-box;
margin-right: 8px;
color: var(--lm-fg-muted);
}
.lm-lora-lock-button svg {
width: 14px;
height: 14px;
}
.lm-lora-lock-button:hover {
background-color: var(--lm-accent-soft);
border-color: var(--lm-accent-border);
color: var(--lm-fg);
transform: scale(1.05);
}
.lm-lora-lock-button:active {
transform: scale(0.95);
}
.lm-lora-lock-button:focus {
outline: none;
}
.lm-lora-lock-button:focus-visible {
box-shadow: 0 0 0 2px var(--lm-accent-border);
}
.lm-lora-lock-button--locked {
background-color: var(--lm-warning-soft);
border-color: var(--lm-warning-border);
color: var(--lm-warning);
box-shadow: 0 0 8px var(--lm-warning-soft);
}
.lm-lora-lock-button--locked:hover {
background-color: var(--lm-warning-soft);
border-color: var(--lm-warning-border);
box-shadow: 0 0 12px var(--lm-warning-soft);
}
/* Visual styling for locked lora entries */
.lm-lora-entry[data-locked="true"] {
background-color: color-mix(in srgb, var(--lm-warning) 14%, var(--lm-surface));
border-left: 3px solid var(--lm-warning-border);
box-shadow: inset 0 0 20px color-mix(in srgb, var(--lm-warning) 6%, transparent);
}
.lm-lora-entry[data-locked="true"]:hover {
background-color: color-mix(in srgb, var(--lm-warning) 18%, var(--lm-surface));
box-shadow: inset 0 0 24px color-mix(in srgb, var(--lm-warning) 8%, transparent);
}
.lm-lora-entry[data-locked="true"][data-active="false"] {
background-color: color-mix(in srgb, var(--lm-warning) 10%, var(--lm-surface));
border-left: 3px solid var(--lm-warning-border);
}
.lm-lora-entry[data-locked="true"][data-active="false"]:hover {
background-color: color-mix(in srgb, var(--lm-warning) 12%, var(--lm-surface));
}
body.lm-lora-strength-dragging,
body.lm-lora-strength-dragging * {
cursor: ew-resize !important;
user-select: none !important;
}
body.lm-lora-reordering,
body.lm-lora-reordering * {
cursor: grabbing !important;
user-select: none !important;
}
.lm-lora-entry--dragging {
opacity: 0.5;
transform: scale(0.98);
}
.lm-lora-drop-indicator {
position: absolute;
left: 0;
right: 0;
height: 3px;
background-color: var(--lm-accent);
border-radius: 2px;
opacity: 0;
transition: opacity 0.2s ease;
box-shadow: 0 0 6px var(--lm-accent);
z-index: 10;
pointer-events: none;
}
.lm-lora-strength-wrapper {
display: flex;
align-items: center;
gap: 6px;
}
.lm-lora-entry-left {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
gap: 6px;
}
.lm-lora-clip-entry {
margin-left: 10px;
margin-bottom: 4px;
padding: 6px;
padding-left: 20px;
border-radius: 6px;
background-color: var(--lm-surface-raised);
border: 1px solid var(--lm-accent-soft);
border-left: 2px solid var(--lm-accent-border);
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.2s ease;
margin-top: -2px;
touch-action: none;
cursor: pointer !important;
}
.lm-lora-clip-entry[data-active="false"] {
background-color: var(--lm-surface);
border-color: var(--lm-accent-soft);
border-left-color: var(--lm-accent-soft);
}
.lm-lora-clip-entry:hover {
background-color: var(--lm-surface-hover);
}
.lm-lora-clip-entry[data-active="false"]:hover {
background-color: var(--lm-surface-hover);
}
.lm-lora-clip-entry .lm-lora-name {
font-size: 13px;
color: var(--lm-fg);
margin-left: 0;
}
.lm-lora-clip-entry[data-active="false"] .lm-lora-name {
color: var(--lm-fg-subtle);
}
.lm-lora-clip-entry .lm-lora-strength-input {
color: var(--lm-fg);
}
.lm-lora-clip-entry[data-active="false"] .lm-lora-strength-input {
color: var(--lm-fg-subtle);
}
.lm-lora-badge {
padding: 2px 6px;
border-radius: 12px;
background: var(--lm-accent-soft);
color: var(--lm-fg-muted);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.lm-lora-context-menu {
position: fixed;
background: var(--lm-surface);
border-radius: 8px;
box-shadow: var(--lm-shadow-lg);
padding: 8px 0;
z-index: 10000;
min-width: 180px;
border: 1px solid var(--lm-accent-soft);
backdrop-filter: blur(6px);
}
.lm-lora-menu-item {
padding: 6px 20px;
cursor: pointer;
color: var(--lm-fg);
font-size: 13px;
user-select: none;
display: flex;
align-items: center;
gap: 8px;
}
.lm-lora-menu-item:hover {
background-color: var(--lm-accent-soft);
}
.lm-lora-menu-item-icon {
width: 14px;
height: 14px;
display: flex;
align-items: center;
justify-content: center;
}
.lm-lora-context-menu hr {
border: none;
border-top: 1px solid var(--lm-border-soft);
margin: 6px 0;
}
.lm-trigger-count-badge {
font-variant-numeric: tabular-nums;
background-color: color-mix(in srgb, var(--lm-fg) 14%, transparent);
color: inherit;
}
.lm-trigger-count-badge--edited {
color: var(--lm-fg);
background-color: color-mix(in srgb, var(--lm-fg) 8%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--lm-fg) 28%, transparent);
}
.lm-trigger-group-edit-button {
transition: opacity 0.18s ease, color 0.18s ease;
}
.lm-trigger-group-edit-button:hover {
color: var(--lm-fg);
}
.lm-trigger-group-editor {
background: var(--lm-surface);
border: 1px solid var(--lm-accent-soft);
box-shadow: var(--lm-shadow-lg);
backdrop-filter: blur(6px);
}
.lm-trigger-group-editor__title {
color: var(--lm-fg);
}
.lm-trigger-group-editor__subtitle,
.lm-trigger-group-editor__empty {
color: var(--lm-fg-muted);
}
/* Autocomplete styling */
.lm-autocomplete-name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.lm-autocomplete-meta {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.lm-autocomplete-count {
font-size: 11px;
color: var(--lm-fg-subtle);
}
/* Category badges: one hue per category, mixed against the popover surface
so both the tint and the text contrast flip with the theme. */
.lm-autocomplete-category {
--lm-cat: var(--lm-cat-general);
font-size: 10px;
padding: 2px 6px;
border-radius: 10px;
white-space: nowrap;
background: color-mix(in oklab, var(--lm-cat) 22%, var(--lm-surface));
color: color-mix(in oklab, var(--lm-cat) 60%, var(--lm-fg));
}
.lm-autocomplete-category--general { --lm-cat: var(--lm-cat-general); }
.lm-autocomplete-category--artist { --lm-cat: var(--lm-cat-artist); }
.lm-autocomplete-category--copyright { --lm-cat: var(--lm-cat-copyright); }
.lm-autocomplete-category--character { --lm-cat: var(--lm-cat-character); }
.lm-autocomplete-category--meta { --lm-cat: var(--lm-cat-meta); }
.lm-autocomplete-category--species { --lm-cat: var(--lm-cat-species); }
.lm-autocomplete-category--lore { --lm-cat: var(--lm-cat-lore); }
/* "Already added" cue. Green means the node has it enabled, amber means it is
listed but turned off (selecting it enables it). Both hues are mixed against
the popover surface so contrast follows the theme. */
.lm-autocomplete-added {
display: inline-flex;
align-items: center;
gap: 3px;
flex: none;
padding: 2px 7px;
border-radius: 10px;
border: 1px solid transparent;
font-size: 10px;
font-weight: 600;
line-height: 1.2;
white-space: nowrap;
}
.lm-autocomplete-added.is-active {
color: color-mix(in oklab, var(--lm-ok) 65%, var(--lm-fg));
background: color-mix(in oklab, var(--lm-ok) 18%, var(--lm-surface));
border-color: color-mix(in oklab, var(--lm-ok) 38%, transparent);
}
.lm-autocomplete-added.is-inactive {
color: color-mix(in oklab, var(--lm-warning) 65%, var(--lm-fg));
background: color-mix(in oklab, var(--lm-warning) 18%, var(--lm-surface));
border-color: color-mix(in oklab, var(--lm-warning) 40%, transparent);
}
.lm-autocomplete-added-icon {
font-size: 10px;
line-height: 1;
}
/* Command list styling */
.comfy-autocomplete-command {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
}
.lm-autocomplete-command-name {
font-family: 'Consolas', 'Monaco', monospace;
color: var(--lm-accent-text);
}
.lm-autocomplete-command-label {
font-size: 12px;
color: var(--lm-fg-subtle);
}
/* ---- Widget flash highlight (visual cue after a value is sent to a node) ---- */
/* Applied to a widget row element when its value is updated by LoRA Manager.
Shifts the value text color to the LM brand accent with a CSS transition
for fade-in/fade-out. Removal (timeout / hover) is handled by JS.
The transition is declared on .lm-flash-host (added alongside .lm-flash)
rather than on ComfyUI's .lg-node-widget, so we don't impose a global
color transition on every widget input. The host class persists until
cleanup so fade-out still applies after .lm-flash is removed. */
.lm-flash-host input,
.lm-flash-host textarea,
.lm-flash-host [role="combobox"] {
transition: color 0.25s ease, -webkit-text-fill-color 0.25s ease;
}
.lm-flash input,
.lm-flash textarea,
.lm-flash [role="combobox"] {
color: var(--lm-accent) !important;
-webkit-text-fill-color: var(--lm-accent) !important;
}
/* ---- Autocomplete dropdown (appended to <body>, so it is an .lm-popover) ---- */
.comfy-autocomplete-dropdown {
background-color: var(--lm-surface);
border: 1px solid var(--lm-border-soft);
border-radius: 8px;
box-shadow: var(--lm-shadow-lg);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
.comfy-autocomplete-dropdown::-webkit-scrollbar,
.comfy-autocomplete-scroll-container::-webkit-scrollbar {
width: 8px;
}
.comfy-autocomplete-dropdown::-webkit-scrollbar-track,
.comfy-autocomplete-scroll-container::-webkit-scrollbar-track {
background: var(--lm-surface-raised);
border-radius: 4px;
}
.comfy-autocomplete-dropdown::-webkit-scrollbar-thumb,
.comfy-autocomplete-scroll-container::-webkit-scrollbar-thumb {
background: var(--lm-border);
border-radius: 4px;
}
.comfy-autocomplete-dropdown::-webkit-scrollbar-thumb:hover,
.comfy-autocomplete-scroll-container::-webkit-scrollbar-thumb:hover {
background: var(--lm-fg-subtle);
}
.comfy-autocomplete-item {
color: var(--lm-fg-muted);
border-bottom: 1px solid var(--lm-border-soft);
transition: background-color 0.12s ease;
}
.comfy-autocomplete-item-selected,
.comfy-autocomplete-item:hover {
background-color: var(--lm-surface-selected);
}
.comfy-autocomplete-item:last-child {
border-bottom: none;
}
.comfy-autocomplete-info-item {
cursor: default;
color: var(--lm-fg-muted);
}
.comfy-autocomplete-loading {
color: var(--lm-fg-subtle);
}
.lm-matched-alias {
font-size: 11px;
color: var(--lm-fg-subtle);
}
.lm-autocomplete-command-footer {
color: var(--lm-fg-subtle);
border-top: 1px solid var(--lm-border-soft);
}
.lm-autocomplete-first-run-hint {
color: var(--lm-fg-subtle);
border-bottom: 1px solid var(--lm-border-soft);
}
.lm-autocomplete-first-run-hint__dismiss {
background: none;
border: none;
color: var(--lm-fg-subtle);
cursor: pointer;
font-size: 14px;
line-height: 1;
padding: 0 2px;
}
.lm-autocomplete-first-run-hint__dismiss:hover {
color: var(--lm-fg);
}
.lm-autocomplete-info-title {
font-size: 13px;
font-weight: 600;
margin-bottom: 6px;
}
.lm-autocomplete-info-description {
font-size: 12px;
line-height: 1.45;
color: var(--lm-fg-muted);
}
/* Wildcards info item: path block + usage examples + action buttons */
.lm-autocomplete-info-path {
margin-top: 10px;
padding: 8px 10px;
border-radius: 6px;
background: var(--lm-surface-raised);
font-size: 11px;
line-height: 1.5;
}
.lm-autocomplete-info-path strong {
color: var(--lm-fg);
font-weight: 600;
margin-bottom: 4px;
display: block;
}
.lm-autocomplete-info-path code {
color: var(--lm-accent-text);
word-break: break-all;
}
.lm-autocomplete-info-note {
margin-top: 6px;
color: var(--lm-fg-subtle);
}
.lm-autocomplete-info-examples {
margin-top: 10px;
font-size: 11px;
line-height: 1.55;
color: var(--lm-fg-muted);
}
.lm-autocomplete-info-examples strong {
display: block;
color: var(--lm-fg);
font-weight: 600;
margin-bottom: 4px;
}
.lm-autocomplete-info-actions {
display: flex;
gap: 8px;
margin-top: 12px;
flex-wrap: wrap;
}
.lm-autocomplete-action {
border-radius: 6px;
padding: 6px 10px;
font-size: 11px;
cursor: pointer;
}
.lm-autocomplete-action--primary {
border: 1px solid var(--lm-accent-border);
background: var(--lm-accent-soft);
color: var(--lm-accent-text);
}
.lm-autocomplete-action--secondary {
border: 1px solid var(--lm-border-soft);
background: var(--lm-surface-hover);
color: var(--lm-fg);
}
.lm-autocomplete-match {
background-color: var(--lm-accent-soft);
color: var(--lm-accent-text);
padding: 1px 2px;
border-radius: 2px;
}
/* ---- Trigger-word chips (created in tags_widget.js) ---- */
.comfy-tag {
background-color: var(--lm-surface-raised);
color: var(--lm-fg-muted);
border: 1px solid var(--lm-border-soft);
box-shadow: var(--lm-shadow);
}
.comfy-tag[data-active="true"] {
background-color: var(--lm-accent);
border-color: var(--lm-accent);
color: var(--lm-on-accent);
}
/* Trigger words that belong to the LoRA currently selected in the loader.
The chip fill is accent (active) or surface (inactive), so the ring sits
outside a 1px surface-coloured gap and takes its colour from the theme
(accent blended towards the foreground). The gap uses the same token as the
container background, so it reads as a punched-out ring in every palette
and renderer, and the contrast flips with the theme automatically. */
.comfy-tag[data-highlighted="true"] {
border-color: transparent;
box-shadow:
0 0 0 1px var(--lm-surface),
0 0 0 3px color-mix(in oklab, var(--lm-accent) 35%, var(--lm-fg)),
var(--lm-shadow);
}
.comfy-tag:hover {
box-shadow: var(--lm-shadow-lg);
}
/* Keep the ring on hover: the generic :hover rule above has the same
specificity and would otherwise replace the whole box-shadow. */
.comfy-tag[data-highlighted="true"]:hover {
box-shadow:
0 0 0 1px var(--lm-surface),
0 0 0 3px color-mix(in oklab, var(--lm-accent) 35%, var(--lm-fg)),
var(--lm-shadow-lg);
}
.comfy-tag-strength {
background-color: var(--lm-surface-hover);
color: var(--lm-fg);
border: 1px solid var(--lm-border-soft);
}
/* See .lm-lora-empty-state: canvas mode needs an explicit size so the
placeholder matches the Vue node renderer's typography. */
.lm-tags-empty {
color: var(--lm-fg-subtle);
font-size: 12px;
font-style: italic;
text-align: center;
padding: 16px 0;
width: 100%;
user-select: none;
-webkit-user-select: none;
}