commit 77

This commit is contained in:
Moritz Ruth 2025-03-13 02:28:03 +01:00
parent 18dc17d6d5
commit 9a8dcabdd6
Signed by: moritzruth
GPG key ID: C9BBAB79405EE56D
22 changed files with 258 additions and 149 deletions

View file

@ -14,7 +14,7 @@ fun SceneBuilderContext.songMittsommernacht() {
step(StepCue.MusicStart("Mittsommernacht", 4.minutes + 30.seconds)) { step(StepCue.MusicStart("Mittsommernacht", 4.minutes + 30.seconds)) {
actors { actors {
// TODO: Expand // TODO: Expand
+"Einwohner von Huntington / von links & durch Mitte" +"Einwohner / von links & durch Mitte"
} }
curtainState = CurtainState.OPEN curtainState = CurtainState.OPEN
@ -281,7 +281,7 @@ fun SceneBuilderContext.songMittsommernacht() {
step(StepCue.MusicEnd) { step(StepCue.MusicEnd) {
actors { actors {
-"Einwohner von Huntington" -"Einwohner"
+"Mina / durch Mitte, mit Koffer & Taschentuch" +"Mina / durch Mitte, mit Koffer & Taschentuch"
+"Lucy / durch Mitte" +"Lucy / durch Mitte"
+"Jonathan / von links" +"Jonathan / von links"

View file

@ -6,6 +6,7 @@ import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap import kotlinx.collections.immutable.toImmutableMap
import kotlinx.collections.immutable.toImmutableSet import kotlinx.collections.immutable.toImmutableSet
import java.util.*
@DslMarker @DslMarker
annotation class TheaterDslMarker annotation class TheaterDslMarker
@ -103,8 +104,8 @@ private fun buildAct(actIndex: Int, actName: String, build: ActBuilderContext.()
override fun step(cue: StepCue, build: StepDataBuilderContext.() -> Unit) { override fun step(cue: StepCue, build: StepDataBuilderContext.() -> Unit) {
val changedProps = mutableMapOf<PropPosition, StringWithDetails?>() val changedProps = mutableMapOf<PropPosition, StringWithDetails?>()
val actorEntrances = mutableSetOf<StringWithDetails>() val actorEntrances = TreeSet<StringWithDetails>()
val actorExits = mutableSetOf<StringWithDetails>() val actorExits = TreeSet<StringWithDetails>()
var runner: StepRunner? = null var runner: StepRunner? = null
object : StepDataBuilderContext { object : StepDataBuilderContext {
@ -168,6 +169,7 @@ private fun buildAct(actIndex: Int, actName: String, build: ActBuilderContext.()
actorsOnStage.removeAll(actorExitsNames.toSet()) actorsOnStage.removeAll(actorExitsNames.toSet())
actorsOnStage.addAll(actorEntrancesNames) actorsOnStage.addAll(actorEntrancesNames)
actorsOnStage.sort()
changedProps.forEach { (k, v) -> props[k] = v } changedProps.forEach { (k, v) -> props[k] = v }

View file

@ -37,24 +37,9 @@ sealed interface StepCue {
override fun format() = "curtain: $state${if (whileMoving) "(while moving)" else ""}" override fun format() = "curtain: $state${if (whileMoving) "(while moving)" else ""}"
} }
@Serializable
@SerialName("LIGHT")
data class Light(val state: State, val whileFading: Boolean) : StepCue {
enum class State {
@SerialName("on")
ON,
@SerialName("off")
OFF
}
override fun format() = "light: $state${if (whileFading) "(while fading)" else ""}"
}
@Serializable @Serializable
@SerialName("CUSTOM") @SerialName("CUSTOM")
data class Custom(val text: String) : StepCue { data class Custom(val text: String) : StepCue {
override fun format() = "custom: $text" override fun format() = "custom: $text"
} }
} }

View file

@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable
*/ */
@JvmInline @JvmInline
@Serializable @Serializable
value class StringWithDetails(val value: String) { value class StringWithDetails(val value: String) : Comparable<StringWithDetails> {
companion object { companion object {
const val DELIMITER = " / " const val DELIMITER = " / "
} }
@ -15,4 +15,6 @@ value class StringWithDetails(val value: String) {
val main get() = value.split(DELIMITER)[0] val main get() = value.split(DELIMITER)[0]
val details: String? get() = value.split(DELIMITER, limit = 2).takeIf { it.size == 2 }?.let { it[1] } val details: String? get() = value.split(DELIMITER, limit = 2).takeIf { it.size == 2 }?.let { it[1] }
val hasDetails get() = details !== null val hasDetails get() = details !== null
override fun compareTo(other: StringWithDetails): Int = value.compareTo(other.value)
} }

View file

@ -4,10 +4,10 @@
<div class="text-s1">Janus, created by Moritz Ruth</div> <div class="text-s1">Janus, created by Moritz Ruth</div>
</div> </div>
<div v-else class="h-100dvh flex flex-col"> <div v-else class="h-100dvh flex flex-col">
<div class="font-black text-2xl md:text-4xl p-4 md:p-8 pb-0 md:pb-0 flex-shrink-0"> <div class="font-black text-2xl md:text-4xl px-4 pt-4 flex-shrink-0 truncate">
{{ current.act === null ? "" : `${current.act.name}` }}{{ current.scene.name }} {{ current.act === null ? "" : `${current.act.name}` }}{{ current.scene.name }}
</div> </div>
<div class="flex-grow p-4 md:p-8 overflow-hidden"> <div class="flex-grow p-4 overflow-hidden">
<router-view/> <router-view/>
</div> </div>
<MusicProgressBar class="h-10 flex-shrink-0"/> <MusicProgressBar class="h-10 flex-shrink-0"/>

View file

@ -1,16 +1,14 @@
<template> <template>
<div class="overflow-y-auto"> <transition-group name="list" tag="div" class="overflow-y-auto md:columns-200px">
<transition-group name="list" tag="div"> <div v-if="current.step.actorsOnStage.length === 0" key="" class="text-gray-400">Niemand</div>
<div v-if="current.step.actorsOnStage.length === 0" key="" class="text-gray-400">Niemand</div> <div
<div v-for="actor in current.step.actorsOnStage"
v-for="actor in current.step.actorsOnStage" :key="parseStringWithDetails(actor).main"
:key="parseStringWithDetails(actor).main" class="truncate line-height-normal"
class="truncate text-4.5" >
> {{ actor }}
{{ actor }} </div>
</div> </transition-group>
</transition-group>
</div>
</template> </template>
<style> <style>

View file

@ -1,11 +1,14 @@
<template> <template>
<div :data-blinking="isBlinking" class="overflow-hidden text-6 box p-4"> <div :data-blinking="isBlinking" class="overflow-hidden text-6 py-2" :class="$style.root">
{{ message }} <div v-if="message.trim().length === 0" class="text-gray-400">
Keine Nachricht vorhanden.
</div>
<template v-else>{{ message }}</template>
</div> </div>
</template> </template>
<style scoped> <style module>
.box { .root {
&[data-blinking="true"] { &[data-blinking="true"] {
animation: alternate infinite 500ms ease-in-out pulse; animation: alternate infinite 500ms ease-in-out pulse;
} }
@ -22,25 +25,15 @@
} }
</style> </style>
<script> <script setup lang="ts">
import { state } from "../state" import { state } from "@/state"
import { toRef, watch } from "vue" import { toRef, watch } from "vue"
import { autoResetRef } from "@vueuse/core" import { autoResetRef } from "@vueuse/core"
export default { const message = toRef(state, "message")
name: "MessageDisplay", const isBlinking = autoResetRef(false, 10 * 1000)
setup() {
const message = toRef(state, "message")
const isBlinking = autoResetRef(false, 10 * 1000)
watch(message, () => { watch(message, () => {
isBlinking.value = message.value !== "" isBlinking.value = message.value !== ""
}) })
return {
message,
isBlinking
}
}
}
</script> </script>

View file

@ -1,6 +1,6 @@
<template> <template>
<div :class="scrollable ? 'overflow-y-auto' : 'overflow-hidden'" class="flex flex-col space-y-5"> <div :class="scrollable ? 'overflow-y-auto' : 'overflow-hidden'" class="flex flex-col gap-5">
<div v-for="(act, actIndex) in show.acts"> <div v-for="(act, index) in show.acts" :key="index">
<MotionsListAct :act="act" :center-current="Boolean(centerCurrent)"/> <MotionsListAct :act="act" :center-current="Boolean(centerCurrent)"/>
</div> </div>
<div class="h-[50%] flex-shrink-0"/> <div class="h-[50%] flex-shrink-0"/>
@ -9,7 +9,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import MotionsListAct from "./MotionsListAct.vue" import MotionsListAct from "./MotionsListAct.vue"
import { show } from "../state" import { show } from "@/state"
const props = defineProps<{ const props = defineProps<{
centerCurrent?: boolean centerCurrent?: boolean

View file

@ -1,16 +1,16 @@
<template> <template>
<div class="pt-2"> <div class="pt-2">
<div class="font-bold text-7 flex gap-5 items-center pb-4"> <div class="font-bold text-7 flex gap-7 items-center pb-4">
<span class="flex-grow h-2px bg-white"></span> <span class="flex-grow h-2px bg-gray-300"></span>
<span>{{ act.name }}</span> <span>{{ act.name }}</span>
<span class="flex-grow h-2px bg-white"></span> <span class="flex-grow h-2px bg-gray-300"></span>
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-6">
<div <div
v-for="(scene, sceneIndex) in scenes" v-for="(scene, sceneIndex) in scenes"
:key="sceneIndex" :key="sceneIndex"
> >
<div class="text-gray-400 pl-3 text-5"> <div class="text-gray-400 pl-3 text-5 pb-1">
{{ scene.name }} {{ scene.name }}
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
@ -32,7 +32,7 @@
</style> </style>
<script lang="ts" setup> <script lang="ts" setup>
import { Act, Scene, ShowPosition, Step } from "../state" import { Act, Scene, ShowPosition, Step } from "@/state"
import { computed } from "vue" import { computed } from "vue"
import MotionsListStep from "./MotionsListStep.vue" import MotionsListStep from "./MotionsListStep.vue"

View file

@ -1,8 +1,8 @@
<template> <template>
<div :class="isActive && 'bg-green-800'" class="transition p-3"> <div :class="isActive && 'bg-green-800'" class="transition p-3 rounded-2xl">
<div class="flex space-x-2"> <div class="flex space-x-2">
<div class="flex-grow"> <div class="flex-grow">
<CueBox :step="step" text-class="text-6"/> <CueBox class="flex-shrink-0" text-class="text-6" :step="step"/>
<div v-if="step.actorEntrances.length + step.actorExits.length > 0" class="py-2 pl-8 space-y-2 text-7"> <div v-if="step.actorEntrances.length + step.actorExits.length > 0" class="py-2 pl-8 space-y-2 text-7">
<div class="flex flex-col space-y-1"> <div class="flex flex-col space-y-1">
<div <div
@ -47,7 +47,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { parseStringWithDetails, ShowPosition, START_STEP, state, Step } from "../state" import { parseStringWithDetails, ShowPosition, START_STEP, state, Step } from "@/state"
import CueBox from "./CueBox.vue" import CueBox from "./CueBox.vue"
import CaretDoubleRightIcon from "virtual:icons/ph/caret-double-right" import CaretDoubleRightIcon from "virtual:icons/ph/caret-double-right"
import CaretDoubleLeftIcon from "virtual:icons/ph/caret-double-left" import CaretDoubleLeftIcon from "virtual:icons/ph/caret-double-left"
@ -63,7 +63,7 @@
}>() }>()
const position = toRef(state, "position") const position = toRef(state, "position")
const element = useCurrentElement() const element = useCurrentElement<HTMLElement>()
const allPositions = computed(() => [props.step.position, ...(props.morePositions ?? [])]) const allPositions = computed(() => [props.step.position, ...(props.morePositions ?? [])])
const isActive = computed(() => allPositions.value.some(p => isEqual(p, position.value))) const isActive = computed(() => allPositions.value.some(p => isEqual(p, position.value)))

View file

@ -1,19 +1,14 @@
<template> <template>
<ChangeBlinkingBox :blink-seconds="20" :value="prop" class="items-center"> <ChangeBlinkingBox :blink-seconds="20" :value="prop" class="flex flex-col items-center">
<div class="text-s1 tracking-wide text-gray-500"> <div class="text-s1 tracking-wide text-gray-400">
{{ positionName }} {{ positionName }}
</div> </div>
<div class="flex-grow w-full"> <div class="flex-grow w-full">
<transition mode="out-in" name="fade"> <transition mode="out-in" name="fade">
<div :key="prop" class="flex flex-col items-center justify-center"> <div :key="prop ?? ''" class="flex flex-col items-center justify-center">
<template v-if="prop !== null"> <div v-if="prop !== null" class="font-bold text-5 text-center">
<div class="font-bold text-3 text-center"> {{ prop }}
{{ parseStringWithDetails(prop).main }} </div>
</div>
<div v-if="parseStringWithDetails(prop).details" class="text-center px-2">
{{ parseStringWithDetails(prop).details }}
</div>
</template>
</div> </div>
</transition> </transition>
</div> </div>
@ -32,10 +27,9 @@
} }
</style> </style>
<script lang="ts" setup> <script setup lang="ts">
import { toRef, watch } from "vue" import { toRef, watch } from "vue"
import { autoResetRef } from "@vueuse/core" import { autoResetRef } from "@vueuse/core"
import { parseStringWithDetails } from "../state"
import ChangeBlinkingBox from "./ChangeBlinkingBox.vue" import ChangeBlinkingBox from "./ChangeBlinkingBox.vue"
const props = defineProps<{ const props = defineProps<{

View file

@ -5,17 +5,17 @@
Publikum Publikum
</div> </div>
</div> </div>
<div :class="$style.row" class="border-t border-b border-dark-300 h-30"> <div :class="$style.row" class="border-t border-b border-dark-300 h-20">
<PropBox :prop="current.step.props.PROSCENIUM_RIGHT" position-name="Rechte Vorbühne"/> <PropBox :prop="current.step.props.PROSCENIUM_RIGHT" position-name="Rechte Vorbühne"/>
<PropBox :prop="current.step.props.PROSCENIUM_CENTER" position-name="Mitte der Vorbühne"/> <PropBox :prop="current.step.props.PROSCENIUM_CENTER" position-name="Mitte der Vorbühne"/>
<PropBox :prop="current.step.props.PROSCENIUM_LEFT" position-name="Linke der Vorbühne"/> <PropBox :prop="current.step.props.PROSCENIUM_LEFT" position-name="Linke der Vorbühne"/>
</div> </div>
<div :class="$style.row" class="flex-grow h-0"> <div :class="$style.row" class="flex-grow h-20">
<PropBox :prop="current.step.props.RIGHT" position-name="Rechts"/> <PropBox :prop="current.step.props.RIGHT" position-name="Rechts"/>
<PropBox :prop="current.step.props.CENTER" position-name="Mitte"/> <PropBox :prop="current.step.props.CENTER" position-name="Mitte"/>
<PropBox :prop="current.step.props.LEFT" position-name="Links"/> <PropBox :prop="current.step.props.LEFT" position-name="Links"/>
</div> </div>
<div :class="$style.row" class="border-t border-dark-300 py-3 h-20"> <div :class="$style.row" class="border-t border-dark-300 h-11">
<div/> <div/>
<PropBox :prop="current.step.props.BACKDROP" position-name="Rückwand"/> <PropBox :prop="current.step.props.BACKDROP" position-name="Rückwand"/>
<div/> <div/>
@ -33,7 +33,7 @@
} }
</style> </style>
<script lang="ts" setup> <script setup lang="ts">
import { current } from "../state" import { current } from "@/state"
import PropBox from "./PropBox.vue" import PropBox from "./PropBox.vue"
</script> </script>

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="absolute right-3 top-2 text-5 pointer-events-none flex items-center gap-1"> <div class="absolute right-3 top-2 text-5 pointer-events-none flex items-center gap-1 <md:hidden">
<ClockIcon/> <ClockIcon/>
<span class="font-bold tabular-nums tracking-tighter">{{ format.format(syncedTime) }}</span> <span class="font-bold tabular-nums tracking-tighter">{{ format.format(syncedTime) }}</span>
</div> </div>
@ -10,7 +10,7 @@
</style> </style>
<script lang="ts" setup> <script lang="ts" setup>
import { syncedTime } from "../syncing" import { syncedTime } from "@/syncing"
import ClockIcon from "virtual:icons/ph/clock-bold" import ClockIcon from "virtual:icons/ph/clock-bold"
const format = new Intl.DateTimeFormat("de", { const format = new Intl.DateTimeFormat("de", {

View file

@ -1,25 +0,0 @@
<template>
<div class="flex flex-col h-full">
<h1 class="font-800 text-9 p-4 pb-0">
{{ current.act === null ? "" : `${current.act.name}` }}{{ current.scene.name }}
</h1>
<div class="h-full flex space-x-4 p-4 pt-8 flex-grow overflow-hidden">
<MotionsList center-current class="w-3/7" scrollable/>
<div class="w-4/7 flex flex-col space-y-4">
<ActorsOnStageBox class="h-full text-7"/>
<MessageEdit class="h-40"/>
<CurtainLightControl/>
</div>
</div>
<MusicProgressBar class="h-10"/>
</div>
</template>
<script lang="ts" setup>
import MusicProgressBar from "../../components/MusicProgressBar.vue"
import MotionsList from "../../components/MotionsList.vue"
import { current } from "../../state"
import ActorsOnStageBox from "../../components/ActorsOnStageBox.vue"
import MessageEdit from "../../components/MessageEdit.vue"
import CurtainLightControl from "../../components/CurtainLightControl.vue"
</script>

View file

@ -0,0 +1,75 @@
<template>
<div class="flex flex-col gap-50">
<div class="flex flex-col gap-2">
<div class="text-gray text-2xl">
Aktuelle Anweisung
</div>
<ChangeBlinkingBox :blink-seconds="10" :value="currentState" class="text-7 md:text-10">
{{ getDisplayedCommand(currentState) }}
</ChangeBlinkingBox>
</div>
<div v-if="nextStepWithChange !== null" class="flex flex-col gap-2">
<div class="text-gray text-6">
Nächste Anweisung
<span class="whitespace-nowrap">
[{{
nextStepWithChange.delta === 0
? "in dieser Szene"
: nextStepWithChange.delta === 1
? "in der nächsten Szene"
: `in ${nextStepWithChange.delta} Szenen`
}}]
</span>
</div>
<div class="text-7 md:text-10">{{ getDisplayedCommand(nextStepWithChange.state) }}</div>
</div>
</div>
</template>
<style module>
</style>
<script setup lang="ts">
import { computed } from "vue"
import { current, CurtainState, getNextValidPosition, getSceneIndex, getStep, ShowPosition } from "@/state"
import ChangeBlinkingBox from "@/components/ChangeBlinkingBox.vue"
const currentState = computed(() => current.step.curtainState)
interface StepWithChange {
delta: number
position: ShowPosition
state: CurtainState
}
function getDisplayedCommand(value: CurtainState) {
if (value === "open") {
return "Öffnen"
} else {
return "Schließen"
}
}
const nextStepWithChange = computed<StepWithChange | null>(() => {
let position: ShowPosition | null = getNextValidPosition(current.step.position)
let lastState: CurtainState = currentState.value
while (position !== null) {
const step = getStep(position)
if (step.curtainState !== lastState) {
return {
position: step.position,
delta: getSceneIndex(step.position) - current.sceneIndex,
state: step.curtainState
}
}
lastState = step.curtainState
position = getNextValidPosition(position)
}
return null
})
</script>

View file

@ -0,0 +1,36 @@
<template>
<div class="grid grid-cols-2 gap-6 h-full">
<MotionsList center-current scrollable/>
<div class="flex flex-col gap-5 overflow-y-auto">
<div>
<div class="pb-2 font-bold text-3.5 tracking-wider uppercase">
Vorhang
</div>
<div class="text-4.5">
{{ current.step.curtainState === "open" ? "öffnen" : "schließen" }}
</div>
</div>
<div class="flex-grow overflow-y-hidden flex flex-col">
<div class="pb-2 font-bold text-3.5 tracking-wider uppercase">
Auf der Bühne
</div>
<ActorsOnStageBox class="flex-grow text-8"/>
</div>
<div>
<div class="pb-2 font-bold text-3.5 tracking-wider uppercase">
Umbaulicht
</div>
<CurtainLightControl/>
</div>
<MessageEdit class="h-25 md:h-40 mb-1"/>
</div>
</div>
</template>
<script lang="ts" setup>
import MotionsList from "../../components/MotionsList.vue"
import ActorsOnStageBox from "../../components/ActorsOnStageBox.vue"
import MessageEdit from "../../components/MessageEdit.vue"
import CurtainLightControl from "../../components/CurtainLightControl.vue"
import { current } from "@/state"
</script>

33
ui/src/pages/for/stage.vue Executable file
View file

@ -0,0 +1,33 @@
<template>
<div class="grid grid-cols-2 gap-6 h-full" :class="$style.root">
<MotionsList center-current scrollable/>
<div class="flex flex-col gap-5 overflow-y-hidden">
<StageTopDownView/>
<div class="flex-grow overflow-hidden">
<div class="pb-2 font-bold text-6 tracking-wider uppercase">
Auf der Bühne
</div>
<ActorsOnStageBox class="flex-grow h-full text-7"/>
</div>
<div class="h-35">
<div class="font-bold text-6 tracking-wider uppercase">
Nachricht
</div>
<MessageDisplay/>
</div>
</div>
</div>
</template>
<style module lang="scss">
.root {
grid-template-columns: 2fr 3fr;
}
</style>
<script lang="ts" setup>
import MotionsList from "@/components/MotionsList.vue"
import ActorsOnStageBox from "@/components/ActorsOnStageBox.vue"
import MessageDisplay from "@/components/MessageDisplay.vue"
import StageTopDownView from "@/components/StageTopDownView.vue"
</script>

View file

@ -2,8 +2,7 @@
<div class="grid md:grid-cols-2 gap-6 h-full" :class="$style.root"> <div class="grid md:grid-cols-2 gap-6 h-full" :class="$style.root">
<StepSelection/> <StepSelection/>
<div class="flex flex-col gap-5 overflow-y-auto"> <div class="flex flex-col gap-5 overflow-y-auto">
<MessageEdit class="h-25 md:h-40"/> <div>
<div class="text-5">
<div class="pb-2 font-bold text-3.5 tracking-wider uppercase"> <div class="pb-2 font-bold text-3.5 tracking-wider uppercase">
Vorhang Vorhang
</div> </div>
@ -11,11 +10,11 @@
{{ current.step.curtainState === "open" ? "öffnen" : "schließen" }} {{ current.step.curtainState === "open" ? "öffnen" : "schließen" }}
</div> </div>
</div> </div>
<div class="flex-grow overflow-hidden"> <div class="flex-grow overflow-y-hidden flex flex-col">
<div class="pb-2 font-bold text-3.5 tracking-wider uppercase"> <div class="pb-2 font-bold text-3.5 tracking-wider uppercase">
Auf der Bühne Auf der Bühne
</div> </div>
<ActorsOnStageBox class="flex-grow h-full"/> <ActorsOnStageBox class="flex-grow text-4.5"/>
</div> </div>
<div> <div>
<div class="pb-2 font-bold text-3.5 tracking-wider uppercase"> <div class="pb-2 font-bold text-3.5 tracking-wider uppercase">
@ -29,6 +28,7 @@
</div> </div>
<CurtainLightControl/> <CurtainLightControl/>
</div> </div>
<MessageEdit class="h-25 md:h-40 mb-1"/>
</div> </div>
</div> </div>
</template> </template>

View file

@ -1,6 +1,14 @@
<template> <template>
<div class="flex flex-col gap-4 text-xl"> <div class="flex flex-col items-center gap-4 text-2xl pt-20">
<router-link to="/mixer">audio operator</router-link> <router-link
v-for="dashboard in DASHBOARDS"
:key="dashboard.to"
class="flex items-center gap-2 rounded-xl bg-gray-900 p-5 hover:bg-gray-800 transition"
:to="dashboard.to"
>
<component :is="dashboard.icon"/>
<div class="font-bold">{{ dashboard.label }}</div>
</router-link>
</div> </div>
</template> </template>
@ -9,5 +17,38 @@
</style> </style>
<script lang="ts" setup> <script lang="ts" setup>
import { type Component } from "vue"
import FlyingSaucerIcon from "virtual:icons/ph/flying-saucer"
import CircleWavyIcon from "virtual:icons/ph/circle-wavy"
import FadersIcon from "virtual:icons/ph/faders"
import StorefrontIcon from "virtual:icons/ph/storefront"
interface Dashboard {
to: string
label: string
icon: Component
}
const DASHBOARDS: Dashboard[] = [
{
to: "/for/fly-crew",
label: "Fly crew",
icon: FlyingSaucerIcon
},
{
to: "/for/followspot-operator",
label: "Followspot operator",
icon: CircleWavyIcon
},
{
to: "/for/sound-operator",
label: "Sound operator",
icon: FadersIcon
},
{
to: "/for/stage",
label: "Stage",
icon: StorefrontIcon
}
]
</script> </script>

View file

@ -1,23 +0,0 @@
<template>
<div class="flex flex-col h-full">
<h1 class="font-800 text-9 p-4 pb-0">
{{ current.act === null ? "" : `${current.act.name}` }}{{ current.scene.name }}
</h1>
<div class="h-full flex space-x-4 p-4 pt-8 flex-grow overflow-hidden">
<MotionsList center-current class="w-3/7"/>
<div class="w-4/7 flex flex-col space-y-4">
<StageTopDownView class="h-full"/>
<MessageDisplay class="h-30"/>
</div>
</div>
<MusicProgressBar class="h-15"/>
</div>
</template>
<script lang="ts" setup>
import MusicProgressBar from "../../components/MusicProgressBar.vue"
import { current } from "../../state"
import MotionsList from "../../components/MotionsList.vue"
import StageTopDownView from "../../components/StageTopDownView.vue"
import MessageDisplay from "../../components/MessageDisplay.vue"
</script>

View file

@ -21,9 +21,11 @@ export interface Step {
hasChangedProps: boolean hasChangedProps: boolean
leftSpotTarget: string | null leftSpotTarget: string | null
rightSpotTarget: string | null rightSpotTarget: string | null
curtainState: "open" | "closed" curtainState: CurtainState
} }
export type CurtainState = "open" | "closed"
export type StepCue = { export type StepCue = {
type: "TEXT", type: "TEXT",
speaker: string speaker: string
@ -37,13 +39,9 @@ export type StepCue = {
type: "MUSIC_END" type: "MUSIC_END"
} | { } | {
type: "CURTAIN", type: "CURTAIN",
state: "open" | "closed" state: CurtainState
whileMoving: boolean whileMoving: boolean
} | { } | {
type: "LIGHTS"
state: "on" | "off"
whileFading: boolean
} | {
type: "CUSTOM" type: "CUSTOM"
text: string text: string
} }