svelte-p5 0.2.2 → 0.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/README.md CHANGED
@@ -8,6 +8,14 @@ Svelte 5 bindings for p5.js. This package is the primitive layer: a canvas wrapp
8
8
  pnpm add svelte-p5 p5
9
9
  ```
10
10
 
11
+ To test a change that's merged on `main` but not yet released — or any specific commit — install the preview build from [pkg.pr.new](https://pkg.pr.new/):
12
+
13
+ ```bash
14
+ pnpm add https://pkg.pr.new/edw1nzhao/svelte-p5/svelte-p5@main
15
+ # or pin an exact SHA:
16
+ pnpm add https://pkg.pr.new/edw1nzhao/svelte-p5/svelte-p5@<commit-sha>
17
+ ```
18
+
11
19
  ## `<P5Canvas>`
12
20
 
13
21
  Mounts a p5 sketch in instance mode, cleans it up on unmount.
@@ -91,11 +99,42 @@ What each does and when to reach for it lives in the [performance recipe](../../
91
99
  ## Type exports
92
100
 
93
101
  ```ts
94
- import type { SketchFn, P5CanvasProps, P5Bridge } from 'svelte-p5';
102
+ import type { SketchFn, ExtendedP5, P5CanvasProps, P5Bridge } from 'svelte-p5';
95
103
  ```
96
104
 
97
105
  `SketchFn` is `(p: p5) => void`. `SketchFn`, not `Sketch`, because the components package exports a `<Sketch>` component and the collision would force aliased imports.
98
106
 
107
+ ### Typed instance extensions
108
+
109
+ Sketches that install custom members on the instance (`p.myHelper = ...`) can type
110
+ them instead of casting. `SketchFn<Ext>` types the instance inside the sketch as
111
+ `p5 & Ext`, and `ExtendedP5<Ext>` names that same intersection for stores and
112
+ variables that hold the bound instance:
113
+
114
+ ```ts
115
+ interface AppSketchExt {
116
+ resetView(): void;
117
+ zoomLevel: number;
118
+ }
119
+ type AppP5 = ExtendedP5<AppSketchExt>;
120
+
121
+ const sketch: SketchFn<AppSketchExt> = (p) => {
122
+ p.resetView = () => {
123
+ /* ... */
124
+ };
125
+ p.draw = () => {
126
+ /* p.zoomLevel is typed */
127
+ };
128
+ };
129
+
130
+ let instance: AppP5 | null = $state(null);
131
+ ```
132
+
133
+ ```svelte
134
+ <P5Canvas {sketch} bind:instance />
135
+ <!-- instance is inferred as AppP5 | null; instance?.resetView() type-checks -->
136
+ ```
137
+
99
138
  ## License
100
139
 
101
140
  MIT
@@ -1,7 +1,7 @@
1
- <script lang="ts">
1
+ <script lang="ts" generics="Ext = unknown">
2
2
  import { onMount } from 'svelte';
3
3
  import type p5Type from 'p5';
4
- import type { P5CanvasProps } from './types.js';
4
+ import type { ExtendedP5, P5CanvasProps } from './types.js';
5
5
 
6
6
  let {
7
7
  sketch,
@@ -9,7 +9,7 @@
9
9
  class: className = '',
10
10
  style = 'display: block; width: 100%; height: 100%;',
11
11
  onReady
12
- }: P5CanvasProps = $props();
12
+ }: P5CanvasProps<Ext> = $props();
13
13
 
14
14
  let container: HTMLDivElement | null = null;
15
15
 
@@ -24,9 +24,12 @@
24
24
  if (cancelled || !container) return;
25
25
 
26
26
  const p5Ctor = mod.default;
27
- local = new p5Ctor((p: p5Type) => sketch(p), container);
28
- instance = local;
29
- onReady?.(local);
27
+ // The sketch installs its Ext members during construction (p5 calls the
28
+ // sketch function synchronously inside the constructor), so by the time
29
+ // the instance is observable it is already the extended type.
30
+ local = new p5Ctor((p: p5Type) => sketch(p as ExtendedP5<Ext>), container);
31
+ instance = local as ExtendedP5<Ext>;
32
+ onReady?.(local as ExtendedP5<Ext>);
30
33
  })();
31
34
 
32
35
  return () => {
@@ -1,4 +1,25 @@
1
1
  import type { P5CanvasProps } from './types.js';
2
- declare const P5Canvas: import("svelte").Component<P5CanvasProps, {}, "instance">;
3
- type P5Canvas = ReturnType<typeof P5Canvas>;
2
+ declare function $$render<Ext = unknown>(): {
3
+ props: P5CanvasProps<Ext>;
4
+ exports: {};
5
+ bindings: "instance";
6
+ slots: {};
7
+ events: {};
8
+ };
9
+ declare class __sveltets_Render<Ext = unknown> {
10
+ props(): ReturnType<typeof $$render<Ext>>['props'];
11
+ events(): ReturnType<typeof $$render<Ext>>['events'];
12
+ slots(): ReturnType<typeof $$render<Ext>>['slots'];
13
+ bindings(): "instance";
14
+ exports(): {};
15
+ }
16
+ interface $$IsomorphicComponent {
17
+ new <Ext = unknown>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Ext>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Ext>['props']>, ReturnType<__sveltets_Render<Ext>['events']>, ReturnType<__sveltets_Render<Ext>['slots']>> & {
18
+ $$bindings?: ReturnType<__sveltets_Render<Ext>['bindings']>;
19
+ } & ReturnType<__sveltets_Render<Ext>['exports']>;
20
+ <Ext = unknown>(internal: unknown, props: ReturnType<__sveltets_Render<Ext>['props']> & {}): ReturnType<__sveltets_Render<Ext>['exports']>;
21
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
22
+ }
23
+ declare const P5Canvas: $$IsomorphicComponent;
24
+ type P5Canvas<Ext = unknown> = InstanceType<typeof P5Canvas<Ext>>;
4
25
  export default P5Canvas;
@@ -2,7 +2,7 @@
2
2
  * Bridge reactive Svelte state into a p5 sketch closure.
3
3
  *
4
4
  * Mutations from either side are seen by the other. Because p5's draw loop
5
- * reads fields on every frame, you don't need effects or subscriptions
5
+ * reads fields on every frame, you don't need effects or subscriptions -
6
6
  * just read `bridge.state.foo` inside your sketch and write to it from
7
7
  * Svelte UI.
8
8
  *
@@ -2,7 +2,7 @@
2
2
  * Bridge reactive Svelte state into a p5 sketch closure.
3
3
  *
4
4
  * Mutations from either side are seen by the other. Because p5's draw loop
5
- * reads fields on every frame, you don't need effects or subscriptions
5
+ * reads fields on every frame, you don't need effects or subscriptions -
6
6
  * just read `bridge.state.foo` inside your sketch and write to it from
7
7
  * Svelte UI.
8
8
  *
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { default as P5Canvas } from './P5Canvas.svelte';
2
2
  export { createP5Bridge, type P5Bridge } from './createP5Bridge.svelte.js';
3
- export type { SketchFn, P5CanvasProps } from './types.js';
3
+ export type { SketchFn, ExtendedP5, P5CanvasProps } from './types.js';
4
4
  export * from './utils/index.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { default as P5Canvas } from './P5Canvas.svelte';
2
2
  export { createP5Bridge } from './createP5Bridge.svelte.js';
3
- // Re-export utils from the main entry for convenience users doing
3
+ // Re-export utils from the main entry for convenience - users doing
4
4
  // `import { disableFES } from 'svelte-p5'` get the same
5
5
  // helpers they'd get from `.../utils`. Tree-shakeable via `sideEffects: false`.
6
6
  export * from './utils/index.js';
package/dist/types.d.ts CHANGED
@@ -2,20 +2,41 @@ import type p5 from 'p5';
2
2
  /**
3
3
  * A p5 sketch function in instance mode. Called with the p5 instance;
4
4
  * the function should assign `p.setup`, `p.draw`, etc. on it.
5
+ *
6
+ * `Ext` types the members your sketch installs onto the instance
7
+ * (`p.myHelper = ...`). Inside the sketch the instance is typed as
8
+ * already-extended, because the sketch itself is what installs those
9
+ * members before anyone else can observe them:
10
+ *
11
+ * ```ts
12
+ * interface MyExt {
13
+ * resetView(): void;
14
+ * }
15
+ * const sketch: SketchFn<MyExt> = (p) => {
16
+ * p.resetView = () => { ... };
17
+ * p.draw = () => { ... };
18
+ * };
19
+ * ```
5
20
  */
6
- export type SketchFn = (p: p5) => void;
21
+ export type SketchFn<Ext = unknown> = (p: p5 & Ext) => void;
22
+ /**
23
+ * A p5 instance decorated with app-specific members (see {@link SketchFn}).
24
+ * Useful for typing stores or variables that hold the bound instance:
25
+ * `let instance: ExtendedP5<MyExt> | null`.
26
+ */
27
+ export type ExtendedP5<Ext> = p5 & Ext;
7
28
  /**
8
29
  * Props accepted by the `<P5Canvas>` component.
9
30
  */
10
- export interface P5CanvasProps {
11
- /** Your sketch function assigns p.setup, p.draw, etc. */
12
- sketch: SketchFn;
31
+ export interface P5CanvasProps<Ext = unknown> {
32
+ /** Your sketch function - assigns p.setup, p.draw, etc. */
33
+ sketch: SketchFn<Ext>;
13
34
  /** Bindable: the p5 instance, available after mount. `null` before mount or after unmount. */
14
- instance?: p5 | null;
35
+ instance?: ExtendedP5<Ext> | null;
15
36
  /** Optional class applied to the container div. */
16
37
  class?: string;
17
38
  /** Optional inline style on the container div. Defaults to `display: block; width: 100%; height: 100%;`. */
18
39
  style?: string;
19
40
  /** Called once, synchronously, when the p5 instance has been created and the sketch function has returned. */
20
- onReady?: (instance: p5) => void;
41
+ onReady?: (instance: ExtendedP5<Ext>) => void;
21
42
  }
@@ -2,8 +2,8 @@
2
2
  * Lazy cache for CSS color strings.
3
3
  *
4
4
  * p5.color() allocates a p5.Color instance on every call (object + typed
5
- * arrays + regex parse). Calling it in a draw loop e.g. to colorize per
6
- * speaker or per data-point leaks a lot of garbage. Use a ColorCache to
5
+ * arrays + regex parse). Calling it in a draw loop - e.g. to colorize per
6
+ * speaker or per data-point - leaks a lot of garbage. Use a ColorCache to
7
7
  * compute each color once and reuse the CSS string.
8
8
  *
9
9
  * @example
@@ -19,11 +19,11 @@ export interface FontAtlas {
19
19
  * Pre-render a fixed set of strings to an offscreen buffer **once**, then
20
20
  * composite them each frame with `p5.image()`. This bypasses the OpenType.js
21
21
  * path-rendering slow path that fires for every `p5.text()` call when you've
22
- * `loadFont()`'d an OTF/TTF file typically a 10-100× speedup for text-heavy
22
+ * `loadFont()`'d an OTF/TTF file - typically a 10-100× speedup for text-heavy
23
23
  * sketches (word clouds, axis labels, small-multiples titles).
24
24
  *
25
25
  * Use for a fixed vocabulary (speaker names, category labels, axis ticks).
26
- * Don't use for per-frame dynamic text that defeats the point.
26
+ * Don't use for per-frame dynamic text - that defeats the point.
27
27
  *
28
28
  * @see https://github.com/processing/p5.js/blob/main/src/core/p5.Renderer2D.js
29
29
  */
@@ -2,11 +2,11 @@
2
2
  * Pre-render a fixed set of strings to an offscreen buffer **once**, then
3
3
  * composite them each frame with `p5.image()`. This bypasses the OpenType.js
4
4
  * path-rendering slow path that fires for every `p5.text()` call when you've
5
- * `loadFont()`'d an OTF/TTF file typically a 10-100× speedup for text-heavy
5
+ * `loadFont()`'d an OTF/TTF file - typically a 10-100× speedup for text-heavy
6
6
  * sketches (word clouds, axis labels, small-multiples titles).
7
7
  *
8
8
  * Use for a fixed vocabulary (speaker names, category labels, axis ticks).
9
- * Don't use for per-frame dynamic text that defeats the point.
9
+ * Don't use for per-frame dynamic text - that defeats the point.
10
10
  *
11
11
  * @see https://github.com/processing/p5.js/blob/main/src/core/p5.Renderer2D.js
12
12
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-p5",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "description": "Svelte 5 + p5.js integration primitives — <P5Canvas> wrapper, rune bridge, and performance utilities",
5
5
  "keywords": [
6
6
  "svelte",
@@ -44,6 +44,9 @@
44
44
  "README.md",
45
45
  "LICENSE"
46
46
  ],
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
47
50
  "sideEffects": false,
48
51
  "scripts": {
49
52
  "build": "svelte-package -o dist && publint",