CI and images / lint (push) Successful in 2s
CI and images / extension-version (push) Successful in 2s
CI and images / extension-test (push) Successful in 20s
CI and images / frontend-build (push) Successful in 24s
CI and images / backend-lint-and-test (push) Successful in 32s
CI and images / integration (push) Successful in 2m22s
CI and images / build-agent (push) Successful in 5s
CI and images / sign-extension (push) Successful in 3m13s
CI and images / build-web (push) Successful in 1m42s
CI and images / smoke-web (push) Successful in 54s
CI and images / promote (push) Successful in 1s
Server (#4420) - extension_service gains a Discord pattern (server or channel, jump links, ptb/canary; not DMs or threads), mirrored in platforms.js and pinned by the shared artist-url-samples.json. - probe on a Discord URL matches the source by ids under any artist, reports a whole-server source as covering the channel, suggests the artist who owns another source on the same server, and names server/channel via the stored token (best-effort, bounded, no rate-limit waits). - quick-add takes artist_id / artist_name; Discord URLs are stored canonical. Extension (#4421, #4422) - Content script on discord.com; SPA navigation by URL polling (the old pushState patch ran in the isolated world and never fired); stale probes are dropped. - Discord chip opens an Add panel: this channel or the whole server, and the suggested artist / a search / a new name. - Popup: sources show artist, platform and state; a Discord token export is verified by FC and the result shown. Token capture covers ptb/canary. - Pure logic in lib/chip.js and lib/popup-format.js, with specs. CI (#4423) - extension.yml's lane (web-ext lint, vitest, XPI contents) moves into build.yml as extension-test and joins the needs of sign-extension, build-web and build-agent. As a separate workflow it gated nothing: a red extension suite still signed and shipped the XPI (rule 177). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
85 lines
3.7 KiB
CSS
85 lines
3.7 KiB
CSS
.fc-add-source-btn {
|
|
all: revert;
|
|
position: fixed; bottom: 24px; right: 24px; z-index: 2147483647;
|
|
padding: 10px 16px; border-radius: 999px; border: none;
|
|
background: rgb(20, 23, 26); color: rgb(244, 186, 122);
|
|
font: 500 14px/1.2 system-ui, sans-serif;
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); cursor: pointer;
|
|
transition: transform 100ms ease, background 150ms ease, color 150ms ease;
|
|
}
|
|
.fc-add-source-btn:hover { transform: translateY(-1px); }
|
|
.fc-add-source-btn:disabled { opacity: 0.6; cursor: wait; }
|
|
|
|
/* state colors map to the FC palette: parchment-on-slate base,
|
|
accent-orange for new, sage for already-subscribed, amber-warning for
|
|
artist-exists-but-source-missing. All readable on the dark base. */
|
|
.fc-add-source-btn--new {
|
|
background: rgb(20, 23, 26); color: rgb(244, 186, 122);
|
|
}
|
|
.fc-add-source-btn--artist-match {
|
|
background: rgb(28, 23, 16); color: rgb(255, 200, 120);
|
|
border: 1px solid rgb(180, 130, 60);
|
|
}
|
|
.fc-add-source-btn--source-match {
|
|
background: rgb(18, 28, 20); color: rgb(140, 220, 160);
|
|
border: 1px solid rgb(80, 160, 100);
|
|
}
|
|
|
|
.fc-toast {
|
|
all: revert;
|
|
position: fixed; bottom: 84px; right: 24px; z-index: 2147483647;
|
|
max-width: 360px; padding: 12px 16px; border-radius: 8px;
|
|
background: rgb(20, 23, 26); color: rgb(232, 228, 216);
|
|
font: 14px/1.4 system-ui, sans-serif;
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
animation: fc-toast-in 200ms ease;
|
|
}
|
|
.fc-toast--success { border-left: 3px solid rgb(244, 186, 122); }
|
|
.fc-toast--error { border-left: 3px solid rgb(220, 80, 80); }
|
|
@keyframes fc-toast-in {
|
|
from { transform: translateY(20px); opacity: 0; }
|
|
to { transform: translateY(0); opacity: 1; }
|
|
}
|
|
|
|
/* Discord Add panel — sits above the chip. Same slate/parchment palette. */
|
|
.fc-panel {
|
|
all: revert;
|
|
position: fixed; bottom: 76px; right: 24px; z-index: 2147483647;
|
|
box-sizing: border-box; width: 320px; max-width: calc(100vw - 48px);
|
|
padding: 14px 16px; border-radius: 10px;
|
|
background: rgb(20, 23, 26); color: rgb(232, 228, 216);
|
|
border: 1px solid rgb(60, 64, 70);
|
|
font: 14px/1.4 system-ui, sans-serif;
|
|
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
|
|
}
|
|
.fc-panel__title { font-weight: 600; color: rgb(244, 186, 122); }
|
|
.fc-panel__sub { color: rgb(170, 166, 156); font-size: 12px; margin-bottom: 8px; }
|
|
.fc-panel__label {
|
|
margin: 10px 0 4px; font-size: 11px; letter-spacing: 0.06em;
|
|
text-transform: uppercase; color: rgb(170, 166, 156);
|
|
}
|
|
.fc-panel__radio { display: flex; gap: 8px; align-items: center; padding: 2px 0; cursor: pointer; }
|
|
.fc-panel__radio input { margin: 0; accent-color: rgb(244, 186, 122); }
|
|
.fc-panel__input {
|
|
all: revert; box-sizing: border-box; width: 100%;
|
|
padding: 7px 9px; border-radius: 6px;
|
|
border: 1px solid rgb(70, 74, 80); background: rgb(12, 14, 16); color: inherit;
|
|
font: inherit;
|
|
}
|
|
.fc-panel__input:focus { outline: 2px solid rgb(244, 186, 122); outline-offset: -1px; }
|
|
.fc-panel__results { display: flex; flex-direction: column; max-height: 160px; overflow-y: auto; }
|
|
.fc-panel__result {
|
|
all: revert; text-align: left; padding: 6px 9px; border: none; border-radius: 4px;
|
|
background: transparent; color: inherit; font: inherit; cursor: pointer;
|
|
}
|
|
.fc-panel__result:hover, .fc-panel__result:focus { background: rgb(36, 40, 46); }
|
|
.fc-panel__hint { margin-top: 8px; font-size: 12px; color: rgb(170, 166, 156); }
|
|
.fc-panel__actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
|
|
.fc-panel__btn {
|
|
all: revert; padding: 6px 14px; border-radius: 999px; cursor: pointer;
|
|
border: 1px solid rgb(70, 74, 80); background: transparent; color: inherit;
|
|
font: 500 13px/1.2 system-ui, sans-serif;
|
|
}
|
|
.fc-panel__btn--primary { border-color: rgb(244, 186, 122); background: rgb(244, 186, 122); color: rgb(20, 23, 26); }
|
|
.fc-panel__btn:disabled { opacity: 0.5; cursor: default; }
|