feat: implement plugin architecture and application settings with Svelte UI

- Added plugin base and loader for backend extensibility
- Implemented application settings management with config persistence
- Created Svelte-based frontend with Dashboard and Settings pages
- Added API routes for plugins, tasks, and settings
- Updated documentation and specifications
- Improved project structure and developer tools
This commit is contained in:
2025-12-20 20:48:18 +03:00
parent ce703322c2
commit 2d8cae563f
98 changed files with 7894 additions and 5021 deletions

127
frontend/src/components/TaskRunner.svelte Normal file → Executable file
View File

@@ -1,54 +1,73 @@
<script>
import { onMount, onDestroy } from 'svelte';
import { selectedTask, taskLogs } from '../lib/stores.js';
let ws;
onMount(() => {
if ($selectedTask) {
taskLogs.set([]); // Clear previous logs
const wsUrl = `ws://localhost:8000/ws/logs/${$selectedTask.id}`;
ws = new WebSocket(wsUrl);
ws.onopen = () => {
console.log('WebSocket connection established');
};
ws.onmessage = (event) => {
const logEntry = JSON.parse(event.data);
taskLogs.update(logs => [...logs, logEntry]);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onclose = () => {
console.log('WebSocket connection closed');
};
}
});
onDestroy(() => {
if (ws) {
ws.close();
}
});
</script>
<div class="p-4 border rounded-lg bg-white shadow-md">
{#if $selectedTask}
<h2 class="text-xl font-semibold mb-2">Task: {$selectedTask.plugin_id}</h2>
<div class="bg-gray-900 text-white font-mono text-sm p-4 rounded-md h-96 overflow-y-auto">
{#each $taskLogs as log}
<div>
<span class="text-gray-400">{new Date(log.timestamp).toLocaleTimeString()}</span>
<span class="{log.level === 'ERROR' ? 'text-red-500' : 'text-green-400'}">[{log.level}]</span>
<span>{log.message}</span>
</div>
{/each}
</div>
{:else}
<p>No task selected.</p>
{/if}
</div>
<!--
[DEF:TaskRunner:Component]
@SEMANTICS: task, runner, logs, websocket
@PURPOSE: Connects to a WebSocket to display real-time logs for a running task.
@LAYER: UI
@RELATION: DEPENDS_ON -> frontend/src/lib/stores.js
@PROPS: None
@EVENTS: None
-->
<script>
import { onMount, onDestroy } from 'svelte';
import { selectedTask, taskLogs } from '../lib/stores.js';
let ws;
// [DEF:onMount:Function]
// @PURPOSE: Initialize WebSocket connection for task logs.
onMount(() => {
if ($selectedTask) {
console.log(`[TaskRunner][Entry] Connecting to logs for task: ${$selectedTask.id}`);
taskLogs.set([]); // Clear previous logs
const wsUrl = `ws://localhost:8000/ws/logs/${$selectedTask.id}`;
ws = new WebSocket(wsUrl);
ws.onopen = () => {
console.log('[TaskRunner][Coherence:OK] WebSocket connection established');
};
ws.onmessage = (event) => {
const logEntry = JSON.parse(event.data);
taskLogs.update(logs => [...logs, logEntry]);
};
ws.onerror = (error) => {
console.error('[TaskRunner][Coherence:Failed] WebSocket error:', error);
};
ws.onclose = () => {
console.log('[TaskRunner][Exit] WebSocket connection closed');
};
}
});
// [/DEF:onMount]
// [DEF:onDestroy:Function]
// @PURPOSE: Close WebSocket connection when the component is destroyed.
onDestroy(() => {
if (ws) {
console.log("[TaskRunner][Action] Closing WebSocket connection.");
ws.close();
}
});
// [/DEF:onDestroy]
</script>
<div class="p-4 border rounded-lg bg-white shadow-md">
{#if $selectedTask}
<h2 class="text-xl font-semibold mb-2">Task: {$selectedTask.plugin_id}</h2>
<div class="bg-gray-900 text-white font-mono text-sm p-4 rounded-md h-96 overflow-y-auto">
{#each $taskLogs as log}
<div>
<span class="text-gray-400">{new Date(log.timestamp).toLocaleTimeString()}</span>
<span class="{log.level === 'ERROR' ? 'text-red-500' : 'text-green-400'}">[{log.level}]</span>
<span>{log.message}</span>
</div>
{/each}
</div>
{:else}
<p>No task selected.</p>
{/if}
</div>
<!-- [/DEF:TaskRunner] -->