Unify cel editing in the timeline

This commit is contained in:
Your Name 2026-10-01 14:37:59 -04:00
parent 4ddd6a8d1d
commit 26ada03591
18 changed files with 534 additions and 922 deletions

View file

@ -228,8 +228,7 @@ input[type="range"] { width: 100%; accent-color: var(--sel); }
/* Buttons that are one control: a transport, a stepper, a mode picker. They
share their borders, so the group reads as a single object with parts rather
than as several things that happen to be adjacent — which is the whole claim
a segmented control makes, and the reason `timeline` and `cel sheet` are one
of these. `.seg` is `.group` with that meaning; they are drawn the same
a segmented control makes. `.seg` is `.group` with that meaning; both are drawn
because the difference is what the buttons do, not how they look.
The negative margin collapses the doubled border between two buttons into
@ -252,12 +251,6 @@ button.ico > svg { display: block; width: 11px; height: 11px; }
/* Play is the one control in the strip you aim at without looking. */
button.ico-play { padding-left: 8px; padding-right: 8px; }
/* `hold −` / `hold +`: one label over two steppers, because the word is shared
and repeating it in both buttons was most of their width. */
.stepper { display: flex; align-items: center; gap: 4px; }
.stepper-label { color: var(--dim); }
.stepper button { padding: 1px 6px; }
/* A number that changes every frame. Tabular figures stop it twitching, and
stop the controls after it being nudged about as the count passes 9 and 99. */
.readout { font-variant-numeric: tabular-nums; }
@ -1003,6 +996,7 @@ button.share-button:hover, button.share-button.on { filter: brightness(1.1); }
}
.tl-label.on { background: var(--sel-bg); }
.tl-label.aimed { box-shadow: inset 0 0 0 2px var(--aim); }
.tl-label:hover:not(.on) { background: #fff; }
.tl-label .name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.tl-label .kind { color: var(--dim); }
@ -1099,6 +1093,29 @@ button.share-button:hover, button.share-button.on { filter: brightness(1.1); }
/* A node's bar slides it along its symbol's time. */
.tl-span.movable { cursor: ew-resize; touch-action: none; }
.tl-span.movable.sliding { border-color: var(--sel); }
.tl-edge {
position: absolute;
top: -1px;
bottom: -1px;
width: 7px;
cursor: e-resize;
touch-action: none;
z-index: 2;
}
.tl-edge.out { right: -3px; }
.tl-edge.in { left: -3px; }
.tl-junction {
position: absolute;
top: -1px;
left: 0;
bottom: -1px;
width: 12px;
transform: translateX(-50%);
cursor: col-resize;
touch-action: none;
z-index: 3;
}
.tl-cel-label { display: block; overflow: hidden; text-overflow: ellipsis; pointer-events: none; }
.tl-span.ghost {
background: transparent;
border: 1px dashed var(--sel);
@ -1143,79 +1160,6 @@ button.share-button:hover, button.share-button.on { filter: brightness(1.1); }
.tl-empty { padding: 9px; color: var(--dim); }
/* The second temporal view. Its cells carry the same selection addresses as
the cel blocks above; only the axes change. */
.cel-sheet {
flex: 1;
min-height: 0;
overflow: auto;
display: grid;
align-content: start;
background: var(--line);
gap: 1px;
}
.cs-head,
.cs-frame,
.cs-cell {
min-width: 0;
height: 24px;
border: 0;
border-radius: 0;
padding: 0 6px;
background: #fff;
color: var(--fg);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cs-head {
position: sticky;
top: 0;
z-index: 2;
display: flex;
align-items: center;
background: var(--chrome);
font-weight: 600;
cursor: pointer;
}
.cs-head.cs-frame { z-index: 3; }
.cs-frame { position: sticky; left: 0; z-index: 1; color: var(--dim); text-align: right; }
.cs-frame.on, .cs-cell.current { box-shadow: inset 3px 0 0 var(--playhead); }
.cs-cell { text-align: left; cursor: pointer; }
.cs-cell:hover { background: var(--sel-bg); }
.cs-cell.selected { background: var(--sel-bg); color: var(--sel); font-weight: 600; }
.cs-head.selected { background: var(--sel-bg); color: var(--sel); }
/* AIMED COMPOSES WITH SELECTED rather than replacing it: the ring is the aim,
the tint is the selection, and a row that is both wears both. No name tag in
the chrome — the thing the ring is around is already the name. */
.tl-label.aimed,
.cs-head.aimed { box-shadow: inset 0 0 0 2px var(--aim); }
.cs-head.aimed { color: var(--aim); }
.cs-empty {
display: grid;
justify-items: center;
align-content: center;
gap: 6px;
padding: 28px 16px;
background: var(--pane);
text-align: center;
}
.cs-empty p { margin: 0; }
.cs-empty .dim { color: var(--dim); font-size: 11px; }
.cs-empty button { margin-top: 4px; }
.cel-sheet { user-select: none; touch-action: none; }
.cs-cell { position: relative; }
.cs-cell:focus-visible { outline: 2px solid var(--sel); outline-offset: -2px; }
.cs-help { padding: 6px 10px; color: var(--dim); font-size: 11px;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cs-fill-handle { position: absolute; right: 0; bottom: 0; width: 9px; height: 9px;
background: var(--sel); border: 1px solid white; cursor: ns-resize; z-index: 2; }
.cs-preview { outline: 1px dashed var(--sel); outline-offset: -1px; }
/* --------------------------------------------------------------------------
the video -> symbol dialog */