commit 76
This commit is contained in:
parent
b90f897123
commit
18dc17d6d5
46 changed files with 2032 additions and 2096 deletions
3
ui/.gitignore
vendored
3
ui/.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
.idea/
|
.idea/
|
||||||
node_modules/
|
node_modules/
|
||||||
*.env
|
|
||||||
dist/
|
dist/
|
||||||
|
src/generated-types
|
||||||
|
*.env
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
18
|
|
|
@ -2,10 +2,11 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Moira</title>
|
<title>Janus</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<link href="./node_modules/@fontsource-variable/manrope/index.css" rel="stylesheet"/>
|
<link href="./node_modules/@fontsource-variable/manrope/index.css" rel="stylesheet"/>
|
||||||
<script type="module" src="./src/main.ts"></script>
|
<script type="module" src="./src/main.ts"></script>
|
||||||
|
<link rel="stylesheet" href="src/global.scss"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
|
@ -1,31 +1,35 @@
|
||||||
{
|
{
|
||||||
"name": "moira",
|
"name": "janus-ui",
|
||||||
|
"private": true,
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Moritz Ruth <dev@moritzruth.de>",
|
"author": "Moritz Ruth <dev@moritzruth.de>",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --port 3000 --host",
|
"dev": "vite --port 3000 --host",
|
||||||
"build": "vite build --emptyOutDir --outDir ../src/main/resources/ui"
|
"build": "vite build --emptyOutDir --outDir ../src/main/resources/ui"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/ph": "^1.1.5",
|
"@iconify-json/ph": "^1.2.2",
|
||||||
"@types/lodash-es": "^4.17.7",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@vitejs/plugin-vue": "^4.2.3",
|
"@types/node": "^22.13.10",
|
||||||
"typescript": "^5.0.4",
|
"@unocss/preset-wind3": "^66.1.0-beta.3",
|
||||||
"unplugin-icons": "^0.16.1",
|
"unocss": "^66.1.0-beta.3",
|
||||||
"vite": "^4.3.9",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"vite-plugin-pages": "^0.30.1",
|
"typescript": "^5.8.2",
|
||||||
"vite-plugin-windicss": "^1.9.0",
|
"unplugin-icons": "^22.1.0",
|
||||||
"windicss": "^3.5.6"
|
"unplugin-vue-router": "^0.12.0",
|
||||||
|
"vite": "^6.2.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/manrope": "^5.0.0",
|
"@fontsource-variable/manrope": "^5.2.5",
|
||||||
"@vueuse/core": "^10.1.2",
|
"@vueuse/core": "^13.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"modern-normalize": "^3.0.1",
|
||||||
"reconnecting-websocket": "^4.4.0",
|
"reconnecting-websocket": "^4.4.0",
|
||||||
"sass": "^1.62.1",
|
"sass": "^1.85.1",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.2.1"
|
"vue-router": "^4.5.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
|
|
2016
ui/pnpm-lock.yaml
generated
2016
ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -1,32 +1,39 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="app" class="bg-black text-white">
|
<div v-if="isConnecting" class="flex flex-col justify-center items-center h-100dvh gap-4">
|
||||||
<div class="flex flex-col justify-center items-center h-full space-y-4" v-if="isConnecting">
|
<div class="font-bold text-10">Connecting…</div>
|
||||||
<div class="font-bold text-10">Connecting...</div>
|
<div class="text-s1">Janus, created by Moritz Ruth</div>
|
||||||
<div class="text-s1">Created by Moritz Ruth</div>
|
</div>
|
||||||
|
<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">
|
||||||
|
{{ current.act === null ? "" : `${current.act.name} — ` }}{{ current.scene.name }}
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow p-4 md:p-8 overflow-hidden">
|
||||||
|
<router-view/>
|
||||||
|
</div>
|
||||||
|
<MusicProgressBar class="h-10 flex-shrink-0"/>
|
||||||
</div>
|
</div>
|
||||||
<router-view v-else/>
|
|
||||||
<TimeDisplay/>
|
<TimeDisplay/>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
html, body, #app {
|
.list-move,
|
||||||
width: 100vw;
|
.list-enter-active,
|
||||||
height: 100vh;
|
.list-leave-active {
|
||||||
overflow: hidden;
|
transition: all 0.5s ease;
|
||||||
font-family: "Manrope Variable", sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
.list-leave-to {
|
||||||
width: 10px;
|
opacity: 0;
|
||||||
|
transform: translateX(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
.list-enter-from {
|
||||||
background: rgb(255 255 255 / 10%);
|
opacity: 0;
|
||||||
|
transform: translateX(-10px);
|
||||||
&:hover {
|
|
||||||
background: rgb(255 255 255 / 20%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-leave-active {
|
||||||
|
position: absolute;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -34,10 +41,17 @@
|
||||||
import { connect } from "./syncing"
|
import { connect } from "./syncing"
|
||||||
import { ref } from "vue"
|
import { ref } from "vue"
|
||||||
import TimeDisplay from "./components/TimeDisplay.vue"
|
import TimeDisplay from "./components/TimeDisplay.vue"
|
||||||
|
import { current } from "@/state.js"
|
||||||
|
import MusicProgressBar from "@/components/MusicProgressBar.vue"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
components: { TimeDisplay },
|
computed: {
|
||||||
|
current() {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: { MusicProgressBar, TimeDisplay },
|
||||||
setup() {
|
setup() {
|
||||||
const isConnecting = ref(true)
|
const isConnecting = ref(true)
|
||||||
connect().then(() => {
|
connect().then(() => {
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="p-2 space-y-8">
|
<div class="overflow-y-auto">
|
||||||
<div>
|
<transition-group name="list" tag="div">
|
||||||
<div class="pb-2 font-bold text-gray-400 text-2 tracking-wider uppercase">
|
<div v-if="current.step.actorsOnStage.length === 0" key="" class="text-gray-400">Niemand</div>
|
||||||
auf der Bühne
|
<div
|
||||||
</div>
|
v-for="actor in current.step.actorsOnStage"
|
||||||
<div>
|
:key="parseStringWithDetails(actor).main"
|
||||||
<EntrancesList :entrances="current.step.actorsOnStage"/>
|
class="truncate text-4.5"
|
||||||
</div>
|
>
|
||||||
|
{{ actor }}
|
||||||
</div>
|
</div>
|
||||||
|
</transition-group>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -16,8 +18,5 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import EntrancesList from "./EntrancesList.vue"
|
import { current, parseStringWithDetails } from "@/state"
|
||||||
import { current, getNextValidPosition, getStep, parseStringWithDetails, ShowPosition, state, Step, show } from "../state"
|
|
||||||
import { computed } from "vue"
|
|
||||||
import { intersection } from "lodash-es"
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
class="px-5 py-2 active:bg-green-800 transition duration-200 font-bold text-5"
|
|
||||||
:class="[$style.root, isActive ? 'bg-green-800' : 'bg-green-600']"
|
:class="[$style.root, isActive ? 'bg-green-800' : 'bg-green-600']"
|
||||||
|
class="px-5 py-2 active:bg-green-800 transition duration-200 font-bold text-4"
|
||||||
@click="e => emit('click', e)"
|
@click="e => emit('click', e)"
|
||||||
>
|
>
|
||||||
<slot/>
|
<slot/>
|
||||||
|
@ -14,10 +14,14 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isActive?: boolean
|
isActive?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits(["click"])
|
const emit = defineEmits<{
|
||||||
|
click: [MouseEvent]
|
||||||
|
pointerdown: [PointerEvent]
|
||||||
|
pointerup: [PointerEvent]
|
||||||
|
}>()
|
||||||
</script>
|
</script>
|
|
@ -1,9 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div :class="$style.root" :data-blinking="isBlinking">
|
||||||
class="flex flex-col justify-center py-4"
|
|
||||||
:class="$style.root"
|
|
||||||
:data-blinking="isBlinking"
|
|
||||||
>
|
|
||||||
<slot/>
|
<slot/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -24,10 +20,9 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { toRef, watch } from "vue"
|
import { computed, toRef, watch } from "vue"
|
||||||
import { autoResetRef } from "@vueuse/core"
|
import { autoResetRef } from "@vueuse/core"
|
||||||
import { computed } from "vue"
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
value: unknown
|
value: unknown
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex items-start space-x-2" :class="textClass ?? 'text-4'">
|
<div :class="textClass ?? 'text-4'" class="flex items-start gap-2 py-0.5">
|
||||||
<component :is="icon" class="mt-0.5 flex-shrink-0"/>
|
<component :is="icon" class="flex-shrink-0"/>
|
||||||
<div :class="singleLine && 'truncate'">
|
<div :class="singleLine && 'truncate'">
|
||||||
{{ text }}
|
{{ text }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import MusicNoteIcon from "virtual:icons/ph/music-note"
|
import MusicNoteIcon from "virtual:icons/ph/music-note"
|
||||||
import ChatCircleTextIcon from "virtual:icons/ph/chat-circle-TEXT"
|
import ChatCircleTextIcon from "virtual:icons/ph/chat-circle-TEXT"
|
||||||
import StopIcon from "virtual:icons/ph/stop"
|
import StopIcon from "virtual:icons/ph/stop"
|
||||||
import HeadlightsIcon from "virtual:icons/ph/headlights"
|
|
||||||
import WarningIcon from "virtual:icons/ph/warning"
|
import WarningIcon from "virtual:icons/ph/warning"
|
||||||
import ArrowsOutLineHorizontalIcon from "virtual:icons/ph/arrows-out-line-horizontal"
|
import ArrowsOutLineHorizontalIcon from "virtual:icons/ph/arrows-out-line-horizontal"
|
||||||
import ArrowsInLineHorizontalIcon from "virtual:icons/ph/arrows-in-line-horizontal"
|
import ArrowsInLineHorizontalIcon from "virtual:icons/ph/arrows-in-line-horizontal"
|
||||||
import DotFillIcon from "virtual:icons/ph/dot-fill"
|
import DotFillIcon from "virtual:icons/ph/dot-fill"
|
||||||
import { computed } from "vue"
|
import { computed } from "vue"
|
||||||
import { START_STEP, Step } from "../state"
|
import { START_STEP, Step } from "@/state"
|
||||||
import { formatSeconds } from "../helpers"
|
import { formatSeconds } from "@/helpers"
|
||||||
import { isEqual } from "lodash-es"
|
import { isEqual } from "lodash-es"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -40,11 +39,17 @@
|
||||||
? ArrowsInLineHorizontalIcon
|
? ArrowsInLineHorizontalIcon
|
||||||
: ArrowsOutLineHorizontalIcon
|
: ArrowsOutLineHorizontalIcon
|
||||||
|
|
||||||
case "LIGHT": return HeadlightsIcon
|
case "TEXT":
|
||||||
case "TEXT": return ChatCircleTextIcon
|
return ChatCircleTextIcon
|
||||||
case "MUSIC_START": return MusicNoteIcon
|
|
||||||
case "MUSIC_END": return StopIcon
|
case "MUSIC_START":
|
||||||
case "CUSTOM": return WarningIcon
|
return MusicNoteIcon
|
||||||
|
|
||||||
|
case "MUSIC_END":
|
||||||
|
return StopIcon
|
||||||
|
|
||||||
|
case "CUSTOM":
|
||||||
|
return WarningIcon
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -59,24 +64,20 @@
|
||||||
return cue.whileMoving ? "Der Vorhang schließt sich" : "Der Vorhang ist geschlossen"
|
return cue.whileMoving ? "Der Vorhang schließt sich" : "Der Vorhang ist geschlossen"
|
||||||
}
|
}
|
||||||
|
|
||||||
case "LIGHT":
|
|
||||||
if (cue.state === "on") {
|
|
||||||
return cue.whileFading ? "Das Licht geht an" : "Das Licht ist an"
|
|
||||||
} else {
|
|
||||||
return cue.whileFading ? "Das Licht geht aus" : "Das Licht ist aus"
|
|
||||||
}
|
|
||||||
|
|
||||||
case "TEXT":
|
case "TEXT":
|
||||||
let suffix = ""
|
let suffix = ""
|
||||||
if (cue.clarification !== undefined) {
|
if (cue.clarification !== undefined) suffix = ` (${cue.clarification})`
|
||||||
suffix = ` (${cue.clarification})`
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${cue.speaker}: »${cue.text}«${suffix}`
|
return `${cue.speaker}: »${cue.text}«${suffix}`
|
||||||
|
|
||||||
case "MUSIC_START": return `${cue.title} [${formatSeconds(cue.duration / 1000)}]`
|
case "MUSIC_START":
|
||||||
case "MUSIC_END": return "Ende der Musik"
|
return `${cue.title} [${formatSeconds(cue.duration / 1000)}]`
|
||||||
case "CUSTOM": return cue.text
|
|
||||||
|
case "MUSIC_END":
|
||||||
|
return "Ende der Musik"
|
||||||
|
|
||||||
|
case "CUSTOM":
|
||||||
|
return cue.text
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<Button class="w-20" :is-active="!state.isLightBehindCurtainOn" @click="onClick()">{{
|
||||||
<Button :is-active="!state.isLightBehindCurtainOn" @click="onClick()">{{ state.isLightBehindCurtainOn ? "Umbaulicht ist AN" : "Umbaulicht ist AUS" }}</Button>
|
state.isLightBehindCurtainOn ? "AN" : "AUS"
|
||||||
</div>
|
}}
|
||||||
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style module>
|
<style module>
|
||||||
|
@ -9,7 +10,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { state } from "../state"
|
import { state } from "@/state"
|
||||||
import Button from "./Button.vue"
|
import Button from "./Button.vue"
|
||||||
|
|
||||||
function onClick() {
|
function onClick() {
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
<template>
|
|
||||||
<transition-group tag="div" name="list">
|
|
||||||
<div
|
|
||||||
v-for="actor in entrances"
|
|
||||||
:key="parseStringWithDetails(actor).main"
|
|
||||||
class="truncate"
|
|
||||||
>
|
|
||||||
<span class="font-bold">
|
|
||||||
{{ parseStringWithDetails(actor).main }}
|
|
||||||
</span>
|
|
||||||
<span class="text-gray-400 pl-2">
|
|
||||||
{{ parseStringWithDetails(actor).details }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</transition-group>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.list-move,
|
|
||||||
.list-enter-active,
|
|
||||||
.list-leave-active {
|
|
||||||
transition: all 0.5s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-enter-from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(-10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-leave-active {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { PropType } from "vue"
|
|
||||||
import { parseStringWithDetails } from "../state"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "EntrancesList",
|
|
||||||
methods: { parseStringWithDetails },
|
|
||||||
props: {
|
|
||||||
entrances: {
|
|
||||||
type: Array as PropType<string[]>,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,11 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-wrap items-center gap-3">
|
<div class="flex items-center gap-1">
|
||||||
<span class="-sm:hidden">Nebel:</span>
|
|
||||||
<Button
|
<Button
|
||||||
v-for="p in buttonPowers"
|
v-for="p in buttonPowers"
|
||||||
|
:is-active="isActive && p[0] === power"
|
||||||
@pointerdown="e => onButtonActive(e, p[0])"
|
@pointerdown="e => onButtonActive(e, p[0])"
|
||||||
@pointerup="e => onButtonInactive(e)"
|
@pointerup="e => onButtonInactive(e)"
|
||||||
:is-active="isActive && p[0] === power"
|
|
||||||
>
|
>
|
||||||
{{ p[0] * 100 }}%
|
{{ p[0] * 100 }}%
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -16,7 +15,7 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import Button from "./Button.vue"
|
import Button from "./Button.vue"
|
||||||
import { computed, ref, watch } from "vue"
|
import { computed, ref, watch } from "vue"
|
||||||
import { onKeyDown, onKeyUp, useEventListener, useIntervalFn } from "@vueuse/core"
|
import { onKeyDown, onKeyUp, useEventListener, useIntervalFn } from "@vueuse/core"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="overflow-hidden text-6 box p-4" :data-blinking="isBlinking">
|
<div :data-blinking="isBlinking" class="overflow-hidden text-6 box p-4">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full max-h-60">
|
<div class="max-h-60">
|
||||||
<textarea
|
<textarea
|
||||||
:value="state.message"
|
:value="state.message"
|
||||||
id="message-box"
|
class="h-full w-full border border-dark-200 focus:border-green-500 focus:outline-none transition rounded-lg bg-dark-800 p-4 text-5 resize-none"
|
||||||
:class="$style.area"
|
|
||||||
class="h-full w-full border border-dark-200 focus:border-green-500 focus:outline-none transition rounded-lg bg-dark-800 p-4 text-3"
|
|
||||||
placeholder="Nachricht an alle"
|
placeholder="Nachricht an alle"
|
||||||
@input="e => setMessage(e.target.value)"
|
@input="e => setMessage((e.target as HTMLInputElement).value)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style module>
|
<style module>
|
||||||
.area {
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { state } from "../state"
|
import { state } from "@/state"
|
||||||
import { setMessage } from "../syncing"
|
import { setMessage } from "@/syncing"
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col space-y-5" :class="scrollable ? 'overflow-y-auto' : 'overflow-hidden'">
|
<div :class="scrollable ? 'overflow-y-auto' : 'overflow-hidden'" class="flex flex-col space-y-5">
|
||||||
<div v-for="(act, actIndex) in show.acts">
|
<div v-for="(act, actIndex) in show.acts">
|
||||||
<MotionsListAct :act="act" :center-current="Boolean(centerCurrent)"/>
|
<MotionsListAct :act="act" :center-current="Boolean(centerCurrent)"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import MotionsListAct from "./MotionsListAct.vue"
|
import MotionsListAct from "./MotionsListAct.vue"
|
||||||
import { show } from "../state"
|
import { show } from "../state"
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<template v-for="step in scene.steps" :key="step.position">
|
<template v-for="step in scene.steps" :key="step.position">
|
||||||
<MotionsListStep
|
<MotionsListStep
|
||||||
:step="step"
|
|
||||||
:more-positions="step.morePositions"
|
|
||||||
:center-current="centerCurrent"
|
:center-current="centerCurrent"
|
||||||
|
:more-positions="step.morePositions"
|
||||||
|
:step="step"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="transition p-3" :class="isActive && 'bg-green-800'">
|
<div :class="isActive && 'bg-green-800'" class="transition p-3">
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<div class="flex-grow">
|
<div class="flex-grow">
|
||||||
<CueBox text-class="text-6" :step="step"/>
|
<CueBox :step="step" text-class="text-6"/>
|
||||||
<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
|
||||||
|
@ -46,14 +46,14 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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"
|
||||||
import BarricadeIcon from "virtual:icons/ph/barricade"
|
import BarricadeIcon from "virtual:icons/ph/barricade"
|
||||||
import { isEqual } from "lodash-es"
|
import { isEqual } from "lodash-es"
|
||||||
import { toRef, computed, watchEffect } from "vue"
|
import { computed, toRef, watchEffect } from "vue"
|
||||||
import { useCurrentElement } from "@vueuse/core"
|
import { useCurrentElement } from "@vueuse/core"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div
|
<div
|
||||||
class="bg-green-700 h-full transition-all"
|
|
||||||
:style="{ width: (progress * 100) + '%' }"
|
|
||||||
:class="barClass"
|
:class="barClass"
|
||||||
|
:style="{ width: (progress * 100) + '%' }"
|
||||||
|
class="bg-green-700 h-full transition-all"
|
||||||
></div>
|
></div>
|
||||||
<div
|
<div
|
||||||
class="absolute left-0 top-0 w-full h-full px-4 text-4 text-white font-bold flex items-center transition flex justify-between"
|
|
||||||
:class="music === null ? 'opacity-0' : 'opacity-100'"
|
:class="music === null ? 'opacity-0' : 'opacity-100'"
|
||||||
|
class="absolute left-0 top-0 w-full h-full px-4 text-4 text-white font-bold flex items-center transition flex justify-between"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{{ lastMusic?.title ?? "" }}
|
{{ lastMusic?.title ?? "" }}
|
||||||
|
@ -35,8 +35,8 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { state, current } from "../state"
|
import { current, state } from "../state"
|
||||||
import { computed, useCssModule } from "vue"
|
import { computed, useCssModule } from "vue"
|
||||||
import { avoidNull, formatSeconds } from "../helpers"
|
import { avoidNull, formatSeconds } from "../helpers"
|
||||||
import { syncedTime } from "../syncing"
|
import { syncedTime } from "../syncing"
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<ChangeBlinkingBox class="items-center" :blink-seconds="20" :value="prop">
|
<ChangeBlinkingBox :blink-seconds="20" :value="prop" class="items-center">
|
||||||
<div class="text-s1 tracking-wide text-gray-500">
|
<div class="text-s1 tracking-wide text-gray-500">
|
||||||
{{ positionName }}
|
{{ positionName }}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow w-full">
|
<div class="flex-grow w-full">
|
||||||
<transition name="fade" mode="out-in">
|
<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">
|
<template v-if="prop !== null">
|
||||||
<div class="font-bold text-3 text-center">
|
<div class="font-bold text-3 text-center">
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
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 { parseStringWithDetails } from "../state"
|
||||||
|
|
|
@ -6,18 +6,18 @@
|
||||||
</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-30">
|
||||||
<PropBox position-name="Rechte Vorbühne" :prop="current.step.props.PROSCENIUM_RIGHT"/>
|
<PropBox :prop="current.step.props.PROSCENIUM_RIGHT" position-name="Rechte Vorbühne"/>
|
||||||
<PropBox position-name="Mitte der Vorbühne" :prop="current.step.props.PROSCENIUM_CENTER"/>
|
<PropBox :prop="current.step.props.PROSCENIUM_CENTER" position-name="Mitte der Vorbühne"/>
|
||||||
<PropBox position-name="Linke der Vorbühne" :prop="current.step.props.PROSCENIUM_LEFT"/>
|
<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-0">
|
||||||
<PropBox position-name="Rechts" :prop="current.step.props.RIGHT"/>
|
<PropBox :prop="current.step.props.RIGHT" position-name="Rechts"/>
|
||||||
<PropBox position-name="Mitte" :prop="current.step.props.CENTER"/>
|
<PropBox :prop="current.step.props.CENTER" position-name="Mitte"/>
|
||||||
<PropBox position-name="Links" :prop="current.step.props.LEFT"/>
|
<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 py-3 h-20">
|
||||||
<div/>
|
<div/>
|
||||||
<PropBox position-name="Rückwand" :prop="current.step.props.BACKDROP"/>
|
<PropBox :prop="current.step.props.BACKDROP" position-name="Rückwand"/>
|
||||||
<div/>
|
<div/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { current } from "../state"
|
import { current } from "../state"
|
||||||
import PropBox from "./PropBox.vue"
|
import PropBox from "./PropBox.vue"
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex-grow overflow-y-auto bg-dark-800 flex flex-col pt-2">
|
<div class="overflow-y-auto bg-dark-800 flex flex-col pt-2">
|
||||||
<StepSelectionStep :step="START_STEP"/>
|
<StepSelectionStep :step="START_STEP"/>
|
||||||
<StepSelectionAct v-for="act in show.acts" :key="act.name" :act="act"/>
|
<StepSelectionAct v-for="act in show.acts" :key="act.name" :act="act"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { show, START_STEP } from "../state"
|
import { show, START_STEP } from "@/state"
|
||||||
import StepSelectionAct from "./StepSelectionAct.vue"
|
import StepSelectionAct from "./StepSelectionAct.vue"
|
||||||
import StepSelectionStep from "./StepSelectionStep.vue"
|
import StepSelectionStep from "./StepSelectionStep.vue"
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<span class="flex-grow h-2px bg-white"></span>
|
<span class="flex-grow h-2px bg-white"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<StepSelectionScene v-for="scene in act.scenes" :key="scene" :scene="scene"/>
|
<StepSelectionScene v-for="(scene, index) in act.scenes" :key="index" :scene="scene"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -15,9 +15,9 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import StepSelectionScene from "./StepSelectionScene.vue"
|
import StepSelectionScene from "./StepSelectionScene.vue"
|
||||||
import { Act } from "../state"
|
import { Act } from "@/state"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
act: Act
|
act: Act
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="not-last:border-b border-dark-300 transition"
|
|
||||||
:class="scene === current.scene ? 'bg-green-900' : ''"
|
:class="scene === current.scene ? 'bg-green-900' : ''"
|
||||||
|
class="not-last:border-b border-dark-300 transition"
|
||||||
>
|
>
|
||||||
<div class="pb-1 px-4 text-4 font-bold">
|
<div class="pb-1 px-4 text-4 font-bold">
|
||||||
{{ scene.name }}
|
{{ scene.name }}
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { current, Scene } from "../state"
|
import { current, Scene } from "../state"
|
||||||
import StepSelectionStep from "./StepSelectionStep.vue"
|
import StepSelectionStep from "./StepSelectionStep.vue"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="px-4 py-1 flex items-center justify-between space-x-2 transition "
|
|
||||||
:class="isActive ? 'bg-green-700' : ''"
|
:class="isActive ? 'bg-green-700' : ''"
|
||||||
|
class="px-4 py-1 flex items-center justify-between gap-2 transition"
|
||||||
>
|
>
|
||||||
<CueBox :step="step"/>
|
<CueBox :step="step"/>
|
||||||
<button class="flex items-center text-4" @click="goToPosition(step.position)">
|
<button class="flex items-center text-4" @click="goToPosition(step.position)">
|
||||||
|
@ -14,9 +14,9 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { state, Step } from "../state"
|
import { state, Step } from "@/state"
|
||||||
import { goToPosition } from "../syncing"
|
import { goToPosition } from "@/syncing"
|
||||||
import KeyReturnIcon from "virtual:icons/ph/key-return"
|
import KeyReturnIcon from "virtual:icons/ph/key-return"
|
||||||
import CueBox from "./CueBox.vue"
|
import CueBox from "./CueBox.vue"
|
||||||
import { useCurrentElement } from "@vueuse/core"
|
import { useCurrentElement } from "@vueuse/core"
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const position = toRef(state, "position")
|
const position = toRef(state, "position")
|
||||||
const element = useCurrentElement()
|
const element = useCurrentElement<HTMLElement>()
|
||||||
const isActive = computed(() => isEqual(props.step.position, position.value))
|
const isActive = computed(() => isEqual(props.step.position, position.value))
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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"
|
||||||
|
|
||||||
|
|
48
ui/src/global.scss
Normal file
48
ui/src/global.scss
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
@import "modern-normalize/modern-normalize.css";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body, #app {
|
||||||
|
width: 100dvw;
|
||||||
|
height: 100dvh;
|
||||||
|
font-family: "Manrope Variable", sans-serif;
|
||||||
|
user-select: none;
|
||||||
|
background: black;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: rgb(255 255 255 / 10%);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgb(255 255 255 / 20%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: rgba(255 255 255 / 20%)
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: solid 2px theme("colors.blue.400");
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { Ref, ComputedRef, computed, UnwrapRef, ref, watchEffect } from "vue"
|
import { computed, ComputedRef, Ref, ref, UnwrapRef, watchEffect } from "vue"
|
||||||
|
|
||||||
export const computedIfPresent = <T, V>(object: Ref<T>, access: (value: Exclude<T, undefined | null>) => V): ComputedRef<V | undefined | null> => computed(() => {
|
export const computedIfPresent = <T, V>(object: Ref<T>, access: (value: Exclude<T, undefined | null>) => V): ComputedRef<V | undefined | null> => computed(() => {
|
||||||
if (object.value === null) return null
|
if (object.value === null) return null
|
||||||
|
@ -24,5 +24,5 @@ export function avoidNull<T>(originRef: Ref<UnwrapRef<T> | null>): ComputedRef<U
|
||||||
|
|
||||||
export function formatSeconds(seconds: number) {
|
export function formatSeconds(seconds: number) {
|
||||||
const duration = new Date(seconds * 1000)
|
const duration = new Date(seconds * 1000)
|
||||||
return `${duration.getMinutes().toFixed()}:${duration.getSeconds().toFixed().padStart(2, '0')}`
|
return `${duration.getMinutes().toFixed()}:${duration.getSeconds().toFixed().padStart(2, "0")}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
import "virtual:windi.css"
|
import "virtual:uno.css"
|
||||||
import { createApp } from "vue"
|
import { createApp } from "vue"
|
||||||
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router"
|
import { createRouter, createWebHistory } from "vue-router"
|
||||||
import App from "./App.vue"
|
import App from "./App.vue"
|
||||||
import originalRoutes from "~pages"
|
import { handleHotUpdate, routes } from "vue-router/auto-routes"
|
||||||
|
|
||||||
const routes = originalRoutes.map(route => {
|
|
||||||
if (typeof route.component !== "function") return route
|
|
||||||
return {
|
|
||||||
...route,
|
|
||||||
props: false
|
|
||||||
}
|
|
||||||
}) as RouteRecordRaw[]
|
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
routes,
|
routes,
|
||||||
|
@ -20,3 +12,7 @@ const router = createRouter({
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.mount("#app")
|
app.mount("#app")
|
||||||
|
|
||||||
|
if (import.meta.hot) {
|
||||||
|
handleHotUpdate(router)
|
||||||
|
}
|
|
@ -1,42 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="flex flex-col overflow-hidden">
|
|
||||||
<div class="flex flex-col space-y-4 p-4 pt-8 flex-grow h-full overflow-hidden">
|
|
||||||
<div class="flex justify-end">
|
|
||||||
<button class="px-5 py-3 bg-green-600 font-bold text-5" @click="goNext()">
|
|
||||||
Next
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<StepSelection class="h-110"/>
|
|
||||||
<FogControl/>
|
|
||||||
<CurtainLightControl/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { current, getNextValidPosition, getPreviousValidPosition, state } from "../state"
|
|
||||||
import { goToPosition } from "../syncing"
|
|
||||||
import { onKeyStroke } from "@vueuse/core"
|
|
||||||
import StepSelection from "../components/StepSelection.vue"
|
|
||||||
import FogControl from "../components/FogControl.vue"
|
|
||||||
import CurtainLightControl from "../components/CurtainLightControl.vue"
|
|
||||||
|
|
||||||
function goNext() {
|
|
||||||
const position = getNextValidPosition(state.position)
|
|
||||||
if (position !== null) goToPosition(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
function goPrevious() {
|
|
||||||
const position = getPreviousValidPosition(state.position)
|
|
||||||
if (position !== null) goToPosition(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyStroke("ArrowLeft", goPrevious)
|
|
||||||
onKeyStroke("ArrowRight", goNext)
|
|
||||||
</script>
|
|
|
@ -1,40 +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-10 pt-8 flex-grow overflow-hidden">
|
|
||||||
<StepSelection class="w-1/2"/>
|
|
||||||
<div class="w-1/2 flex flex-col space-y-4">
|
|
||||||
<MessageEdit class="h-1/2"/>
|
|
||||||
<div class="text-4">Vorhang: {{ current.step.curtainState === "open" ? "auf" : "geschlossen"}}</div>
|
|
||||||
<ActorsOnStageBox class="flex-grow"/>
|
|
||||||
<FogControl/>
|
|
||||||
<CurtainLightControl/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<MusicProgressBar class="h-10"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import MusicProgressBar from "../components/MusicProgressBar.vue"
|
|
||||||
import { onKeyStroke } from "@vueuse/core"
|
|
||||||
import StepSelection from "../components/StepSelection.vue"
|
|
||||||
import MessageEdit from "../components/MessageEdit.vue"
|
|
||||||
import ActorsOnStageBox from "../components/ActorsOnStageBox.vue"
|
|
||||||
import { goToPosition } from "../syncing"
|
|
||||||
import { current, getNextValidPosition, getPreviousValidPosition, state } from "../state"
|
|
||||||
import FogControl from "../components/FogControl.vue"
|
|
||||||
import CurtainLightControl from "../components/CurtainLightControl.vue"
|
|
||||||
|
|
||||||
onKeyStroke("ArrowRight", () => {
|
|
||||||
const position = getNextValidPosition(state.position)
|
|
||||||
if (position !== null) goToPosition(position)
|
|
||||||
})
|
|
||||||
|
|
||||||
onKeyStroke("ArrowLeft", () => {
|
|
||||||
const position = getPreviousValidPosition(state.position)
|
|
||||||
if (position !== null) goToPosition(position)
|
|
||||||
})
|
|
||||||
</script>
|
|
25
ui/src/pages/for/audio-operator.vue
Executable file
25
ui/src/pages/for/audio-operator.vue
Executable file
|
@ -0,0 +1,25 @@
|
||||||
|
<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>
|
63
ui/src/pages/for/show-operator.vue
Executable file
63
ui/src/pages/for/show-operator.vue
Executable file
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<div class="grid md:grid-cols-2 gap-6 h-full" :class="$style.root">
|
||||||
|
<StepSelection/>
|
||||||
|
<div class="flex flex-col gap-5 overflow-y-auto">
|
||||||
|
<MessageEdit class="h-25 md:h-40"/>
|
||||||
|
<div class="text-5">
|
||||||
|
<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-hidden">
|
||||||
|
<div class="pb-2 font-bold text-3.5 tracking-wider uppercase">
|
||||||
|
Auf der Bühne
|
||||||
|
</div>
|
||||||
|
<ActorsOnStageBox class="flex-grow h-full"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="pb-2 font-bold text-3.5 tracking-wider uppercase">
|
||||||
|
Nebel
|
||||||
|
</div>
|
||||||
|
<FogControl/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="pb-2 font-bold text-3.5 tracking-wider uppercase">
|
||||||
|
Umbaulicht
|
||||||
|
</div>
|
||||||
|
<CurtainLightControl/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style module lang="scss">
|
||||||
|
.root {
|
||||||
|
@screen lt-md {
|
||||||
|
grid-template-rows: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onKeyStroke } from "@vueuse/core"
|
||||||
|
import StepSelection from "../../components/StepSelection.vue"
|
||||||
|
import MessageEdit from "../../components/MessageEdit.vue"
|
||||||
|
import ActorsOnStageBox from "../../components/ActorsOnStageBox.vue"
|
||||||
|
import { goToPosition } from "@/syncing"
|
||||||
|
import { current, getNextValidPosition, getPreviousValidPosition, state } from "@/state"
|
||||||
|
import FogControl from "../../components/FogControl.vue"
|
||||||
|
import CurtainLightControl from "../../components/CurtainLightControl.vue"
|
||||||
|
|
||||||
|
onKeyStroke("ArrowRight", () => {
|
||||||
|
const position = getNextValidPosition(state.position)
|
||||||
|
if (position !== null) goToPosition(position)
|
||||||
|
})
|
||||||
|
|
||||||
|
onKeyStroke("ArrowLeft", () => {
|
||||||
|
const position = getPreviousValidPosition(state.position)
|
||||||
|
if (position !== null) goToPosition(position)
|
||||||
|
})
|
||||||
|
</script>
|
71
ui/src/pages/for/spot-operator.vue
Normal file
71
ui/src/pages/for/spot-operator.vue
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-50">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="text-gray text-2xl">
|
||||||
|
Aktuelles Ziel
|
||||||
|
</div>
|
||||||
|
<ChangeBlinkingBox :blink-seconds="currentTarget === null ? 0 : 10" :value="currentTarget" class="text-7 md:text-10">
|
||||||
|
{{ currentTarget ?? "Niemand" }}
|
||||||
|
</ChangeBlinkingBox>
|
||||||
|
</div>
|
||||||
|
<div v-if="nextStepWithChange !== null" class="flex flex-col gap-2">
|
||||||
|
<div class="text-gray text-6">
|
||||||
|
Nächstes Ziel
|
||||||
|
<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">{{ nextStepWithChange.target }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style module>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useRoute } from "vue-router"
|
||||||
|
import { computed } from "vue"
|
||||||
|
import { current, getNextValidPosition, getSceneIndex, getStep, ShowPosition } from "@/state"
|
||||||
|
import ChangeBlinkingBox from "../../components/ChangeBlinkingBox.vue"
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const isLeft = computed(() => route.query.side === "left")
|
||||||
|
const targetProperty = computed<"leftSpotTarget" | "rightSpotTarget">(() => isLeft.value ? "leftSpotTarget" : "rightSpotTarget")
|
||||||
|
const currentTarget = computed(() => current.step[targetProperty.value])
|
||||||
|
|
||||||
|
interface StepWithChange {
|
||||||
|
delta: number
|
||||||
|
position: ShowPosition
|
||||||
|
target: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextStepWithChange = computed<StepWithChange | null>(() => {
|
||||||
|
let position: ShowPosition | null = getNextValidPosition(current.step.position)
|
||||||
|
let lastTarget: string | null = currentTarget.value
|
||||||
|
|
||||||
|
while (position !== null) {
|
||||||
|
const step = getStep(position)
|
||||||
|
|
||||||
|
if (step[targetProperty.value] !== null && step[targetProperty.value] !== lastTarget) {
|
||||||
|
return {
|
||||||
|
position: step.position,
|
||||||
|
delta: getSceneIndex(step.position) - current.sceneIndex,
|
||||||
|
target: step[targetProperty.value]!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTarget = step[targetProperty.value]
|
||||||
|
position = getNextValidPosition(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
</script>
|
13
ui/src/pages/index.vue
Normal file
13
ui/src/pages/index.vue
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-4 text-xl">
|
||||||
|
<router-link to="/mixer">audio operator</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style module>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
</script>
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full">
|
||||||
<h1 class="font-800 text-9 p-4 pb-0">
|
<h1 class="font-800 text-9 p-4 pb-0">
|
||||||
{{ current.act === null ? "" : `${current.act.name} — `}}{{ current.scene.name }}
|
{{ current.act === null ? "" : `${current.act.name} — ` }}{{ current.scene.name }}
|
||||||
</h1>
|
</h1>
|
||||||
<div class="h-full flex space-x-4 p-4 pt-8 flex-grow overflow-hidden">
|
<div class="h-full flex space-x-4 p-4 pt-8 flex-grow overflow-hidden">
|
||||||
<MotionsList center-current class="w-3/7"/>
|
<MotionsList center-current class="w-3/7"/>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import MusicProgressBar from "../../components/MusicProgressBar.vue"
|
import MusicProgressBar from "../../components/MusicProgressBar.vue"
|
||||||
import { current } from "../../state"
|
import { current } from "../../state"
|
||||||
import MotionsList from "../../components/MotionsList.vue"
|
import MotionsList from "../../components/MotionsList.vue"
|
||||||
|
|
|
@ -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 scrollable center-current class="w-3/7"/>
|
|
||||||
<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 setup lang="ts">
|
|
||||||
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>
|
|
|
@ -1,77 +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 flex-col space-y-10 p-7 text-6">
|
|
||||||
<div class="flex flex-col gap-1">
|
|
||||||
<div>
|
|
||||||
Aktuelles Ziel:
|
|
||||||
</div>
|
|
||||||
<ChangeBlinkingBox class="h-full min-w-50" :value="currentTarget" :blink-seconds="10">
|
|
||||||
{{ currentTarget ?? "Niemand" }}
|
|
||||||
</ChangeBlinkingBox>
|
|
||||||
</div>
|
|
||||||
<div v-if="nextStepWithChange !== null" class="h-full flex flex-col space-y-2 text-5">
|
|
||||||
<div class="flex gap-5 items-center">
|
|
||||||
<div>Nächstes Ziel:</div>
|
|
||||||
<div>{{ nextStepWithChange.target }}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{{ nextStepWithChange.delta === 0
|
|
||||||
? "in dieser Szene (rechtzeitig positionieren!)"
|
|
||||||
: nextStepWithChange.delta === 1
|
|
||||||
? "in der nächsten Szene"
|
|
||||||
: `in ${nextStepWithChange.delta} Szenen`
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<MusicProgressBar class="h-10"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style module>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useRoute } from "vue-router"
|
|
||||||
import { computed } from "vue"
|
|
||||||
import { current, getNextValidPosition, getSceneIndex, getStep, ShowPosition, START_STEP } from "../state"
|
|
||||||
import ChangeBlinkingBox from "../components/ChangeBlinkingBox.vue"
|
|
||||||
import MusicProgressBar from "../components/MusicProgressBar.vue"
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const isLeft = computed(() => route.query.side === "left")
|
|
||||||
const targetProperty = computed<"leftSpotTarget" | "rightSpotTarget">(() => isLeft.value ? "leftSpotTarget" : "rightSpotTarget")
|
|
||||||
const currentTarget = computed(() => current.step[targetProperty.value])
|
|
||||||
|
|
||||||
interface StepWithChange {
|
|
||||||
delta: number
|
|
||||||
position: ShowPosition
|
|
||||||
target: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextStepWithChange = computed<StepWithChange | null>(() => {
|
|
||||||
let position: ShowPosition | null = getNextValidPosition(current.step.position)
|
|
||||||
let lastTarget: string | null = currentTarget.value
|
|
||||||
|
|
||||||
while(position !== null) {
|
|
||||||
const step = getStep(position)
|
|
||||||
|
|
||||||
if (step[targetProperty.value] !== null && step[targetProperty.value] !== lastTarget) {
|
|
||||||
return {
|
|
||||||
position: step.position,
|
|
||||||
delta: getSceneIndex(step.position) - current.sceneIndex,
|
|
||||||
target: step[targetProperty.value]!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastTarget = step[targetProperty.value]
|
|
||||||
position = getNextValidPosition(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,77 +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 flex-col space-y-10 p-10 text-10">
|
|
||||||
<div class="flex gap-5 items-center h-18">
|
|
||||||
<div>
|
|
||||||
Aktuelles Ziel:
|
|
||||||
</div>
|
|
||||||
<ChangeBlinkingBox class="px-8 h-full min-w-50 items-center" :value="currentTarget" :blink-seconds="10">
|
|
||||||
{{ currentTarget ?? "Niemand" }}
|
|
||||||
</ChangeBlinkingBox>
|
|
||||||
</div>
|
|
||||||
<div v-if="nextStepWithChange !== null" class="h-full flex flex-col space-y-2 text-7">
|
|
||||||
<div class="flex gap-5 items-center">
|
|
||||||
<div>Nächstes Ziel:</div>
|
|
||||||
<div>{{ nextStepWithChange.target }}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{{ nextStepWithChange.delta === 0
|
|
||||||
? "in dieser Szene (rechtzeitig positionieren!)"
|
|
||||||
: nextStepWithChange.delta === 1
|
|
||||||
? "in der nächsten Szene"
|
|
||||||
: `in ${nextStepWithChange.delta} Szenen`
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<MusicProgressBar class="h-10"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style module>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useRoute } from "vue-router"
|
|
||||||
import { computed } from "vue"
|
|
||||||
import { current, getNextValidPosition, getSceneIndex, getStep, ShowPosition, START_STEP } from "../state"
|
|
||||||
import ChangeBlinkingBox from "../components/ChangeBlinkingBox.vue"
|
|
||||||
import MusicProgressBar from "../components/MusicProgressBar.vue"
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const isLeft = computed(() => route.query.side === "left")
|
|
||||||
const targetProperty = computed<"leftSpotTarget" | "rightSpotTarget">(() => isLeft.value ? "leftSpotTarget" : "rightSpotTarget")
|
|
||||||
const currentTarget = computed(() => current.step[targetProperty.value])
|
|
||||||
|
|
||||||
interface StepWithChange {
|
|
||||||
delta: number
|
|
||||||
position: ShowPosition
|
|
||||||
target: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextStepWithChange = computed<StepWithChange | null>(() => {
|
|
||||||
let position: ShowPosition | null = getNextValidPosition(current.step.position)
|
|
||||||
let lastTarget: string | null = currentTarget.value
|
|
||||||
|
|
||||||
while(position !== null) {
|
|
||||||
const step = getStep(position)
|
|
||||||
|
|
||||||
if (step[targetProperty.value] !== null && step[targetProperty.value] !== lastTarget) {
|
|
||||||
return {
|
|
||||||
position: step.position,
|
|
||||||
delta: getSceneIndex(step.position) - current.sceneIndex,
|
|
||||||
target: step[targetProperty.value]!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastTarget = step[targetProperty.value]
|
|
||||||
position = getNextValidPosition(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
</script>
|
|
3
ui/src/vite-env.d.ts
vendored
3
ui/src/vite-env.d.ts
vendored
|
@ -1,3 +0,0 @@
|
||||||
/// <reference types="vite-plugin-pages/client"/>
|
|
||||||
/// <reference types="unplugin-icons/types/vue"/>
|
|
||||||
/// <reference types="vite/client"/>
|
|
|
@ -1,20 +1,37 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"declaration": false,
|
||||||
"module": "ESNext",
|
|
||||||
"target": "ES2021",
|
|
||||||
"lib": ["DOM", "ESNext"],
|
|
||||||
"strict": true,
|
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": false,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"moduleResolution": "node",
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
],
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"allowJs": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"strictNullChecks": true,
|
"isolatedModules": true,
|
||||||
"downlevelIteration": true,
|
"rootDir": "src",
|
||||||
"forceConsistentCasingInFileNames": true
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"stripInternal": true,
|
||||||
|
"noUncheckedIndexedAccess": false,
|
||||||
|
"target": "esnext",
|
||||||
|
"types": [
|
||||||
|
"vite/client",
|
||||||
|
"unplugin-vue-router/client",
|
||||||
|
"unplugin-icons/types/vue"
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"node_modules/**/*",
|
"src/**/*.ts",
|
||||||
"src/**/*"
|
"src/**/*.vue",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
44
ui/unocss.config.ts
Executable file
44
ui/unocss.config.ts
Executable file
|
@ -0,0 +1,44 @@
|
||||||
|
import { defineConfig, transformerDirectives } from "unocss"
|
||||||
|
import { presetWind, colors } from "@unocss/preset-wind3"
|
||||||
|
|
||||||
|
const generateValues = (max: number, fn: (step: number) => any) => {
|
||||||
|
const object: Record<number, any> = {}
|
||||||
|
|
||||||
|
for (let i = 1; i <= max; i++) {
|
||||||
|
object[i] = fn(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
presets: [
|
||||||
|
presetWind({
|
||||||
|
arbitraryVariants: true,
|
||||||
|
preflight: true
|
||||||
|
})
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
fontFamily: {
|
||||||
|
sans: `"Manrope Variable", sans-serif`,
|
||||||
|
system: "sans-serif"
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
black: colors.black,
|
||||||
|
white: colors.white,
|
||||||
|
gray: colors.stone,
|
||||||
|
red: colors.red,
|
||||||
|
yellow: colors.amber,
|
||||||
|
orange: colors.orange,
|
||||||
|
green: colors.green,
|
||||||
|
blue: colors.blue,
|
||||||
|
violet: colors.fuchsia,
|
||||||
|
light: colors.light,
|
||||||
|
dark: colors.dark,
|
||||||
|
transparent: colors.transparent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transformers: [
|
||||||
|
transformerDirectives()
|
||||||
|
]
|
||||||
|
})
|
|
@ -1,28 +1,39 @@
|
||||||
import { defineConfig, splitVendorChunkPlugin } from "vite"
|
import { defineConfig } from "vite"
|
||||||
import vuePlugin from "@vitejs/plugin-vue"
|
import vuePlugin from "@vitejs/plugin-vue"
|
||||||
import windicssPlugin from "vite-plugin-windicss"
|
|
||||||
import pagesPlugin from "vite-plugin-pages"
|
|
||||||
import iconsPlugin from "unplugin-icons/vite"
|
import iconsPlugin from "unplugin-icons/vite"
|
||||||
|
import vueRouterPlugin from "unplugin-vue-router/vite"
|
||||||
|
import unocssPlugin from "unocss/vite"
|
||||||
|
import { resolve } from "node:path"
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
splitVendorChunkPlugin(),
|
vueRouterPlugin({
|
||||||
vuePlugin(),
|
dts: "./src/generated-types/router.d.ts",
|
||||||
pagesPlugin({
|
routesFolder: "./src/pages",
|
||||||
importMode: "sync"
|
|
||||||
}),
|
}),
|
||||||
windicssPlugin(),
|
unocssPlugin(),
|
||||||
iconsPlugin()
|
iconsPlugin(),
|
||||||
|
vuePlugin()
|
||||||
],
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": resolve(__dirname, "./src")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
css: {
|
||||||
|
preprocessorOptions: {
|
||||||
|
scss: {
|
||||||
|
api: "modern-compiler"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
"/api": {
|
"/api": {
|
||||||
target: "http://localhost:8000",
|
target: "http://localhost:8000",
|
||||||
ws: true
|
ws: true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
build: {
|
allowedHosts: true
|
||||||
reportCompressedSize: false
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { defineConfig } from "windicss/helpers"
|
|
||||||
import colors from "windicss/colors"
|
|
||||||
import lineClampPlugin from "windicss/plugin/line-clamp"
|
|
||||||
|
|
||||||
const generateValues = (max: number, fn: (step: number) => any) => {
|
|
||||||
const object: Record<number, any> = {}
|
|
||||||
|
|
||||||
for (let i = 1; i <= max; i++) {
|
|
||||||
object[i] = fn(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return object
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
theme: {
|
|
||||||
colors: {
|
|
||||||
black: colors.black,
|
|
||||||
white: colors.white,
|
|
||||||
gray: colors.stone,
|
|
||||||
red: colors.red,
|
|
||||||
yellow: colors.amber,
|
|
||||||
orange: colors.orange,
|
|
||||||
green: colors.green,
|
|
||||||
blue: colors.blue,
|
|
||||||
violet: colors.fuchsia,
|
|
||||||
light: colors.light,
|
|
||||||
dark: colors.dark,
|
|
||||||
transparent: colors.transparent
|
|
||||||
},
|
|
||||||
fontSize: {
|
|
||||||
...generateValues(30, step => `${step * 0.25}rem`),
|
|
||||||
4: "1.2rem",
|
|
||||||
3: "1.1rem",
|
|
||||||
2: "1rem",
|
|
||||||
s1: "0.9rem",
|
|
||||||
s2: "0.8rem",
|
|
||||||
s3: "0.7rem"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
lineClampPlugin
|
|
||||||
]
|
|
||||||
})
|
|
Loading…
Add table
Reference in a new issue