Files
plainleaf/plugs/object-graph/ui/object-graph.scss
T

465 lines
12 KiB
SCSS
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.
// 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;
}