codeforge-dev 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/.env +22 -0
- package/.devcontainer/CHANGELOG.md +197 -0
- package/.devcontainer/CLAUDE.md +117 -0
- package/.devcontainer/README.md +222 -0
- package/.devcontainer/config/main-system-prompt.md +502 -0
- package/.devcontainer/config/settings.json +47 -0
- package/.devcontainer/devcontainer.json +94 -0
- package/.devcontainer/features/README.md +113 -0
- package/.devcontainer/features/agent-browser/README.md +65 -0
- package/.devcontainer/features/agent-browser/devcontainer-feature.json +23 -0
- package/.devcontainer/features/agent-browser/install.sh +79 -0
- package/.devcontainer/features/ast-grep/README.md +24 -0
- package/.devcontainer/features/ast-grep/devcontainer-feature.json +24 -0
- package/.devcontainer/features/ast-grep/install.sh +51 -0
- package/.devcontainer/features/ccstatusline/README.md +296 -0
- package/.devcontainer/features/ccstatusline/devcontainer-feature.json +19 -0
- package/.devcontainer/features/ccstatusline/install.sh +290 -0
- package/.devcontainer/features/ccusage/README.md +205 -0
- package/.devcontainer/features/ccusage/devcontainer-feature.json +38 -0
- package/.devcontainer/features/ccusage/install.sh +132 -0
- package/.devcontainer/features/claude-code/README.md +498 -0
- package/.devcontainer/features/claude-code/config/settings.json +36 -0
- package/.devcontainer/features/claude-code/config/system-prompt.md +118 -0
- package/.devcontainer/features/claude-code/config/world-building-sp.md +1432 -0
- package/.devcontainer/features/claude-code/devcontainer-feature.json +42 -0
- package/.devcontainer/features/claude-code/install.sh +466 -0
- package/.devcontainer/features/claude-monitor/README.md +74 -0
- package/.devcontainer/features/claude-monitor/devcontainer-feature.json +38 -0
- package/.devcontainer/features/claude-monitor/install.sh +99 -0
- package/.devcontainer/features/lsp-servers/README.md +85 -0
- package/.devcontainer/features/lsp-servers/devcontainer-feature.json +40 -0
- package/.devcontainer/features/lsp-servers/install.sh +116 -0
- package/.devcontainer/features/mcp-qdrant/CHANGES.md +399 -0
- package/.devcontainer/features/mcp-qdrant/README.md +474 -0
- package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +57 -0
- package/.devcontainer/features/mcp-qdrant/install.sh +295 -0
- package/.devcontainer/features/mcp-qdrant/poststart-hook.sh +129 -0
- package/.devcontainer/features/mcp-reasoner/README.md +177 -0
- package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +20 -0
- package/.devcontainer/features/mcp-reasoner/install.sh +177 -0
- package/.devcontainer/features/mcp-reasoner/poststart-hook.sh +67 -0
- package/.devcontainer/features/notify-hook/README.md +86 -0
- package/.devcontainer/features/notify-hook/devcontainer-feature.json +23 -0
- package/.devcontainer/features/notify-hook/install.sh +38 -0
- package/.devcontainer/features/splitrail/README.md +140 -0
- package/.devcontainer/features/splitrail/devcontainer-feature.json +34 -0
- package/.devcontainer/features/splitrail/install.sh +129 -0
- package/.devcontainer/features/tree-sitter/README.md +138 -0
- package/.devcontainer/features/tree-sitter/devcontainer-feature.json +52 -0
- package/.devcontainer/features/tree-sitter/install.sh +173 -0
- package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +106 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-file.py +101 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +137 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/.claude-plugin/plugin.json +8 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/SKILL.md +387 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/references/cli-flags-and-output.md +312 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/references/sdk-and-mcp.md +569 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/SKILL.md +309 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/references/compose-services.md +438 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/references/dockerfile-patterns.md +340 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/SKILL.md +412 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/references/container-lifecycle.md +388 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/references/resources-and-security.md +444 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/SKILL.md +344 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/middleware-and-lifespan.md +254 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/pydantic-models.md +245 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/routing-and-dependencies.md +255 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/sse-and-streaming.md +318 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/SKILL.md +345 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/references/agents-and-tools.md +271 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/references/models-and-streaming.md +422 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/SKILL.md +220 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/cross-vendor-principles.md +139 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/patterns-and-antipatterns.md +376 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/skill-authoring-patterns.md +356 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/SKILL.md +329 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/advanced-queries.md +314 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/javascript-patterns.md +323 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/python-patterns.md +354 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/schema-and-pragmas.md +326 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/SKILL.md +356 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/ai-sdk-svelte.md +128 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/component-patterns.md +332 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/layercake.md +203 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/migration-guide.md +350 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/runes-and-reactivity.md +328 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/spa-and-routing.md +262 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/svelte-dnd-action.md +181 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/SKILL.md +414 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/references/fastapi-testing.md +411 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/references/svelte-testing.md +538 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +110 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +108 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272create-pr.md +337 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272new.md +166 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272review-commit.md +290 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272work.md +257 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json +8 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/system-prompt.md +184 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/.claude-plugin/plugin.json +6 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/config/planning-instructions.md +14 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/functional-conjuring-map.md +989 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/hooks/hooks.json +33 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/__pycache__/post-enhance-task.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhance-planning.py +71 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-plan.sh +68 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-task.sh +120 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-plan.py +133 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-task.py +253 -0
- package/.devcontainer/scripts/setup-aliases.sh +80 -0
- package/.devcontainer/scripts/setup-config.sh +28 -0
- package/.devcontainer/scripts/setup-irie-claude.sh +32 -0
- package/.devcontainer/scripts/setup-plugins.sh +80 -0
- package/.devcontainer/scripts/setup.sh +58 -0
- package/LICENSE.txt +674 -0
- package/README.md +267 -0
- package/package.json +44 -0
- package/setup.js +83 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# Component Patterns — Deep Dive
|
|
2
|
+
|
|
3
|
+
## 1. Props Patterns
|
|
4
|
+
|
|
5
|
+
### Basic Destructuring with Defaults
|
|
6
|
+
|
|
7
|
+
```svelte
|
|
8
|
+
<script>
|
|
9
|
+
let { title, subtitle = 'Default subtitle', variant = 'primary' } = $props();
|
|
10
|
+
</script>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Missing required props produce a compile-time warning when using TypeScript.
|
|
14
|
+
|
|
15
|
+
### Rest Props
|
|
16
|
+
|
|
17
|
+
Collect remaining props with rest syntax for forwarding to elements:
|
|
18
|
+
|
|
19
|
+
```svelte
|
|
20
|
+
<script>
|
|
21
|
+
let { class: className = '', children, ...rest } = $props();
|
|
22
|
+
</script>
|
|
23
|
+
<div class="wrapper {className}" {...rest}>
|
|
24
|
+
{@render children?.()}
|
|
25
|
+
</div>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Note: `class` is a reserved word in JavaScript — rename it during destructuring.
|
|
29
|
+
|
|
30
|
+
### TypeScript Props
|
|
31
|
+
|
|
32
|
+
Define an interface for type safety and IDE autocompletion:
|
|
33
|
+
|
|
34
|
+
```svelte
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
import type { Snippet } from 'svelte';
|
|
37
|
+
|
|
38
|
+
interface Props {
|
|
39
|
+
title: string;
|
|
40
|
+
count?: number;
|
|
41
|
+
variant?: 'primary' | 'secondary' | 'danger';
|
|
42
|
+
children?: Snippet;
|
|
43
|
+
header?: Snippet<[{ title: string }]>;
|
|
44
|
+
onclick?: (e: MouseEvent) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let { title, count = 0, variant = 'primary', children, header, onclick }: Props = $props();
|
|
48
|
+
</script>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Type snippets using `Snippet` from `svelte`. Parameterized snippets use `Snippet<[ParamType]>`.
|
|
52
|
+
|
|
53
|
+
### $bindable Props
|
|
54
|
+
|
|
55
|
+
Mark a prop as bindable to allow two-way binding from the parent:
|
|
56
|
+
|
|
57
|
+
```svelte
|
|
58
|
+
<!-- Input.svelte -->
|
|
59
|
+
<script lang="ts">
|
|
60
|
+
interface Props {
|
|
61
|
+
value: string;
|
|
62
|
+
placeholder?: string;
|
|
63
|
+
}
|
|
64
|
+
let { value = $bindable(''), placeholder = '' }: Props = $props();
|
|
65
|
+
</script>
|
|
66
|
+
<input bind:value {placeholder} />
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```svelte
|
|
70
|
+
<!-- Parent -->
|
|
71
|
+
<script>
|
|
72
|
+
import Input from './Input.svelte';
|
|
73
|
+
let name = $state('');
|
|
74
|
+
</script>
|
|
75
|
+
<Input bind:value={name} />
|
|
76
|
+
<p>Typed: {name}</p>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Only mark props as `$bindable` when two-way data flow is the intended API. Prefer callback props for explicit data flow.
|
|
80
|
+
|
|
81
|
+
## 2. Snippet Patterns
|
|
82
|
+
|
|
83
|
+
### Basic Children
|
|
84
|
+
|
|
85
|
+
The `children` snippet is the implicit content placed between component tags:
|
|
86
|
+
|
|
87
|
+
```svelte
|
|
88
|
+
<!-- Card.svelte -->
|
|
89
|
+
<script>
|
|
90
|
+
let { children } = $props();
|
|
91
|
+
</script>
|
|
92
|
+
<div class="card">
|
|
93
|
+
{@render children?.()}
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<!-- Usage -->
|
|
97
|
+
<Card>
|
|
98
|
+
<h2>Title</h2>
|
|
99
|
+
<p>Content</p>
|
|
100
|
+
</Card>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use optional chaining (`children?.()`) when the children snippet is optional.
|
|
104
|
+
|
|
105
|
+
### Named Snippets
|
|
106
|
+
|
|
107
|
+
Named snippets replace named slots:
|
|
108
|
+
|
|
109
|
+
```svelte
|
|
110
|
+
<!-- Layout.svelte -->
|
|
111
|
+
<script lang="ts">
|
|
112
|
+
import type { Snippet } from 'svelte';
|
|
113
|
+
interface Props {
|
|
114
|
+
header: Snippet;
|
|
115
|
+
footer?: Snippet;
|
|
116
|
+
children: Snippet;
|
|
117
|
+
}
|
|
118
|
+
let { header, footer, children }: Props = $props();
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<header>{@render header()}</header>
|
|
122
|
+
<main>{@render children()}</main>
|
|
123
|
+
{#if footer}
|
|
124
|
+
<footer>{@render footer()}</footer>
|
|
125
|
+
{/if}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```svelte
|
|
129
|
+
<!-- Usage -->
|
|
130
|
+
<Layout>
|
|
131
|
+
{#snippet header()}
|
|
132
|
+
<h1>Page Title</h1>
|
|
133
|
+
{/snippet}
|
|
134
|
+
|
|
135
|
+
<p>Main content as children</p>
|
|
136
|
+
|
|
137
|
+
{#snippet footer()}
|
|
138
|
+
<small>Footer text</small>
|
|
139
|
+
{/snippet}
|
|
140
|
+
</Layout>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Parameterized Snippets
|
|
144
|
+
|
|
145
|
+
Pass data back to the parent through snippet parameters:
|
|
146
|
+
|
|
147
|
+
```svelte
|
|
148
|
+
<!-- DataTable.svelte -->
|
|
149
|
+
<script lang="ts">
|
|
150
|
+
import type { Snippet } from 'svelte';
|
|
151
|
+
|
|
152
|
+
interface Props<T> {
|
|
153
|
+
items: T[];
|
|
154
|
+
row: Snippet<[T, number]>;
|
|
155
|
+
empty?: Snippet;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let { items, row, empty }: Props<any> = $props();
|
|
159
|
+
</script>
|
|
160
|
+
|
|
161
|
+
{#if items.length === 0}
|
|
162
|
+
{@render empty?.()}
|
|
163
|
+
{:else}
|
|
164
|
+
{#each items as item, index}
|
|
165
|
+
{@render row(item, index)}
|
|
166
|
+
{/each}
|
|
167
|
+
{/if}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```svelte
|
|
171
|
+
<!-- Usage -->
|
|
172
|
+
<DataTable {items}>
|
|
173
|
+
{#snippet row(item, index)}
|
|
174
|
+
<tr>
|
|
175
|
+
<td>{index + 1}</td>
|
|
176
|
+
<td>{item.name}</td>
|
|
177
|
+
</tr>
|
|
178
|
+
{/snippet}
|
|
179
|
+
{#snippet empty()}
|
|
180
|
+
<p>No items found</p>
|
|
181
|
+
{/snippet}
|
|
182
|
+
</DataTable>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Local Snippets for Reuse
|
|
186
|
+
|
|
187
|
+
Define snippets locally within a component for repeated template blocks:
|
|
188
|
+
|
|
189
|
+
```svelte
|
|
190
|
+
{#snippet badge(text, color)}
|
|
191
|
+
<span class="badge" style:background={color}>{text}</span>
|
|
192
|
+
{/snippet}
|
|
193
|
+
|
|
194
|
+
{@render badge('New', 'green')}
|
|
195
|
+
{@render badge('Sale', 'red')}
|
|
196
|
+
{@render badge('Featured', 'blue')}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## 3. Event Patterns
|
|
200
|
+
|
|
201
|
+
### DOM Events
|
|
202
|
+
|
|
203
|
+
Svelte 5 uses standard HTML event attributes — no special directive syntax:
|
|
204
|
+
|
|
205
|
+
```svelte
|
|
206
|
+
<button onclick={handleClick}>Click</button>
|
|
207
|
+
<input oninput={(e) => (query = e.currentTarget.value)} />
|
|
208
|
+
<form onsubmit|preventDefault={handleSubmit}>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Note: Event modifiers use the pipe syntax inline. For complex modifier chains, handle in the callback instead:
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
function handleSubmit(e) {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
e.stopPropagation();
|
|
217
|
+
// handle submission
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Callback Props (Component Events)
|
|
222
|
+
|
|
223
|
+
Replace `createEventDispatcher` with callback props:
|
|
224
|
+
|
|
225
|
+
```svelte
|
|
226
|
+
<!-- Dialog.svelte -->
|
|
227
|
+
<script lang="ts">
|
|
228
|
+
interface Props {
|
|
229
|
+
title: string;
|
|
230
|
+
onclose?: () => void;
|
|
231
|
+
onconfirm?: (result: string) => void;
|
|
232
|
+
}
|
|
233
|
+
let { title, onclose, onconfirm }: Props = $props();
|
|
234
|
+
</script>
|
|
235
|
+
|
|
236
|
+
<dialog open>
|
|
237
|
+
<h2>{title}</h2>
|
|
238
|
+
<button onclick={() => onconfirm?.('yes')}>Confirm</button>
|
|
239
|
+
<button onclick={() => onclose?.()}>Cancel</button>
|
|
240
|
+
</dialog>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
```svelte
|
|
244
|
+
<!-- Parent -->
|
|
245
|
+
<Dialog
|
|
246
|
+
title="Confirm Action"
|
|
247
|
+
onclose={() => (showDialog = false)}
|
|
248
|
+
onconfirm={(result) => handleResult(result)}
|
|
249
|
+
/>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Event Forwarding
|
|
253
|
+
|
|
254
|
+
Forward DOM events from inner elements to the component's consumer by accepting and spreading handler props:
|
|
255
|
+
|
|
256
|
+
```svelte
|
|
257
|
+
<!-- Button.svelte -->
|
|
258
|
+
<script>
|
|
259
|
+
let { children, ...rest } = $props();
|
|
260
|
+
</script>
|
|
261
|
+
<button {...rest}>
|
|
262
|
+
{@render children?.()}
|
|
263
|
+
</button>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
All `on*` attributes passed to `<Button>` forward to the underlying `<button>` element.
|
|
267
|
+
|
|
268
|
+
## 4. Composition Patterns
|
|
269
|
+
|
|
270
|
+
### Wrapper Components
|
|
271
|
+
|
|
272
|
+
Create components that enhance a base element while preserving its full API:
|
|
273
|
+
|
|
274
|
+
```svelte
|
|
275
|
+
<!-- Tooltip.svelte -->
|
|
276
|
+
<script>
|
|
277
|
+
let { text, children, ...rest } = $props();
|
|
278
|
+
</script>
|
|
279
|
+
<div class="tooltip-wrapper" {...rest}>
|
|
280
|
+
{@render children?.()}
|
|
281
|
+
<div class="tooltip-text">{text}</div>
|
|
282
|
+
</div>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Dynamic Components
|
|
286
|
+
|
|
287
|
+
Svelte 5 renders components directly from variables — `<svelte:component>` is no longer necessary:
|
|
288
|
+
|
|
289
|
+
```svelte
|
|
290
|
+
<script>
|
|
291
|
+
import Home from './Home.svelte';
|
|
292
|
+
import About from './About.svelte';
|
|
293
|
+
|
|
294
|
+
const routes = { home: Home, about: About };
|
|
295
|
+
let current = $state('home');
|
|
296
|
+
</script>
|
|
297
|
+
|
|
298
|
+
{@const Page = routes[current]}
|
|
299
|
+
<Page />
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Generic Components with TypeScript
|
|
303
|
+
|
|
304
|
+
Use generics for type-safe reusable components:
|
|
305
|
+
|
|
306
|
+
```svelte
|
|
307
|
+
<script lang="ts" generics="T">
|
|
308
|
+
import type { Snippet } from 'svelte';
|
|
309
|
+
|
|
310
|
+
interface Props {
|
|
311
|
+
items: T[];
|
|
312
|
+
selected?: T;
|
|
313
|
+
row: Snippet<[T]>;
|
|
314
|
+
onselect?: (item: T) => void;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
let { items, selected, row, onselect }: Props = $props();
|
|
318
|
+
</script>
|
|
319
|
+
|
|
320
|
+
<ul>
|
|
321
|
+
{#each items as item}
|
|
322
|
+
<li
|
|
323
|
+
class:selected={item === selected}
|
|
324
|
+
onclick={() => onselect?.(item)}
|
|
325
|
+
>
|
|
326
|
+
{@render row(item)}
|
|
327
|
+
</li>
|
|
328
|
+
{/each}
|
|
329
|
+
</ul>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The `generics` attribute on `<script>` declares type parameters available throughout the component.
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# LayerCake — Reference
|
|
2
|
+
|
|
3
|
+
## Mental Model
|
|
4
|
+
|
|
5
|
+
LayerCake is a headless graphics framework, not a chart library. It provides a coordinate system — reactive scales that map data values to pixel positions — and layout containers for SVG, HTML, Canvas, and WebGL. The developer authors individual layer components (axes, lines, points, labels) that read scale functions from context. This separation means LayerCake handles the math while the developer controls every visual detail.
|
|
6
|
+
|
|
7
|
+
LayerCake does not ship pre-built chart types. Instead, it provides the plumbing: scale computation, responsive dimension tracking, data transformation helpers, and a layered rendering model that composites multiple technologies in a single chart area.
|
|
8
|
+
|
|
9
|
+
> Official guide, API reference, and component gallery: <https://layercake.graphics>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Core API
|
|
14
|
+
|
|
15
|
+
### LayerCake Component
|
|
16
|
+
|
|
17
|
+
The `<LayerCake>` component is the root container. It computes scales from data and exposes them to all descendant layer components through Svelte context.
|
|
18
|
+
|
|
19
|
+
```svelte
|
|
20
|
+
<script>
|
|
21
|
+
import { LayerCake, Svg } from 'layercake';
|
|
22
|
+
import Line from './Line.svelte';
|
|
23
|
+
|
|
24
|
+
let data = [
|
|
25
|
+
{ x: 0, y: 10 },
|
|
26
|
+
{ x: 1, y: 35 },
|
|
27
|
+
{ x: 2, y: 20 }
|
|
28
|
+
];
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<div class="chart-container" style="width: 100%; height: 300px;">
|
|
32
|
+
<LayerCake
|
|
33
|
+
{data}
|
|
34
|
+
x="x"
|
|
35
|
+
y="y"
|
|
36
|
+
>
|
|
37
|
+
<Svg>
|
|
38
|
+
<Line />
|
|
39
|
+
</Svg>
|
|
40
|
+
</LayerCake>
|
|
41
|
+
</div>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
LayerCake requires a positioned container with explicit dimensions. The component measures this container and recomputes scales on resize.
|
|
45
|
+
|
|
46
|
+
### Key Props
|
|
47
|
+
|
|
48
|
+
| Prop | Type | Description |
|
|
49
|
+
|------|------|-------------|
|
|
50
|
+
| `data` | array | The dataset. Each element is one data point |
|
|
51
|
+
| `x` | string / accessor | Key or function to extract the x value from each data point |
|
|
52
|
+
| `y` | string / accessor | Key or function to extract the y value |
|
|
53
|
+
| `z` | string / accessor | Optional third dimension (color, radius, category) |
|
|
54
|
+
| `r` | string / accessor | Optional radius/size dimension |
|
|
55
|
+
| `xScale` | d3 scale | Scale constructor for x axis (default: `scaleLinear`) |
|
|
56
|
+
| `yScale` | d3 scale | Scale constructor for y axis (default: `scaleLinear`) |
|
|
57
|
+
| `xDomain` | [min, max] | Override computed x domain |
|
|
58
|
+
| `yDomain` | [min, max] | Override computed y domain |
|
|
59
|
+
| `xRange` | [min, max] | Override x range (defaults to `[0, width]`) |
|
|
60
|
+
| `yRange` | [min, max] | Override y range (defaults to `[height, 0]` for SVG convention) |
|
|
61
|
+
| `padding` | object | `{ top, right, bottom, left }` in pixels |
|
|
62
|
+
| `xNice` | boolean | Round domain extents to nice values |
|
|
63
|
+
| `yNice` | boolean | Round domain extents to nice values |
|
|
64
|
+
| `flatData` | array | Pre-flattened data for domain calculation on nested/grouped datasets |
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Layout Components
|
|
69
|
+
|
|
70
|
+
Layout components create rendering contexts. Nest them inside `<LayerCake>` to composite multiple technologies:
|
|
71
|
+
|
|
72
|
+
| Component | Renders Into | Use Case |
|
|
73
|
+
|-----------|-------------|----------|
|
|
74
|
+
| `<Svg>` | `<svg>` element | Lines, areas, axes, annotations |
|
|
75
|
+
| `<Html>` | `<div>` overlay | Tooltips, labels, legends |
|
|
76
|
+
| `<Canvas>` | `<canvas>` element | Large point clouds, heatmaps, performance-critical rendering |
|
|
77
|
+
| `<WebGl>` | WebGL `<canvas>` | GPU-accelerated rendering for very large datasets |
|
|
78
|
+
| `<ScaledSvg>` | `<svg>` with viewBox | Resolution-independent SVG (PDF export, fixed coordinate space) |
|
|
79
|
+
|
|
80
|
+
Each layout component occupies the full chart area (respecting padding). Layers stack visually — SVG below, Canvas in the middle, HTML on top is a common arrangement.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Context Values
|
|
85
|
+
|
|
86
|
+
Layer components (axes, lines, points) access chart context using `getContext('LayerCake')`. The context is a Svelte store containing:
|
|
87
|
+
|
|
88
|
+
### Dimensions
|
|
89
|
+
- `width`, `height` — inner dimensions (after padding)
|
|
90
|
+
- `containerWidth`, `containerHeight` — outer dimensions
|
|
91
|
+
|
|
92
|
+
### Scales
|
|
93
|
+
- `xScale`, `yScale`, `zScale`, `rScale` — configured d3 scale functions
|
|
94
|
+
|
|
95
|
+
### Accessors
|
|
96
|
+
- `x`, `y`, `z`, `r` — accessor functions that extract values from data points
|
|
97
|
+
|
|
98
|
+
### Convenience Getters
|
|
99
|
+
- `xGet(d)` — shorthand for `xScale(x(d))`, maps a data point to a pixel x position
|
|
100
|
+
- `yGet(d)` — shorthand for `yScale(y(d))`, maps to pixel y position
|
|
101
|
+
- `zGet(d)`, `rGet(d)` — same pattern for z and r dimensions
|
|
102
|
+
|
|
103
|
+
### Data
|
|
104
|
+
- `data` — the dataset passed to LayerCake
|
|
105
|
+
- `flatData` — flattened version for domain calculation
|
|
106
|
+
|
|
107
|
+
```svelte
|
|
108
|
+
<!-- Line.svelte (layer component) -->
|
|
109
|
+
<script>
|
|
110
|
+
import { getContext } from 'svelte';
|
|
111
|
+
|
|
112
|
+
const { data, xGet, yGet } = getContext('LayerCake');
|
|
113
|
+
|
|
114
|
+
// Build an SVG path from the data
|
|
115
|
+
$: path = 'M' + $data.map(d => `${$xGet(d)},${$yGet(d)}`).join('L');
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<path d={path} fill="none" stroke="steelblue" stroke-width="2" />
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
> Context values are Svelte stores. Access them with `$` prefix in layer components: `$data`, `$xGet`, `$yGet`, `$width`, etc.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Helper Functions
|
|
126
|
+
|
|
127
|
+
| Function | Purpose |
|
|
128
|
+
|----------|---------|
|
|
129
|
+
| `calcExtents(data, keys)` | Calculate min/max extents for specified dimensions |
|
|
130
|
+
| `stack(data, keys)` | D3-compatible stack layout for area/bar charts |
|
|
131
|
+
| `groupLonger(data, keys)` | Pivot wide data to long format for multi-series charts |
|
|
132
|
+
| `scaleCanvas(canvas, ctx)` | Apply device pixel ratio scaling to a canvas context |
|
|
133
|
+
| `bin(data, accessor, options)` | Create histogram bins from continuous data |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Runes Incompatibility
|
|
138
|
+
|
|
139
|
+
**LayerCake uses `export let` and Svelte stores internally.** This makes the library incompatible with Svelte 5's global runes mode.
|
|
140
|
+
|
|
141
|
+
### The Problem
|
|
142
|
+
|
|
143
|
+
Enabling `runes: true` globally in `svelte.config.js` forces all components — including those inside `node_modules` — to use runes syntax. LayerCake's internal components use `export let` for props and `$:` reactive declarations, which are invalid in runes mode. The compiler will emit errors when processing LayerCake's source.
|
|
144
|
+
|
|
145
|
+
### Workarounds
|
|
146
|
+
|
|
147
|
+
**Option A (recommended):** Do not enable global runes mode. Svelte 5 components can use runes without any config flag — runes are auto-detected per file. Global `runes: true` is unnecessary for application code and breaks libraries that have not migrated.
|
|
148
|
+
|
|
149
|
+
**Option B:** If the project requires global runes mode for other reasons, mark individual LayerCake wrapper components with per-component legacy mode:
|
|
150
|
+
|
|
151
|
+
```svelte
|
|
152
|
+
<svelte:options runes={false} />
|
|
153
|
+
<!-- This component can use export let and $: safely -->
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
This workaround applies only to the wrapper component that imports LayerCake. Child layer components authored by the developer can still use runes for their own logic — they access context stores with the `$` prefix as shown above.
|
|
157
|
+
|
|
158
|
+
### Migration Status
|
|
159
|
+
|
|
160
|
+
LayerCake's runes migration is tracked in GitHub issue [#156](https://github.com/mhkeller/layercake/issues/156). Until the migration lands, treat LayerCake components as legacy-mode dependencies.
|
|
161
|
+
|
|
162
|
+
### Layer Components and Runes
|
|
163
|
+
|
|
164
|
+
User-authored layer components (the components nested inside `<Svg>`, `<Html>`, etc.) can use runes for their own local state and derived values. The constraint applies only to LayerCake's own components (`<LayerCake>`, `<Svg>`, `<Html>`, `<Canvas>`, etc.) which live in `node_modules`.
|
|
165
|
+
|
|
166
|
+
However, context values from LayerCake are Svelte stores, not rune-based signals. Layer components must use the `$` store subscription syntax (`$data`, `$xGet`) regardless of whether they use runes for other state.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Multi-Layer Composition
|
|
171
|
+
|
|
172
|
+
A common pattern layers SVG (for axes and lines), Canvas (for dense point rendering), and HTML (for interactive tooltips):
|
|
173
|
+
|
|
174
|
+
```svelte
|
|
175
|
+
<script>
|
|
176
|
+
import { LayerCake, Svg, Canvas, Html } from 'layercake';
|
|
177
|
+
import AxisX from './AxisX.svelte';
|
|
178
|
+
import AxisY from './AxisY.svelte';
|
|
179
|
+
import Points from './Points.canvas.svelte';
|
|
180
|
+
import Tooltip from './Tooltip.svelte';
|
|
181
|
+
|
|
182
|
+
let data = [...];
|
|
183
|
+
</script>
|
|
184
|
+
|
|
185
|
+
<div class="chart-container" style="width: 100%; height: 400px;">
|
|
186
|
+
<LayerCake {data} x="date" y="value">
|
|
187
|
+
<Svg>
|
|
188
|
+
<AxisX />
|
|
189
|
+
<AxisY />
|
|
190
|
+
</Svg>
|
|
191
|
+
<Canvas>
|
|
192
|
+
<Points />
|
|
193
|
+
</Canvas>
|
|
194
|
+
<Html>
|
|
195
|
+
<Tooltip />
|
|
196
|
+
</Html>
|
|
197
|
+
</LayerCake>
|
|
198
|
+
</div>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Each layout component creates its own rendering surface at the same dimensions. Layer components render independently — the SVG axes do not interfere with the Canvas points, and the HTML tooltip floats above both.
|
|
202
|
+
|
|
203
|
+
Canvas layer components receive the canvas context through `getContext('canvas')` and render in the `onMount` or `$effect` lifecycle. Call `scaleCanvas` to handle high-DPI displays.
|