Nano Banana Pro
Agent skill for nano-banana-pro
You are building Svelte applications using Anchor, a state management library that enables direct mutation with reactivity. Follow these directives.
Sign in to like and favorite skills
You are building Svelte applications using Anchor, a state management library that enables direct mutation with reactivity. Follow these directives.
ALWAYS choose state type based on scope and mutability:
<script> import { mutable, immutable, writable, derived, form } from '@anchorlib/svelte'; // Local mutable state - DEFAULT choice const state = mutable({ count: 0 }); state.count++; // Direct mutation works // Primitives - use .value const count = mutable(0); count.value++; // Shared state - use immutable + writable pattern export const state = immutable({ user: null, theme: 'dark', count: 0 }); // Full write access - can modify all properties export const fullControl = writable(state); fullControl.user = { name: 'John' }; // ✅ Works fullControl.theme = 'light'; // ✅ Works // Limited write access - ONLY specific keys export const themeControl = writable(state, ['theme']); // Second param = allowed keys themeControl.theme = 'light'; // ✅ Works themeControl.user = { name: 'Jane' }; // ❌ Error! Not in allowed keys // Computed (same object) - use getters const cart = mutable({ price: 10, qty: 2, get total() { return this.price * this.qty; } }); // Computed (cross-object) - use derived() const total = derived(() => count1.value + count2.value); // Forms with validation - use form() const [state, errors] = form(schema, { email: '', password: '' }); </script>
DECISION TREE:
mutable()immutable() + writable()derived()form()Anchor works with normal Svelte components - NO special wrappers required.
<script> import { mutable } from '@anchorlib/svelte'; // ✅ Normal component - just use mutable() directly const state = mutable({ count: 0 }); </script> <button onclick={() => state.count++}>{state.count}</button>
RULES:
$:) work with Anchor stateALWAYS use the appropriate async primitive:
<script> import { query, fetchState, streamState } from '@anchorlib/svelte'; // General async - query() const user = query( async (signal) => { const res = await fetch('/api/user', { signal }); return res.json(); }, { name: '' } // ALWAYS provide initial data ); // HTTP requests - fetchState() const data = fetchState({ items: [] }, { url: '/api/data' }); // Streaming - streamState() const stream = streamState('', { url: '/api/stream' }); </script> <!-- Status handling --> {#if user.status === 'pending'} <p>Loading...</p> {:else if user.status === 'error'} <p>Error: {user.error?.message}</p> {:else if user.status === 'success'} <p>Hello, {user.data.name}!</p> {/if}
DECISION TREE:
query()fetchState()streamState()CRITICAL: ALWAYS provide initial data to avoid undefined states.
CRITICAL DISTINCTION:
effect() - ONLY tracks Anchor state (mutable, immutable, derived)$:) - Track Svelte stores and Anchor state<script> import { mutable, effect } from '@anchorlib/svelte'; import { writable } from 'svelte/store'; const anchorState = mutable({ count: 0 }); const svelteStore = writable(0); // ✅ Anchor state - use effect() effect(() => { console.log('Anchor count:', anchorState.count); // Tracks correctly }); // ✅ Svelte stores - use reactive statements $: console.log('Store:', $svelteStore); // Tracks correctly // ❌ NEVER use effect() with Svelte stores effect(() => { console.log('Store:', $svelteStore); // Will NOT track! }); </script>
RULES:
effect()$: reactive statementsuntrack()snapshot()ALWAYS use Svelte control flow directives:
<!-- Conditional --> {#if user.isLoggedIn} <Dashboard /> {:else} <Login /> {/if} <!-- Lists --> {#each todos as todo} <TodoItem {todo} /> {/each} <!-- Multiple conditions --> {#if status === 'loading'} <Spinner /> {:else if status === 'error'} <Error /> {:else if status === 'success'} <Content /> {/if}
Use Svelte's native control flow -
{#if}, {#each}, {#await}.
ALWAYS use
form() for validated forms:
<script> import { form } from '@anchorlib/svelte'; import { z } from 'zod'; const schema = z.object({ email: z.string().email('Invalid email'), password: z.string().min(8, 'Min 8 chars'), }); const [state, errors] = form(schema, { email: '', password: '' }); </script> <form> <input bind:value={state.email} /> {#if errors.email} <span>{errors.email.message}</span> {/if} <input type="password" bind:value={state.password} /> {#if errors.password} <span>{errors.password.message}</span> {/if} </form>
RULES:
form() to create state + errorsbind:value for two-way binding{#if} for error display<script> // Core state import { mutable, immutable, writable, derived, query, fetchState, streamState, form, effect, untrack, snapshot, subscribe, undoable } from '@anchorlib/svelte'; // Storage (separate) import { persistent, session } from '@anchorlib/svelte/storage'; // Utils (separate) import { stringify } from '@anchorlib/core'; // Types import type { Bindable, BindableProp } from '@anchorlib/svelte'; </script>
<script> import { mutable } from '@anchorlib/svelte'; const state = mutable({ todos: [], newText: '', addTodo() { this.todos.push({ id: Date.now(), text: this.newText, done: false }); this.newText = ''; }, removeTodo(id) { const idx = this.todos.findIndex(t => t.id === id); if (idx !== -1) this.todos.splice(idx, 1); }, get completedCount() { return this.todos.filter(t => t.done).length; } }); </script> <div> <input bind:value={state.newText} /> <button onclick={() => state.addTodo()}>Add</button> {#each state.todos as todo} <div> <input type="checkbox" bind:checked={todo.done} /> {todo.text} <button onclick={() => state.removeTodo(todo.id)}>Delete</button> </div> {/each} <p>Completed: {state.completedCount}</p> </div>
<script> import { form } from '@anchorlib/svelte'; import { z } from 'zod'; const schema = z.object({ email: z.string().email(), password: z.string().min(8), }); const [state, errors] = form(schema, { email: '', password: '' }); const submit = (e) => { e.preventDefault(); const result = schema.safeParse(state); if (result.success) api.signup(result.data); }; </script> <form onsubmit={submit}> <input bind:value={state.email} /> {#if errors.email}<span>{errors.email.message}</span>{/if} <input type="password" bind:value={state.password} /> {#if errors.password}<span>{errors.password.message}</span>{/if} <button type="submit">Sign Up</button> </form>
<script> import { query } from '@anchorlib/svelte'; const user = query( async (signal) => { const res = await fetch('/api/user', { signal }); return res.json(); }, { name: '', email: '' } // Initial data ); </script> {#if user.status === 'pending'} <Spinner /> {:else if user.status === 'error'} <Error error={user.error} /> {:else if user.status === 'success'} <div> <h1>{user.data.name}</h1> <p>{user.data.email}</p> </div> {/if}
// store.ts import { immutable, writable } from '@anchorlib/svelte'; export const userState = immutable({ name: '', email: '', isLoggedIn: false }); export const userControl = writable(userState);
<!-- Profile.svelte --> <script> import { userState, userControl } from './store'; </script> <div> <h1>{userState.name}</h1> <button onclick={() => userControl.isLoggedIn = false}>Logout</button> </div>
effect() with Svelte stores - use $: reactive statementsquery(), fetchState(), streamState()bind:value for form inputs with Anchor statestringify() from @anchorlib/core{#if}, {#each}, {#await})immutable() + writable() for shared/global statederived() for computed values across multiple objectsbind: directive for two-way binding in Svelte| Need | Use |
|---|---|
| Local state | |
| Shared state | + |
| Computed (same object) | JavaScript getter |
| Computed (cross-object) | |
| Form validation | |
| Async operation | |
| HTTP request | |
| Streaming | |
| Two-way binding | |
| Anchor state effects | |
| Store effects | reactive statements |
| Conditional render | |
| List render | |
| Async render | |
| localStorage | |
| sessionStorage | |
Before completing any Anchor application, verify:
mutable() or immutable() + writable()bind:value or bind:checked{#if}{#each}query(), fetchState(), or streamState()form() with Zod schemaeffect()$: reactive statementsimmutable() + writable()derived()