Canvas V2

This commit is contained in:
2026-10-04 15:58:27 +02:00
parent 95002a34ad
commit 06da56d0a0
7 changed files with 1008 additions and 333 deletions
+110 -46
View File
@@ -950,51 +950,31 @@ label.tree-name { flex: 1; cursor: pointer; position: relative; }
display: flex;
flex-direction: column;
}
.canvas-bar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--space-1) var(--space-4);
padding: var(--space-2) var(--space-4);
border-bottom: var(--border-dashed);
font-size: var(--font-sm);
}
.canvas-title { min-width: 0; max-width: 30rem; }
.canvas-tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1); }
.canvas-status { margin-left: auto; }
.canvas-status.is-error { color: var(--danger); }
.canvas-zoom { min-width: 4.5em; text-align: center; }
.canvas-color-input {
width: 1.6rem;
height: 1.2rem;
padding: 0;
border: var(--border);
background: none;
cursor: pointer;
}
.canvas-swatch {
display: inline-block;
width: 0.8em;
height: 0.8em;
margin-right: var(--space-2);
background: var(--node-color);
}
.canvas-colors .btn-block { justify-content: flex-start; align-items: center; }
.canvas-error { padding: var(--space-2) var(--space-4); color: var(--danger); }
/* The stage holds the viewport and, floating over its top edge while a note
is open, the page editor's toolbar, so opening it moves nothing. */
/* The stage holds the viewport and what floats over it: the status line,
the context menu and the edit window. */
.canvas-stage { position: relative; flex: 1 1 auto; min-height: 0; }
.canvas-node-toolbar { position: absolute; top: 0; left: 0; right: 0; z-index: 10; }
.canvas-node-toolbar[hidden] { display: none; }
.canvas-node-toolbar .editor-toolbar { position: static; border-left: none; border-right: none; }
.canvas-statusbar {
position: absolute;
left: var(--space-2);
bottom: var(--space-2);
z-index: 10;
display: flex;
gap: var(--space-3);
padding: 0 var(--space-2);
background: var(--bg);
pointer-events: none;
}
.canvas-status.is-error { color: var(--danger); pointer-events: auto; }
.canvas-editor-toolbar[hidden] { display: none; }
.canvas-viewport {
position: absolute;
inset: 0;
overflow: hidden;
touch-action: none;
cursor: grab;
outline: none;
-webkit-touch-callout: none;
background-image: radial-gradient(circle, var(--bg-panel-hover) 1px, transparent 1.5px);
}
.canvas-viewport.is-far { background-image: none; }
@@ -1066,7 +1046,7 @@ label.tree-name { flex: 1; cursor: pointer; position: relative; }
.canvas-node-body {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
overflow: hidden;
padding: var(--space-2) var(--space-3);
word-break: break-word;
}
@@ -1097,13 +1077,6 @@ audio.canvas-media { height: auto; }
.canvas-group-bg-ratio { background-size: contain; background-repeat: no-repeat; }
.canvas-group-bg-repeat { background-repeat: repeat; }
/* The note editor inside a text node (editor/core.js). */
.canvas-node.is-editing { z-index: 5; cursor: auto; user-select: text; -webkit-user-select: text; }
.canvas-node-editor { flex: 1 1 auto; min-height: 0; display: flex; }
.canvas-node-editor .cm-editor { flex: 1 1 auto; min-width: 0; border: none; }
.canvas-node-editor .cm-editor .cm-scroller { min-height: 0; }
.canvas-node-editor .cm-editor .cm-content { padding: var(--space-2) var(--space-3); }
/* Handles: connect from a side, resize from the corner. */
.canvas-handle, .canvas-resize { position: absolute; display: none; z-index: 1; }
.canvas-handle {
@@ -1132,8 +1105,6 @@ audio.canvas-media { height: auto; }
@media (hover: hover) {
.canvas-node:hover > .canvas-handle { display: block; }
}
.canvas-node.is-editing > .canvas-handle,
.canvas-node.is-editing > .canvas-resize { display: none; }
/* Edges: an SVG sized to their bounding box (canvas.js). The wide invisible
hit path makes thin lines easy to click. */
@@ -1169,6 +1140,99 @@ audio.canvas-media { height: auto; }
}
.canvas-edge-label.is-selected { color: var(--primary-hover); }
/* Context menu (canvas.js openMenu): fixed at the pointer. */
.canvas-menu {
position: fixed;
z-index: 100;
min-width: 12rem;
padding: var(--space-1) 0;
font-size: var(--font-sm);
}
.canvas-menu .btn-block { padding: 0.3rem var(--space-3); }
.canvas-menu .btn:disabled { color: var(--text-muted); cursor: default; }
.canvas-menu-sep { border-top: var(--border-dashed); margin: var(--space-1) 0; }
.canvas-menu-row {
display: flex;
align-items: center;
gap: var(--space-2);
padding: 0.3rem var(--space-3);
}
.canvas-menu-row > .muted { min-width: 3rem; }
.canvas-swatch {
width: 1.1rem;
height: 1.1rem;
padding: 0;
border: var(--border);
background: var(--node-color, transparent);
cursor: pointer;
}
.canvas-swatch:not([data-color]):not(input) {
background: linear-gradient(to top right, transparent 45%, var(--text-muted) 45%, var(--text-muted) 55%, transparent 55%);
}
.canvas-swatch.is-current { outline: 2px solid var(--primary-hover); outline-offset: 1px; }
input.canvas-swatch { background: none; }
/* Edit window (canvas.js openWindow): not modal, placed beside its card;
small screens dock it to the bottom. */
.canvas-window {
position: absolute;
z-index: 20;
width: 24rem;
max-width: calc(100% - 2rem);
max-height: calc(100% - 2rem);
display: flex;
flex-direction: column;
box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.4);
}
.canvas-window.is-wide { width: 34rem; height: 24rem; resize: both; overflow: hidden; min-width: 18rem; min-height: 12rem; }
.canvas-window .panel-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-2);
font-size: var(--font-sm);
color: var(--text);
padding: 0.4rem var(--space-4);
margin-bottom: 0;
cursor: move;
user-select: none;
}
.canvas-window .panel-body { flex: 1 1 auto; min-height: 0; overflow: auto; }
.canvas-window .panel-footer { justify-content: flex-end; }
.canvas-window .input { background: var(--bg); }
.canvas-field { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.canvas-field-label { font-size: var(--font-xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.canvas-field-row { display: flex; align-items: baseline; gap: var(--space-2); min-width: 0; }
.canvas-field-row > :first-child { flex: 1 1 auto; min-width: 0; }
.canvas-field-pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.canvas-fields { display: flex; flex-direction: column; gap: var(--space-3); }
.canvas-window.is-wide .panel-body { padding: 0; }
.canvas-window-editor { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.canvas-window-editor .editor-toolbar { position: static; border-left: none; border-right: none; border-top: none; }
.canvas-window-cm { flex: 1 1 auto; min-height: 0; display: flex; }
.canvas-window-cm .cm-editor { flex: 1 1 auto; min-width: 0; border: none; }
.canvas-window-cm .cm-editor .cm-scroller { min-height: 0; }
.canvas-window-cm .cm-editor .cm-content { padding: var(--space-2) var(--space-3); }
@media (max-width: 600px) {
.canvas-window, .canvas-window.is-wide {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: auto;
width: auto;
max-width: none;
height: auto;
max-height: 60vh;
resize: none;
padding-bottom: env(safe-area-inset-bottom);
}
.canvas-window.is-wide { height: 50vh; }
.canvas-window .panel-header { cursor: default; }
.canvas-menu .btn-block { min-height: 2.5rem; align-items: center; }
.canvas-swatch { width: 1.6rem; height: 1.6rem; }
}
/* === Responsive === */
@media (max-width: 1100px) {
.tree-sidebar, .sidebar { display: none; }