few shots update

This commit is contained in:
2026-03-11 09:08:32 +03:00
parent b77fa45e4e
commit 765178f12e
9 changed files with 203 additions and 425 deletions

View File

@@ -11,45 +11,27 @@
* @INVARIANT: Loading state must always terminate (no infinite spinner).
* @INVARIANT: User must receive feedback on both success and failure.
*
* @UX_REACTIVITY: Props -> $props(), LocalState -> $state(isLoading).
*
* @TEST_CONTRACT: ComponentState ->
* {
* required_fields: {
* isLoading: bool
* },
* invariants: [
* required_fields: { isLoading: bool },
* invariants:[
* "isLoading=true implies button.disabled=true",
* "isLoading=true implies aria-busy=true",
* "isLoading=true implies spinner visible"
* "isLoading=true implies aria-busy=true"
* ]
* }
*
* @TEST_CONTRACT: ApiResponse ->
* {
* required_fields: {},
* optional_fields: {
* task_id: str
* }
* }
* @TEST_FIXTURE: idle_state ->
* {
* isLoading: false
* }
*
* @TEST_FIXTURE: successful_response ->
* {
* task_id: "task_123"
* }
* @TEST_FIXTURE: idle_state -> { isLoading: false }
* @TEST_FIXTURE: successful_response -> { task_id: "task_123" }
*
* @TEST_EDGE: api_failure -> raises Error("Network")
* @TEST_EDGE: empty_response -> {}
* @TEST_EDGE: rapid_double_click -> special: concurrent_click
* @TEST_EDGE: unresolved_promise -> special: pending_state
* @TEST_INVARIANT: prevent_double_submission -> verifies: [rapid_double_click]
* @TEST_INVARIANT: loading_state_consistency -> verifies: [idle_state, pending_state]
* @TEST_INVARIANT: feedback_always_emitted -> verifies: [successful_response, api_failure]
*
* @TEST_INVARIANT: prevent_double_submission -> VERIFIED_BY:[rapid_double_click]
* @TEST_INVARIANT: feedback_always_emitted -> VERIFIED_BY:[successful_response, api_failure]
*
* @UX_STATE: Idle -> Button enabled, primary color, no spinner.
* @UX_STATE: Loading -> Button disabled, spinner visible, aria-busy=true.
* @UX_STATE: Success -> Toast success displayed.
@@ -59,44 +41,39 @@
*
* @UX_TEST: Idle -> {click: spawnTask, expected: isLoading=true}
* @UX_TEST: Loading -> {double_click: ignored, expected: single_api_call}
* @UX_TEST: Success -> {api_resolve: task_id, expected: toast.success called}
* @UX_TEST: Error -> {api_reject: error, expected: toast.error called}
*/
-->
<script>
import { postApi } from "$lib/api.js";
import { t } from "$lib/i18n";
import { toast } from "$lib/stores/toast";
export let plugin_id = "";
export let params = {};
let isLoading = false;
// GRACE Svelte 5 Runes
let { plugin_id = "", params = {} } = $props();
let isLoading = $state(false);
// [DEF:spawnTask:Function]
/**
* @purpose Execute task creation request and emit user feedback.
* @pre plugin_id is resolved and request params are serializable.
* @post isLoading is reset and user receives success/error feedback.
* @PURPOSE: Execute task creation request and emit user feedback.
* @PRE: plugin_id is resolved and request params are serializable.
* @POST: isLoading is reset and user receives success/error feedback.
*/
async function spawnTask() {
isLoading = true;
console.log("[FrontendComponentShot][Loading] Spawning task...");
console.info("[spawnTask][REASON] Spawning task...", { plugin_id });
try {
// 1. Action: API Call
const response = await postApi("/api/tasks", {
plugin_id,
params
});
const response = await postApi("/api/tasks", { plugin_id, params });
// 2. Feedback: Success
// 2. Feedback: Success validation
if (response.task_id) {
console.log("[FrontendComponentShot][Success] Task created.");
console.info("[spawnTask][REFLECT] Task created.", { task_id: response.task_id });
toast.success($t.tasks.spawned_success);
}
} catch (error) {
// 3. Recovery: User notification
console.log("[FrontendComponentShot][Error] Failed:", error);
// 3. Recovery: Error handling & fallback logic
console.error("[spawnTask][EXPLORE] Failed to spawn task. Notifying user.", { error });
toast.error(`${$t.errors.task_failed}: ${error.message}`);
} finally {
isLoading = false;
@@ -106,7 +83,7 @@
</script>
<button
on:click={spawnTask}
onclick={spawnTask}
disabled={isLoading}
class="btn-primary flex items-center gap-2"
aria-busy={isLoading}