Files
magpie/internal/gui/assets/library.css
T
yetone 23365f2216 library: a long link or description stays inside its row, an icon whose picture failed is drawn again, and what the Library couldn't write is listed on the page (#257 long links break an item; Discord, neiko: Claude's icon missing until a refresh; JasonLeeForOnly: the toast says "see Library" and the Library shows nothing)
#257: in a skill's source line the "linked from" / 链接自 label could shrink, and Chinese breaks after any character, so a long path squeezed it into a column of single characters; the line grew tall, and skill rows in a long list have a fixed height with overflow hidden, so the name was clipped and the path cut. The label now stays whole on one line and the path gives way with an ellipsis; long names and descriptions break anywhere rather than widening the row. Icons: each agent's icon was made once and copied onto every row, so if the first copy's picture failed to load every copy stayed empty until a reload. An icon whose picture fails is dropped from that cache, a drawn one asks for its picture again (up to 3 times, growing delays), and the Library's icons are built apart from the Providers page's recycled ones. Problems: the Library only showed a problem as an amber dot on a chip, with nowhere for an agent's instructions, its whole MCP file, a removed server, a project, or an agent hidden from the page, and the failures of applying a profile were never shown although the toast pointed there. GET /api/library now returns every standing problem, and a card above each tab's content lists them (amber dot, no stripe): which agent or project, what failed, the error, with Try again re-running all/sync; it updates in place when a chip is toggled. en and zh. Tested with a new library-items.test.cjs, Chromium and WebKit, en and zh (18/18): long names, links and descriptions inside their rows with the label on one line in light and dark at 980 and 560px, the card's lines and errors, Try again posting all/sync without moving the page, a chip click updating the card with the row in place, Claude Code's icon appearing after its picture fails for the first 300ms; all fail against the old library.js/css. click-scroll, old-webkit and shared-skills pass. go vet, go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass (TestClaudeSignedOut timed out under load and passes alone). Not tried in the real Wails window; the icon's first failure is simulated, its real cause not reproduced.
2026-09-30 11:01:17 +08:00

399 lines
30 KiB
CSS

