diff --git a/.gitignore b/.gitignore index 65b8c2d8..bfc4a15d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ CLAUDE.md .idea/ .playwright-mcp/ -PLAN.md +plans/ # Gradle and Maven with auto-import # When using Gradle or Maven with auto-import, you should exclude module files, diff --git a/client/src/assets/styles/timeline.scss b/client/src/assets/styles/timeline.scss new file mode 100644 index 00000000..e271a2ef --- /dev/null +++ b/client/src/assets/styles/timeline.scss @@ -0,0 +1,121 @@ +/** + * Shared styles for timeline visualization components (MicTimeline, StageTimeline). + * + * Usage: Import in Vue component diff --git a/client/src/vue_components/show/config/stage/StageTimeline.vue b/client/src/vue_components/show/config/stage/StageTimeline.vue new file mode 100644 index 00000000..14a3d217 --- /dev/null +++ b/client/src/vue_components/show/config/stage/StageTimeline.vue @@ -0,0 +1,318 @@ + + + + + + + + + + Combined + + + Props + + + Scenery + + + + + + + + + No allocation data to display for this view + + + + + + + {{ actGroup.actName }} + + + + + + {{ scene.name }} + + + + + + + + + + {{ bar.tooltip }} + + {{ bar.label }} + + + + + + + + + {{ row.name }} + + + + + + + + + + + + diff --git a/client/src/vue_components/show/live/StageManagerPane.vue b/client/src/vue_components/show/live/StageManagerPane.vue index 107975b5..70425282 100644 --- a/client/src/vue_components/show/live/StageManagerPane.vue +++ b/client/src/vue_components/show/live/StageManagerPane.vue @@ -49,25 +49,6 @@ - - - Scenery - - - {{ item.name }} - - - - - - Props - - - {{ item.name }} - - - - + Plan + + + Scenery + + + {{ getSceneryDisplayName(item) }} + + + + + Props + + + {{ getPropDisplayName(item) }} + + + + + + + + + + + + Setting + + + + + + + + + Scenery + + + Props + + + + + + + {{ getSceneryDisplayName(item) }} + + + None + + + + + {{ getPropDisplayName(item) }} + + + None + + + + + + + + + + + + Striking + + + + + + + + + Scenery + + + Props + + + + + + + {{ getSceneryDisplayName(item) }} + + + None + + + + + {{ getPropDisplayName(item) }} + + + None + + + + + + + + + No scene selected. + + @@ -102,6 +213,9 @@ export default { expandedScenes: {}, pinnedScenes: {}, debounceContentSize: null, + smPlanScene: null, + smPlanSet: true, + smPlanStrike: true, }; }, computed: { @@ -157,6 +271,8 @@ export default { 'SCENERY_ALLOCATIONS', 'PROPS_LIST', 'SCENERY_LIST', + 'PROP_TYPES_DICT', + 'SCENERY_TYPES_DICT', ]), }, watch: { @@ -213,6 +329,8 @@ export default { this.GET_SCENERY_LIST(), this.GET_PROPS_ALLOCATIONS(), this.GET_SCENERY_ALLOCATIONS(), + this.GET_PROP_TYPES(), + this.GET_SCENERY_TYPES(), ]); this.loaded = true; @@ -267,6 +385,14 @@ export default { .map((alloc) => this.sceneryDict[alloc.scenery_id]) .filter((scenery) => scenery != null); }, + getPropDisplayName(prop) { + const propType = this.PROP_TYPES_DICT[prop.prop_type_id]; + return propType ? `${propType.name}: ${prop.name}` : prop.name; + }, + getSceneryDisplayName(scenery) { + const sceneryType = this.SCENERY_TYPES_DICT[scenery.scenery_type_id]; + return sceneryType ? `${sceneryType.name}: ${scenery.name}` : scenery.name; + }, autoScrollToCurrentScene(currentSceneId) { const container = this.$refs.scrollContainer; if (!container) return; @@ -295,6 +421,66 @@ export default { container.scrollTo({ top: scrollOffset, behavior: 'smooth' }); } }, + resetSMPlanScene() { + this.smPlanScene = null; + }, + showSMPlanModal(scene) { + this.smPlanScene = scene; + this.$bvModal.show('sm-plan-modal'); + }, + togglePlanSet() { + this.smPlanSet = !this.smPlanSet; + }, + togglePlanStrike() { + this.smPlanStrike = !this.smPlanStrike; + }, + getPreviousScene(scene) { + const currentIndex = this.orderedScenes.findIndex((s) => s.id === scene.id); + if (currentIndex <= 0) { + return null; // First scene or not found + } + return this.orderedScenes[currentIndex - 1]; + }, + getSettingScenery(scene) { + const currentScenery = this.getSceneryForScene(scene.id); + const previousScene = this.getPreviousScene(scene); + if (!previousScene) { + return currentScenery; // First scene - all items are being set + } + const previousSceneryIds = new Set( + this.getSceneryForScene(previousScene.id).map((s) => s.id) + ); + return currentScenery.filter((item) => !previousSceneryIds.has(item.id)); + }, + getSettingProps(scene) { + const currentProps = this.getPropsForScene(scene.id); + const previousScene = this.getPreviousScene(scene); + if (!previousScene) { + return currentProps; // First scene - all items are being set + } + const previousPropsIds = new Set(this.getPropsForScene(previousScene.id).map((p) => p.id)); + return currentProps.filter((item) => !previousPropsIds.has(item.id)); + }, + getStrikingScenery(scene) { + const previousScene = this.getPreviousScene(scene); + if (!previousScene) { + return []; // First scene - nothing to strike + } + const currentSceneryIds = new Set(this.getSceneryForScene(scene.id).map((s) => s.id)); + return this.getSceneryForScene(previousScene.id).filter( + (item) => !currentSceneryIds.has(item.id) + ); + }, + getStrikingProps(scene) { + const previousScene = this.getPreviousScene(scene); + if (!previousScene) { + return []; // First scene - nothing to strike + } + const currentPropsIds = new Set(this.getPropsForScene(scene.id).map((p) => p.id)); + return this.getPropsForScene(previousScene.id).filter( + (item) => !currentPropsIds.has(item.id) + ); + }, ...mapActions([ 'GET_ACT_LIST', 'GET_SCENE_LIST', @@ -302,6 +488,8 @@ export default { 'GET_SCENERY_LIST', 'GET_PROPS_ALLOCATIONS', 'GET_SCENERY_ALLOCATIONS', + 'GET_PROP_TYPES', + 'GET_SCENERY_TYPES', ]), }, }; @@ -431,4 +619,31 @@ export default { color: #6c757d; font-style: italic; } + +.plan-header-row { + margin: 0; + padding: 0; + border-bottom: 1px solid #dee2e6; +} + +.plan-header-col { + padding: 0.5rem 0.75rem; +} + +.plan-header-col.border-right { + border-right: 1px solid #dee2e6; +} + +.plan-content-row { + margin: 0; + padding: 0; +} + +.plan-content-col { + padding: 0.5rem 0.75rem; +} + +.plan-content-col.border-right { + border-right: 1px solid #dee2e6; +}
None
No scene selected.