Skip to content

Commit afa5bfc

Browse files
aster-voidclaude
andcommitted
modules/claude-code: add FSM (xstate, robot3) to svelte skill
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 2171c6b commit afa5bfc

File tree

3 files changed

+208
-18
lines changed

3 files changed

+208
-18
lines changed

modules/home/profile-dev/programs/claude-code/skills/svelte/SKILL.md

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ description: Build Svelte 5 apps with async svelte and remote functions. Always
44
---
55

66
<rules>
7-
1. [Reactivity]: `$derived` not `$effect` — $effect is escape hatch for external systems (DOM APIs, timers)
8-
2. [Query refresh]: After mutation, call `relatedQuery.refresh()` to invalidate
9-
3. [Single-flight]: Same query + same args = one request (auto-deduplicated)
10-
4. [Auth at DAL]: Every `.remote.ts` validates auth before DB calls
11-
5. [DB layer]: Authorization-unaware, no `getRequestEvent()`
7+
1. [Reactivity]: `$derived` over `$effect` — $effect is escape hatch for external systems (DOM APIs, timers)
8+
2. [Single-flight]: Same query + same args = one request (auto-deduplicated)
9+
3. [Auth at DAL]: Every `.remote.ts` validates auth before DB calls
10+
4. [DB layer]: Authorization-unaware, no `getRequestEvent()`
1211
</rules>
1312

1413
## Type-safe Context
@@ -24,18 +23,18 @@ export const [useFoo, setupFoo] = createContext<Foo>();
2423
- top-level `await` - `{#snippet pending}` 不要。省略可
2524
- context は `await` より前に呼ぶ必要あり
2625
- `<slot />` は非推奨。`{#snippet}``{@render}` を使う:
27-
```svelte
28-
<!-- 親: propsとしてsnippetを渡す -->
29-
<Card>
30-
{#snippet header()}<h1>Title</h1>{/snippet}
31-
{#snippet children()}Content here{/snippet}
32-
</Card>
33-
34-
<!-- 子: propsで受け取って@renderで描画 -->
35-
<script>let { header, children } = $props()</script>
36-
{@render header()}
37-
{@render children()}
38-
```
26+
```svelte
27+
<!-- 親: propsとしてsnippetを渡す -->
28+
<Card>
29+
{#snippet header()}<h1>Title</h1>{/snippet}
30+
{#snippet children()}Content here{/snippet}
31+
</Card>
32+
33+
<!-- 子: propsで受け取って@renderで描画 -->
34+
<script>let { header, children } = $props()</script>
35+
{@render header()}
36+
{@render children()}
37+
```
3938
</tips>
4039

4140
<layers>
@@ -138,10 +137,12 @@ See context7 for library docs.
138137
- svelte-radix - Radix icons
139138
- svelte-heroes-v2 - hero-icons for svelte
140139

141-
### Async & Data loading
140+
### State & Async
142141

143142
- Remote Functions & Async Svelte - builtin to svelte and kit, very good
144143
- @tanstack/svelte-query - async state management
144+
- xstate + @xstate/svelte - statecharts, actor model FSM
145+
- robot3 - lightweight (1kb) functional FSM
145146

146147
### Utility
147148

