Files
obsidian/.obsidian/plugins/git-file-sync/styles.css
T
2026-09-24 13:22:58 +05:00

1754 lines
45 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ── VaultBridge Source Control View ────────────────────────────── */
/* Semantic diff tokens: shared by every surface that renders a diff
(desktop DiffTabView, mobile detail, conflict modals; split + unified).
Components only ever consume these -- never raw Obsidian theme vars --
so contrast tweaks happen in exactly one place.
Any diff-rendering root adds .gfs-diff-surface, so a new surface never
needs a selector added here. */
.gfs-diff-surface {
--scv-diff-added-bg:
color-mix(in srgb, var(--color-green) 16%, var(--background-primary));
--scv-diff-added-emphasis: var(--color-green);
--scv-diff-removed-bg:
color-mix(in srgb, var(--color-red) 15%, var(--background-primary));
--scv-diff-removed-emphasis: var(--color-red);
--scv-diff-unified-added-bg:
color-mix(in srgb, var(--color-green) 18%, var(--background-primary));
--scv-diff-unified-removed-bg:
color-mix(in srgb, var(--color-red) 18%, var(--background-primary));
--scv-diff-empty-bg: var(--background-secondary);
--scv-diff-border: var(--background-modifier-border);
--scv-diff-line-number: var(--text-muted);
}
/* Same mix percentage reads weaker on dark backgrounds; raise the tint. */
.theme-dark .gfs-diff-surface {
--scv-diff-added-bg:
color-mix(in srgb, var(--color-green) 22%, var(--background-primary));
--scv-diff-removed-bg:
color-mix(in srgb, var(--color-red) 20%, var(--background-primary));
--scv-diff-unified-added-bg:
color-mix(in srgb, var(--color-green) 26%, var(--background-primary));
--scv-diff-unified-removed-bg:
color-mix(in srgb, var(--color-red) 24%, var(--background-primary));
}
.scv-root {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
padding: 0;
container-type: inline-size;
}
.scv-header {
flex-shrink: 0;
padding: 8px 12px;
border-bottom: 1px solid var(--background-modifier-border);
}
.scv-header-title-row {
display: flex;
align-items: center;
gap: 8px;
padding: 0 10px;
}
/* ── Workspace info strip (provider · branch · vault folder · last sync) ── */
.scv-info {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 4px;
margin-top: 4px;
font-size: 0.75em;
color: var(--text-muted);
}
.scv-info-item {
display: inline-flex;
align-items: center;
}
.scv-info-icon {
display: inline-flex;
align-items: center;
}
.scv-info-icon .svg-icon { width: 11px; height: 11px; }
.scv-info-sep { color: var(--text-faint); }
.scv-info-time { white-space: nowrap; }
/* ── Search / path filter ─────────────────────────────────────── */
.scv-search {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border-bottom: 1px solid var(--background-modifier-border);
flex-shrink: 0;
}
.scv-search-icon {
display: flex;
align-items: center;
color: var(--text-faint);
flex-shrink: 0;
}
.scv-search-icon .svg-icon { width: 14px; height: 14px; }
.scv-search-input {
flex: 1;
min-width: 0;
height: 26px;
padding: 0 6px;
font-size: 0.82em;
background: var(--background-modifier-form-field);
border: 1px solid var(--background-modifier-border);
border-radius: 4px;
color: var(--text-normal);
}
.scv-search-clear {
display: none;
align-items: center;
justify-content: center;
padding: 2px;
height: 22px;
width: 22px;
flex-shrink: 0;
background: transparent;
border: none;
box-shadow: none;
color: var(--text-muted);
cursor: pointer;
}
.scv-search.has-query .scv-search-clear { display: flex; }
.scv-search-clear:hover { color: var(--text-normal); }
.scv-search-clear .svg-icon { width: 14px; height: 14px; }
.scv-main {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
min-width: 0;
}
.scv-body {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
}
/* ── Filter menu ───────────────────────────────────────────────── */
.scv-filter-menu {
display: flex;
gap: 4px;
padding: 8px 10px;
border-bottom: 1px solid var(--background-modifier-border);
overflow-x: auto;
scrollbar-width: none;
flex-shrink: 0;
}
.scv-filter-menu::-webkit-scrollbar { display: none; }
.scv-filter-option {
display: flex;
align-items: center;
gap: 5px;
padding: 4px 10px;
border-radius: var(--radius-s);
border: 1px solid transparent;
font-size: 0.80em;
font-weight: 500;
cursor: pointer;
white-space: nowrap;
background: transparent;
color: var(--text-muted);
transition: background 0.12s, color 0.12s;
min-height: 28px;
}
.scv-filter-option:hover {
background: var(--background-modifier-hover);
color: var(--text-normal);
}
.scv-filter-option.is-active {
background: var(--interactive-accent);
color: var(--text-on-accent);
border-color: var(--interactive-accent);
}
.scv-filter-count {
background: rgba(0, 0, 0, 0.12);
border-radius: var(--radius-s);
padding: 1px 6px;
font-size: 0.85em;
min-width: 18px;
text-align: center;
}
.scv-filter-option.is-active .scv-filter-count {
background: rgba(255, 255, 255, 0.22);
}
/* ── Show synced toggle ───────────────────────────────────────── */
.scv-filter-show-synced {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
font-size: 0.78em;
color: var(--text-muted);
cursor: pointer;
white-space: nowrap;
margin-left: auto;
}
.scv-filter-show-synced-checkbox {
margin: 0;
cursor: pointer;
}
.scv-filter-show-synced-label {
text-transform: none;
letter-spacing: 0;
}
/* ── Active filter header ─────────────────────────────────────── */
.scv-collapsible-header { cursor: pointer; user-select: none; }
.scv-section-toggle {
flex-shrink: 0;
color: var(--text-faint);
font-size: 0.7em;
line-height: 1;
width: 1.2em;
text-align: center;
}
/* ── Repository Changes header (collapsible title + Tree/List toggle) ── */
.scv-repository-header {
display: flex;
flex-shrink: 0;
align-items: center;
gap: 6px;
padding: 6px 12px 4px 8px;
cursor: pointer;
border-radius: var(--radius-s);
color: var(--text-muted);
font-size: 0.78em;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.02em;
}
.scv-repository-header:hover { background: var(--background-modifier-hover); }
.scv-repository-title { flex-shrink: 0; }
.scv-repository-count {
background: var(--background-modifier-border);
border-radius: 10px;
padding: 1px 6px;
font-size: 0.9em;
min-width: 18px;
text-align: center;
}
/* ── Tree/List view toggle (Repository Changes only) ──────────── */
.scv-view-toggle {
display: inline-flex;
align-items: center;
gap: 2px;
margin-left: auto;
flex-shrink: 0;
padding: 1px;
border-radius: var(--radius-s);
background: var(--background-modifier-border);
}
.scv-view-toggle-btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 6px;
border: none;
background: transparent;
color: var(--text-muted);
border-radius: calc(var(--radius-s) - 1px);
cursor: pointer;
font-size: 0.7em;
line-height: 1;
}
.scv-view-toggle-btn:hover { color: var(--text-normal); }
.scv-view-toggle-btn.is-active {
background: var(--background-primary);
color: var(--text-normal);
}
.scv-view-toggle-icon { display: inline-flex; }
.scv-view-toggle-icon .svg-icon { width: 13px; height: 13px; }
.scv-view-toggle-label { white-space: nowrap; }
/* Mobile: icon-only toggle (labels drop) to save horizontal space. */
body.is-mobile .scv-view-toggle-label { display: none; }
/* ── Selected-for-sync workspace ───────────────────────────── */
.scv-selected-section {
flex-shrink: 0;
margin: 6px 8px 4px 8px;
padding: 6px 0;
border-radius: var(--radius-m);
background: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
overflow: hidden;
}
.scv-selected-section-header {
display: flex;
align-items: center;
gap: 6px;
padding: 2px 10px 6px 10px;
color: var(--text-muted);
font-size: 0.74em;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.02em;
}
.scv-selected-section-title { flex-shrink: 0; }
.scv-selected-section-clear {
margin-left: auto;
border: none;
background: transparent;
color: var(--text-muted);
font-size: 0.9em;
padding: 2px 6px;
border-radius: var(--radius-s);
cursor: pointer;
}
.scv-selected-section-clear:hover {
color: var(--text-normal);
background: var(--background-modifier-hover);
}
.scv-selected-section-subtitle {
padding: 0 10px 4px 10px;
color: var(--text-faint);
font-size: 0.72em;
line-height: 1.2;
}
.scv-selected-section-list {
display: flex;
flex-direction: column;
max-height: 38vh;
overflow-y: auto;
}
/* ── Changes region (independent scroll so the tree never hides the queue) ── */
.scv-changes-region {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
overflow: hidden;
}
.scv-changes-tree {
flex: 1;
min-height: 0;
overflow-y: auto;
}
/* ── Push button ───────────────────────────────────────────────── */
.scv-push-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
flex: 1 1 auto;
margin: 8px 0;
padding: 7px 11px;
border-radius: 5px;
font-size: 0.85em;
font-weight: 500;
cursor: pointer;
border: 1px solid transparent;
background: var(--color-green);
color: white;
min-height: 32px;
flex-shrink: 0;
transition: opacity 0.12s;
}
.scv-push-btn:disabled {
opacity: 0.38;
cursor: not-allowed;
}
.scv-push-btn:not(:disabled):hover { opacity: 0.85; }
/* ── Refresh button ───────────────────────────────────────────── */
.scv-refresh-btn {
display: flex;
align-items: center;
gap: 5px;
flex: 0 0 auto;
margin: 8px 0;
padding: 6px 9px;
border-radius: 5px;
font-size: 0.8em;
cursor: pointer;
border: 1px solid var(--background-modifier-border);
background: var(--background-modifier-form-field);
color: var(--text-muted);
min-height: 32px;
}
.scv-refresh-btn:hover { color: var(--text-normal); }
.scv-refresh-btn.is-loading { cursor: progress; }
.scv-refresh-btn.is-loading .scv-refresh-btn-icon { animation: scv-refresh-spin 1s linear infinite; }
.scv-refresh-btn.is-failed {
color: var(--text-error);
border-color: var(--background-modifier-error-border, var(--background-modifier-border));
}
.scv-refresh-btn:disabled { opacity: 0.7; cursor: progress; }
.scv-refresh-btn-label { white-space: nowrap; }
.scv-refresh-btn.is-idle .scv-refresh-btn-label { display: none; }
@keyframes scv-refresh-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* ── Change sections & tree ───────────────────────────────────────── */
.scv-section-header {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px 6px 8px;
cursor: pointer;
color: var(--text-muted);
font-size: 0.78em;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.02em;
}
.scv-section-header:hover { background: var(--background-modifier-hover); }
.scv-section-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
padding: 0;
border: 0;
background: transparent;
color: var(--text-muted);
cursor: pointer;
}
.scv-section-title { flex: 1; }
.scv-section-count {
background: var(--background-modifier-border);
border-radius: 10px;
padding: 1px 6px;
font-size: 0.9em;
min-width: 18px;
text-align: center;
}
.scv-tree-folder-row {
display: flex;
align-items: center;
gap: 7px;
min-height: 32px;
padding: 4px 12px 4px 8px;
color: var(--text-normal);
cursor: pointer;
}
.scv-tree-folder-row:hover { background: var(--background-modifier-hover); }
.scv-tree-folder-select {
width: 15px;
height: 15px;
flex-shrink: 0;
cursor: pointer;
}
.scv-tree-folder-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
padding: 0;
border: 0;
background: transparent;
color: var(--text-muted);
cursor: pointer;
}
.scv-tree-folder-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.84em;
font-weight: 600;
}
.scv-tree-children {
margin-left: 13px;
border-left: 1px solid var(--background-modifier-border);
}
/* ── Change item row ───────────────────────────────────────────── */
.scv-change-item {
display: flex;
align-items: center;
gap: 7px;
min-height: 30px;
padding: 5px 12px 5px 8px;
cursor: pointer;
border-left: 3px solid transparent;
transition: background 0.1s;
}
.scv-change-item:hover { background: var(--background-modifier-hover); }
.scv-change-item.scv-kind-local-only { border-left-color: var(--color-green); }
.scv-change-item.scv-kind-local-modified { border-left-color: var(--text-warning); }
.scv-change-item.scv-kind-local-deleted { border-left-color: var(--color-red); }
.scv-change-item.scv-kind-remote-only { border-left-color: var(--color-blue); }
.scv-change-item.scv-kind-remote-modified { border-left-color: var(--color-blue); }
.scv-change-item.scv-kind-moved { border-left-color: var(--color-purple); }
.scv-change-item.scv-kind-conflict { border-left-color: var(--color-red); }
.scv-change-item.scv-kind-synced { border-left-color: var(--background-modifier-border); }
.scv-change-select {
width: 15px;
height: 15px;
flex-shrink: 0;
cursor: pointer;
}
.scv-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
flex-shrink: 0;
font-size: 0.68em;
font-weight: 700;
border-radius: 3px;
border: 1px solid currentColor;
opacity: 0.85;
}
.scv-badge-local-only { color: var(--color-green); }
.scv-badge-local-modified,
.scv-badge-remote-modified { color: var(--text-warning); }
.scv-badge-local-deleted { color: var(--color-red); }
.scv-badge-remote-only { color: var(--text-warning); }
.scv-badge-moved { color: var(--color-purple); }
.scv-badge-conflict { color: var(--color-red); }
.scv-badge-synced { color: var(--text-muted); }
.scv-change-name {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
gap: 5px;
font-family: var(--font-monospace);
font-size: 0.80em;
color: var(--text-normal);
}
.scv-change-name-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* List-view variant: the name shrinks to its content so the folder path
suffix can sit on the right, disambiguating flat rows without nesting. */
.scv-change-item-list .scv-change-name { flex: 0 1 auto; }
.scv-change-path {
flex: 1 1 auto;
min-width: 0;
margin-left: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: right;
color: var(--text-faint);
font-family: var(--font-monospace);
font-size: 0.74em;
}
.scv-change-list {
display: flex;
flex-direction: column;
}
.scv-diff-stat {
display: inline-flex;
align-items: baseline;
gap: 1px;
font-family: var(--font-monospace);
font-size: 0.72em;
flex-shrink: 0;
white-space: nowrap;
}
.scv-diff-stat-add { color: var(--color-green); }
.scv-diff-stat-del { color: var(--color-red); }
.scv-diff-stat-sep { width: 2px; }
/* ── Sync Queue operation groups (Upload / Download) ───────────────── */
.scv-queue-group-label {
padding: 6px 10px 2px 10px;
color: var(--text-faint);
font-size: 0.68em;
text-transform: uppercase;
letter-spacing: 0.04em;
}
/* ── Inline Download action (remote-only rows) ─────────────────────── */
.scv-change-download {
display: inline-flex;
align-items: center;
gap: 3px;
margin-left: auto;
padding: 1px 6px;
background: transparent;
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-s);
color: var(--text-muted);
font-size: 0.72em;
line-height: 1.4;
cursor: pointer;
flex-shrink: 0;
}
.scv-change-download:hover {
background: var(--background-modifier-hover);
color: var(--text-normal);
border-color: var(--interactive-accent);
}
.scv-change-download .svg-icon,
.scv-change-download-icon .svg-icon { width: 12px; height: 12px; }
.scv-change-download-label { white-space: nowrap; }
/* Phone: icon-only, so the button doesn't crowd out the filename at narrow widths. */
body.is-mobile .scv-change-download-label { display: none; }
body.is-mobile .scv-change-download { padding: 4px; min-width: 28px; min-height: 28px; justify-content: center; }
/* ── Sync Queue row action control (resolved action + override menu) ── */
.scv-change-action {
display: inline-flex;
align-items: center;
gap: 3px;
margin-left: auto;
padding: 1px 6px;
background: transparent;
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-s);
color: var(--text-muted);
font-size: 0.72em;
line-height: 1.4;
cursor: pointer;
flex-shrink: 0;
}
.scv-change-action:hover {
background: var(--background-modifier-hover);
color: var(--text-normal);
border-color: var(--interactive-accent);
}
.scv-change-action .svg-icon,
.scv-change-action-icon .svg-icon { width: 12px; height: 12px; }
.scv-change-action-label { white-space: nowrap; }
/* Phone: icon-only, matching the row's tighter width budget. */
body.is-mobile .scv-change-action-label { display: none; }
body.is-mobile .scv-change-action { padding: 4px; min-width: 28px; min-height: 28px; justify-content: center; }
/* ── Repository Changes row "⋯" menu ───────────────────────────────── */
.scv-change-menu {
display: inline-flex;
align-items: center;
justify-content: center;
margin-left: auto;
padding: 2px;
background: transparent;
border: none;
border-radius: var(--radius-s);
color: var(--text-muted);
cursor: pointer;
flex-shrink: 0;
}
.scv-change-menu:hover {
background: var(--background-modifier-hover);
color: var(--text-normal);
}
.scv-change-menu .svg-icon { width: 14px; height: 14px; }
body.is-mobile .scv-change-menu { min-width: 28px; min-height: 28px; }
.scv-change-rename-from {
color: var(--text-faint);
text-decoration: line-through;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-shrink: 1;
min-width: 0;
}
.scv-change-rename-arrow {
display: inline-flex;
align-items: center;
color: var(--text-faint);
flex-shrink: 0;
}
.scv-change-rename-arrow .svg-icon { width: 12px; height: 12px; }
/* ── Operation indicator ──────────────────────────────────────────── */
.scv-op-indicator {
display: inline-flex;
align-items: center;
gap: 3px;
flex-shrink: 0;
}
.scv-op-indicator .svg-icon { width: 14px; height: 14px; }
.scv-op-label {
font-size: 0.72em;
white-space: nowrap;
}
.scv-op-running { color: var(--text-muted); }
.scv-op-success { color: var(--color-green); }
.scv-op-failed { color: var(--color-red); }
/* ── Empty state ───────────────────────────────────────────────── */
.scv-empty {
padding: 36px 20px;
text-align: center;
color: var(--text-muted);
font-size: 0.88em;
}
/* ── Diff tab (desktop: full main-area workspace tab) ─────────────── */
.scv-diff-tab {
padding: 10px 14px;
overflow: hidden;
display: flex;
flex-direction: column;
height: 100%;
container-type: inline-size;
}
.scv-diff-tab-header {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 8px;
}
.scv-diff-tab-path {
font-family: var(--font-monospace);
font-size: 0.82em;
color: var(--text-muted);
overflow-wrap: anywhere;
}
.scv-diff-tab-body {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
min-width: 0;
max-width: 100%;
}
/* Let the diff fill all the space this full tab gives it, instead of the
compact scroll box used elsewhere -- there's a whole tab of room here.
Deliberately doesn't touch `display` (owned by the toggle-class rules
below), so split and unified never both end up visible at once. */
.scv-diff-tab-body .ssv-diff-split {
flex: 1;
min-height: 0;
}
.scv-diff-tab-body .ssv-diff-grid {
height: 100%;
max-height: none;
min-height: 0;
}
.scv-diff-tab-body .ssv-diff-unified {
flex: 1;
max-height: none;
min-height: 0;
}
/* Explicit layout toggle (button-driven, not tab width) -- only one of
split/unified is ever shown, so they never both take up space. These
two-class selectors outrank the width-based @container rules below. */
.scv-diff-tab-body.scv-diff-layout-split .ssv-diff-split { display: block; }
.scv-diff-tab-body.scv-diff-layout-split .ssv-diff-unified { display: none; }
.scv-diff-tab-body.scv-diff-layout-unified .ssv-diff-split { display: none; }
.scv-diff-tab-body.scv-diff-layout-unified .ssv-diff-unified { display: block; }
.scv-diff-empty {
padding: 36px 20px;
text-align: center;
color: var(--text-muted);
font-size: 0.88em;
}
/* ── Mobile detail view (list/detail with back button) ────────────── */
.scv-detail {
display: flex;
flex-direction: column;
height: 100%;
min-width: 0;
max-width: 100%;
}
.scv-detail-bar {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin: 8px 10px;
}
.scv-detail-back {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 7px 14px 7px 10px;
border-radius: 6px;
border: 1px solid var(--background-modifier-border);
background: var(--interactive-normal);
color: var(--text-normal);
cursor: pointer;
font-size: 0.88em;
font-weight: 600;
}
.scv-detail-back:hover { background: var(--interactive-hover); }
.scv-detail-back-icon { display: inline-flex; align-items: center; }
.scv-detail-back-icon .svg-icon { width: 15px; height: 15px; }
.scv-diff-layout-toggle {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 7px 12px;
border-radius: 6px;
border: 1px solid var(--background-modifier-border);
background: var(--interactive-normal);
color: var(--text-normal);
cursor: pointer;
font-size: 0.85em;
font-weight: 600;
white-space: nowrap;
}
.scv-diff-layout-toggle:hover { background: var(--interactive-hover); }
.scv-diff-layout-toggle-icon { display: inline-flex; align-items: center; }
.scv-diff-layout-toggle-icon .svg-icon { width: 15px; height: 15px; }
.scv-detail-diff {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
padding: 0 12px 12px;
container-type: inline-size;
}
/* Same "fill the room this dedicated page has" fix as the desktop tab --
the mobile detail view is its own full-screen page, not a cramped
sidebar split, so the diff shouldn't be capped to a short scroll box.
Deliberately doesn't touch `display` (owned by the toggle-class rules
below), so split and unified never both end up visible at once. */
.scv-detail-diff .ssv-diff-split {
flex: 1;
min-height: 0;
}
.scv-detail-diff .ssv-diff-grid {
height: 100%;
max-height: none;
min-height: 0;
}
.scv-detail-diff .ssv-diff-unified {
flex: 1;
max-height: none;
min-height: 0;
}
/* Explicit layout toggle (button-driven, not container width) -- only one
of split/unified is ever shown, so they never both take up space. These
two-class selectors outrank the width-based @container rules below. */
.scv-detail-diff.scv-diff-layout-split .ssv-diff-split { display: block; }
.scv-detail-diff.scv-diff-layout-split .ssv-diff-unified { display: none; }
.scv-detail-diff.scv-diff-layout-unified .ssv-diff-split { display: none; }
.scv-detail-diff.scv-diff-layout-unified .ssv-diff-unified { display: block; }
/* ── Side-by-side diff (default for wide panels) ──────────────────── */
.ssv-diff-split {
border: 1px solid var(--background-modifier-border);
border-radius: 5px;
overflow: hidden;
background: var(--background-primary);
}
.ssv-diff-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
font-family: var(--font-monospace);
font-size: 0.74em;
max-height: 260px;
overflow: auto;
min-width: 0;
max-width: 100%;
}
.ssv-diff-hd {
position: sticky;
top: 0;
padding: 4px 8px;
background: var(--background-secondary);
color: var(--text-muted);
font-size: 0.9em;
font-weight: 600;
border-bottom: 1px solid var(--background-modifier-border);
z-index: 1;
}
.ssv-diff-hd:first-child {
border-right: 1px solid var(--background-modifier-border);
}
.ssv-diff-cell {
display: flex;
align-items: baseline;
gap: 6px;
padding: 1px 6px;
min-height: 18px;
overflow: hidden;
}
/* Right column of each row gets a left separator */
.ssv-diff-cell:nth-child(even) {
border-left: 1px solid var(--scv-diff-border);
}
/* Row separator (every pair of cells) */
.ssv-diff-cell:nth-child(4n+3),
.ssv-diff-cell:nth-child(4n+4) {
background-color: transparent;
}
.ssv-diff-cell.removed {
background: var(--scv-diff-removed-bg);
box-shadow: inset 2px 0 0 var(--scv-diff-removed-emphasis);
}
.ssv-diff-cell.added {
background: var(--scv-diff-added-bg);
box-shadow: inset 2px 0 0 var(--scv-diff-added-emphasis);
}
.ssv-diff-cell.unchanged { background: transparent; }
.ssv-diff-cell.empty { background: var(--scv-diff-empty-bg); }
.ssv-diff-ln {
color: var(--scv-diff-line-number);
min-width: 22px;
text-align: right;
flex-shrink: 0;
user-select: none;
opacity: 0.6;
font-size: 0.88em;
}
.ssv-diff-code {
flex: 1;
white-space: pre-wrap;
word-break: break-all;
color: var(--text-normal);
}
/* ── Unified diff (narrow containers / mobile) ─────────────────────── */
.ssv-diff-unified {
display: none;
background: var(--background-secondary);
border-radius: 5px;
padding: 8px 10px;
font-family: var(--font-monospace);
font-size: 0.74em;
white-space: pre-wrap;
word-break: break-all;
max-height: 240px;
overflow-y: auto;
margin: 0;
}
.ssv-u-line {
display: block;
padding: 1px 4px;
border-radius: 2px;
}
.ssv-u-line.removed {
background: var(--scv-diff-unified-removed-bg);
}
.ssv-u-line.added {
background: var(--scv-diff-unified-added-bg);
}
.ssv-u-line.unchanged { color: var(--text-muted); }
/* Container query: switch layout based on the diff panel's own width */
@container (max-width: 440px) {
.ssv-diff-split { display: none; }
.ssv-diff-unified { display: block; }
}
@container (min-width: 441px) {
.ssv-diff-split { display: block; }
.ssv-diff-unified { display: none; }
}
/* ── Mobile adjustments ─────────────────────────────────────────── */
@container (max-width: 480px) {
.scv-filter-menu { padding: 6px 8px; gap: 3px; }
.scv-filter-option { padding: 4px 8px; font-size: 0.76em; }
.scv-change-item { padding: 6px 10px; }
.scv-change-name { font-size: 0.76em; }
}
/* ── Mobile (Platform.isMobile) layout: filter dropdown + bottom sync bar ── */
.scv-mobile .scv-filter-dropdown {
width: 100%;
padding: 6px 8px;
font-size: 0.85em;
margin-bottom: 6px;
}
.scv-mobile .scv-filter-show-synced {
margin-top: 4px;
}
.scv-mobile-sync-bar {
position: sticky;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
background: var(--background-primary);
border-top: 1px solid var(--background-modifier-border);
z-index: 1;
}
.scv-mobile-sync-label {
flex: 0 1 auto;
color: var(--text-muted);
font-size: 0.85em;
}
.scv-mobile-sync-btn {
flex: 1;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 10px 12px;
border-radius: var(--radius-m);
background: var(--interactive-accent);
color: var(--text-on-accent);
font-size: 0.9em;
font-weight: 600;
cursor: pointer;
}
.scv-mobile-sync-btn:hover { background: var(--interactive-accent-hover); }
/* ── Settings connection status badge ──────────────────────────── */
.gfs-connection-status {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
margin-bottom: 12px;
border-radius: 12px;
font-size: 0.85em;
font-weight: 500;
}
.gfs-connection-status::before {
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
background: currentColor;
}
.gfs-connection-status.is-checking {
color: var(--text-muted);
background: var(--background-secondary);
}
/* Background intentionally stays the same neutral --background-secondary as
* the checking state rather than --background-modifier-success/error: some
* themes define those modifier colors identically to --text-success/error,
* which would make the label text invisible against its own background. */
.gfs-connection-status.is-connected {
color: var(--text-success);
background: var(--background-secondary);
}
.gfs-connection-status.is-disconnected {
color: var(--text-error);
background: var(--background-secondary);
}
/* ── Global status bar connection indicator ────────────────────── */
.gfs-status-bar-connection {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
}
.gfs-status-bar-icon {
display: inline-flex;
align-items: center;
}
.gfs-status-bar-icon svg {
width: 12px;
height: 12px;
}
.gfs-status-bar-connection.is-checking {
color: var(--text-muted);
}
.gfs-status-bar-connection.is-connected {
color: var(--text-success);
}
.gfs-status-bar-connection.is-disconnected {
color: var(--text-error);
}
/* ── Conflict Modal ─────────────────────────────────────────────── */
/* Shell shared by single + batch; variants only override what differs.
Widths differ by purpose: the single modal shows an inline diff and
genuinely needs the room; the batch modal is a resolution checklist —
its width trades against row density (see the container query below). */
.gfs-conflict-modal.modal {
display: flex;
flex-direction: column;
height: auto;
max-height: 92vh;
}
.gfs-conflict-modal--single.modal {
width: min(1600px, 96vw);
max-width: 96vw;
}
/* Width ladder: Single Diff 1600 > Batch Review 1400 > Sync Plan 640.
40-conflict batches skim better dense — the grid turns width into
fewer, shorter rows. */
.gfs-conflict-modal--batch.modal {
width: min(1400px, 96vw);
max-width: 96vw;
}
/* The modal content is the container the batch rows measure themselves
against — not the viewport. A narrow desktop window gets the stacked
layout its real space demands; a wide iPad's modal gets the dense grid
(density itself is gated by the platform class below). */
.gfs-conflict-modal--batch .modal-content {
container-type: inline-size;
}
.gfs-conflict-modal .modal-content {
display: flex;
flex-direction: column;
min-height: 0;
max-height: inherit;
overflow: hidden;
}
/* Fixed top region of the single-file conflict modal: title, description,
and the "Differences + layout toggle" view controls. Layout (not
position:sticky) keeps it visible — only the diff below scrolls. */
.gfs-conflict-modal .conflict-header,
.gfs-conflict-modal .conflict-diff-header,
.gfs-conflict-modal .conflict-description,
.gfs-conflict-modal h2 {
flex-shrink: 0;
}
.conflict-description {
color: var(--text-muted);
margin-bottom: 12px;
font-size: 0.9em;
flex-shrink: 0;
}
/* The ONLY scroll region of the conflict modal: the diff lines. The header
(title + view controls) and footer (resolution buttons) are flex-fixed. */
.conflict-content-area {
flex: 1 1 auto;
overflow-y: auto;
min-height: 0;
}
/* Fixed bottom region: resolution actions never scroll away with a long
diff. Opaque background so scrolled diff lines can't show through the
border seam. */
.gfs-conflict-modal .conflict-buttons {
background: var(--background-primary);
}
.conflict-diff-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 8px;
}
/* Diff lines inside the modal's ONLY scroll region fill it: the modal
provides the viewport chrome, so the old inner max-height scroll boxes
would nest scrolling inside scrolling. */
.gfs-conflict-modal .scv-diff-tab-body .ssv-diff-grid,
.gfs-conflict-modal .scv-diff-tab-body .ssv-diff-unified {
max-height: none;
}
.conflict-diff-header h3 {
margin: 0;
font-size: 0.9em;
font-weight: 600;
}
.conflict-binary-notice {
color: var(--text-muted);
padding: 16px;
background: var(--background-secondary);
border-radius: 5px;
}
.conflict-diff-section { margin-bottom: 18px; }
.conflict-buttons {
display: flex;
justify-content: flex-end;
gap: 10px;
padding-top: 12px;
border-top: 1px solid var(--background-modifier-border);
flex-wrap: wrap;
flex-shrink: 0;
}
@media (max-width: 480px) {
.conflict-buttons { justify-content: stretch; }
.conflict-buttons .setting-item-control { flex: 1; display: flex; flex-direction: column; gap: 8px; }
.gfs-conflict-modal .conflict-buttons button { width: 100%; margin: 0; }
}
.conflict-buttons .setting-item {
border: none;
padding: 0;
}
.conflict-buttons .setting-item-control {
display: flex;
gap: 10px;
}
/* ── Batch Conflict Resolution Modal ────────────────────────────── */
/* Review-list, not card gallery: identity column on the left, actions on
the right. Default (narrow container / touch) is stacked; a wide
container collapses each row into a dense ~52px list entry so 40
conflicts still skim in one viewport. Width decides density via
@container; the platform class (is-mobile) decides touch targets. */
.batch-conflict-bulk-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 14px;
flex-shrink: 0;
}
/* The ONLY scroll region of the batch modal; header/bulk actions/footer
stay fixed. Grows while short a conflict list exists and caps at the
modal's remaining height while long. */
.batch-conflict-row-list {
flex: 0 1 auto;
overflow-y: auto;
min-height: 0;
display: flex;
flex-direction: column;
}
.batch-conflict-row {
display: flex;
flex-direction: column;
gap: 8px;
padding: 10px 12px;
background: var(--background-secondary);
border-radius: 6px;
}
/* Left block: identity — filename + diff stat on one line, parent dir
below. Allowed to shrink; the actions column keeps its natural width. */
.batch-conflict-row-info {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}
/* Filename + its +/- stat read as one information group: stat sits right
next to the name (gap 10px), not pushed to the far edge of a wide modal
— a 1000px gap makes them look like unrelated columns. The name stays
untruncated; a long name still can't push the stat out of view. */
.batch-conflict-row-name-line {
display: flex;
align-items: baseline;
justify-content: flex-start;
gap: 10px;
min-width: 0;
}
/* The name is allowed to shrink so a very long filename can never push
the +/- stat (or, in the desktop grid, the actions column) out of its
identity column; it ellipsizes and the full path lives on the row's
title tooltip (set in TS). */
.batch-conflict-row-name {
font-family: var(--font-monospace);
font-size: 0.9em;
font-weight: 600;
white-space: nowrap;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
/* Fills with the shared .scv-diff-stat spans when a stat lands; empty (no
gap, no reserved space) until then. */
.batch-conflict-row-stat {
display: inline-flex;
flex-shrink: 0;
}
.batch-conflict-row-stat:empty {
display: none;
}
.batch-conflict-row-dir {
font-family: var(--font-monospace);
font-size: 0.85em;
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.batch-conflict-row-binary-badge {
font-size: 0.78em;
color: var(--text-muted);
white-space: nowrap;
}
/* Actions — View Diff + the three radios as one group. Base is
left-aligned stacked-below-identity (tablet/phone); desktop grid puts
it in the right column. */
.batch-conflict-row-actions {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 16px;
flex-wrap: wrap;
}
.batch-conflict-view-diff {
font-size: 0.85em;
padding: 4px 10px;
border-radius: 5px;
border: 1px solid var(--background-modifier-border);
background: var(--background-primary);
color: var(--text-normal);
cursor: pointer;
}
.batch-conflict-row-radios {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.batch-conflict-radio-label {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
cursor: pointer;
white-space: nowrap;
}
.batch-conflict-buttons {
margin-top: 12px;
}
/* Desktop dense grid, decided by the MODAL's width, not the viewport:
Obsidian with a narrow sidebar setup must not be denied the dense grid
just because the window is < 900px, and a wide iPad should not get
mouse-density chrome just because the viewport is > 900. The 760px
threshold is the point where identity (300px min) + actions fit side
by side with breathing room. Touch density is separately guarded by
the body.is-mobile override below. */
@container (min-width: 760px) {
.batch-conflict-row-list {
border-top: 1px solid var(--background-modifier-border);
}
.batch-conflict-row {
display: grid;
grid-template-columns: minmax(300px, 1fr) auto;
align-items: center;
gap: 24px;
padding: 7px 12px;
background: none;
border-radius: 0;
border-bottom: 1px solid var(--background-modifier-border);
}
.batch-conflict-row:last-child {
border-bottom: none;
}
.batch-conflict-row-actions {
justify-content: flex-end;
flex-wrap: nowrap;
gap: 14px;
}
.batch-conflict-row-name {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.batch-conflict-row-dir {
max-width: 100%;
}
}
/* Touch platforms keep comfortable tap targets and stacked actions even
on a wide iPad: container width says space exists; platform says
density must stay touch-shaped. */
body.is-mobile .gfs-conflict-modal--batch .batch-conflict-row {
display: flex;
flex-direction: column;
gap: 6px;
padding: 8px 12px;
}
body.is-mobile .gfs-conflict-modal--batch .batch-conflict-row-actions {
flex-direction: column;
align-items: stretch;
justify-content: flex-start;
flex-wrap: wrap;
gap: 10px;
}
/* Tablet: two stacked lines (identity, actions) with comfortable touch
targets; no card chrome either — density still matters at 40 rows. */
@container (max-width: 759px) {
.batch-conflict-row {
gap: 6px;
padding: 8px 12px;
background: none;
border-radius: 0;
border-bottom: 1px solid var(--background-modifier-border);
}
.batch-conflict-row:last-child {
border-bottom: none;
}
}
/* Phone < 700px: full stacking — radios drop under actions so they never
squeeze horizontally; names/dirs wrap instead of clipping. */
@container (max-width: 700px) {
.batch-conflict-row {
gap: 8px;
padding: 10px 12px;
}
.batch-conflict-row-actions {
flex-direction: column;
align-items: stretch;
}
.batch-conflict-row-radios {
justify-content: space-between;
}
.batch-conflict-row-name {
white-space: normal;
word-break: break-all;
}
.batch-conflict-row-dir {
white-space: normal;
word-break: break-all;
text-overflow: unset;
}
}
/* ── Sync Plan Modal ────────────────────────────────────────────── */
.sync-plan-modal.modal {
width: min(640px, 92vw);
max-height: 85vh;
display: flex;
flex-direction: column;
}
.sync-plan-description {
color: var(--text-muted);
font-size: 0.9em;
margin: -4px 0 12px;
}
.sync-plan-sections {
overflow-y: auto;
flex: 1;
min-height: 0;
}
.sync-plan-section {
margin-bottom: 16px;
}
.sync-plan-section-heading {
display: flex;
align-items: center;
gap: 6px;
font-weight: 600;
margin-bottom: 6px;
}
.sync-plan-section-icon {
display: flex;
color: var(--text-muted);
}
.sync-plan-section.is-destructive .sync-plan-section-heading,
.sync-plan-section.is-destructive .sync-plan-section-icon {
color: var(--text-error);
}
.sync-plan-warning {
color: var(--text-error);
font-size: 0.85em;
margin-bottom: 6px;
}
.sync-plan-file-list {
list-style: none;
margin: 0;
padding: 0;
max-height: 220px;
overflow-y: auto;
background: var(--background-secondary);
border-radius: 5px;
}
.sync-plan-section.is-destructive .sync-plan-file-list {
border: 1px solid var(--text-error);
}
.sync-plan-file-item {
padding: 4px 10px;
font-family: var(--font-monospace);
font-size: 0.82em;
word-break: break-all;
display: flex;
flex-direction: column;
}
.sync-plan-file-row {
display: flex;
align-items: center;
gap: 6px;
}
.sync-plan-file-icon {
display: flex;
flex-shrink: 0;
color: var(--text-faint);
}
.sync-plan-file-icon .svg-icon { width: 11px; height: 11px; }
.sync-plan-section.is-destructive .sync-plan-file-icon { color: var(--text-error); }
.sync-plan-file-moved-from {
color: var(--text-muted);
font-size: 0.9em;
padding-left: 17px;
}
.sync-plan-buttons {
padding-top: 12px;
border-top: 1px solid var(--background-modifier-border);
flex-shrink: 0;
}
.ssv-whats-new-list {
margin: 0 0 12px;
padding-left: 20px;
}
.ssv-whats-new-notable {
font-weight: 600;
}
.ssv-whats-new-summary {
color: var(--text-muted);
margin: 0 0 12px;
}
.ssv-whats-new-steps {
list-style: none;
margin: 0 0 16px;
padding: 0;
display: flex;
flex-direction: column;
gap: 10px;
}
.ssv-whats-new-step {
padding: 8px 10px;
border-radius: 6px;
background: var(--background-secondary);
}
.ssv-whats-new-step-label {
display: block;
font-size: 0.8em;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.02em;
}
.ssv-whats-new-step strong {
display: block;
}
.ssv-whats-new-step p {
margin: 2px 0 0;
color: var(--text-muted);
}
/* ── Settings "what's new" banner ──────────────────────────────── */
.gfs-whats-new-banner {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
padding: 10px 12px;
margin-bottom: 12px;
border-radius: 8px;
background: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
}
.gfs-whats-new-banner-text {
flex: 1;
}
.gfs-whats-new-banner-list {
margin: 4px 0 0;
padding-left: 20px;
}
.gfs-whats-new-banner-view {
margin-top: 6px;
}
.gfs-whats-new-banner-dismiss {
background: none;
border: none;
box-shadow: none;
cursor: pointer;
color: var(--text-muted);
font-size: 1.1em;
line-height: 1;
padding: 2px 4px;
}
.gfs-whats-new-banner-dismiss:hover {
color: var(--text-normal);
}