This commit is contained in:
2026-02-28 10:06:36 +01:00
parent 8a124fe17d
commit 99dcbca8ac
8 changed files with 82 additions and 38 deletions

View File

@@ -1,42 +1,58 @@
<script lang="ts">
import { buzzer } from '../lib/buzzerStore';
import { playFile, deleteFile } from '../lib/buzzerActions';
import { MusicNotesIcon, WrenchIcon, PlayIcon, TrashIcon, ArrowsLeftRightIcon, QuestionMarkIcon } from "phosphor-svelte";
// Die Datei-Daten werden vom Parent (FileStorage) übergeben
export let file: { name: string, size: string, isSystem: boolean };
export let file: { name: string, size: string, isSystem: boolean, crc32?: number};
export let selected = false;
// Wir benötigen den Port für die Kommandos
// In einem echten Szenario würden wir den Port in einem Store speichern.
// Hier nehmen wir an, er wird über die Actions gehandelt.
</script>
<div
class="flex items-center justify-between p-3 cursor-pointer transition-all group border-b border-slate-700/30
{selected ? 'bg-blue-600/20 border-l-4 border-blue-500 shadow-[inset_0_0_20px_rgba(59,130,246,0.1)]' : 'hover:bg-slate-700/40 border-l-4 border-transparent'}"
>
<div class="flex items-center gap-4 overflow-hidden">
<div class="flex-1 flex items-center gap-4 min-w-0">
<div class="text-2xl {selected ? 'text-blue-400' : 'text-slate-500'} shrink-0">
{#if file.isSystem}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M225.9,103.11l-40.45,40.44a8,8,0,0,1-11.31,0L148,117.41a8,8,0,0,1,0-11.31l40.44-40.45a72,72,0,0,0-85.3,109.11l-51.5,51.5a24,24,0,0,0,33.94,33.94l51.5-51.5A72,72,0,0,0,225.9,103.11ZM160,80a8,8,0,1,1-8-8A8,8,0,0,1,160,80Z"></path></svg>
<WrenchIcon size={24} weight="fill" />
{:else}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M210.3,56.34l-80-24A8,8,0,0,0,120,40V148.26A48,48,0,1,0,136,184V98.71l69.7,20.91a8,8,0,0,0,10.3-7.62V64A8,8,0,0,0,210.3,56.34ZM88,216a32,32,0,1,1,32-32A32,32,0,0,1,88,216Zm112-112.71L136,83.89V49.11L200,68.31Z"></path></svg>
<MusicNotesIcon size={24} weight="fill" />
{/if}
</div>
<div class="flex flex-col overflow-hidden">
<span class="text-sm truncate {selected ? 'font-bold text-white' : 'text-slate-200'}">{file.name}</span>
<span class="text-[10px] text-slate-500 font-mono tracking-tighter uppercase">{file.size}</span>
<div class="flex-1 flex flex-col min-w-0">
<span class="text-sm truncate overflow-hidden {selected ? 'font-bold text-white' : 'text-slate-200'}">
{file.name}
</span>
<div class="flex items-center gap-2">
<div class="relative group/sync flex items-center">
{#if !file.crc32 || isNaN(file.crc32)}
<QuestionMarkIcon size={12} weight="bold" class="text-slate-700" />
{:else}
<ArrowsLeftRightIcon size={12} weight="bold" class="text-slate-300" />
{/if}
<div class="absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 bg-slate-900 text-[10px] text-white rounded opacity-0 group-hover/sync:opacity-100 pointer-events-none transition-opacity whitespace-nowrap z-50 border border-slate-700 shadow-xl">
{file.crc32 ? `CRC32: 0x${file.crc32.toString(16).toUpperCase()}` : 'CRC unbekannt'}
<div class="absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-slate-900"></div>
</div>
</div>
<span class="text-[10px] text-slate-500 font-mono tracking-tighter uppercase">
{file.size}
</span>
</div>
</div>
</div>
<div class="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity shrink-0">
<div class="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity shrink-0 ml-4">
<button
on:click|stopPropagation={() => playFile(file.name)}
class="p-2 hover:bg-blue-500/20 rounded-lg text-blue-400 transition-colors"
title="Play Sound"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 256 256"><path d="M240,128a15.74,15.74,0,0,1-7.6,13.51L88.32,229.75a16,16,0,0,1-16.2,0A15.86,15.86,0,0,1,64,216.24V39.76a15.86,15.86,0,0,1,8.12-13.51,16,16,0,0,1,16.2,0L232.4,114.49A15.74,15.74,0,0,1,240,128Z"></path></svg>
<PlayIcon size={16} weight="fill" />
</button>
<button
@@ -44,7 +60,7 @@
class="p-2 hover:bg-red-500/20 rounded-lg text-red-400 transition-colors"
title="Delete File"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path></svg>
<TrashIcon size={16} weight="fill" />
</button>
</div>
</div>

View File

@@ -2,10 +2,9 @@
import { buzzer } from '../lib/buzzerStore';
import { refreshFileList } from '../lib/buzzerActions';
import FileRow from './FileRow.svelte';
import { ArrowsCounterClockwiseIcon } from 'phosphor-svelte';
async function handleRefresh() {
// Falls wir den Port im Store haben, hier nutzen
// ansonsten wird er über die Actions verwaltet
console.log("Aktualisiere Dateiliste...");
await refreshFileList();
}
</script>
@@ -19,9 +18,10 @@
<button
on:click={handleRefresh}
class="text-slate-500 hover:text-blue-400 transition-colors active:rotate-180 duration-500">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 256 256">
<rect width="256" height="256" fill="none"/><polyline points="88 96 40 96 40 48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M40,96,68.28,67.72A88,88,0,0,1,192,67" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><polyline points="168 160 216 160 216 208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M216,160l-28.28,28.28A88,88,0,0,1,64,189" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/> </svg>
class="text-slate-500 hover:text-blue-400 transition-colors active:rotate-180 duration-500"
title="Refresh File List"
>
<ArrowsCounterClockwiseIcon size={16} weight="fill" />
</button>
</div>
@@ -44,14 +44,4 @@
</div>
{/if}
</div>
<div class="absolute bottom-6 right-6 text-slate-700 opacity-5 pointer-events-none">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" fill="currentColor" viewBox="0 0 256 256">
<path d="M208,40V216a16,16,0,0,1-16,16H64a16,16,0,0,1-16-16V40A16,16,0,0,1,64,24H192A16,16,0,0,1,208,40Z" opacity="0.2"></path>
<path d="M208,40V216a16,16,0,0,1-16,16H64a16,16,0,0,1-16-16V40A16,16,0,0,1,64,24H192A16,16,0,0,1,208,40Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path>
<line x1="80" y1="24" x2="80" y2="8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
<line x1="128" y1="24" x2="128" y2="8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
<line x1="176" y1="24" x2="176" y2="8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
</svg>
</div>
</div>

View File

@@ -1,4 +1,5 @@
// src/lib/buzzerActions.ts
import { nan } from 'astro:schema';
import { buzzer } from './buzzerStore';
let activePort: SerialPort | null = null;
@@ -53,7 +54,10 @@ export async function updateDeviceInfo(port: SerialPort) {
// WICHTIG: Auch hier "export"
export async function refreshFileList(port: SerialPort) {
if (!port) return;
if (!port) {
if (!activePort) return;
port = activePort;
}
let audioSize = 0;
let sysSize = 0;
const audioFiles: {name: string, size: string, crc32: number, isSystem: boolean}[] = [];
@@ -73,7 +77,7 @@ export async function refreshFileList(port: SerialPort) {
if (path.startsWith('/lfs/a')) {
audioSize += size;
console.log("Audio-Datei gefunden:", name);
audioFiles.push({ name, size: (size / 1024).toFixed(1) + " KB", crc32: 0, isSystem: false });
audioFiles.push({ name, size: (size / 1024).toFixed(1) + " KB", crc32: NaN, isSystem: false, isSynced: false });
} else if (path.startsWith('/lfs/sys')) {
sysSize += size;
}

View File

@@ -7,10 +7,10 @@ export const buzzer = writable({
build: 'unknown',
storage: {
total: 8.0, // 8 MB Flash laut Spezifikation
unknown: 8.0,
available: 0.0,
unknown: 8.0,
usedSys: 0.0,
usedAudio: 0.0
},
files: [] as {name: string, size: string, crc32: number, isSystem: boolean}[]
files: [] as {name: string, size: string, crc32: number, isSystem: boolean, isSynced: boolean}[]
});