bak
This commit is contained in:
1 parent
e4aa0dce86
commit
cc6b11e61d
23 files changed
+2047
-524
No files matched your search
+78
-103
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import {
|
||||
PhArrowClockwise,
|
||||
PhArrowCounterClockwise,
|
||||
PhExport,
|
||||
PhFolderOpen,
|
||||
PhFloppyDisk,
|
||||
@@ -15,10 +17,13 @@ import AssetSidebar from './components/AssetSidebar.vue'
|
||||
import CanvasStage from './components/CanvasStage.vue'
|
||||
import DocumentSettings from './components/DocumentSettings.vue'
|
||||
import ExportDialog from './components/ExportDialog.vue'
|
||||
import FullscreenPreview from './components/FullscreenPreview.vue'
|
||||
import PropertiesPanel from './components/PropertiesPanel.vue'
|
||||
import Timeline from './components/Timeline.vue'
|
||||
import WelcomeDialog from './components/WelcomeDialog.vue'
|
||||
import TestOverlay from './devtools/TestOverlay.vue'
|
||||
import { IconButton, Toast } from './components/ui'
|
||||
import { useHistory } from './composables/useHistory'
|
||||
import { usePlaybackLoop } from './composables/usePlaybackLoop'
|
||||
import {
|
||||
ProjectFileError,
|
||||
@@ -39,6 +44,7 @@ const settingsOpen = ref(false)
|
||||
const exportOpen = ref(false)
|
||||
const testsOpen = ref(false)
|
||||
const welcomeOpen = ref(false)
|
||||
const fullscreenOpen = ref(false)
|
||||
const loadError = ref<string | null>(null)
|
||||
const savedToast = ref<string | null>(null)
|
||||
const currentProjectId = ref<string | null>(null)
|
||||
@@ -46,6 +52,7 @@ const currentProjectName = ref<string | null>(null)
|
||||
|
||||
usePlaybackLoop()
|
||||
const store = useSceneStore()
|
||||
const history = useHistory()
|
||||
|
||||
function rewind() {
|
||||
store.setPlayhead(0)
|
||||
@@ -94,6 +101,7 @@ async function importFromFile() {
|
||||
try {
|
||||
const scene = await readProjectFile(file)
|
||||
store.loadScene(scene)
|
||||
history.resetTo(scene)
|
||||
currentProjectId.value = null
|
||||
currentProjectName.value = file.name.replace(/\.[^.]+$/, '')
|
||||
loadError.value = null
|
||||
@@ -108,7 +116,9 @@ async function importFromFile() {
|
||||
}
|
||||
|
||||
function openFromRecent(project: StoredProject) {
|
||||
store.loadScene(JSON.parse(JSON.stringify(project.scene)))
|
||||
const cloned = JSON.parse(JSON.stringify(project.scene))
|
||||
store.loadScene(cloned)
|
||||
history.resetTo(cloned)
|
||||
currentProjectId.value = project.id
|
||||
currentProjectName.value = project.name
|
||||
setLastOpenedId(project.id)
|
||||
@@ -150,9 +160,28 @@ function onKeyDown(e: KeyboardEvent) {
|
||||
testsOpen.value = true
|
||||
return
|
||||
}
|
||||
const mod = e.ctrlKey || e.metaKey
|
||||
if (mod && !isEditable(e.target)) {
|
||||
const key = e.key.toLowerCase()
|
||||
if (key === 'z' && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
history.undo()
|
||||
return
|
||||
}
|
||||
if ((key === 'z' && e.shiftKey) || key === 'y') {
|
||||
e.preventDefault()
|
||||
history.redo()
|
||||
return
|
||||
}
|
||||
}
|
||||
if (e.code !== 'Space') return
|
||||
if (isEditable(e.target)) return
|
||||
e.preventDefault()
|
||||
if (e.shiftKey) {
|
||||
store.pause()
|
||||
fullscreenOpen.value = true
|
||||
return
|
||||
}
|
||||
store.togglePlay()
|
||||
}
|
||||
|
||||
@@ -171,23 +200,47 @@ onBeforeUnmount(() => document.removeEventListener('keydown', onKeyDown))
|
||||
<PhSparkle :size="16" weight="fill" class="brand-icon" />
|
||||
Animatr
|
||||
</span>
|
||||
<span class="hint">PoC</span>
|
||||
</div>
|
||||
<div class="transport">
|
||||
<button class="tbtn" title="Zum Anfang" @click="rewind">
|
||||
<PhSkipBack :size="14" weight="fill" />
|
||||
</button>
|
||||
<button
|
||||
class="tbtn primary"
|
||||
<IconButton
|
||||
:icon="PhArrowCounterClockwise"
|
||||
icon-weight="bold"
|
||||
size="md"
|
||||
:disabled="!history.canUndo.value"
|
||||
title="Rückgängig (Ctrl+Z)"
|
||||
@click="history.undo"
|
||||
/>
|
||||
<IconButton
|
||||
:icon="PhArrowClockwise"
|
||||
icon-weight="bold"
|
||||
size="md"
|
||||
:disabled="!history.canRedo.value"
|
||||
title="Wiederholen (Ctrl+Shift+Z)"
|
||||
@click="history.redo"
|
||||
/>
|
||||
<div class="tsep"></div>
|
||||
<IconButton
|
||||
:icon="PhSkipBack"
|
||||
icon-weight="fill"
|
||||
size="md"
|
||||
title="Zum Anfang"
|
||||
@click="rewind"
|
||||
/>
|
||||
<IconButton
|
||||
:icon="store.playing ? PhPause : PhPlay"
|
||||
icon-weight="fill"
|
||||
size="md"
|
||||
variant="primary"
|
||||
:title="store.playing ? 'Pause (Space)' : 'Play (Space)'"
|
||||
@click="store.togglePlay()"
|
||||
>
|
||||
<PhPause v-if="store.playing" :size="14" weight="fill" />
|
||||
<PhPlay v-else :size="14" weight="fill" />
|
||||
</button>
|
||||
<button class="tbtn" title="Stop" @click="stop">
|
||||
<PhStop :size="14" weight="fill" />
|
||||
</button>
|
||||
/>
|
||||
<IconButton
|
||||
:icon="PhStop"
|
||||
icon-weight="fill"
|
||||
size="md"
|
||||
title="Stop"
|
||||
@click="stop"
|
||||
/>
|
||||
<span class="time">{{ store.playhead.toFixed(2) }}s / {{ store.scene.duration }}s</span>
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
@@ -216,6 +269,7 @@ onBeforeUnmount(() => document.removeEventListener('keydown', onKeyDown))
|
||||
<DocumentSettings v-model="settingsOpen" />
|
||||
<ExportDialog v-model="exportOpen" />
|
||||
<TestOverlay v-model="testsOpen" />
|
||||
<FullscreenPreview v-model="fullscreenOpen" />
|
||||
<WelcomeDialog
|
||||
v-model="welcomeOpen"
|
||||
@open="openFromRecent"
|
||||
@@ -223,15 +277,11 @@ onBeforeUnmount(() => document.removeEventListener('keydown', onKeyDown))
|
||||
@import="importFromFile"
|
||||
@export="exportProjectFile"
|
||||
/>
|
||||
<Transition name="fade">
|
||||
<div v-if="loadError" class="load-error" role="alert">
|
||||
<strong>Projekt konnte nicht geladen werden.</strong>
|
||||
<span>{{ loadError }}</span>
|
||||
</div>
|
||||
</Transition>
|
||||
<Transition name="fade">
|
||||
<div v-if="savedToast" class="saved-toast" role="status">{{ savedToast }}</div>
|
||||
</Transition>
|
||||
<Toast :visible="!!loadError" variant="error">
|
||||
<strong>Projekt konnte nicht geladen werden.</strong>
|
||||
<span>{{ loadError }}</span>
|
||||
</Toast>
|
||||
<Toast :visible="!!savedToast" variant="info" :message="savedToast ?? ''" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -279,49 +329,17 @@ onBeforeUnmount(() => document.removeEventListener('keydown', onKeyDown))
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: var(--text-dim);
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
padding: 2px 6px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.transport {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tbtn {
|
||||
width: 32px;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-dim-strong);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.tbtn:hover:not(:disabled) {
|
||||
background: var(--panel-alt);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.tbtn.primary {
|
||||
background: var(--accent-soft);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.tbtn.primary:hover:not(:disabled) {
|
||||
background: var(--accent-soft-strong);
|
||||
color: var(--accent);
|
||||
.tsep {
|
||||
width: 1px;
|
||||
height: 18px;
|
||||
background: var(--border);
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.time {
|
||||
@@ -398,40 +416,6 @@ onBeforeUnmount(() => document.removeEventListener('keydown', onKeyDown))
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.load-error {
|
||||
position: fixed;
|
||||
bottom: 240px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1500;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 10px 14px;
|
||||
background: var(--danger-soft);
|
||||
color: var(--danger);
|
||||
border: 1px solid var(--danger);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-md);
|
||||
font-size: 12px;
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.saved-toast {
|
||||
position: fixed;
|
||||
bottom: 240px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1500;
|
||||
padding: 8px 14px;
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border-strong);
|
||||
color: var(--text);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-md);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.project-name {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -447,13 +431,4 @@ onBeforeUnmount(() => document.removeEventListener('keydown', onKeyDown))
|
||||
text-overflow: ellipsis;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 140ms var(--ease);
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user