Files
BetterDesk/web-nodejs/public/css/cdap-studio.css
T
UNITRONIX 45e5fda9d0 Implement RBAC v52, org scoping and assorted fixes
Adds a full Phase-52 RBAC implementation and multiple server/frontend fixes. Key changes: new auth/permissions.go with 28 granular permissions and DefaultRolePermissions, expanded 7-role hierarchy and helpers in auth/roles.go, JWT org context and GenerateOrgToken, requirePermission/requireOrgMembership middlewares (Go + Node.js), DB schema & adapter changes for role_permissions and is_server_admin, org role boundary checks and peer org scoping, and guards for last-admin demotion and self-demotion. Also: TCP EOF/connection-reset log filtering in signal/relay servers, improved startup banner port display, KEYS_PATH auto-detect warning, CSS hover/transition layout fixes, admin password race mitigation, ID-change ghost peer cleanup, added Tauri ACL schema files, and a new RBAC_PHASE52.md doc. Misc: numerous web-nodejs i18n, CSS, JS and route updates and an updated .github/copilot-instructions.md timestamp/summary.
2026-04-10 23:40:55 +02:00

942 lines
23 KiB
CSS

/* ═══════════════════════════════════════════════════════════════════
CDAP SDK Studio — Visual Node Editor Styles
Phase 14 of BetterDesk 3.0 Roadmap
═══════════════════════════════════════════════════════════════════ */
/* ── CSS Variables ─────────────────────────────────────────────── */
:root {
--studio-bg: var(--bg-primary, #0d1117);
--studio-panel-bg: var(--bg-secondary, #161b22);
--studio-panel-border: var(--border-primary, #30363d);
--studio-canvas-bg: #0a0e14;
--studio-grid-color: rgba(110, 118, 129, 0.12);
--studio-toolbar-bg: var(--bg-tertiary, #1c2128);
--studio-text: var(--text-primary, #e6edf3);
--studio-text-muted: var(--text-secondary, #8b949e);
--studio-accent: var(--accent-blue, #58a6ff);
--studio-green: #3fb950;
--studio-yellow: #d29922;
--studio-red: #f85149;
--studio-orange: #db6d28;
--studio-purple: #bc8cff;
/* Node colors */
--studio-node-source: #1f6feb;
--studio-node-process: #9e6a03;
--studio-node-output: #238636;
--studio-node-selected: var(--studio-accent);
--studio-node-bg: #21262d;
--studio-node-border: #30363d;
/* Wire colors */
--studio-wire-data: var(--studio-accent);
--studio-wire-control: var(--studio-orange);
--studio-wire-error: var(--studio-red);
/* Sizes */
--studio-palette-width: 220px;
--studio-right-width: 280px;
--studio-toolbar-h: 42px;
--studio-statusbar-h: 28px;
--studio-node-w: 180px;
--studio-node-h: 52px;
--studio-port-r: 6px;
}
/* ── Light theme overrides ──────────────────────────────────────── */
[data-theme="light"] {
--studio-bg: #ffffff;
--studio-panel-bg: #f6f8fa;
--studio-panel-border: #d0d7de;
--studio-canvas-bg: #f0f3f6;
--studio-grid-color: rgba(0, 0, 0, 0.06);
--studio-toolbar-bg: #f6f8fa;
--studio-text: #1f2328;
--studio-text-muted: #656d76;
--studio-node-bg: #ffffff;
--studio-node-border: #d0d7de;
}
/* ── Container ──────────────────────────────────────────────────── */
.studio-container {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
background: var(--studio-bg);
overflow: hidden;
position: relative;
}
/* ── Back button ────────────────────────────────────────────────── */
.studio-back-btn {
text-decoration: none;
color: var(--studio-text-muted);
}
.studio-back-btn:hover {
color: var(--studio-text);
background: rgba(110, 118, 129, 0.15);
}
/* ── Toolbar ────────────────────────────────────────────────────── */
.studio-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
height: var(--studio-toolbar-h);
padding: 0 8px;
background: var(--studio-toolbar-bg);
border-bottom: 1px solid var(--studio-panel-border);
flex-shrink: 0;
gap: 4px;
}
.studio-toolbar-left,
.studio-toolbar-right {
display: flex;
align-items: center;
gap: 2px;
}
.studio-tool-btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
border: none;
border-radius: 4px;
background: transparent;
color: var(--studio-text-muted);
font-size: 12px;
cursor: pointer;
transition: background 0.15s, color 0.15s;
white-space: nowrap;
}
.studio-tool-btn:hover {
background: rgba(110, 118, 129, 0.15);
color: var(--studio-text);
}
.studio-tool-btn .material-icons {
font-size: 18px;
}
.studio-tool-btn.studio-tool-sm {
padding: 3px 4px;
}
.studio-tool-btn.studio-tool-sm .material-icons {
font-size: 16px;
}
.studio-tool-btn.studio-tool-accent {
color: var(--studio-green);
}
.studio-tool-btn.studio-tool-accent:hover {
background: rgba(63, 185, 80, 0.15);
color: var(--studio-green);
}
.studio-tool-label {
font-size: 12px;
}
.studio-toolbar-sep {
width: 1px;
height: 20px;
background: var(--studio-panel-border);
margin: 0 4px;
}
.studio-flow-name {
font-size: 12px;
color: var(--studio-text-muted);
font-style: italic;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.studio-zoom-group {
display: flex;
align-items: center;
gap: 2px;
background: rgba(110, 118, 129, 0.08);
border-radius: 4px;
padding: 0 2px;
}
.studio-zoom-label {
font-size: 11px;
color: var(--studio-text-muted);
min-width: 36px;
text-align: center;
font-variant-numeric: tabular-nums;
}
/* ── Workspace (3-column) ───────────────────────────────────────── */
.studio-workspace {
display: flex;
flex: 1;
overflow: hidden;
}
/* ── Palette Panel (left) ───────────────────────────────────────── */
.studio-palette {
width: var(--studio-palette-width);
min-width: 180px;
background: var(--studio-panel-bg);
border-right: 1px solid var(--studio-panel-border);
display: flex;
flex-direction: column;
overflow-y: auto;
flex-shrink: 0;
}
.studio-panel-header {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
font-size: 12px;
font-weight: 600;
color: var(--studio-text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
border-bottom: 1px solid var(--studio-panel-border);
}
.studio-panel-header .material-icons {
font-size: 16px;
}
.studio-palette-search {
margin: 8px;
padding: 6px 10px;
background: var(--studio-canvas-bg);
border: 1px solid var(--studio-panel-border);
border-radius: 4px;
color: var(--studio-text);
font-size: 12px;
outline: none;
}
.studio-palette-search:focus {
border-color: var(--studio-accent);
}
.studio-palette-search::placeholder {
color: var(--studio-text-muted);
}
.studio-palette-category {
border-bottom: 1px solid var(--studio-panel-border);
}
.studio-palette-cat-btn {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
padding: 8px 12px;
border: none;
background: transparent;
color: var(--studio-text);
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: background 0.15s;
}
.studio-palette-cat-btn:hover {
background: rgba(110, 118, 129, 0.1);
}
.studio-palette-cat-btn .material-icons {
font-size: 16px;
}
.studio-palette-cat-btn[data-cat="sources"] .material-icons:first-child { color: var(--studio-node-source); }
.studio-palette-cat-btn[data-cat="processing"] .material-icons:first-child { color: var(--studio-yellow); }
.studio-palette-cat-btn[data-cat="outputs"] .material-icons:first-child { color: var(--studio-node-output); }
.studio-cat-chevron {
margin-left: auto;
font-size: 18px !important;
transition: transform 0.2s;
}
.studio-palette-category.collapsed .studio-cat-chevron {
transform: rotate(-90deg);
}
.studio-palette-category.collapsed .studio-palette-items {
display: none;
}
.studio-palette-items {
padding: 4px 8px 8px;
}
.studio-palette-node {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 8px;
border-radius: 4px;
cursor: grab;
font-size: 12px;
color: var(--studio-text);
transition: background 0.15s;
user-select: none;
}
.studio-palette-node:hover {
background: rgba(110, 118, 129, 0.15);
}
.studio-palette-node:active {
cursor: grabbing;
}
.studio-palette-node .studio-node-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.studio-palette-node[data-cat="sources"] .studio-node-dot { background: var(--studio-node-source); }
.studio-palette-node[data-cat="processing"] .studio-node-dot { background: var(--studio-yellow); }
.studio-palette-node[data-cat="outputs"] .studio-node-dot { background: var(--studio-node-output); }
.studio-palette-node .studio-node-icon {
font-size: 16px;
color: var(--studio-text-muted);
}
/* ── Canvas ─────────────────────────────────────────────────────── */
.studio-canvas-wrap {
flex: 1;
position: relative;
overflow: hidden;
background: var(--studio-canvas-bg);
}
.studio-canvas-svg {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
.studio-grid-rect {
pointer-events: all;
}
.studio-canvas-svg.no-grid .studio-grid-rect {
fill: transparent;
}
/* ── Nodes on canvas ────────────────────────────────────────────── */
.studio-node {
cursor: move;
}
.studio-node-body {
fill: var(--studio-node-bg);
stroke: var(--studio-node-border);
stroke-width: 1.5;
rx: 6;
ry: 6;
transition: stroke 0.15s;
}
.studio-node.selected .studio-node-body {
stroke: var(--studio-node-selected);
stroke-width: 2;
}
.studio-node.running .studio-node-body {
stroke: var(--studio-green);
stroke-dasharray: 4 2;
animation: studio-node-pulse 1s infinite;
}
.studio-node.error .studio-node-body {
stroke: var(--studio-red);
}
@keyframes studio-node-pulse {
0%, 100% { stroke-opacity: 1; }
50% { stroke-opacity: 0.5; }
}
.studio-node-accent {
width: 4px;
rx: 6;
ry: 6;
}
.studio-node[data-category="sources"] .studio-node-accent { fill: var(--studio-node-source); }
.studio-node[data-category="processing"] .studio-node-accent { fill: var(--studio-yellow); }
.studio-node[data-category="outputs"] .studio-node-accent { fill: var(--studio-node-output); }
.studio-node-label {
fill: var(--studio-text);
font-size: 12px;
font-family: inherit;
pointer-events: none;
}
.studio-node-type {
fill: var(--studio-text-muted);
font-size: 10px;
font-family: inherit;
pointer-events: none;
}
.studio-node-icon {
fill: var(--studio-text-muted);
font-family: 'Material Icons';
font-size: 16px;
pointer-events: none;
}
/* Ports */
.studio-port {
fill: var(--studio-node-border);
stroke: var(--studio-canvas-bg);
stroke-width: 2;
cursor: crosshair;
transition: fill 0.15s, r 0.15s;
}
.studio-port:hover {
fill: var(--studio-accent);
r: 8;
}
.studio-port-in { /* left side */ }
.studio-port-out { /* right side */ }
/* ── Wires ──────────────────────────────────────────────────────── */
.studio-wire {
fill: none;
stroke: var(--studio-wire-data);
stroke-width: 2;
stroke-linecap: round;
pointer-events: stroke;
cursor: pointer;
transition: stroke-width 0.15s;
}
.studio-wire:hover {
stroke-width: 3;
}
.studio-wire.wire-error {
stroke: var(--studio-wire-error);
}
.studio-wire.wire-control {
stroke: var(--studio-wire-control);
stroke-dasharray: 6 3;
}
.studio-wire.selected {
stroke-width: 3;
stroke: var(--studio-accent);
}
.studio-wire.running {
stroke-dasharray: 8 4;
animation: studio-wire-flow 0.8s linear infinite;
}
@keyframes studio-wire-flow {
from { stroke-dashoffset: 0; }
to { stroke-dashoffset: -12; }
}
.studio-temp-wire {
stroke: var(--studio-accent);
stroke-width: 2;
stroke-dasharray: 6 3;
opacity: 0.6;
pointer-events: none;
}
/* ── Selection Box ──────────────────────────────────────────────── */
.studio-select-box {
position: absolute;
border: 1px solid var(--studio-accent);
background: rgba(88, 166, 255, 0.08);
pointer-events: none;
z-index: 10;
}
/* ── Minimap ────────────────────────────────────────────────────── */
.studio-minimap {
position: absolute;
bottom: 12px;
right: 12px;
width: 160px;
height: 100px;
background: var(--studio-panel-bg);
border: 1px solid var(--studio-panel-border);
border-radius: 6px;
overflow: hidden;
opacity: 0.85;
transition: opacity 0.2s;
z-index: 5;
}
.studio-minimap:hover {
opacity: 1;
}
.studio-minimap canvas {
width: 100%;
height: 100%;
}
.studio-minimap-viewport {
position: absolute;
border: 1.5px solid var(--studio-accent);
background: rgba(88, 166, 255, 0.08);
pointer-events: none;
}
/* ── Empty State ────────────────────────────────────────────────── */
.studio-empty-state {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: var(--studio-text-muted);
pointer-events: none;
z-index: 2;
}
.studio-empty-state .material-icons {
font-size: 56px;
opacity: 0.3;
}
.studio-empty-state h3 {
margin: 12px 0 6px;
font-size: 16px;
color: var(--studio-text);
font-weight: 500;
}
.studio-empty-state p {
font-size: 13px;
margin: 0 0 16px;
}
.studio-empty-state .studio-empty-actions {
pointer-events: auto;
}
/* ── Right Panel ────────────────────────────────────────────────── */
.studio-right-panel {
width: var(--studio-right-width);
min-width: 220px;
display: flex;
flex-direction: column;
border-left: 1px solid var(--studio-panel-border);
background: var(--studio-panel-bg);
flex-shrink: 0;
}
/* ── Inspector ──────────────────────────────────────────────────── */
.studio-inspector {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
border-bottom: 1px solid var(--studio-panel-border);
}
.studio-inspector-body {
flex: 1;
overflow-y: auto;
padding: 12px;
}
.studio-inspector-empty {
text-align: center;
padding: 32px 16px;
color: var(--studio-text-muted);
}
.studio-inspector-empty .material-icons {
font-size: 36px;
opacity: 0.3;
}
.studio-inspector-empty p {
font-size: 12px;
margin-top: 8px;
}
/* Inspector form fields */
.studio-field {
margin-bottom: 12px;
}
.studio-field label {
display: block;
font-size: 11px;
color: var(--studio-text-muted);
margin-bottom: 4px;
text-transform: uppercase;
letter-spacing: 0.3px;
}
.studio-field input,
.studio-field select,
.studio-field textarea {
width: 100%;
padding: 6px 8px;
background: var(--studio-canvas-bg);
border: 1px solid var(--studio-panel-border);
border-radius: 4px;
color: var(--studio-text);
font-size: 12px;
font-family: inherit;
outline: none;
box-sizing: border-box;
}
.studio-field input:focus,
.studio-field select:focus,
.studio-field textarea:focus {
border-color: var(--studio-accent);
}
.studio-field textarea {
resize: vertical;
min-height: 60px;
}
.studio-field-row {
display: flex;
gap: 8px;
}
.studio-field-row .studio-field {
flex: 1;
}
.studio-inspector-section {
font-size: 11px;
font-weight: 600;
color: var(--studio-text-muted);
margin: 16px 0 8px;
padding-bottom: 4px;
border-bottom: 1px solid var(--studio-panel-border);
text-transform: uppercase;
letter-spacing: 0.3px;
}
.studio-inspector-connections {
font-size: 12px;
color: var(--studio-text-muted);
}
.studio-inspector-connections span {
display: block;
padding: 2px 0;
}
.studio-delete-node-btn {
width: 100%;
padding: 6px;
margin-top: 16px;
background: transparent;
border: 1px solid var(--studio-red);
border-radius: 4px;
color: var(--studio-red);
font-size: 12px;
cursor: pointer;
transition: background 0.15s;
}
.studio-delete-node-btn:hover {
background: rgba(248, 81, 73, 0.12);
}
/* ── Console ────────────────────────────────────────────────────── */
.studio-console {
height: 180px;
min-height: 100px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.studio-console-header {
position: relative;
}
.studio-console-clear {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: var(--studio-text-muted);
cursor: pointer;
padding: 2px;
border-radius: 3px;
}
.studio-console-clear:hover {
background: rgba(110, 118, 129, 0.15);
color: var(--studio-text);
}
.studio-console-clear .material-icons {
font-size: 16px;
}
.studio-console-body {
flex: 1;
overflow-y: auto;
padding: 6px 10px;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
font-size: 11px;
line-height: 1.5;
}
.studio-console-line {
display: flex;
gap: 6px;
padding: 1px 0;
}
.studio-console-time {
color: var(--studio-text-muted);
flex-shrink: 0;
min-width: 50px;
}
.studio-console-msg {
word-break: break-word;
}
.studio-console-info .studio-console-msg { color: var(--studio-text-muted); }
.studio-console-success .studio-console-msg { color: var(--studio-green); }
.studio-console-warn .studio-console-msg { color: var(--studio-yellow); }
.studio-console-error .studio-console-msg { color: var(--studio-red); }
.studio-console-data .studio-console-msg { color: var(--studio-purple); }
/* ── Status Bar ─────────────────────────────────────────────────── */
.studio-statusbar {
display: flex;
align-items: center;
height: var(--studio-statusbar-h);
padding: 0 12px;
background: var(--studio-toolbar-bg);
border-top: 1px solid var(--studio-panel-border);
font-size: 11px;
color: var(--studio-text-muted);
flex-shrink: 0;
gap: 16px;
}
.studio-status-item {
display: flex;
align-items: center;
gap: 4px;
}
.studio-status-item .material-icons {
font-size: 14px;
}
.studio-status-item b {
color: var(--studio-text);
font-weight: 600;
}
.studio-status-sep {
flex: 1;
}
.studio-status-right {
margin-left: auto;
}
.studio-status-dot {
width: 7px;
height: 7px;
border-radius: 50%;
}
.studio-status-ready { background: var(--studio-green); }
.studio-status-running { background: var(--studio-accent); animation: studio-dot-pulse 1s infinite; }
.studio-status-error { background: var(--studio-red); }
.studio-status-draft { background: var(--studio-text-muted); }
@keyframes studio-dot-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
/* ── Modals ──────────────────────────────────────────────────────── */
.modal-lg {
max-width: 700px;
width: 95%;
}
.studio-flow-list {
display: flex;
flex-direction: column;
gap: 8px;
max-height: 400px;
overflow-y: auto;
}
.studio-flow-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
background: var(--studio-canvas-bg);
border: 1px solid var(--studio-panel-border);
border-radius: 6px;
cursor: pointer;
transition: border-color 0.15s, background 0.15s;
}
.studio-flow-item:hover {
border-color: var(--studio-accent);
background: rgba(88, 166, 255, 0.05);
}
.studio-flow-item-info {
flex: 1;
min-width: 0;
}
.studio-flow-item-name {
font-size: 14px;
font-weight: 500;
color: var(--studio-text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.studio-flow-item-desc {
font-size: 12px;
color: var(--studio-text-muted);
margin-top: 2px;
}
.studio-flow-item-meta {
font-size: 11px;
color: var(--studio-text-muted);
margin-top: 4px;
}
.studio-flow-item-actions {
display: flex;
gap: 4px;
}
.studio-flow-item-actions button {
background: none;
border: none;
color: var(--studio-text-muted);
cursor: pointer;
padding: 4px;
border-radius: 4px;
}
.studio-flow-item-actions button:hover {
background: rgba(110, 118, 129, 0.15);
color: var(--studio-text);
}
.studio-flow-item-actions button.danger:hover {
color: var(--studio-red);
}
.studio-flow-item-actions .material-icons {
font-size: 18px;
}
/* Template grid */
.studio-template-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 12px;
max-height: 400px;
overflow-y: auto;
}
.studio-template-card {
padding: 16px;
background: var(--studio-canvas-bg);
border: 1px solid var(--studio-panel-border);
border-radius: 8px;
cursor: pointer;
transform: translateY(0);
transition: border-color 0.15s, transform 0.15s;
}
.studio-template-card:hover {
border-color: var(--studio-accent);
transform: translateY(-1px);
}
.studio-template-card .material-icons {
font-size: 28px;
color: var(--studio-accent);
margin-bottom: 8px;
}
.studio-template-card h4 {
font-size: 14px;
font-weight: 500;
color: var(--studio-text);
margin: 0 0 4px;
}
.studio-template-card p {
font-size: 12px;
color: var(--studio-text-muted);
margin: 0;
line-height: 1.4;
}
/* ── Responsive ─────────────────────────────────────────────────── */
@media (max-width: 1200px) {
:root {
--studio-palette-width: 180px;
--studio-right-width: 240px;
}
.studio-tool-label { display: none; }
}
@media (max-width: 900px) {
.studio-palette { display: none; }
.studio-right-panel { display: none; }
}
@media (max-width: 600px) {
.studio-toolbar { padding: 0 4px; }
.studio-zoom-group { display: none; }
.studio-minimap { display: none; }
}