Files
magpie/internal/gui/assets/plugins.css
T
tsuixl bd33e575f1 gui: let users choose installed interface and code fonts (#1007)
* gui: choose installed interface and code fonts and styles

Discover installed styles through DirectWrite, CoreText and Fontconfig. Keep choices per computer, apply them before paint and notify both desktop webviews after saving; defaults retain platform and Omarchy fallbacks.

Verified seven focused Go tests, including real Windows discovery (90 families, 276 styles), 14 font browser cases in Chromium/WebKit, 68 settings/zoom/Omarchy/web cases, go vet, Windows amd64/arm64 and Linux nogui builds. Against the original assets, the new UI test fails with Settings must offer an interface font picker (0 != 1). Full Windows suites have separately reproduced upstream failures; macOS/Linux native discovery is covered by the added CI step.

* gui: retain bundled Mac fonts and variable style precision

Only filter an undownloaded CoreText face when it is explicitly downloadable; bundled fonts are installed without being downloaded. Use resolved font names when descriptors omit them. Preserve fractional weights and nonnegative CSS widths for named variable styles.

* gui: restore the authored font cascade when returning to defaults

Apply family-only trait overrides only while a custom face is active. The browser regression catches code under a bold parent becoming 700 instead of its original 400. Keep the compatibility shim first, and apply boot fonts before visible content.

* gui: keep font picker review separate from CI integration

Leave the native test sources and manual verification command with the feature. Move the added workflow step to a follow-up so the font picker can take the normal review path requested on PR #1007.
2026-10-06 19:32:56 +08:00

221 lines
16 KiB
CSS

/* Plugins: the market of provider plugins, and the ones installed. */
#view-plugins { gap: 0; }
.pm-head { gap: 10px; }
.pm-mirror { display: inline-flex; align-items: center; gap: 7px; padding: 2px 0; background: none; border: 0; font: inherit; font-size: 12px; color: var(--muted); cursor: pointer; white-space: nowrap; }
.pm-mirror:hover, .pm-mirror.on { color: var(--fg); }
.pm-mirror:disabled { opacity: .6; cursor: default; }
.pm-mirror .lib-switch { width: 28px; height: 16px; }
.pm-mirror .lib-switch i { width: 12px; height: 12px; }
.pm-mirror .lib-switch.on i { transform: translateX(12px); }
.pm-find {
display: flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px; width: min(280px, 46%);
border-radius: 8px; background: var(--pill); color: var(--muted);
box-shadow: inset 0 0 0 1px transparent; transition: box-shadow .14s var(--ease), background .14s var(--ease);
}
.pm-find:focus-within { background: var(--card); box-shadow: inset 0 0 0 1px var(--accent), 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
.pm-find svg { flex: none; }
.pm-find input { flex: 1; min-width: 0; border: 0; outline: none; background: none; color: var(--fg); font: inherit; font-size: 12.5px; }
.pm-find input::placeholder { color: var(--faint); }
.pm-find input::-webkit-search-cancel-button { cursor: default; }
.pm-body { gap: 18px; padding: 10px 0 18px; }
/* the opening: what plugins are, and a few of the subscriptions they bring */
.pm-intro {
position: relative; overflow: hidden; display: flex; align-items: center; gap: 18px;
padding: 16px 18px; border-radius: 14px; border: 1px solid var(--line);
background:
radial-gradient(120% 140% at 100% 0%, color-mix(in srgb, var(--accent) 11%, transparent), transparent 60%),
var(--card);
}
.pm-introtext { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.pm-intro h2 { margin: 0; font-size: 15px; font-weight: 650; letter-spacing: -.01em; text-wrap: balance; }
.pm-intro p { margin: 0; font-size: 12px; line-height: 1.55; color: var(--fg-2); max-width: 560px; }
.pm-intro .pm-trust { display: flex; align-items: center; gap: 6px; margin-top: 3px; font-size: 11.5px; color: var(--muted); }
.pm-intro .pm-trust svg { flex: none; color: var(--green); }
.pm-art { flex: none; display: grid; grid-template-columns: repeat(3, 34px); gap: 8px; transform: rotate(-6deg); margin-right: 6px; }
.pm-art .pm-logo { width: 34px; height: 34px; border-radius: 10px; box-shadow: 0 1px 2px rgba(0, 0, 0, .06), 0 0 0 1px var(--line-2); background: var(--card); }
.pm-art .pm-logo:nth-child(4) { grid-column: 2; }
.pm-art .pm-logo:nth-child(odd) { transform: translateY(4px); }
/* sections of cards */
.pm-sec { display: flex; flex-direction: column; gap: 9px; }
.pm-sechead { display: flex; align-items: baseline; gap: 8px; padding: 0 2px; flex-wrap: wrap; }
.pm-sechead h3 { margin: 0; font-size: 12.5px; font-weight: 650; }
.pm-sechead span { font-size: 11.5px; color: var(--muted); }
.pm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(262px, 1fr)); gap: 10px; }
.pm-logo { flex: none; width: 36px; height: 36px; border-radius: 10px; display: grid; place-items: center; background: var(--pill); color: var(--fg-2); }
.pm-logo .ic { width: 20px; height: 20px; }
.pm-logo.big { width: 44px; height: 44px; border-radius: 12px; }
.pm-logo.big .ic { width: 25px; height: 25px; }
.pm-card {
display: flex; flex-direction: column; gap: 8px; min-width: 0; padding: 12px 12px 11px 13px;
border-radius: 12px; border: 1px solid var(--line); background: var(--card); cursor: default; outline: none;
transition: border-color .16s var(--ease), box-shadow .16s var(--ease), transform .16s var(--ease);
}
.pm-card:hover { border-color: color-mix(in srgb, var(--fg) 16%, var(--line)); box-shadow: 0 4px 14px rgba(20, 22, 30, .07); }
.pm-card:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 28%, transparent); border-color: var(--accent); }
.pm-top { display: flex; align-items: center; gap: 10px; min-width: 0; }
.pm-who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pm-name { display: flex; align-items: center; gap: 6px; min-width: 0; }
.pm-name b { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pm-by { display: flex; align-items: center; gap: 4px; min-width: 0; font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pm-by > span { overflow: hidden; text-overflow: ellipsis; }
.pm-verified { display: inline-flex; align-items: center; gap: 3px; color: var(--green); font-weight: 500; }
.pm-sum {
margin: 0; font-size: 12px; line-height: 1.45; color: var(--fg-2); min-height: 2.9em;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.pm-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; min-height: 18px; }
.pm-dl { display: inline-flex; align-items: center; gap: 3px; }
.pm-ver { font: var(--code-style, normal) var(--code-weight, 400) 10.5px var(--code-font, var(--code)); font-stretch: var(--code-stretch, 100%); color: var(--muted); }
.pm-chip { display: inline-flex; align-items: center; height: 17px; padding: 0 6px; border-radius: 5px; font-size: 10.5px; font-weight: 500; background: var(--pill); color: var(--fg-2); }
.pm-chip.up { background: var(--accent-soft); color: var(--accent); }
button.pm-chip { border: 0; font-family: inherit; cursor: pointer; }
button.pm-chip.up:hover:not(:disabled) { background: var(--accent); color: var(--accent-fg); }
button.pm-chip:disabled { cursor: default; opacity: .6; }
.pm-mw-fail { color: var(--red); }
.pm-chip.bad { background: var(--red-soft); color: var(--red); }
.pm-chip.moved { gap: 4px; background: var(--accent-soft); color: var(--accent); }
.pm-chip.moved .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.pm-row .sub.ask { color: var(--fg); }
.pm-chip.warn { gap: 4px; background: var(--amber-soft); color: var(--amber); }
.pm-chip.warn .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.pm-foot .pm-check { display: inline-flex; align-items: center; gap: 5px; }
.pm-foot .pm-check .spin { width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid currentColor; border-right-color: transparent; animation: spin .7s linear infinite; }
.pm-chip.kind { gap: 3px; flex: none; }
.pm-chip.kind svg { flex: none; }
.pm-chip.kind.pv { background: var(--green-soft); color: var(--green); }
.pm-chip.kind.mw { background: var(--accent-soft); color: var(--accent); }
.pm-chip.soft { background: none; box-shadow: inset 0 0 0 1px var(--line); color: var(--muted); }
/* a plugin's one button */
.pm-act {
flex: none; display: inline-flex; align-items: center; justify-content: center; gap: 5px;
height: 26px; min-width: 64px; padding: 0 12px; border-radius: 13px;
font-size: 12px; font-weight: 600; white-space: nowrap;
background: var(--pill); color: var(--fg-2); transition: background .14s var(--ease), color .14s var(--ease), filter .14s;
}
.pm-act:hover:not(:disabled) { background: var(--pill-hover); color: var(--fg); }
.pm-act.get { background: var(--accent-soft); color: var(--accent); }
.pm-act.get:hover:not(:disabled) { background: var(--accent); color: var(--accent-fg); }
.pm-act.go { background: var(--accent); color: var(--accent-fg); }
.pm-act.go:hover:not(:disabled) { filter: brightness(1.08); background: var(--accent); color: var(--accent-fg); }
.pm-act.done { background: none; color: var(--green); padding: 0 8px; min-width: 0; }
.pm-act.done:hover:not(:disabled) { background: var(--green-soft); color: var(--green); }
.pm-act.busy { color: var(--muted); font-weight: 500; }
.pm-act:disabled:not(.busy):not(.done) { opacity: .55; }
.pm-act .spin { width: 10px; height: 10px; border-radius: 50%; border: 1.5px solid currentColor; border-right-color: transparent; animation: spin .7s linear infinite; }
/* loading: cards drawn as grey bars */
.pm-card.ghost { gap: 10px; pointer-events: none; }
.pm-card.ghost > div { border-radius: 6px; background: var(--pill); animation: pm-pulse 1.3s ease-in-out infinite; }
.pm-card.ghost .g1 { height: 36px; width: 60%; }
.pm-card.ghost .g2 { height: 11px; }
.pm-card.ghost .g3 { height: 11px; width: 40%; }
@keyframes pm-pulse { 50% { opacity: .5; } }
.pm-note { margin: 0; padding: 4px 2px; font-size: 12px; color: var(--muted); }
.pm-note.bad { color: var(--red); }
.pm-empty { margin: 24px 4px; font-size: 12.5px; color: var(--red); }
/* a package the market doesn't list */
.pm-manual { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-radius: 12px; border: 1px dashed var(--line); flex-wrap: wrap; }
.pm-mlabel { font-size: 12px; color: var(--muted); }
.pm-minput { flex: 1; min-width: 180px; height: 26px; padding: 0 9px; border-radius: 7px; border: 1px solid var(--line); background: var(--card); color: var(--fg); font: var(--code-style, normal) var(--code-weight, 400) 12px var(--code-font, var(--code)); font-stretch: var(--code-stretch, 100%); outline: none; }
.pm-minput:focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
/* the system's folder picker, beside the field it fills */
.pm-browse { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; flex: none; padding: 0; border-radius: 7px; border: 1px solid var(--line); background: var(--card); color: var(--muted); cursor: pointer; transition: color .14s var(--ease), border-color .14s var(--ease); }
.pm-browse:hover:not(:disabled) { color: var(--fg); border-color: var(--faint); }
.pm-browse:disabled { opacity: .5; cursor: default; }
/* Installed */
.pm-listhead { margin: 0 0 -10px; padding: 0 2px; }
.pm-list .row { min-height: 54px; padding: 8px 10px 8px 12px; gap: 11px; cursor: default; transition: background .14s var(--ease); }
.pm-list .row:hover { background: var(--card-2); }
.pm-list .row + .row { border-top: 1px solid var(--line-2); }
.pm-row .pm-logo { width: 32px; height: 32px; border-radius: 9px; }
.pm-row .pm-logo .ic { width: 18px; height: 18px; }
.pm-row .who { flex: 1; min-width: 0; }
.pm-row .name { display: flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 500; }
.pm-row .sub { display: flex; align-items: center; flex-wrap: wrap; gap: 0 2px; margin-top: 2px; font-size: 11.5px; color: var(--muted); }
.pm-row .sub.bad { color: var(--red); display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pm-row .sub .sep { color: var(--faint); }
.pm-sub { display: inline-flex; align-items: center; gap: 5px; }
.pm-sub .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--faint); }
.pm-sub.in .dot { background: var(--green); }
.pm-opts { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.pm-opts textarea {
width: 100%; box-sizing: border-box; resize: vertical; padding: 8px 10px; border-radius: 8px;
border: 1px solid var(--line); background: var(--card); color: var(--fg); font: var(--code-style, normal) var(--code-weight, 400) 12px/1.5 var(--code-font, var(--code)); font-stretch: var(--code-stretch, 100%); tab-size: 2; outline: none;
}
.pm-opts textarea:focus { border-color: var(--accent); }
.pm-opts-err { font-size: 11.5px; color: var(--red); }
.pm-opts-err:empty { display: none; }
.pm-opts-bar { display: flex; align-items: center; gap: 2px; }
.pm-opts-bar > span { flex: 1; min-width: 0; font-size: 11.5px; color: var(--muted); }
.pm-row .val { display: flex; align-items: center; gap: 2px; flex: none; }
.pm-row.off .who, .pm-row.off .pm-logo { opacity: .5; }
.pm-foot { display: flex; align-items: center; gap: 8px; padding: 0 4px; font-size: 11px; color: var(--faint); }
.pm-none { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 44px 16px; text-align: center; }
.pm-none b { font-size: 13.5px; font-weight: 600; margin-top: 4px; }
.pm-none p { margin: 0 0 6px; font-size: 12px; color: var(--muted); max-width: 320px; line-height: 1.5; }
/* a plugin's page */
.modal .editor.pm-detail.framed > .ehead { min-height: 76px; gap: 12px; }
.modal .editor.pm-detail.framed > .ebody { display: block; padding: 0; }
.pm-dhead .pm-name b { font-size: 15px; }
.pm-dhead .pm-by { font-size: 11.5px; }
.pm-dact .pm-act { height: 28px; min-width: 80px; border-radius: 14px; }
.pm-dbody { display: flex; flex-direction: column; gap: 12px; padding: 14px 18px 18px; }
.pm-dsum { margin: 0; font-size: 13px; line-height: 1.5; color: var(--fg); }
.pm-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 1px; border-radius: 10px; overflow: hidden; background: var(--line-2); border: 1px solid var(--line-2); }
.pm-fact { display: flex; flex-direction: column; gap: 2px; padding: 8px 11px; background: var(--card); min-width: 0; }
.pm-fact .k { font-size: 10.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.pm-fact .v { font-size: 12.5px; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.pm-links { display: flex; gap: 6px; flex-wrap: wrap; }
.pm-link { display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 9px; border-radius: 7px; background: var(--pill); color: var(--fg-2); font-size: 11.5px; text-decoration: none; }
.pm-link:hover { background: var(--pill-hover); color: var(--fg); }
.pm-readme { border-top: 1px solid var(--line-2); padding-top: 12px; min-height: 60px; }
.pm-rskel { height: 11px; margin: 8px 0; border-radius: 5px; background: var(--pill); animation: pm-pulse 1.3s ease-in-out infinite; }
.pm-rskel.short { width: 55%; }
/* a README */
.pm-md { font-size: 12.5px; line-height: 1.6; color: var(--fg-2); overflow-wrap: anywhere; }
.pm-md > :first-child { margin-top: 0; }
.pm-md h3, .pm-md h4, .pm-md h5, .pm-md h6 { color: var(--fg); margin: 16px 0 6px; line-height: 1.3; text-wrap: balance; }
.pm-md h3 { font-size: 15px; font-weight: 650; }
.pm-md h4 { font-size: 13.5px; font-weight: 650; }
.pm-md h5, .pm-md h6 { font-size: 12.5px; font-weight: 600; }
.pm-md p { margin: 0 0 9px; }
.pm-md ul, .pm-md ol { margin: 0 0 9px; padding-left: 20px; }
.pm-md li + li { margin-top: 2px; }
.pm-md a { color: var(--accent); text-decoration: none; }
.pm-md a:hover { text-decoration: underline; }
.pm-md code { font: var(--code-style, normal) var(--code-weight, 400) 11.5px var(--code-font, var(--code)); font-stretch: var(--code-stretch, 100%); padding: 1px 4px; border-radius: 4px; background: var(--pill); color: var(--fg); }
.pm-md pre { margin: 0 0 10px; padding: 9px 11px; border-radius: 8px; background: var(--card-2); border: 1px solid var(--line-2); overflow-x: auto; }
.pm-md pre code { padding: 0; background: none; font-size: 11.5px; line-height: 1.35; white-space: pre; } /* close enough for a box drawn in │ and ─ to join */
.pm-md blockquote { margin: 0 0 9px; padding: 7px 11px; border-radius: 8px; background: var(--card-2); color: var(--fg-2); }
.pm-md hr { border: 0; border-top: 1px solid var(--line-2); margin: 12px 0; }
.pm-table { overflow-x: auto; margin: 0 0 10px; }
.pm-md table { border-collapse: collapse; font-size: 12px; }
.pm-md th, .pm-md td { padding: 5px 9px; border: 1px solid var(--line-2); text-align: left; vertical-align: top; }
.pm-md th { background: var(--card-2); font-weight: 600; color: var(--fg); }
/* the add sheet's row to Plugins */
.tile.more-plugins { color: var(--accent); }
.tile.more-plugins .puzzle { flex: none; width: 18px; height: 18px; display: grid; place-items: center; }
.tile.more-plugins > svg { flex: none; opacity: .7; }
.tile.more-plugins:hover { background: var(--accent-soft); }
@media (max-width: 560px) {
.pm-head { flex-wrap: wrap; }
.pm-find { width: 100%; order: 3; }
.pm-art { display: none; }
}
@media (prefers-reduced-motion: reduce) {
.pm-card { transition: none; }
.pm-card.ghost > div, .pm-rskel { animation: none; }
}