sync
This commit is contained in:
20
webpage/package-lock.json
generated
20
webpage/package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
"astro": "^5.17.1",
|
||||
"astro-icon": "^1.1.5",
|
||||
"phosphor-svelte": "^3.1.0",
|
||||
"svelte": "^5.53.5",
|
||||
"tailwindcss": "^4.2.1",
|
||||
"typescript": "^5.9.3"
|
||||
@@ -4981,6 +4982,25 @@
|
||||
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/phosphor-svelte": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/phosphor-svelte/-/phosphor-svelte-3.1.0.tgz",
|
||||
"integrity": "sha512-nldtxx+XCgNREvrb7O5xgDsefytXpSkPTx8Rnu3f2qQCUZLDV1rLxYSd2Jcwckuo9lZB1qKMqGR17P4UDC0PrA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5.0.0 || ^5.0.0-next.96",
|
||||
"vite": ">=5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"vite": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/piccolore": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
"astro": "^5.17.1",
|
||||
"astro-icon": "^1.1.5",
|
||||
"phosphor-svelte": "^3.1.0",
|
||||
"svelte": "^5.53.5",
|
||||
"tailwindcss": "^4.2.1",
|
||||
"typescript": "^5.9.3"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}[]
|
||||
});
|
||||
Reference in New Issue
Block a user