1754 lines
45 KiB
CSS
1754 lines
45 KiB
CSS
/* ── 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);
|
||
}
|