/* Library: instructions, MCP servers and skills for every agent. */
#view-library { gap: 0; }
/* The tabs stay at the top while the page scrolls under them, on a frosted
strip as wide as the view; a hairline shows once something is beneath. */
.lib-head {
position: sticky; top: 0; z-index: 5;
display: flex; align-items: center; gap: 8px; flex: none;
margin: 0 -12px; padding: 6px 12px 4px;
background: color-mix(in srgb, var(--bg) 92%, transparent);
-webkit-backdrop-filter: saturate(1.6) blur(16px); backdrop-filter: saturate(1.6) blur(16px);
box-shadow: 0 1px 0 transparent; transition: box-shadow .18s var(--ease);
}
/* the window's view is padded 2px at the top: the strip reaches over it */
body.window .lib-head { top: -2px; margin: -2px -16px 0; padding: 8px 16px 4px; }
.lib-head.stuck { box-shadow: 0 1px 0 var(--line-2); }
.lib-tabs .opt { height: 24px; padding: 0 12px; font-size: 12.5px; }
.lib-more { display: inline-flex; align-items: center; gap: 5px; }
.lib-mini { display: inline-grid; place-items: center; color: var(--muted); }
.lib-mini svg { width: 13px; height: 13px; }
.lib-body { display: flex; flex-direction: column; flex: none; }
.lib-body.enter { animation: content-in .22s var(--ease); }
.lib-intro { margin: 8px 2px 10px; font-size: 12px; line-height: 1.5; color: var(--muted); max-width: 640px; }
.lib-aside { margin: 8px 4px 0; font-size: 11.5px; line-height: 1.5; color: var(--faint); }
.lib-aside.center { text-align: center; margin-top: 14px; }
.lib-foot { margin: auto 0 0; padding: 18px 4px 6px; font-size: 11px; color: var(--faint); flex: none; }
.lib-link { font-size: 11px; color: var(--muted); text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 2px; }
.lib-link:hover { color: var(--fg); text-decoration-color: currentColor; }
/* the glyph at the front of a row: drawn, in the text colour */
.lib-glyph { flex: none; width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center; color: var(--fg-2); background: var(--pill); }
.lib-glyph svg { width: 15px; height: 15px; }
.lib-rtk-logo .mask { width: 20px; height: 20px; background: currentColor; -webkit-mask: var(--i) center / contain no-repeat; mask: var(--i) center / contain no-repeat; }
.mk-icon.lib-logo { flex: none; width: 26px; height: 26px; border-radius: 7px; }
.lib-editor .ehead .mk-icon.lib-logo { width: 24px; height: 24px; }
/* cards: the shared text, the skill finder */
.lib-card { display: flex; flex-direction: column; }
.lib-cardhead { display: flex; align-items: center; gap: 9px; padding: 9px 12px 0; }
.lib-cardhead b { font-weight: 600; font-size: 13px; }
.lib-cardhead .note { font-size: 11px; color: var(--faint); }
.lib-text {
display: block; width: 100%; box-sizing: border-box; resize: none; border: 0; outline: none; background: none; color: var(--fg);
padding: 10px 14px 14px; font: 12.5px/1.6 var(--code); tab-size: 2; min-height: 150px;
}
.lib-text::placeholder { color: var(--faint); font-family: var(--font); font-size: 12.5px; }
.lib-text.small { min-height: 64px; border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; background: var(--card); transition: border-color .12s, box-shadow .12s; }
.lib-text.small:focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
/* unsaved text: a bar that stays in view at the bottom until it's saved */
.lib-savebar {
position: sticky; bottom: 8px; z-index: 3; margin-top: 12px; display: flex; align-items: center; gap: 6px;
padding: 7px 8px 7px 14px; border-radius: 11px; background: var(--pop-bg); box-shadow: var(--shadow);
-webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); animation: lib-rise .22s var(--ease);
}
.lib-savebar[hidden] { display: none; }
.lib-savebar .note { font-size: 12px; color: var(--fg-2); display: inline-flex; align-items: center; gap: 7px; }
.lib-savebar .note::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--fg); }
@keyframes lib-rise { from { opacity: 0; transform: translateY(8px); } }
/* rows */
.lib-list .row { min-height: 50px; padding: 7px 10px 7px 12px; gap: 11px; }
.lib-row.click { cursor: default; transition: background .14s var(--ease); }
.lib-row.click:hover { background: var(--card-2); }
/* a long name, link or description breaks anywhere rather than pushing
the row wider than the list (#257) */
.lib-row .who { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
/* a server's or skill's picture sits level with the top of its name */
.lib-row > .lib-logo, .lib-row > .lib-glyph { align-self: flex-start; margin-top: 1px; }
.lib-row .name.mono, .lib-pick .name.mono { font: 500 12.5px var(--code); }
.lib-row .sub { max-width: 100%; }
.lib-row .sub.mono, .lib-pick .sub.mono { font: 11px var(--code); color: var(--muted); }
.lib-row .chev { flex: none; color: var(--faint); transition: transform .2s var(--ease); display: grid; place-items: center; }
.lib-row.open .chev { transform: rotate(90deg); }
.lib-row.open { background: var(--card-2); }
.lib-skill .sub { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.4; }
.lib-skill.missing .name { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--faint); }
.lib-skill.missing .sub { color: var(--amber); }
.lib-src { display: flex; align-items: center; gap: 5px; margin-top: 3px; font-size: 11px; color: var(--faint); min-width: 0; }
/* "linked from" stays whole, one line, and the path beside it gives way:
CJK can break after any character, so the label shrank to a column of
them and the row, a fixed height in a long list, lost its name (#257) */
.lib-src > span { flex: none; white-space: nowrap; }
.lib-src > .lib-path, .lib-src > .lib-srclink { flex: 0 1 auto; min-width: 0; }
.lib-srclink { font: 11px var(--code); color: var(--muted); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lib-srclink:hover { color: var(--fg); text-decoration: underline; text-underline-offset: 2px; }
/* a path: shown in the file manager on a click */
.lib-path { font: 11px var(--code); color: var(--muted); padding: 0; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: left; border-radius: 3px; }
.lib-path:hover { color: var(--fg); text-decoration: underline; text-underline-offset: 2px; }
/* tags beside a name */
.lib-tags { display: flex; gap: 5px; flex: none; flex-wrap: wrap; justify-content: flex-end; }
.lib-tag { flex: none; display: inline-flex; align-items: center; gap: 5px; height: 20px; padding: 0 7px; border-radius: 6px; font-size: 11px; color: var(--fg-2); background: var(--pill); white-space: nowrap; }
.lib-tag.warn { color: var(--amber); background: var(--amber-soft); }
.lib-tag.warn::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
/* what a check for updates found, beside a skill's name */
.lib-row .name .lib-tag { height: 17px; padding: 0 6px; margin-left: 7px; font-size: 10.5px; font-weight: 500; vertical-align: 1px; }
.lib-tag.lib-new { color: var(--accent); background: var(--accent-soft); }
.lib-tag.lib-new::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.lib-tag.lib-new.link { cursor: pointer; }
.lib-tag.lib-new.link:hover { background: color-mix(in srgb, var(--accent) 22%, var(--accent-soft)); }
.lib-tag.lib-unchecked { color: var(--faint); font-weight: 400; }
/* the switch */
.lib-switch { flex: none; position: relative; width: 32px; height: 19px; border-radius: 10px; background: var(--line); transition: background .2s var(--ease); }
.lib-switch i { position: absolute; top: 2px; left: 2px; width: 15px; height: 15px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .25); transition: transform .22s var(--ease); }
.lib-switch.on { background: var(--fg); }
.lib-switch.on i { transform: translateX(13px); background: var(--card); }
:root[data-theme="dark"] .lib-switch.on i { background: var(--bg); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .lib-switch.on i { background: var(--bg); } }
/* the sets of shared instructions: a radio picks the one the agents read */
.lib-sets .lib-cardhead { padding-bottom: 8px; }
.lib-radio { flex: none; width: 17px; height: 17px; border-radius: 50%; border: 1.5px solid var(--faint); display: grid; place-items: center; transition: border-color .14s; }
.lib-radio:hover { border-color: var(--fg-2); }
.lib-radio.on { border-color: var(--fg); }
.lib-radio.on::after { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--fg); }
.lib-set.on .name { font-weight: 600; }
.lib-sets .lib-detail { padding-left: 40px; }
.lib-sets > .lib-cardhead + .row, .lib-sets .lib-detail + .row, .lib-sets .lib-detail + .lib-setadd, .lib-sets .row + .lib-setadd { border-top: 1px solid var(--line-2); }
.lib-setadd { padding: 8px 10px 10px; }
input.lib-setname { height: 28px; padding: 0 9px; border-radius: 7px; border: 1px solid var(--line); background: var(--card); font-size: 12.5px; outline: none; max-width: 280px; transition: border-color .12s, box-shadow .12s; }
input.lib-setname.grow { flex: 1 1 auto; max-width: none; }
input.lib-setname:focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
/* an agent's instructions, opened */
.lib-detail { padding: 4px 14px 14px 50px; background: var(--card-2); display: flex; flex-direction: column; gap: 8px; border-top: 0 !important; animation: content-in .2s var(--ease); }
.lib-lab { font-size: 11.5px; color: var(--muted); }
.lib-detail .lib-aside { margin: 0; }
.lib-acts { display: flex; gap: 6px; flex-wrap: wrap; }
.lib-warn { font-size: 12px; line-height: 1.5; color: var(--fg); background: var(--amber-soft); border-radius: 8px; padding: 8px 10px; }
.lib-warn .lib-path { display: inline; }
/* what the last change couldn't write: for which agent, and why */
.lib-problems { flex: none; margin: 8px 0 6px; padding: 9px 12px 10px; border-radius: 10px; background: var(--amber-soft); color: var(--fg); font-size: 12px; line-height: 1.45; min-width: 0; overflow-wrap: anywhere; }
.lib-problems-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
.lib-problems-head b { font-weight: 600; min-width: 0; }
.lib-problems-head .action { flex: none; }
.lib-problems-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--amber); }
.lib-problems ul { list-style: none; margin: 6px 0 0; padding: 0 0 0 15px; display: grid; gap: 4px; }
.lib-problems li { min-width: 0; }
.lib-problems-sep { color: var(--muted); }
.lib-problems-err { display: block; color: var(--fg-2); font: 11px var(--code); margin-top: 1px; }
/* agents a server or skill is given to: an icon each, lit when it has it */
.lib-agents { display: flex; flex-wrap: wrap; gap: 3px; flex: none; justify-content: flex-end; max-width: 50%; }
.lib-ag {
position: relative; display: inline-flex; align-items: center; gap: 6px; height: 26px; min-width: 26px; padding: 0 5px; border-radius: 7px;
border: 1px solid transparent; font-size: 12px; color: var(--muted); transition: background .14s, border-color .14s, color .14s, transform .14s var(--ease);
}
.lib-ag .ic { width: 15px; height: 15px; filter: grayscale(1); opacity: .35; transition: filter .14s, opacity .14s; }
.lib-ag:hover { background: var(--pill); }
.lib-ag:hover .ic { opacity: .7; }
.lib-ag.on { background: var(--card); border-color: var(--line); color: var(--fg); box-shadow: 0 1px 1px rgba(0, 0, 0, .05); }
.lib-ag.on .ic { filter: none; opacity: 1; }
/* an icon made grey once rather than through the filter on every paint
(library.js, greyIcon): a picture's grey copy stands in for it, and one
drawn in the text's colour takes that colour grey — the same pixels.
The copy is hidden wherever the icon goes, not only in a chip: an
agent's icon is copied from one made once (agentIcon), grey copy and
all, into the rows of what agents have of their own too */
.ic > img.grey { display: none; }
.lib-ag:not(.on) .ic.baked { filter: none; }
.lib-ag:not(.on) .ic.baked > img:not(.grey) { display: none; }
.lib-ag:not(.on) .ic.baked > img.grey { display: block; }
@supports (color: rgb(from currentColor r g b)) {
.lib-ag:not(.on) .ic.flat { filter: none; color: rgb(from currentColor calc(r * .2126 + g * .7152 + b * .0722) calc(r * .2126 + g * .7152 + b * .0722) calc(r * .2126 + g * .7152 + b * .0722)); }
}
.lib-ag.via { border: 1px dashed var(--line); }
.lib-ag.via .ic { opacity: .6; }
.lib-ag.warn::after { content: ""; position: absolute; top: -2px; right: -2px; width: 7px; height: 7px; border-radius: 50%; background: var(--amber); box-shadow: 0 0 0 2px var(--card); }
.lib-ag.blocked { opacity: .35; }
.lib-ag.blocked:hover { background: none; }
.lib-ag:active:not(:disabled) { transform: scale(.92); }
.editor .lib-agents { max-width: none; justify-content: flex-start; gap: 5px; }
.editor .lib-ag { padding: 0 9px 0 6px; border-color: var(--line); }
.editor .lib-ag.on { background: var(--accent-soft); border-color: color-mix(in srgb, var(--fg) 30%, transparent); }
.lib-probefoot .lib-agents { max-width: none; justify-content: flex-start; }
/* in a list row they sit stacked like a hand of cards, on one line, and fan
out when the pointer or focus comes to them */
.lib-row > .lib-agents { flex-wrap: nowrap; gap: 0; max-width: none; padding-left: 4px; }
.lib-row > .lib-agents .lib-ag { flex: none; width: 24px; min-width: 0; height: 24px; padding: 0 0 0 4px; justify-content: flex-start; background: var(--card); border-color: var(--line-2); box-shadow: none; transition: margin .22s var(--ease), background .14s, border-color .14s, transform .14s var(--ease); }
.lib-row > .lib-agents .lib-ag .ic { width: 14px; height: 14px; }
/* the ring in the card's colour that cuts each chip out of the one under
it: an outline rather than a spread shadow, the same ring cheaper to
paint; a chip with the focus shows the focus ring instead */
.lib-row > .lib-agents .lib-ag:not(:focus-visible) { outline: 1.5px solid var(--card); outline-offset: 0; }
.lib-row > .lib-agents .lib-ag + .lib-ag { margin-left: -7px; }
.lib-row > .lib-agents .lib-ag.on { border-color: var(--line); box-shadow: 0 1px 2px rgba(0, 0, 0, .08); }
.lib-row > .lib-agents .lib-ag.via { border-style: dashed; }
.lib-row > .lib-agents:hover .lib-ag + .lib-ag, .lib-row > .lib-agents:focus-within .lib-ag + .lib-ag { margin-left: 3px; }
.lib-row > .lib-agents:hover .lib-ag, .lib-row > .lib-agents:focus-within .lib-ag { box-shadow: none; }
.lib-row > .lib-agents:hover .lib-ag:not(:focus-visible), .lib-row > .lib-agents:focus-within .lib-ag:not(:focus-visible) { outline-color: transparent; }
.lib-row > .lib-agents .lib-ag:hover { background: var(--pill); z-index: 1; }
.lib-row.click:hover > .lib-agents .lib-ag { box-shadow: none; }
.lib-row.click:hover > .lib-agents .lib-ag:not(:focus-visible) { outline-color: var(--card-2); }
.lib-row.click:hover > .lib-agents:hover .lib-ag:not(:focus-visible) { outline-color: transparent; }
/* agents that have one of their own, not in the library */
.lib-have { display: flex; gap: 4px; flex: none; }
.lib-have .ic { width: 15px; height: 15px; }
/* a skill's actions show on hover */
.lib-rowacts { display: flex; gap: 2px; flex: none; opacity: 0; transition: opacity .14s; }
.lib-row:hover .lib-rowacts, .lib-rowacts:focus-within { opacity: 1; }
.lib-icon { width: 26px; height: 26px; padding: 0; display: inline-grid; place-items: center; }
.lib-icon.busy svg { animation: lib-bob .8s var(--ease) infinite alternate; }
@keyframes lib-bob { to { transform: translateY(-2px); } }
/* the skill finder */
.lib-install { margin-bottom: 4px; }
.lib-find { display: flex; align-items: center; gap: 8px; padding: 8px 8px 8px 12px; }
.lib-find input { flex: 1; min-width: 0; height: 28px; border: 0; outline: none; background: none; font-size: 13px; color: var(--fg); }
.lib-find input::placeholder { color: var(--faint); }
.lib-find .text.busy { color: var(--muted); }
.lib-err { padding: 0 14px 10px 36px; font-size: 12px; color: var(--red); line-height: 1.45; overflow-wrap: anywhere; }
.lib-probe { border-top: 1px solid var(--line-2); padding: 8px 8px 8px; display: flex; flex-direction: column; gap: 6px; animation: content-in .22s var(--ease); }
.lib-probehead { display: flex; align-items: center; gap: 6px; padding: 0 4px; }
.lib-probehead .note { font-size: 11.5px; color: var(--muted); }
.lib-filter { height: 26px; margin: 0 4px; padding: 0 9px; border-radius: 7px; border: 1px solid var(--line); background: var(--card); color: var(--fg); font-size: 12px; outline: none; }
.lib-filter:focus { border-color: var(--accent); }
.lib-picks { display: flex; flex-direction: column; max-height: 300px; overflow-y: auto; }
.lib-pick { display: flex; align-items: flex-start; gap: 10px; padding: 7px 6px; border-radius: 8px; cursor: default; }
.lib-pick:hover:not(.dim) { background: var(--pill); }
.lib-pick.dim { opacity: .55; }
.lib-pick input { margin: 2px 0 0; accent-color: var(--fg); flex: none; }
.lib-pick .who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.lib-pick .name { font-size: 13px; font-weight: 500; }
.lib-pick .sub { font-size: 11.5px; color: var(--muted); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.lib-probefoot { display: flex; align-items: center; gap: 8px; padding: 8px 4px 0; border-top: 1px solid var(--line-2); flex-wrap: wrap; }
.lib-probefoot .note { font-size: 12px; color: var(--muted); }
/* nothing yet */
.lib-empty { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 34px 20px 30px; text-align: center; background: var(--card); border: 1px dashed var(--line); border-radius: 12px; }
.lib-empty b { font-weight: 600; font-size: 13px; }
.lib-empty p { margin: 0 0 8px; font-size: 12px; color: var(--muted); max-width: 380px; line-height: 1.5; }
.lib-none { padding: 14px; font-size: 12px; color: var(--muted); }
.lib-skel .row { height: 50px; }
/* the dialog */
.lib-editor .ehead .lib-glyph { width: 24px; height: 24px; }
.lib-editor .ehead .note { font-size: 11.5px; color: var(--faint); margin-left: auto; }
.lib-editor .lib-slot { display: contents; }
.lib-editor .lib-grid { display: contents; }
.lib-editor input.mono { font-family: var(--code); font-size: 12px; }
.lib-pairs { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.lib-pair { display: flex; gap: 6px; width: 100%; align-items: center; }
.lib-pair input { flex: 1; min-width: 0; font-family: var(--code); font-size: 12px; }
.lib-pair input:first-child { flex: .8; }
.lib-x { width: 24px; height: 24px; padding: 0; display: inline-grid; place-items: center; flex: none; }
.lib-addpair { padding-left: 0; }
.lib-addpair:hover { background: none; }
.lib-editor .lib-picks { grid-column: 1 / -1; max-height: 320px; }
.lib-confirm { grid-column: 1 / -1; margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--fg-2); }
.text.danger-fill { background: var(--red); color: #fff; }
.lib-reader { grid-template-columns: minmax(0, 1fr); }
.lib-md { margin: 0; max-height: min(60vh, 520px); overflow: auto; font: 12px/1.6 var(--code); color: var(--fg-2); white-space: pre-wrap; overflow-wrap: anywhere; background: var(--card-2); border-radius: 8px; padding: 12px 14px; user-select: text; -webkit-user-select: text; }
.lib-reader .bar .lib-path { max-width: 70%; }
.editor .editor-error:empty { display: none; }
@media (max-width: 560px) {
.lib-list .row { flex-wrap: wrap; }
.lib-agents { max-width: 100%; }
}
/* a list's heading: its label, and a quiet line of what it does */
#view-library .row-head { height: auto; min-height: 26px; margin: 18px 0 6px; padding: 0 2px; gap: 12px; }
#view-library .row-head .note { min-width: 0; font-size: 11px; color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#view-library .row-head .lib-updall { height: 24px; padding: 0 9px; font-size: 12px; }
/* the market: servers and skills to add, each with its own picture */
.mk { display: flex; flex-direction: column; }
.mk-find { display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 12px; margin-bottom: 10px; border-radius: 10px; background: var(--card); border: 1px solid var(--line); transition: border-color .14s, box-shadow .14s; }
.mk-find:focus-within { border-color: color-mix(in srgb, var(--fg) 35%, transparent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--fg) 7%, transparent); }
.mk-find input { flex: 1; min-width: 0; height: 100%; border: 0; outline: none; background: none; font-size: 13px; color: var(--fg); }
.mk-find input::placeholder { color: var(--faint); }
.mk-find input::-webkit-search-cancel-button { -webkit-appearance: none; }
.mk-spin { flex: none; width: 12px; height: 12px; border-radius: 50%; border: 1.5px solid var(--line); border-top-color: var(--fg-2); opacity: 0; transition: opacity .15s; }
.mk.loading .mk-spin { opacity: 1; animation: mk-spin .7s linear infinite; }
@keyframes mk-spin { to { transform: rotate(360deg); } }
.mk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(236px, 1fr)); gap: 10px; transition: opacity .2s var(--ease); }
.mk.loading .mk-grid { opacity: .55; }
.mk-card {
position: relative; display: flex; flex-direction: column; gap: 9px; min-width: 0; padding: 12px 12px 10px; border-radius: 12px;
background: var(--card); border: 1px solid var(--line); box-shadow: 0 1px 1px rgba(0, 0, 0, .03);
transition: border-color .16s var(--ease), box-shadow .16s var(--ease), transform .16s var(--ease);
animation: content-in .24s var(--ease) both;
}
.mk-card.click:hover { border-color: color-mix(in srgb, var(--fg) 22%, transparent); box-shadow: 0 6px 18px -8px rgba(0, 0, 0, .18); transform: translateY(-1px); }
.mk-top { display: flex; align-items: center; gap: 10px; min-width: 0; }
.mk-icon {
flex: none; width: 36px; height: 36px; border-radius: 9px; overflow: hidden; display: grid; place-items: center;
background: #fff; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .07);
}
.mk-icon img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mk-icon.svg img { width: 72%; height: 72%; object-fit: contain; }
.mk-icon.mono { background: var(--pill); box-shadow: none; }
.mk-mono { font-size: 15px; font-weight: 600; color: var(--fg-2); }
.mk-icon.skeleton { box-shadow: none; }
:root[data-theme="dark"] .mk-icon:not(.mono):not(.skeleton) { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .mk-icon:not(.mono):not(.skeleton) { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08); } }
.mk-who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.mk-name { display: flex; align-items: center; gap: 5px; min-width: 0; font-size: 13px; font-weight: 600; line-height: 1.2; }
.mk-title { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mk-name.big { font-size: 15px; }
.mk-meta { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: 11px; color: var(--muted); }
.mk-pub { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mk-badge { flex: none; display: inline-flex; align-items: center; gap: 3px; height: 17px; padding: 0 6px; border-radius: 5px; background: var(--pill); color: var(--fg-2); font-size: 10.5px; font-family: var(--code); }
.mk-badge.mk-needs { width: 17px; padding: 0; justify-content: center; }
.mk-official { flex: none; display: inline-grid; place-items: center; width: 13px; height: 13px; border-radius: 50%; background: var(--fg); color: var(--card); }
.mk-desc { margin: 0; min-height: 2.9em; font-size: 12px; line-height: 1.45; color: var(--fg-2); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.mk-desc.wait { background: linear-gradient(90deg, var(--pill) 25%, var(--pill-hover) 45%, var(--pill) 65%); background-size: 240% 100%; animation: skeleton-shift 1.4s ease-in-out infinite; border-radius: 5px; min-height: 0; height: 8px; margin: 6px 20% 10px 0; }
.mk-foot { display: flex; align-items: center; gap: 8px; min-height: 26px; margin-top: auto; }
.mk-id { min-width: 0; font: 11px var(--code); color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mk-installs { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.mk-add { flex: none; height: 26px; }
.mk-add.busy { color: var(--muted); }
.mk-have { flex: none; display: inline-flex; align-items: center; gap: 4px; height: 26px; padding: 0 4px; font-size: 12px; color: var(--muted); }
.mk-card.have .mk-icon { opacity: .9; }
.mk-msg { grid-column: 1 / -1; padding: 22px 12px; text-align: center; font-size: 12px; color: var(--muted); }
.mk-msg.err { padding: 8px 12px; color: var(--amber); background: var(--amber-soft); border-radius: 8px; text-align: left; line-height: 1.45; }
.mk-skel { gap: 12px; min-height: 104px; }
.mk-skel .mk-who { gap: 7px; }
/* a market item up close */
.mk-sheet .mk-sheethead { grid-column: 1 / -1; display: flex; align-items: center; gap: 12px; padding-bottom: 12px; border-bottom: 1px solid var(--line-2); }
.mk-sheet .mk-icon { width: 44px; height: 44px; border-radius: 11px; }
.mk-sheet .mk-about { grid-column: 1 / -1; margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--fg-2); }
.mk-sheet .mk-hint { grid-column: 1 / -1; display: flex; align-items: center; gap: 6px; margin: 0; font-size: 11.5px; color: var(--muted); }
.mk-ext { flex: none; display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); text-decoration: none; padding: 3px 6px; border-radius: 6px; }
.mk-ext:hover { color: var(--fg); background: var(--pill); }
.mk-sheet .mk-hint .mk-ext { padding: 0; font-family: var(--code); font-size: 11.5px; }
.mk-sheet .mk-hint .mk-ext:hover { background: none; text-decoration: underline; text-underline-offset: 2px; }
.mk-sheet .bar .mk-id { margin-left: 2px; }
.lib-rtk-gain { margin: 6px 12px 10px; font-size: 12px; line-height: 1.5; color: var(--muted); }
.lib-card > .lib-acts { padding: 0 12px 12px; }
.lib-rtk-note { margin: 6px 12px 0; font-size: 12px; line-height: 1.5; color: var(--amber); }
.lib-rtk-chart { margin: 0 12px 12px; padding: 10px 12px 8px; }
.lib-rtk-chart .bars { height: 88px; }
.lib-rtk-foot { display: flex; align-items: center; gap: 12px; margin-top: 8px; font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.lib-rtk-foot .grow { flex: 1; }
.lib-rtk-key { display: inline-flex; align-items: center; gap: 5px; }
.lib-rtk-key i { width: 8px; height: 8px; border-radius: 2px; background: var(--accent); }
.lib-rtk-key i.in { opacity: .38; }
.lib-cardhead button.text { height: 22px; font-size: 11.5px; }
.lib-rtk-cmd { margin: -4px 12px 10px; font: 11.5px var(--code); color: var(--muted); user-select: text; }
/* projects: a card each, its skills shown when it is opened */
.lib-project { margin-bottom: 8px; }
.lib-project.missing .lib-projhead .name { color: var(--muted); }
.lib-projhead .lib-tags { max-width: 45%; overflow: hidden; flex-wrap: nowrap; }
.lib-tag.lib-dot::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--muted); }
.lib-projopts { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 8px 12px 8px 49px; border-top: 1px solid var(--line-2); background: var(--card-2); }
.lib-projopts .note { font-size: 11px; color: var(--faint); line-height: 1.45; min-width: 0; flex: 1 1 220px; }
.lib-projcopy { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; color: var(--fg-2); cursor: default; }
.lib-project .lib-projskill { border-top: 1px solid var(--line-2); }
.lib-projfoot { margin: 0; padding: 8px 12px 10px 49px; border-top: 1px solid var(--line-2); }
.lib-projfoot + .lib-projfoot { border-top: 0; padding-top: 0; }
/* the library's skills: a card for each GitHub repository they came from,
and one for those on this computer, folded to its heading line */
.lib-groups { display: flex; flex-direction: column; gap: 8px; }
.lib-grouphead .sub { font-size: 11px; }
.lib-grouphead > .lib-logo, .lib-grouphead > .lib-glyph { align-self: center; margin-top: 0; }
.lib-grouphead .lib-updall { height: 24px; padding: 0 9px; font-size: 12px; }
.lib-have .ic.some { opacity: .4; }
#view-library .row-head .lib-skillq { width: 190px; height: 24px; margin: 0; flex: 0 1 auto; min-width: 90px; }
.lib-skillq::-webkit-search-cancel-button { -webkit-appearance: none; }
/* a project's skills out of view are neither laid out nor painted until they near it */
.lib-projskill { content-visibility: auto; contain-intrinsic-size: auto 76px; }
.lib-grouphead .lib-rowacts { min-width: 26px; }
/* A group's skills are drawn near the view first (library.js, windowed),
room kept for the rest, which are drawn in while nothing else is going
on and then stay. Every row is held to the height the list counts on —
its description one line, the whole of it in its tooltip — so the room
kept is exactly what's missing. */
.lib-vl { flex: none; }
/* each row kept to itself, as a skipped one was; and a row the pointer
passes over in a scroll shows its buttons at once rather than fading them
in — in WebKit each fade, given a layer of its own while it ran, cost a
long frame (library.js marks a list .scrolling while it scrolls) */
.lib-vl > .row { border-top: 1px solid var(--line-2); overflow: hidden; flex-wrap: nowrap; contain: layout paint style; }
.lib-vl.scrolling > .row .lib-rowacts { transition: none; }
.lib-vl.bare > .row.first { border-top: 0; }
.lib-vl > .row.lib-skill { height: 51px; min-height: 0; padding: 7px 10px 7px 12px; gap: 11px; }
.lib-vl > .row.lib-skill.src { height: 67px; }
.lib-vl > .lib-skill .sub { display: block; white-space: nowrap; -webkit-line-clamp: none; }
.lib-vl > .lib-skill .lib-src { margin-top: 2px; }
/* a part of a group: the folder its skills sit in, or the start of their names */
.lib-vl > .row.lib-subhead { height: 36px; min-height: 0; padding: 0 12px 0 14px; gap: 8px; }
.lib-subhead { background: var(--card-2); }
.lib-subhead .name { font-size: 12px; color: var(--fg-2); }
.lib-subhead .name.mono { font: 500 11.5px var(--code); }
.lib-subhead .sub { margin: 0; font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums; }
.lib-subhead .lib-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex: none; }
.lib-row.lib-subhead:hover { background: color-mix(in srgb, var(--card-2) 70%, var(--pill)); }
/* no color-mix() before Safari 16.2 (#220): the sticky head stays opaque */
@supports not (color: color-mix(in srgb, red 50%, blue)) { .lib-head { background: var(--bg); } }