465 lines
12 KiB
SCSS
465 lines
12 KiB
SCSS
// Visual language aligned with SilverBullet's configuration-manager plug
|
||
// — color tokens, section header style, button style, and border radii
|
||
// match `silverbullet/plugs/configuration-manager/ui/configuration.scss`.
|
||
// Dark mode is driven by both `prefers-color-scheme` (for iframes that
|
||
// don't see the parent's data-theme) AND `html[data-theme="dark"]` (for
|
||
// when SilverBullet propagates its explicit theme into the iframe).
|
||
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
html {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||
Helvetica, Arial, sans-serif;
|
||
font-size: 14px;
|
||
|
||
// Chrome colors (background, text, borders, sections, hover, accent) come
|
||
// from the shared panel-surface palette in components.css. Only the
|
||
// graph-specific node/edge palette below is defined locally.
|
||
|
||
// Graph palette — node-color tokens are object-graph specific.
|
||
--gv-node: #111;
|
||
--gv-node-current: #1e63d8;
|
||
--gv-node-dim: #9e4705; // missing-link rust
|
||
--gv-label: #333;
|
||
--gv-label-dim: #777;
|
||
--gv-link: #c8c8c8;
|
||
--gv-link-dim: #e6e6e6;
|
||
--gv-link-hot: #464cfc;
|
||
--gv-accent: #650007; // SB meta-color — current-page ring
|
||
--gv-label-halo: rgba(255, 255, 255, 0.78);
|
||
|
||
--gv-tag-bg: #4070d0;
|
||
--gv-tag-fg: #ffffff;
|
||
--gv-tag-border: #2a5099;
|
||
|
||
--gv-untagged: #9e9e9e;
|
||
}
|
||
|
||
// Dark tokens — same values configuration-manager uses, applied for either
|
||
// the explicit theme attribute or the user's system preference.
|
||
@mixin gv-dark-tokens {
|
||
color-scheme: dark;
|
||
|
||
--gv-node: #fff;
|
||
--gv-node-current: #6e9bff;
|
||
--gv-node-dim: #c97c4a;
|
||
--gv-label: #d8d8d8;
|
||
--gv-label-dim: #888;
|
||
--gv-link: #555;
|
||
--gv-link-dim: #333;
|
||
--gv-link-hot: #6e74ff;
|
||
--gv-accent: #d6222e;
|
||
--gv-label-halo: rgba(30, 30, 30, 0.78);
|
||
|
||
--gv-tag-bg: #5b85e8;
|
||
--gv-tag-fg: #ffffff;
|
||
--gv-tag-border: #3868c8;
|
||
|
||
--gv-untagged: #6e6e6e;
|
||
}
|
||
|
||
html[data-theme="dark"] { @include gv-dark-tokens; }
|
||
@media (prefers-color-scheme: dark) {
|
||
html:not([data-theme="light"]) { @include gv-dark-tokens; }
|
||
}
|
||
|
||
html, body { height: 100%; background: var(--ui-surface-background-color); color: var(--ui-surface-color); }
|
||
|
||
#graph-root {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: var(--ui-surface-background-color);
|
||
color: var(--ui-surface-color);
|
||
// No border/radius: the host modal panel already frames the iframe.
|
||
overflow: hidden;
|
||
outline: none;
|
||
}
|
||
|
||
.graph-root-inner {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
outline: none;
|
||
}
|
||
|
||
.graph-app {
|
||
flex: 1 1 auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
}
|
||
|
||
.graph-canvas {
|
||
position: absolute;
|
||
inset: 0;
|
||
}
|
||
|
||
// Floating controls overlay (bottom-right of the canvas stage).
|
||
// Pan pad on the left, zoom in/out stacked on the right.
|
||
.graph-controls {
|
||
position: absolute;
|
||
bottom: 12px;
|
||
right: 12px;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: stretch;
|
||
gap: 8px;
|
||
z-index: 2;
|
||
pointer-events: none; // children opt in
|
||
|
||
button {
|
||
pointer-events: auto;
|
||
background: var(--ui-surface-background-color);
|
||
color: var(--ui-surface-color);
|
||
border: 1px solid var(--ui-surface-border-color);
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
width: 28px;
|
||
height: 28px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 14px;
|
||
line-height: 1;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||
|
||
&:hover { background: var(--ui-surface-hover-background-color); }
|
||
&:active { background: var(--ui-surface-section-background-color); }
|
||
}
|
||
}
|
||
|
||
.graph-zoom {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
|
||
button {
|
||
flex: 1 1 0;
|
||
height: auto; // let flex stretch determine the height
|
||
}
|
||
}
|
||
|
||
// 3x3 directional pad — middle row has ←, center, →; top/bottom rows
|
||
// have just the up/down arrow in the middle column.
|
||
.graph-pan {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 28px);
|
||
grid-template-rows: repeat(3, 28px);
|
||
gap: 4px;
|
||
|
||
.graph-pan-up { grid-column: 2; grid-row: 1; }
|
||
.graph-pan-left { grid-column: 1; grid-row: 2; }
|
||
.graph-pan-center{ grid-column: 2; grid-row: 2; }
|
||
.graph-pan-right { grid-column: 3; grid-row: 2; }
|
||
.graph-pan-down { grid-column: 2; grid-row: 3; }
|
||
}
|
||
|
||
.gv-app {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
background: var(--ui-surface-background-color);
|
||
color: var(--ui-surface-color);
|
||
}
|
||
.gv-body {
|
||
display: grid;
|
||
grid-template-columns: var(--gv-sidebar-width, 230px) 5px minmax(0, 1fr);
|
||
flex: 1 1 auto;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
.gv-resizer {
|
||
cursor: col-resize;
|
||
background: transparent;
|
||
border-left: 1px solid var(--ui-surface-border-color);
|
||
user-select: none;
|
||
transition: background 120ms ease;
|
||
}
|
||
.gv-resizer:hover,
|
||
.gv-resizer.dragging {
|
||
background: var(--ui-accent-color, rgba(70, 76, 252, 0.25));
|
||
}
|
||
.gv-canvas-wrap {
|
||
position: relative;
|
||
min-width: 0;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
.gv-canvas-wrap .graph-canvas {
|
||
position: absolute;
|
||
inset: 0;
|
||
}
|
||
|
||
/* Object section inside the sidebar (selected node attributes). */
|
||
.gv-object-section {
|
||
margin-top: auto;
|
||
padding: 6px 14px 10px;
|
||
border-top: 1px solid var(--ui-surface-border-color);
|
||
background: var(--ui-surface-background-color);
|
||
}
|
||
.gv-object-section .gv-object-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
padding-bottom: 4px;
|
||
margin-bottom: 4px;
|
||
border-bottom: 1px solid var(--ui-surface-border-color);
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: var(--ui-surface-color);
|
||
}
|
||
.gv-object-section .gv-object-title {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.gv-object-section .gv-object-title-link {
|
||
cursor: pointer;
|
||
color: var(--ui-accent-color);
|
||
text-decoration: none;
|
||
}
|
||
.gv-object-section .gv-object-title-link:hover { text-decoration: underline; }
|
||
.gv-object-section .gv-object-body {
|
||
margin: 0;
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||
font-size: 11px;
|
||
line-height: 1.45;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
color: var(--ui-surface-color);
|
||
}
|
||
|
||
/* Header — sized roughly like configuration-manager's #cfg-header. */
|
||
.gv-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
padding: 10px 16px;
|
||
border-bottom: 1px solid var(--ui-surface-border-color);
|
||
flex-shrink: 0;
|
||
}
|
||
.gv-header-title {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
margin: 0;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.gv-header-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
// Toolbar buttons + checkboxes now use the shared `.sb-button` / `.sb-checkbox`
|
||
// components; only the toggle-label layout and close-button positioning remain
|
||
// graph-specific.
|
||
.gv-header-toggle {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 13px;
|
||
color: var(--ui-surface-color);
|
||
user-select: none;
|
||
cursor: pointer;
|
||
}
|
||
|
||
// Close button uses the `.sb-button-icon` component; keep only positioning and
|
||
// the larger glyph size for the `×`.
|
||
.gv-close-button {
|
||
margin-left: auto;
|
||
font-size: 18px;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* Ghost-node hover tooltip */
|
||
.gv-ghost-tooltip {
|
||
position: absolute;
|
||
z-index: 20;
|
||
max-width: 280px;
|
||
background: var(--ui-surface-background-color);
|
||
border: 1px solid var(--ui-surface-border-color);
|
||
border-radius: 6px;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
|
||
padding: 5px 8px;
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||
font-size: 12px;
|
||
color: var(--ui-surface-color);
|
||
pointer-events: none;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
/* Edge hover tooltip */
|
||
.gv-edge-tooltip {
|
||
position: absolute;
|
||
z-index: 20;
|
||
max-width: 360px;
|
||
background: var(--ui-surface-background-color);
|
||
border: 1px solid var(--ui-surface-border-color);
|
||
border-radius: 6px;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
|
||
padding: 6px 8px;
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||
font-size: 12px;
|
||
color: var(--ui-surface-color);
|
||
pointer-events: none;
|
||
line-height: 1.4;
|
||
}
|
||
.gv-edge-tooltip-label {
|
||
font-weight: 600;
|
||
margin-bottom: 4px;
|
||
}
|
||
.gv-edge-tooltip-snippet {
|
||
font-family: ui-monospace, Menlo, monospace;
|
||
font-size: 11px;
|
||
color: var(--ui-surface-color);
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
max-height: 8em;
|
||
overflow: hidden;
|
||
}
|
||
.gv-edge-tooltip-empty {
|
||
color: var(--ui-surface-subtle-color);
|
||
font-style: italic;
|
||
}
|
||
|
||
/* Sidebar */
|
||
.gv-sidebar {
|
||
border-right: 1px solid var(--ui-surface-border-color);
|
||
overflow: auto;
|
||
font-size: 13px;
|
||
}
|
||
// Filter sections — title underline matches configuration-manager's
|
||
// .lib-section-header / .cfg-category-title pattern.
|
||
.gv-section { padding: 12px 14px 6px; }
|
||
.gv-section + .gv-section { padding-top: 14px; }
|
||
.gv-section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
padding-bottom: 6px;
|
||
margin-bottom: 4px;
|
||
border-bottom: 1px solid var(--ui-surface-border-color);
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: var(--ui-surface-subtle-color);
|
||
cursor: pointer;
|
||
user-select: none;
|
||
}
|
||
.gv-section-title { display: inline-flex; align-items: center; }
|
||
.gv-twisty {
|
||
display: inline-block;
|
||
transition: transform 0.12s;
|
||
margin-right: 6px;
|
||
font-size: 10px;
|
||
color: var(--ui-surface-subtle-color);
|
||
}
|
||
.gv-twisty.open { transform: rotate(90deg); }
|
||
.gv-section-actions {
|
||
font-size: 11px;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
.gv-section-actions a {
|
||
cursor: pointer;
|
||
color: var(--ui-surface-subtle-color);
|
||
text-decoration: none;
|
||
}
|
||
.gv-section-actions a:hover { color: var(--ui-surface-color); text-decoration: underline; }
|
||
.gv-section-body { padding: 0; }
|
||
.gv-forces-body { display: flex; flex-direction: column; gap: 8px; padding: 4px 0; }
|
||
.gv-force-row { display: flex; flex-direction: column; gap: 2px; font-size: 12px; }
|
||
.gv-force-row input[type="range"] { width: 100%; }
|
||
.gv-force-label { display: flex; justify-content: space-between; color: var(--ui-surface-subtle-color); }
|
||
.gv-force-value { font-variant-numeric: tabular-nums; }
|
||
// Search field uses the shared `.sb-input`; keep only layout here.
|
||
.gv-section-search {
|
||
width: 100%;
|
||
margin: 4px 0 8px;
|
||
}
|
||
.gv-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 3px 0;
|
||
font-size: 13px;
|
||
}
|
||
.gv-count { margin-left: auto; color: var(--ui-surface-subtle-color); font-size: 12px; }
|
||
.gv-swatch {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
display: inline-block;
|
||
flex-shrink: 0;
|
||
}
|
||
.gv-tag-row { display: inline-flex; align-items: center; gap: 6px; }
|
||
.gv-more {
|
||
display: block;
|
||
margin-top: 6px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
color: var(--ui-accent-color);
|
||
}
|
||
.gv-more:hover { text-decoration: underline; }
|
||
|
||
/* Object view */
|
||
.gv-object-view {
|
||
border-left: 1px solid var(--ui-surface-border-color);
|
||
background: var(--ui-surface-background-color);
|
||
overflow: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.gv-object-view-empty {
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--ui-surface-subtle-color);
|
||
font-size: 12px;
|
||
padding: 18px;
|
||
}
|
||
.gv-object-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 10px 12px;
|
||
border-bottom: 1px solid var(--ui-surface-border-color);
|
||
}
|
||
.gv-object-title {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-weight: 600;
|
||
}
|
||
.gv-object-body {
|
||
margin: 0;
|
||
padding: 10px 12px;
|
||
font-family: ui-monospace, Menlo, monospace;
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
white-space: pre-wrap;
|
||
color: var(--ui-surface-color);
|
||
}
|
||
|
||
/* Empty graph overlay */
|
||
.gv-empty {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--ui-surface-subtle-color);
|
||
font-size: 13px;
|
||
}
|