dustinkeeton_obsidian-synapse/styles.css
bot 31ddcf6037
feat: promote the time-range choice from a toast to a modal
A blocking follow-up decision deserves a first-class surface: the
trim-range picker is now a TimeRangeModal (front and center, can't be
lost to a stray click) replacing both the slider toast and the fallback
text-input toast. Dismissing it resolves 'cancelled' — do nothing —
instead of silently transcribing the whole file.

The trim bar's handles now actually overlay the bar: Obsidian styles
input[type='range'] itself at higher specificity than a bare class, so
every slider rule is prefixed with the attribute selector. The duplicate
range readout above the bar and the redundant "Duration:" label are
gone — the live start/end endpoint labels say it all.

Co-Authored-By: Claude <bot@wafflenet.io>
Claude-Session: https://claude.ai/code/session_012HVfTic7eKWvFiEYkoJe6m
2026-07-15 01:38:58 -07:00

1558 lines
41 KiB
CSS

/* Synapse plugin styles */
/* ══════════════════════════════════════════════════════════════
* Action-type color tokens — SINGLE SOURCE OF TRUTH (#342)
*
* One semantic token per Synapse action type. EVERY action-type surface
* (cards, badges, review labels, checklist headings, callouts, actions-sidebar
* groups, modal accents) consumes these — no action-type hue is hardcoded
* anywhere else. Re-theming a type is a one-line edit here.
*
* Base tokens map to Obsidian's `--color-*` vars so they adapt to the active
* theme (and light/dark) automatically. The TypeScript guard in
* `src/views/proposal-styles.ts` (`Record<ProposalKind, string>`) fails the
* build if a proposal kind ever loses its token.
*
* Notes:
* - `elaboration` uses `--color-blue` (moved OFF `--interactive-accent` so it
* no longer collides with the user's accent or the generic CTA buttons).
* - `tidy` intentionally shares organize's hue (sibling structural-cleanup
* ops) but keeps its own token, so it can diverge with a one-line change.
* - `main` ("General") is neutral.
* ══════════════════════════════════════════════════════════════ */
.theme-light,
.theme-dark {
--synapse-color-elaboration: var(--color-blue);
--synapse-color-enrichment: var(--color-green);
--synapse-color-organize: var(--color-orange);
--synapse-color-deep-dive: var(--color-purple);
--synapse-color-title: var(--color-cyan);
--synapse-color-rem: var(--color-pink);
--synapse-color-summarize: var(--color-red);
--synapse-color-tidy: var(--color-orange);
--synapse-color-video: var(--color-yellow);
--synapse-color-main: var(--text-muted);
/*
* Callout tints take comma-separated RGB CHANNELS (not a color value), so
* these companion tokens carry literal channels aligned to each hue above.
* Only the types that render as callouts need an `-rgb` companion. Channels
* are fixed (theme vars are not channel-decomposable), matching how the
* callouts already worked; the low-opacity tint reads fine in light + dark.
*/
--synapse-color-elaboration-rgb: 100, 160, 255;
--synapse-color-enrichment-rgb: 80, 180, 120;
--synapse-color-deep-dive-rgb: 160, 120, 220;
--synapse-color-summarize-rgb: 230, 110, 110;
--synapse-color-video-rgb: 220, 180, 70;
}
/*
* Brand impulse gold (2026 Iris+Gold refresh). Every registered glyph draws
* its body in currentColor and its single gold gesture in this var (see
* src/brand-icons.ts / assets/brand/README.md). Deepened on light surfaces
* for contrast against white.
*/
body {
--synapse-gold: #FFD23F;
}
.theme-light {
--synapse-gold: #E8B419;
}
/*
* Cap the height of embedded videos so portrait-oriented content
* (TikTok, Instagram Reels, phone recordings) does not dominate the note.
* Landscape videos are unaffected because they hit width constraints first.
*/
.markdown-preview-view video,
.markdown-reading-view video,
.markdown-source-view video {
max-height: 50vh;
width: auto;
}
/* ── Enhanced slider labels and tick marks ── */
.synapse-slider-wrapper {
display: flex;
flex-direction: column;
width: 100%;
min-width: 200px;
gap: 0;
}
.synapse-slider-wrapper input[type="range"] {
width: 100%;
}
.synapse-slider-current-value {
text-align: right;
font-size: var(--font-ui-small);
font-weight: var(--font-semibold);
color: var(--text-accent);
margin-bottom: 2px;
min-width: 2em;
}
.synapse-slider-labels {
display: flex;
justify-content: space-between;
font-size: var(--font-ui-smaller);
color: var(--text-muted);
margin-top: 2px;
padding: 0 2px;
}
.synapse-slider-ticks {
display: flex;
justify-content: space-between;
padding: 0 6px;
margin-top: 1px;
}
.synapse-slider-tick {
width: 1px;
height: 6px;
background-color: var(--text-faint);
flex-shrink: 0;
}
/* ── AI content callout styles ── */
.callout[data-callout^="synapse-"] {
--callout-blend-mode: normal;
border-left-width: 3px;
opacity: 0.95;
}
/* synapse-summary ↔ summarize feature */
.callout[data-callout="synapse-summary"] {
--callout-color: var(--synapse-color-summarize-rgb);
--callout-icon: lucide-file-text;
}
/* synapse-transcription ↔ video feature */
.callout[data-callout="synapse-transcription"] {
--callout-color: var(--synapse-color-video-rgb);
--callout-icon: lucide-mic;
}
.callout[data-callout="synapse-enrichment"] {
--callout-color: var(--synapse-color-enrichment-rgb);
--callout-icon: lucide-link;
}
.callout[data-callout="synapse-elaboration"] {
--callout-color: var(--synapse-color-elaboration-rgb);
--callout-icon: lucide-pen-tool;
}
.callout[data-callout="synapse-deep-dive"] {
--callout-color: var(--synapse-color-deep-dive-rgb);
--callout-icon: lucide-layers;
}
/* ── Modal action buttons ── */
.synapse-modal-actions {
display: flex;
gap: 8px;
justify-content: flex-end;
margin-top: 16px;
}
/* ── Modal type accents (#342) ──
* A type-colored underline beneath the modal title ties each detail modal to
* its action type, matching the sidebar card/badge for that type. */
.synapse-elaboration-detail h2 {
border-bottom: 2px solid var(--synapse-color-elaboration);
padding-bottom: 6px;
}
.synapse-enrichment-detail h2 {
border-bottom: 2px solid var(--synapse-color-enrichment);
padding-bottom: 6px;
}
/* ── Proposal editor textarea ── */
.synapse-proposal-editor {
width: 100%;
font-family: var(--font-monospace, monospace);
max-height: 60vh;
resize: vertical;
}
/* ── Settings tab version footer ── */
.synapse-settings-footer {
margin-top: 24px;
text-align: center;
opacity: 0.7;
}
/* ── Dual-handle time-range slider (video trim bar) ──
*
* Styled as an editor's trim bar: a quiet rail, a solid gold selection
* between two vertical pill "in/out" handles. Gold (--synapse-gold) marks
* the selection — the one thing the user is adding — and everything else
* stays on neutral theme tokens so the control sits natively in any theme.
*
* Selector note: every rule targets `input[type='range'].synapse-…` because
* Obsidian styles `input[type='range']` itself (sizing + white thumb) at
* (0,1,1) specificity — a bare class selector loses and the handles render
* as Obsidian's detached default thumbs instead of overlaying the bar.
*/
.synapse-time-range-container {
display: flex;
flex-direction: column;
gap: 4px;
width: 100%;
padding: 8px 0 2px;
}
.synapse-time-range-track-wrapper {
position: relative;
height: 32px;
width: 100%;
}
/* Base rail — drawn once here so both range inputs stay transparent */
.synapse-time-range-track-wrapper::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
height: 6px;
background: var(--background-modifier-border);
border-radius: 3px;
}
/* Selected region: solid gold fill between the trim handles */
.synapse-time-range-track-highlight {
position: absolute;
top: 50%;
transform: translateY(-50%);
height: 6px;
background-color: var(--synapse-gold, #FFD23F);
border-radius: 3px;
pointer-events: none;
z-index: 0;
}
input[type='range'].synapse-time-range-input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background: none;
pointer-events: none;
-webkit-appearance: none;
appearance: none;
z-index: 1;
}
/* Trim handles: vertical pills (an editor's in/out points), not blobs.
* The invisible 6px runnable track centers the 22px thumb via margin-top. */
input[type='range'].synapse-time-range-input::-webkit-slider-runnable-track {
height: 6px;
background: transparent;
border: none;
}
input[type='range'].synapse-time-range-input::-moz-range-track {
height: 6px;
background: transparent;
border: none;
}
input[type='range'].synapse-time-range-input::-webkit-slider-thumb {
pointer-events: auto;
-webkit-appearance: none;
appearance: none;
width: 12px;
height: 22px;
margin-top: -8px;
border-radius: 6px;
background: var(--synapse-gold, #FFD23F);
border: 2px solid var(--background-primary);
box-shadow: var(--shadow-s);
cursor: grab;
position: relative;
z-index: 2;
}
input[type='range'].synapse-time-range-input::-moz-range-thumb {
pointer-events: auto;
width: 12px;
height: 22px;
border-radius: 6px;
background: var(--synapse-gold, #FFD23F);
border: 2px solid var(--background-primary);
box-shadow: var(--shadow-s);
cursor: grab;
}
input[type='range'].synapse-time-range-input::-webkit-slider-thumb:active {
cursor: grabbing;
}
/* Visible keyboard focus on the handles */
input[type='range'].synapse-time-range-input:focus-visible::-webkit-slider-thumb {
box-shadow: 0 0 0 2px var(--background-modifier-border-focus);
}
input[type='range'].synapse-time-range-input:focus-visible::-moz-range-thumb {
box-shadow: 0 0 0 2px var(--background-modifier-border-focus);
}
/* Live start/end timestamps under their handles */
.synapse-time-range-labels {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 12px;
font-size: var(--font-ui-smaller);
color: var(--text-muted);
font-variant-numeric: tabular-nums;
padding: 0 2px;
}
/* ── Time-range modal (choose what to transcribe) ── */
.synapse-time-range-modal .synapse-time-range-modal-media-title {
color: var(--text-muted);
font-size: var(--font-ui-small);
line-height: 1.4;
margin: 0 0 12px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.synapse-time-range-modal .synapse-time-range-modal-desc {
font-size: var(--font-ui-small);
color: var(--text-muted);
margin-bottom: 10px;
}
.synapse-time-range-modal .synapse-time-range-modal-inputs {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.synapse-time-range-modal .synapse-time-range-modal-time-input {
width: 100px;
padding: 4px 8px;
font-size: var(--font-ui-small);
font-variant-numeric: tabular-nums;
border: 1px solid var(--background-modifier-border);
border-radius: 4px;
background: var(--background-primary);
color: var(--text-normal);
}
/* ── Collapsible settings accordions (#235) ── */
.synapse-accordion {
border-bottom: 1px solid var(--background-modifier-border);
}
.synapse-accordion-header {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 4px;
cursor: pointer;
user-select: none;
border-radius: 6px;
}
.synapse-accordion-header:hover {
background-color: var(--background-modifier-hover);
}
.synapse-accordion-header:focus-visible {
outline: 2px solid var(--interactive-accent);
outline-offset: -2px;
}
.synapse-accordion-chevron {
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
width: 1em;
font-size: 0.7em;
color: var(--text-muted);
/* Rotates from ▶ (collapsed) to ▼ (expanded). */
transform: rotate(90deg);
transition: transform 150ms ease;
}
.synapse-accordion.is-collapsed .synapse-accordion-chevron {
transform: rotate(0deg);
}
.synapse-accordion-title {
flex-grow: 1;
font-weight: var(--font-semibold);
color: var(--text-normal);
}
.synapse-accordion-control {
flex-shrink: 0;
display: flex;
align-items: center;
}
/*
* Per-section "Reset to defaults" footer row (#442). Appended to the bottom of
* each resettable section body as a labeled Setting row (replaces the #420
* header icon button). A subtle top border sets it apart as a section footer;
* the Reset button dims when the section already equals shipped defaults and is
* therefore disabled.
*/
.synapse-section-reset {
border-top: 1px solid var(--background-modifier-border);
margin-top: 8px;
}
.synapse-section-reset button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/*
* The enable toggle lives inside a borderless Setting so we reuse the native
* ToggleComponent. Strip the Setting's default block layout/border/padding so
* it sits inline in the header next to the title.
*/
/*
* Compound `.setting-item.synapse-accordion-toggle-setting` selector wins on
* specificity over Obsidian's `.setting-item` defaults without needing
* `!important` (both classes co-occur on the Setting's settingEl).
*/
.setting-item.synapse-accordion-toggle-setting {
border-top: none;
padding: 0;
margin: 0;
}
.synapse-accordion-toggle-setting .setting-item-info {
display: none;
}
.synapse-accordion-toggle-setting .setting-item-control {
padding: 0;
}
/*
* Body: animate height + opacity on collapse/expand. A generous max-height
* accommodates the tallest feature section; it is purely an animation ceiling.
* Environments without this CSS fall back to instant show/hide via the
* is-collapsed display rule below.
*/
.synapse-accordion-body {
overflow: hidden;
max-height: 4000px;
opacity: 1;
transition: max-height 250ms ease, opacity 200ms ease;
}
.synapse-accordion.is-collapsed .synapse-accordion-body {
max-height: 0;
opacity: 0;
/*
* display:none after the transition would kill the animation, so we keep it
* laid out but zero-height. For no-transition fallbacks, max-height:0 +
* overflow:hidden already hides the content.
*/
pointer-events: none;
}
/* ── Mobile-responsive overrides ── */
@media (max-width: 768px) {
.synapse-slider-wrapper {
min-width: 0;
}
.synapse-accordion-header {
padding: 12px 4px;
}
.synapse-proposal-editor {
max-height: 40vh;
}
.synapse-modal-actions {
flex-direction: column;
}
.synapse-modal-actions button {
width: 100%;
}
}
/* ── Notification toasts (severity + tracked operations) ── */
/*
* Obsidian renders a toast as `.notice > .notice-message`, and styleNotice()
* adds the `synapse-notice*` classes to whatever `Notice.noticeEl` points at —
* the inner `.notice-message` in current Obsidian, not the `.notice` element.
* So the severity class is NOT reliably co-located with `.notice`. Match BOTH
* shapes for each rule:
* - `.notice.synapse-notice` — class on the `.notice` element itself
* - `.notice:has(.synapse-notice)` — class on a descendant (`.notice-message`)
* `:has()` keeps the (0,2,0) specificity (so we still beat core `.notice`
* padding/background without `!important`) AND styles the real `.notice`
* container, so the error wash is full-bleed rather than inset to the message.
*/
.notice.synapse-notice,
.notice:has(.synapse-notice) {
border-left: 3px solid var(--text-muted);
padding-left: 10px;
transition: border-color 0.3s ease;
}
/* Severity colors (source order after the base wins on equal specificity). */
.notice.synapse-notice--info, .notice:has(.synapse-notice--info) { border-left-color: var(--text-muted); }
.notice.synapse-notice--progress, .notice:has(.synapse-notice--progress) { border-left-color: var(--interactive-accent); }
.notice.synapse-notice--success, .notice:has(.synapse-notice--success) { border-left-color: var(--color-green); }
.notice.synapse-notice--warning, .notice:has(.synapse-notice--warning) { border-left-color: var(--color-yellow); }
/*
* Error toasts read unmistakably as errors and are click-to-copy/dismiss
* (see NotificationManager.showErrorNotice): a soft, theme-aware error wash
* behind the red accent, and a pointer cursor to signal they're clickable.
*/
.notice.synapse-notice--error,
.notice:has(.synapse-notice--error) {
border-left-color: var(--color-red);
background-color: var(--background-modifier-error);
cursor: pointer;
}
/* Running operation layout */
.synapse-notice-op {
display: flex;
align-items: center;
gap: 10px;
}
.synapse-notice-op-text {
flex: 1;
}
.synapse-notice-op-dots {
display: inline-block;
min-width: 1.5ch;
text-align: left;
}
.synapse-notice-op-cancel {
flex-shrink: 0;
padding: 2px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
border: 1px solid var(--text-muted);
background: transparent;
color: var(--text-muted);
transition: color 0.15s, border-color 0.15s;
}
.synapse-notice-op-cancel:hover {
color: var(--text-normal);
border-color: var(--text-normal);
}
/* Prevent click-to-dismiss on running operations */
.synapse-notice--no-dismiss {
cursor: default;
}
/* Confirmation snackbar buttons */
.synapse-notice-actions {
display: flex;
gap: 8px;
margin-top: 8px;
}
.synapse-notice-actions button {
padding: 2px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
border: 1px solid var(--background-modifier-border);
}
.synapse-notice-actions button.mod-cta {
background: var(--interactive-accent);
color: var(--text-on-accent);
border-color: var(--interactive-accent);
}
.synapse-notice-actions button.mod-cancel {
background: transparent;
color: var(--text-muted);
}
/*
* Actionable success/info toasts carry a single button (e.g. "Review");
* right-align it so it reads as a trailing call-to-action rather than a
* left-aligned form control. Scoped to single-action toasts so the
* confirmation snackbar's Proceed/Cancel pair is unaffected.
*/
.notice.synapse-notice--success .synapse-notice-actions,
.notice.synapse-notice--info .synapse-notice-actions,
.notice:has(.synapse-notice--success) .synapse-notice-actions,
.notice:has(.synapse-notice--info) .synapse-notice-actions {
justify-content: flex-end;
}
/* ══════════════════════════════════════════════════════════════
* Unified Proposal View (sidebar)
* ══════════════════════════════════════════════════════════════ */
/* ── Layout ── */
.synapse-view-root {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.synapse-view-root > h3 {
flex-shrink: 0;
margin: 0 0 8px;
padding: 0;
}
/* ── List Mode ── */
.synapse-proposal-list {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 4px;
}
.synapse-proposal-card {
border-left: 3px solid var(--text-muted);
padding: 8px 12px;
margin: 6px 0;
border-radius: 4px;
background: var(--background-secondary);
min-height: 100px;
flex: 1 0 auto;
display: flex;
flex-direction: column;
}
.synapse-proposal-card .synapse-preview {
flex: 1;
}
.synapse-proposal-card .synapse-actions {
margin-top: auto;
}
.synapse-card--elaboration {
border-left-color: var(--synapse-color-elaboration);
}
.synapse-card--enrichment {
border-left-color: var(--synapse-color-enrichment);
}
.synapse-card--organize {
border-left-color: var(--synapse-color-organize);
}
.synapse-badge {
display: inline-block;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 1px 6px;
border-radius: 3px;
margin-bottom: 4px;
width: min-content;
white-space: nowrap;
}
.synapse-badge--elaboration {
background: var(--synapse-color-elaboration);
color: var(--text-on-accent);
}
.synapse-badge--enrichment {
background: var(--synapse-color-enrichment);
color: var(--text-on-accent);
}
.synapse-badge--organize {
background: var(--synapse-color-organize);
color: var(--text-on-accent);
}
.synapse-reasons {
display: block;
color: var(--text-muted);
margin: 4px 0;
}
.synapse-preview {
font-size: 13px;
color: var(--text-normal);
margin: 4px 0;
}
.synapse-actions {
display: flex;
gap: 6px;
margin-top: 6px;
}
.synapse-actions button {
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--background-modifier-border);
background: var(--background-primary);
color: var(--text-normal);
}
.synapse-actions button:hover {
background: var(--background-modifier-hover);
}
.synapse-proposal-group h4 {
margin: 12px 0 4px;
font-size: 13px;
color: var(--text-muted);
}
/*
* No underline at rest; underline + accent color on hover. We transition only
* `color` (text-decoration-color animation has partial support on older
* Obsidian/Electron, flagged by the community CSS lint). Loses just the
* underline fade-in animation.
*/
.synapse-note-link {
cursor: pointer;
text-decoration: none;
transition: color 0.15s;
}
.synapse-note-link:hover {
color: var(--text-accent);
text-decoration: underline;
}
.synapse-empty {
color: var(--text-muted);
font-style: italic;
}
/* ── Review Mode (shared) ── */
.synapse-review-header {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 6px;
}
.synapse-review-back {
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--background-modifier-border);
background: var(--background-primary);
color: var(--text-muted);
flex-shrink: 0;
}
.synapse-review-back:hover {
color: var(--text-normal);
border-color: var(--text-muted);
}
.synapse-review-title {
font-weight: 600;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.synapse-review-reasons {
flex-shrink: 0;
display: block;
color: var(--text-muted);
margin-bottom: 8px;
font-size: 11px;
}
.synapse-review-actions {
flex-shrink: 0;
display: flex;
gap: 8px;
padding: 8px 0 0;
border-top: 1px solid var(--background-modifier-border);
margin-top: 8px;
}
.synapse-review-actions button {
padding: 4px 16px;
border-radius: 4px;
font-size: 13px;
cursor: pointer;
border: 1px solid var(--background-modifier-border);
background: var(--background-primary);
color: var(--text-normal);
}
.synapse-review-actions button.mod-cta {
background: var(--interactive-accent);
color: var(--text-on-accent);
border-color: var(--interactive-accent);
}
.synapse-review-actions button:hover {
opacity: 0.9;
}
/* ── Elaboration Review ── */
.synapse-review-pane {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
.synapse-review-pane-label {
flex-shrink: 0;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 4px 8px;
border-radius: 4px 4px 0 0;
}
.synapse-review-pane-label--elaboration {
background: var(--synapse-color-elaboration);
color: var(--text-on-accent);
}
.synapse-review-pane-label--enrichment {
background: var(--synapse-color-enrichment);
color: var(--text-on-accent);
}
.synapse-review-pane-label--rem {
background: var(--synapse-color-rem);
color: var(--text-on-accent);
}
/*
* Shared editor base — neutral structural border. The action-type hue is
* applied per editor via a `.synapse-review-editor--<type>` modifier so the
* editor matches its label/card (elaboration was previously hardcoded to
* `--interactive-accent`, colliding with the user's accent).
*/
.synapse-review-editor {
flex: 1;
padding: 8px;
font-size: 13px;
font-family: var(--font-monospace);
line-height: 1.5;
background: var(--background-primary);
border: 1px solid var(--background-modifier-border);
border-top: none;
border-radius: 0 0 4px 4px;
resize: none;
color: var(--text-normal);
min-height: 120px;
}
.synapse-review-editor--elaboration {
border-color: var(--synapse-color-elaboration);
}
.synapse-review-editor:focus {
outline: none;
border-color: var(--text-accent);
}
/* ── Enrichment Review (checklist) ── */
.synapse-enrichment-checklist {
flex: 1;
overflow-y: auto;
min-height: 0;
}
.synapse-checklist-section {
margin-bottom: 12px;
}
.synapse-checklist-heading {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 4px 8px;
border-radius: 4px;
background: var(--synapse-color-enrichment);
color: var(--text-on-accent);
margin-bottom: 4px;
}
.synapse-checklist-row {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
line-height: 1.4;
}
.synapse-checklist-row:hover {
background: var(--background-modifier-hover);
}
.synapse-checklist-row input[type="checkbox"] {
margin-top: 2px;
flex-shrink: 0;
}
.synapse-checklist-row span {
color: var(--text-normal);
word-break: break-word;
}
/* ── Deep Dive ── */
.synapse-card--deep-dive {
border-left-color: var(--synapse-color-deep-dive);
}
.synapse-badge--deep-dive {
background: var(--synapse-color-deep-dive);
color: var(--text-on-accent);
}
.synapse-badge-row {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 4px;
}
.synapse-depth-badge {
font-size: 10px;
font-weight: 600;
padding: 1px 6px;
border-radius: 3px;
background: var(--background-modifier-border);
color: var(--text-muted);
}
.synapse-quality-badge {
font-size: 10px;
padding: 1px 6px;
border-radius: 3px;
background: var(--background-modifier-border);
color: var(--text-muted);
}
.synapse-deep-dive-meta {
display: flex;
gap: 8px;
margin-bottom: 6px;
}
.synapse-review-pane-label--deep-dive {
background: var(--synapse-color-deep-dive);
color: var(--text-on-accent);
}
.synapse-review-editor--deep-dive {
border-color: var(--synapse-color-deep-dive);
}
/* ── Title ── */
.synapse-card--title {
border-left-color: var(--synapse-color-title);
}
.synapse-badge--title {
background: var(--synapse-color-title);
color: var(--text-on-accent);
}
.synapse-review-pane-label--title {
background: var(--synapse-color-title);
color: var(--text-on-accent);
}
/* ── REM ── */
.synapse-card--rem {
border-left-color: var(--synapse-color-rem);
}
.synapse-badge--rem {
background: var(--synapse-color-rem);
color: var(--text-on-accent);
}
.synapse-badge--rem-title {
background: var(--interactive-accent);
color: var(--text-on-accent);
font-size: 9px;
padding: 0 4px;
border-radius: 2px;
}
.synapse-badge--rem-alias {
background: var(--color-green);
color: var(--text-on-accent);
font-size: 9px;
padding: 0 4px;
border-radius: 2px;
}
.synapse-badge--rem-semantic {
background: var(--color-purple);
color: var(--text-on-accent);
font-size: 9px;
padding: 0 4px;
border-radius: 2px;
}
.synapse-rem-candidate-header {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
margin-bottom: 2px;
}
.synapse-rem-target {
font-weight: 600;
font-size: 13px;
color: var(--text-accent);
}
.synapse-rem-context {
font-size: 12px;
color: var(--text-muted);
padding: 2px 8px 4px 32px;
font-family: var(--font-monospace);
line-height: 1.4;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.synapse-rem-context mark {
background: var(--text-highlight-bg);
color: var(--text-normal);
padding: 0 2px;
border-radius: 2px;
}
/* ── Organize Review ── */
.synapse-review-pane-label--organize {
background: var(--synapse-color-organize);
color: var(--text-on-accent);
}
.synapse-organize-detail {
margin-bottom: 12px;
}
/*
* `.synapse-organize-directory` / `.synapse-organize-reasoning` are the generic
* "detail box" styles, shared by the organize, deep-dive, and title reviews.
* The base border is neutral structural chrome; the action-type hue is applied
* per box via a `.synapse-review-box--<type>` modifier (below) so each review's
* box matches its label and card instead of all defaulting to one hue.
*/
.synapse-organize-directory {
font-size: 14px;
font-weight: 600;
font-family: var(--font-monospace);
color: var(--text-normal);
padding: 8px;
background: var(--background-primary);
border: 1px solid var(--background-modifier-border);
border-top: none;
border-radius: 0 0 4px 4px;
margin: 0;
}
.synapse-organize-reasoning {
font-size: 13px;
line-height: 1.5;
color: var(--text-normal);
padding: 8px;
background: var(--background-primary);
border: 1px solid var(--background-modifier-border);
border-top: none;
border-radius: 0 0 4px 4px;
margin: 0;
}
/* Per-type accent for detail boxes — keeps each box on its own action token. */
.synapse-review-box--organize {
border-color: var(--synapse-color-organize);
}
.synapse-review-box--deep-dive {
border-color: var(--synapse-color-deep-dive);
}
.synapse-review-box--title {
border-color: var(--synapse-color-title);
}
/* ── Title collision (#414) ── */
.synapse-badge--conflict {
background: var(--color-orange);
color: var(--text-on-accent);
}
.synapse-title-conflict {
margin-top: 8px;
padding: 8px;
background: var(--background-secondary);
border-radius: 6px;
border-left: 3px solid var(--color-orange);
}
.synapse-title-conflict-heading {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--color-orange);
margin-bottom: 4px;
}
.synapse-title-conflict-body {
display: block;
color: var(--text-muted);
}
/* ── Checkpoint Banner ── */
.synapse-checkpoint-banner {
margin-bottom: 12px;
padding: 8px;
background: var(--background-secondary);
border-radius: 6px;
border-left: 3px solid var(--color-yellow);
}
.synapse-checkpoint-heading {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--color-yellow);
margin-bottom: 6px;
}
.synapse-checkpoint-card {
padding: 6px 0;
border-bottom: 1px solid var(--background-modifier-border);
}
.synapse-checkpoint-card:last-child {
border-bottom: none;
}
.synapse-checkpoint-info {
display: flex;
flex-direction: column;
gap: 2px;
margin-bottom: 4px;
}
.synapse-checkpoint-info strong {
font-size: 13px;
}
.synapse-checkpoint-track {
width: 100%;
height: 4px;
border-radius: 2px;
background: var(--background-modifier-border);
overflow: hidden;
margin-bottom: 6px;
}
.synapse-checkpoint-fill {
height: 100%;
border-radius: 2px;
background: var(--color-yellow);
transition: width 0.2s ease;
}
/* ── Accept All / Reject All ── */
.synapse-accept-all-bar {
flex-shrink: 0;
display: flex;
gap: 6px;
margin-bottom: 8px;
}
.synapse-accept-all-btn {
flex: 1;
padding: 6px 16px;
border-radius: 4px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
border: 1px solid var(--interactive-accent);
background: var(--interactive-accent);
color: var(--text-on-accent);
}
.synapse-accept-all-btn:hover {
opacity: 0.9;
}
.synapse-accept-all-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.synapse-reject-all-btn {
flex: 1;
padding: 6px 16px;
border-radius: 4px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
border: 1px solid var(--background-modifier-border);
background: var(--background-primary);
color: var(--text-normal);
}
.synapse-reject-all-btn:hover {
background: var(--background-modifier-hover);
}
.synapse-reject-all-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.synapse-accept-all-progress {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
padding: 32px 16px;
}
.synapse-accept-all-progress-text {
font-size: 14px;
font-weight: 600;
color: var(--text-normal);
margin: 0;
}
.synapse-accept-all-track {
width: 100%;
height: 6px;
border-radius: 3px;
background: var(--background-modifier-border);
overflow: hidden;
}
.synapse-accept-all-fill {
height: 100%;
border-radius: 3px;
background: var(--interactive-accent);
transition: width 0.2s ease;
}
/* ── First-run: required-field emphasis (API key, #89) ──
* Marks the one field that gates every AI feature while it is still unset.
* The accent is drawn with an inset box-shadow (not a border) so it adds no
* layout box and never reflows the row; it clears live the moment a key is
* entered. Uses the user's accent (violet by default — bridges the brand). */
.synapse-setting-required {
box-shadow: inset 3px 0 0 0 var(--interactive-accent);
padding-left: 12px;
border-radius: 2px;
}
.synapse-setting-required .setting-item-name {
font-weight: var(--font-semibold);
}
/* ── Friendlier provider auth: get-key link + live validation (#335) ──
* The "Get an API key →" deep link and the ✓/✗ status chip render INSIDE the
* credential row's `.setting-item`, on their own full-width line beneath the
* name/control (the host `.setting-item` sets `flex-wrap: wrap`), so the helper
* still wraps independently of the row. Colors use Obsidian theme vars for
* light/dark compatibility. */
.synapse-credential-extras {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 14px;
flex-basis: 100%;
margin-top: 4px;
font-size: var(--font-ui-smaller);
}
/* Lets a nested full-width helper (credential extras / exclusion chips) drop onto
* its own line below the row's name + control instead of overflowing the flex row. */
.setting-item.synapse-setting--has-helper {
flex-wrap: wrap;
}
.synapse-get-key-link {
font-size: var(--font-ui-smaller);
}
.synapse-credential-chip {
min-height: 1em;
font-size: var(--font-ui-smaller);
color: var(--text-muted);
}
.synapse-credential-chip.is-valid {
color: var(--text-success);
}
.synapse-credential-chip.is-invalid {
color: var(--text-error);
}
.synapse-credential-chip.is-error {
color: var(--text-warning);
}
.synapse-credential-chip.is-checking,
.synapse-credential-chip.is-hint {
color: var(--text-muted);
}
/* ── Exclusions: feature scope chips (#328) ──
* A compact, wrapping row of removable chips (the rule's blocked features) plus
* an inline "add" dropdown, replacing the per-feature toggle grid. Rendered
* INSIDE each exclusion rule / add-folder / add-pattern `.setting-item`, on its
* own full-width line beneath the row (the host sets `flex-wrap: wrap`), so it
* still wraps independently. */
.synapse-exclusion-chips {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
flex-basis: 100%;
padding: 4px 0 8px;
}
.synapse-chips-container {
display: flex;
align-items: center;
flex: 1;
gap: 6px;
flex-wrap: wrap;
}
.synapse-chip {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 4px 2px 10px;
border-radius: 12px;
background: var(--interactive-accent);
color: var(--text-on-accent);
font-size: var(--font-ui-small);
line-height: 1.4;
}
.synapse-chip-remove {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
padding: 0;
border: none;
border-radius: 50%;
background: transparent;
color: inherit;
font-size: var(--font-ui-smaller);
cursor: pointer;
opacity: 0.75;
box-shadow: none;
}
.synapse-chip-remove:hover {
opacity: 1;
background: var(--background-modifier-hover);
}
.synapse-chip-remove:focus-visible {
outline: 2px solid var(--interactive-accent);
outline-offset: 1px;
opacity: 1;
}
.synapse-chip-add {
font-size: var(--font-ui-small);
}
.synapse-exclusion-chips-empty {
font-size: var(--font-ui-small);
color: var(--text-muted);
font-style: italic;
}
/* ══════════════════════════════════════════════════════════════
* Synapse actions sidebar (#289)
* Registry-driven action buttons, grouped by feature. Buttons are
* sized for touch so mobile users reach key functions in <=2 taps.
* ══════════════════════════════════════════════════════════════ */
.synapse-actions-view-root {
padding: 0 8px;
}
.synapse-actions-group {
margin-bottom: 12px;
}
.synapse-actions-group-heading {
font-size: var(--font-ui-smaller);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-muted);
margin: 8px 0 4px;
display: flex;
align-items: center;
gap: 6px;
/*
* Subtle per-feature accent: a colored left marker on the group heading,
* driven by the same action-type tokens as the cards/badges. Per-feature
* overrides below recolor it; `main` keeps the neutral default.
*/
padding-left: 8px;
border-left: 3px solid var(--synapse-color-main);
}
.synapse-actions-group--elaboration .synapse-actions-group-heading {
border-left-color: var(--synapse-color-elaboration);
}
.synapse-actions-group--enrichment .synapse-actions-group-heading {
border-left-color: var(--synapse-color-enrichment);
}
.synapse-actions-group--organize .synapse-actions-group-heading {
border-left-color: var(--synapse-color-organize);
}
.synapse-actions-group--deep-dive .synapse-actions-group-heading {
border-left-color: var(--synapse-color-deep-dive);
}
.synapse-actions-group--summarize .synapse-actions-group-heading {
border-left-color: var(--synapse-color-summarize);
}
.synapse-actions-group--tidy .synapse-actions-group-heading {
border-left-color: var(--synapse-color-tidy);
}
.synapse-actions-group--rem .synapse-actions-group-heading {
border-left-color: var(--synapse-color-rem);
}
.synapse-actions-group--video .synapse-actions-group-heading {
border-left-color: var(--synapse-color-video);
}
.synapse-actions-group--main .synapse-actions-group-heading {
border-left-color: var(--synapse-color-main);
}
.synapse-actions-button {
display: block;
width: 100%;
min-height: 44px; /* comfortable touch target (iOS/Material guidance) */
margin: 4px 0;
padding: 8px 12px;
text-align: left;
white-space: normal;
cursor: pointer;
}
.synapse-actions-button:disabled {
opacity: 0.5;
cursor: default;
}
/*
* Feature glyph prefixed to the group heading (#349). The registered SVG draws
* in `currentColor`, so the feature tint is applied by setting `color` on this
* span; it reuses the same action-type tokens that accent the heading's left
* marker. `main`/General keeps the muted default; per-feature overrides recolor.
*/
.synapse-actions-group-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
color: var(--synapse-color-main);
}
.synapse-actions-group-icon .svg-icon {
width: 16px;
height: 16px;
}
.synapse-actions-group-label {
flex: 1 1 auto;
}
.synapse-actions-group--elaboration .synapse-actions-group-icon {
color: var(--synapse-color-elaboration);
}
.synapse-actions-group--enrichment .synapse-actions-group-icon {
color: var(--synapse-color-enrichment);
}
.synapse-actions-group--organize .synapse-actions-group-icon {
color: var(--synapse-color-organize);
}
.synapse-actions-group--deep-dive .synapse-actions-group-icon {
color: var(--synapse-color-deep-dive);
}
.synapse-actions-group--summarize .synapse-actions-group-icon {
color: var(--synapse-color-summarize);
}
.synapse-actions-group--tidy .synapse-actions-group-icon {
color: var(--synapse-color-tidy);
}
.synapse-actions-group--rem .synapse-actions-group-icon {
color: var(--synapse-color-rem);
}
.synapse-actions-group--video .synapse-actions-group-icon {
color: var(--synapse-color-video);
}
.synapse-actions-group--main .synapse-actions-group-icon {
color: var(--synapse-color-main);
}
.synapse-actions-empty {
color: var(--text-muted);
font-style: italic;
padding: 8px;
}
/* ── Video install help (#382/#383) ── */
/*
* Per-OS install-command panel toggled open by the yt-dlp/ffmpeg `?` help
* button (it adds `.is-open`). Renders directly beneath its path row; left
* aligned, with each command code-styled and individually copy-able.
*/
.synapse-install-help {
display: none;
margin: 4px 0 12px;
padding: 8px 10px;
border-left: 2px solid var(--background-modifier-border);
text-align: left;
}
.synapse-install-help.is-open {
display: block;
}
.synapse-install-help-heading {
margin-bottom: 6px;
color: var(--text-muted);
font-size: var(--font-ui-smaller);
}
.synapse-install-row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.synapse-install-row:last-child {
margin-bottom: 0;
}
.synapse-install-os {
flex: 0 0 auto;
min-width: 56px;
color: var(--text-muted);
font-size: var(--font-ui-smaller);
}
.synapse-install-cmd {
flex: 1 1 auto;
font-family: var(--font-monospace);
font-size: var(--font-ui-smaller);
background: var(--code-background, var(--background-primary-alt));
color: var(--code-normal, var(--text-normal));
padding: 2px 6px;
border-radius: 4px;
user-select: text;
white-space: pre;
overflow-x: auto;
}
.synapse-install-copy {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 4px;
border: none;
border-radius: 4px;
background: transparent;
color: var(--text-muted);
cursor: pointer;
}
.synapse-install-copy:hover {
color: var(--text-normal);
background: var(--background-modifier-hover);
}
.synapse-install-copy.is-copied {
color: var(--text-success);
}
/* ── Changelog view (#375) ──
* In-app "What's new" modal: a scrollable, lightly-styled render of CHANGELOG.md.
* The entry matching the installed version is accented so users can orient. */
.synapse-changelog .synapse-changelog-body {
max-height: 60vh;
overflow-y: auto;
padding-right: 8px;
}
.synapse-changelog-entry {
margin-bottom: 1.25em;
}
.synapse-changelog-version {
margin-bottom: 0.25em;
}
.synapse-changelog-entry--current {
border-left: 2px solid var(--text-accent);
padding-left: 0.75em;
}
.synapse-changelog-entry--current .synapse-changelog-version {
color: var(--text-accent);
}
.synapse-changelog-section {
margin: 0.5em 0 0.25em;
color: var(--text-muted);
}
.synapse-changelog-list {
margin-top: 0;
}
.synapse-changelog-link {
cursor: pointer;
}