From 5af0cdb3d4f7b9853063dbb3d939803013466c66 Mon Sep 17 00:00:00 2001
From: th
Date: Fri, 24 Apr 2026 14:55:22 +0200
Subject: [PATCH] 0.18.1: editor-hide cascades to groups + rename for layers &
clip tracks
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Track groups get the same editor-hide affordance as layer and clip
tracks: new eye button on the group header, setGroupEditorHidden
action (clears selection pointing inside the group), and a cascade
through the Konva stackItems + render.ts so every child track in a
hidden group vanishes in the editor.
Track renaming via right-click context menu now covers layers and
clip tracks too — setLayerName / setClipTrackName actions, new i18n
strings, and end-to-end UI tests driving the menu → promptDialog →
rename flow.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
package.json | 2 +-
src/components/CanvasStage.vue | 9 ++
src/components/Timeline.vue | 53 +++++++
src/devtools/tests.ts | 260 +++++++++++++++++++++++++++++++++
src/i18n/messages.ts | 10 ++
src/lib/render.ts | 4 +
src/stores/scene.ts | 39 +++++
src/types/scene.ts | 5 +
src/version.ts | 2 +-
9 files changed, 382 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index d0b7ff3..4f84adc 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "ruwio",
"private": true,
- "version": "0.18.0",
+ "version": "0.18.1",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/src/components/CanvasStage.vue b/src/components/CanvasStage.vue
index 81eb633..d9bce85 100644
--- a/src/components/CanvasStage.vue
+++ b/src/components/CanvasStage.vue
@@ -821,11 +821,20 @@ const stackItems = computed(() => {
instancesByTrack.set(ci.clipTrackId, arr)
}
+ // Tracks whose parent TrackGroup is editor-hidden — same cascade
+ // as the Timeline's lockedGroupChildIds logic, just for the eye
+ // toggle. A child with its own editorHidden flag unset still
+ // disappears if its group carries one.
+ const hiddenGroupChildIds = new Set()
+ for (const g of store.scene.groups ?? []) {
+ if (g.editorHidden) for (const cid of g.childIds) hiddenGroupChildIds.add(cid)
+ }
const expanded = expandedTrackOrder(store.scene)
const order = expanded.length ? expanded : store.scene.layers.map((l) => l.id)
// iterate bottom → top: reverse the array
for (let i = order.length - 1; i >= 0; i--) {
const id = order[i]
+ if (hiddenGroupChildIds.has(id)) continue
const layer = layerById.get(id)
if (layer) {
// Editor-only hide: skip on the Konva stage while the scene
diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue
index d9b2f9a..8820381 100644
--- a/src/components/Timeline.vue
+++ b/src/components/Timeline.vue
@@ -89,6 +89,7 @@ interface TimelineGroupTrack {
collapsed: boolean
color: string | null
locked: boolean
+ editorHidden: boolean
orderIndex: number
childCount: number
}
@@ -808,6 +809,7 @@ const tracks = computed(() => {
collapsed: !!grp.collapsed,
color: grp.color ?? null,
locked: !!grp.locked,
+ editorHidden: !!grp.editorHidden,
orderIndex,
childCount: grp.childIds.length,
})
@@ -2741,6 +2743,36 @@ function duplicateTrackFromMenu() {
async function renameFromMenu() {
if (!trackMenu.value) return
+ if (trackMenu.value.kind === 'layer') {
+ const layer = store.activeLayers.find((l) => l.id === trackMenu.value!.trackId)
+ if (!layer) return
+ const id = trackMenu.value.trackId
+ const current = layer.name
+ trackMenu.value = null
+ const next = await promptDialog({
+ title: t('timeline.renameLayerDialog.title'),
+ defaultValue: current,
+ confirmLabel: t('timeline.renameLayerDialog.confirm'),
+ })
+ if (next === null) return
+ store.setLayerName(id, next)
+ return
+ }
+ if (trackMenu.value.kind === 'clip') {
+ const track = store.scene.clipTracks?.find((t) => t.id === trackMenu.value!.trackId)
+ if (!track) return
+ const id = trackMenu.value.trackId
+ const current = track.name ?? t('timeline.clipTrackName')
+ trackMenu.value = null
+ const next = await promptDialog({
+ title: t('timeline.renameClipTrackDialog.title'),
+ defaultValue: current,
+ confirmLabel: t('timeline.renameClipTrackDialog.confirm'),
+ })
+ if (next === null) return
+ store.setClipTrackName(id, next)
+ return
+ }
if (trackMenu.value.kind === 'group') {
const group = groupById.value.get(trackMenu.value.trackId)
if (!group) return
@@ -3778,6 +3810,16 @@ onBeforeUnmount(() => {
{{ track.name }}
{{ t('timeline.groupChildCount', { n: track.childCount }) }}
+
+
+
+
+
|