@@ -156,4 +157,6 @@ See context7 for library docs.
156157
- [Architecture](./references/architecture.md) - structures, auth helpers
157158
- [Advanced Patterns](./references/advanced-patterns.md) - batching, optimistic UI, advanced form
158159
- [File Uploads](./references/file-uploads.md) - form/command file upload patterns
160+
- [XState](./references/xstate.md) - full-featured statecharts, actors, parallel states
161+
- [Robot3](./references/robot3.md) - lightweight (1kb) functional FSM
159162
- [Svelte Docs](https://svelte.dev/llms-medium.txt) - full documentation
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Robot3
2+
3+
Lightweight (1kb) functional FSM. Simpler API than XState.
4+
5+
## Basic Usage
6+
7+
```ts
8+
import { createMachine, state, transition } from 'robot3';
9+
10+
const toggleMachine = createMachine({
11+
inactive: state(
12+
transition('toggle', 'active')
13+
),
14+
active: state(
15+
transition('toggle', 'inactive')
16+
)
17+
});
18+
```
19+
20+
## Async Actions
21+
22+
```ts
23+
import { createMachine, state, transition, invoke, reduce } from 'robot3';
24+
25+
const fetchMachine = createMachine({
26+
idle: state(
27+
transition('fetch', 'loading')
28+
),
29+
loading: invoke(
30+
() => fetch('/api/data').then(r => r.json()),
31+
transition('done', 'success', reduce((ctx, ev) => ({ ...ctx, data: ev.data }))),
32+
transition('error', 'error')
33+
),
34+
success: state(),
35+
error: state(
36+
transition('retry', 'loading')
37+
)
38+
});
39+
```
40+
41+
## Svelte 5 Integration
42+
43+
```ts
44+
// $lib/useMachine.svelte.ts
45+
import { interpret } from 'robot3';
46+
47+
export function useMachine<T>(machine: T, initialContext = {}) {
48+
let current = $state(machine.current);
49+
let context = $state(initialContext);
50+
51+
const service = interpret(machine, (service) => {
52+
current = service.machine.current;
53+
context = service.context;
54+
}, initialContext);
55+
56+
return {
57+
get current() { return current },
58+
get context() { return context },
59+
send: service.send
60+
};
61+
}
62+
```
63+
64+
```svelte
65+
<script>
66+
import { useMachine } from '$lib/useMachine.svelte';
67+
import { fetchMachine } from './machines';
68+
69+
const { current, context, send } = useMachine(fetchMachine);
70+
</script>
71+
72+
<p>State: {current}</p>
73+
{#if current === 'idle'}
74+
<button onclick={() => send('fetch')}>Fetch</button>
75+
{:else if current === 'loading'}
76+
<p>Loading...</p>
77+
{:else if current === 'success'}
78+
<pre>{JSON.stringify(context.data)}</pre>
79+
{:else if current === 'error'}
80+
<button onclick={() => send('retry')}>Retry</button>
81+
{/if}
82+
```
83+
84+
## When to Use
85+
86+
- Simple state machines
87+
- Bundle size matters (1kb vs 13kb)
88+
- Prefer functional composition
89+
- Quick prototyping
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# XState (@xstate/svelte)
2+
3+
Full-featured statecharts: actors, parallel states, visual editor (Stately).
4+
5+
## Basic Usage
6+
7+
```svelte
8+
<script>
9+
import { useMachine } from '@xstate/svelte';
10+
import { createMachine, assign } from 'xstate';
11+
12+
const toggleMachine = createMachine({
13+
id: 'toggle',
14+
initial: 'inactive',
15+
context: { count: 0 },
16+
states: {
17+
inactive: {
18+
on: { TOGGLE: 'active' }
19+
},
20+
active: {
21+
entry: assign({ count: ({ context }) => context.count + 1 }),
22+
on: { TOGGLE: 'inactive' }
23+
}
24+
}
25+
});
26+
27+
const { state, send } = useMachine(toggleMachine);
28+
</script>
29+
30+
<button onclick={() => send({ type: 'TOGGLE' })}>
31+
{$state.value} (count: {$state.context.count})
32+
</button>
33+
```
34+
35+
## Async Actions
36+
37+
```ts
38+
import { createMachine, assign, fromPromise } from 'xstate';
39+
40+
const fetchMachine = createMachine({
41+
id: 'fetch',
42+
initial: 'idle',
43+
context: { data: null, error: null },
44+
states: {
45+
idle: {
46+
on: { FETCH: 'loading' }
47+
},
48+
loading: {
49+
invoke: {
50+
src: 'fetchData',
51+
onDone: { target: 'success', actions: assign({ data: ({ event }) => event.output }) },
52+
onError: { target: 'error', actions: assign({ error: ({ event }) => event.error }) }
53+
}
54+
},
55+
success: { on: { FETCH: 'loading' } },
56+
error: { on: { FETCH: 'loading' } }
57+
}
58+
}, {
59+
actors: {
60+
fetchData: fromPromise(async () => {
61+
const res = await fetch('/api/data');
62+
return res.json();
63+
})
64+
}
65+
});
66+
```
67+
68+
## Parallel States
69+
70+
```ts
71+
const editorMachine = createMachine({
72+
id: 'editor',
73+
type: 'parallel',
74+
states: {
75+
bold: {
76+
initial: 'off',
77+
states: {
78+
off: { on: { TOGGLE_BOLD: 'on' } },
79+
on: { on: { TOGGLE_BOLD: 'off' } }
80+
}
81+
},
82+
italic: {
83+
initial: 'off',
84+
states: {
85+
off: { on: { TOGGLE_ITALIC: 'on' } },
86+
on: { on: { TOGGLE_ITALIC: 'off' } }
87+
}
88+
}
89+
}
90+
});
91+
```
92+
93+
## When to Use
94+
95+
- Complex flows with nested/parallel states
96+
- Need visual state editor (Stately)
97+
- Actor model for spawning child machines
98+
- Large teams benefiting from formal statecharts

0 commit comments

Comments
 (0)