원클릭으로
svelte-component
Conventions for writing Svelte 5 components in deep-cuts — runes, stores, props, event listeners, and file layout
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Conventions for writing Svelte 5 components in deep-cuts — runes, stores, props, event listeners, and file layout
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | svelte-component |
| description | Conventions for writing Svelte 5 components in deep-cuts — runes, stores, props, event listeners, and file layout |
Deep Cuts uses Svelte 5 with runes throughout. Do not use the legacy writable/readable store API or $: reactive statements — use runes everywhere.
| What | Where |
|---|---|
| Components | src/lib/components/ComponentName.svelte |
| Global stores | src/lib/stores/*.svelte.ts (class-based, exported as singletons) |
| Types | src/lib/types.ts |
| Utility functions | src/lib/utils/*.ts |
| Routes/pages | src/routes/ |
Store files use the .svelte.ts extension so runes compile correctly outside .svelte files.
<script lang="ts">
// Props — declare with $props()
let { track, onSelect }: { track: Track; onSelect: (t: Track) => void } = $props();
// Local reactive state
let isOpen = $state(false);
// Derived (computed) values
let label = $derived(track.title ?? track.filename);
// Side-effects that re-run when dependencies change
$effect(() => {
console.log('track changed:', track.id);
});
// {@const} must be an immediate child of a block tag, not a DOM element
</script>
$effect replaces $: and onMount. Use it for any reactive side-effect. Use untrack() to read a value without subscribing to it:
<script lang="ts">
import { untrack } from 'svelte';
$effect(() => {
const id = track.id; // subscribes
const current = untrack(() => isOpen); // does NOT subscribe
});
</script>
All stores are pre-instantiated singletons. Import them directly — do not new them in components:
<script lang="ts">
import { library } from '$lib/stores/library.svelte';
import { ui } from '$lib/stores/ui.svelte';
import { filters } from '$lib/stores/filters.svelte';
import { player } from '$lib/stores/player.svelte';
</script>
<!-- Use store properties directly — they are already reactive $state fields -->
<p>{library.trackCount} tracks</p>
<button onclick={() => ui.setActiveView('map')}>Map</button>
Store classes use $state fields and plain methods. There is no .subscribe() or $store sigil.
Use a class with $state fields, exported as a singleton:
// src/lib/stores/my-store.svelte.ts
class MyStore {
value = $state(0);
doubled = $derived(this.value * 2);
increment() {
this.value++;
}
}
export const myStore = new MyStore();
For stores that need more encapsulation, use a function factory (see ui.svelte.ts for the pattern):
function createUiStore() {
let activeView = $state<ActiveView>('table');
// ... methods ...
return { get activeView() { return activeView; }, setActiveView };
}
export const ui = createUiStore();
Use $effect with an async unlisten pattern. The listener is automatically cleaned up when the effect re-runs or the component is destroyed:
<script lang="ts">
import { listen } from '$lib/ipc';
$effect(() => {
let unlisten: (() => void) | undefined;
listen<{ percent: number }>('my-event', (e) => {
progress = e.payload.percent;
}).then(fn => { unlisten = fn; });
return () => unlisten?.(); // cleanup
});
</script>
For persistent app-wide listeners (scan progress, analysis events), prefer wiring them into the appropriate store's init() method instead of individual components.
Stores that register Tauri listeners must be idempotent. Keep an initialized flag, retain every unlisten function, and expose a dispose() method for tests and hot-reload cleanup. Avoid hidden cross-store writes; route shared updates through explicit store methods.
App components and stores should import invoke and listen from $lib/ipc. Do not import directly from @tauri-apps/api/core or @tauri-apps/api/event unless you are adding a low-level wrapper in src/lib/ipc.ts. This keeps local-debug mode, browser-only UI debugging, and tests consistent.
| Mistake | Fix |
|---|---|
{@const} inside a DOM element | Move it to an immediate child of {#each} / {#if} |
$: reactive statement | Replace with $derived (value) or $effect (side-effect) |
writable()/ readable() | Use a class with $state fields |
import { get } from 'svelte/store' | Not needed — access store properties directly |
Direct Tauri invoke / listen imports in app code | Import from $lib/ipc so mocks and typed wrappers stay centralized |
Store init() adds listeners every call | Make init() idempotent and keep unlisten functions for dispose() |
Hardcoded #hex, rgb(), or rgba() in <style> | Use var(--sg-*) design tokens; see skills/ui-design/SKILL.md for the full token reference |
Forgetting lang="ts" on <script> | Add it — the project is fully TypeScript |
New IPC command without a CommandMap entry | Add the command to CommandMap in src/lib/ipc.ts |
Experimental protocol for Deep Cuts research, prototypes, model evaluations, threshold tuning, ablations, metric comparisons, and claims about accuracy or quality. Use before running or interpreting experiments so bots preserve train/validation/test boundaries, avoid leakage, compare against baselines, and report results honestly.
Guidelines for creating, updating, reorganizing, and reviewing Deep Cuts documentation in the project wiki, including page taxonomy, lifecycle status, protected pages, proposal handling, and link verification.
Pattern for multi-agent collaboration sessions in the deep-cuts fam — forge-first coordination over the botfam substrate (Gitea issues/PRs as the coordination plane, the unified `botfam wait` wake loop, bare-actor worktrees), with IRC opt-in for design sprints, plus session-log conventions
Draft, structure, and finalize a Deep Cuts blog-series post so it conforms to the post template, house voice, and cross-post link rules. Use when writing, drafting, assembling, or editing a "Deep Cuts" blog post (the rlupi.com series), preparing a post for publishing, or adding front matter to one. Covers the template, front matter schema, voice, agent crediting, and the link linter.
Checklist and guide for adding a new analysis pass to the trait-based modular pipeline
Safe pattern for adding SQLite schema migrations in the deep-cuts Rust/rusqlite_migration stack