svelte-p5-viz 0.2.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/LICENSE +21 -0
- package/README.md +132 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/registry.d.ts +37 -0
- package/dist/registry.js +38 -0
- package/dist/scene-state.svelte.d.ts +37 -0
- package/dist/scene-state.svelte.js +25 -0
- package/dist/scene.d.ts +67 -0
- package/dist/scene.js +1 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.js +1 -0
- package/package.json +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Edwin Zhao
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# svelte-p5-viz
|
|
2
|
+
|
|
3
|
+
> Visualization panel contract, registry, and scene-config types for [`svelte-p5`](../core). The load-bearing abstraction for composing canvas-based dashboards — by hand, or eventually through a no-code studio.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm add svelte-p5-viz svelte-p5 p5
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## What this is
|
|
10
|
+
|
|
11
|
+
A set of TypeScript types and tiny runtime helpers that let multiple visualizations share one canvas, coordinate hover state, and serialize to JSON. It is deliberately thin: no actual visualization code ships in this package. Visualizations live in your app (or eventually in a separate viz-pack) and implement the contract below.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import type { VizPanel } from 'svelte-p5-viz';
|
|
15
|
+
|
|
16
|
+
interface WordCloudData {
|
|
17
|
+
words: { text: string; weight: number }[];
|
|
18
|
+
}
|
|
19
|
+
interface WordCloudConfig {
|
|
20
|
+
minFontSize: number;
|
|
21
|
+
maxFontSize: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const WordCloudPanel: VizPanel<WordCloudData, WordCloudConfig> = {
|
|
25
|
+
type: 'word-cloud',
|
|
26
|
+
defaultConfig: { minFontSize: 10, maxFontSize: 48 },
|
|
27
|
+
render(ctx, data, config) {
|
|
28
|
+
const { sk, bounds, mouse, highlights } = ctx;
|
|
29
|
+
// …draw into sk within bounds…
|
|
30
|
+
return { hover: null };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A scene mounts N panels into a canvas, reads hover from each, surfaces tooltips and cross-highlighting through shared chrome. Because panels share a contract, chrome doesn't care which panel type is in any given slot.
|
|
36
|
+
|
|
37
|
+
## The contract
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
41
|
+
│ Scene runner (your app, or a future studio runtime) │
|
|
42
|
+
│ ──────────── │
|
|
43
|
+
│ Reads SceneConfig → resolves types via PanelRegistry → │
|
|
44
|
+
│ renders each panel into its Bounds → funnels hover into │
|
|
45
|
+
│ a shared SceneState → exposes hover to other panels via │
|
|
46
|
+
│ PanelContext.highlights │
|
|
47
|
+
└──────────────┬──────────────────────────────────────────────┘
|
|
48
|
+
│ VizPanel.render(ctx, data, config)
|
|
49
|
+
▼
|
|
50
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
51
|
+
│ Your panel implementations │
|
|
52
|
+
│ Pure functions of (data, config, ctx) → PanelResult │
|
|
53
|
+
└─────────────────────────────────────────────────────────────┘
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The four types that matter:
|
|
57
|
+
|
|
58
|
+
- **`VizPanel<TData, TConfig>`** — the renderer. `type`, `defaultConfig`, and a `render(ctx, data, config)` method.
|
|
59
|
+
- **`PanelContext`** — what the renderer receives: live `sk` instance, `bounds` rect, optional `mouse` position, and `highlights` from sibling panels.
|
|
60
|
+
- **`PanelResult`** — what the renderer returns: at minimum a `hover: PanelHit | null`. Optional `overflow: boolean`.
|
|
61
|
+
- **`PanelHit`** — `{ id, meta? }`. Two panels agreeing on the same `id` shape is how cross-highlighting works — no event bus required.
|
|
62
|
+
|
|
63
|
+
## Scenes
|
|
64
|
+
|
|
65
|
+
A `SceneConfig` is JSON-serializable. Today, you hand-write one and hand it to your app's scene runner. Tomorrow, a studio app will read and write them.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import type { SceneConfig } from 'svelte-p5-viz';
|
|
69
|
+
|
|
70
|
+
const scene: SceneConfig = {
|
|
71
|
+
version: 1,
|
|
72
|
+
name: 'Speaker overview',
|
|
73
|
+
panels: [
|
|
74
|
+
{ id: 'cloud', type: 'word-cloud' },
|
|
75
|
+
{ id: 'timeline', type: 'timeline-track' }
|
|
76
|
+
],
|
|
77
|
+
layout: {
|
|
78
|
+
kind: 'split',
|
|
79
|
+
orientation: 'vertical',
|
|
80
|
+
position: 70,
|
|
81
|
+
first: 'cloud',
|
|
82
|
+
second: 'timeline'
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Layouts cover the three shapes every multi-panel canvas app ends up reinventing:
|
|
88
|
+
|
|
89
|
+
- `{ kind: 'single', panel }` — full-bleed.
|
|
90
|
+
- `{ kind: 'split', orientation, position, first, second }` — two-up with a divider.
|
|
91
|
+
- `{ kind: 'grid', columns, gap, padding, panels }` — N-up grid.
|
|
92
|
+
|
|
93
|
+
That's intentionally sparse. Arbitrary dock trees (tabs, nested splits, floating windows) are out of scope — for those use [`dockview`](https://dockview.dev/) or similar. Chrome for each of the three layouts lives in [`svelte-p5-components`](../components).
|
|
94
|
+
|
|
95
|
+
## Registry
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { createPanelRegistry } from 'svelte-p5-viz';
|
|
99
|
+
|
|
100
|
+
export const registry = createPanelRegistry();
|
|
101
|
+
registry.register(WordCloudPanel);
|
|
102
|
+
registry.register(TimelineTrackPanel);
|
|
103
|
+
// registry.get('word-cloud') → WordCloudPanel
|
|
104
|
+
// registry.list() → all of them
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Use a single registry per scene runner. The runner's job is to look up each `PanelInstance.type`, instantiate it with merged config, and call `render(ctx, data, config)` inside the p5 draw loop.
|
|
108
|
+
|
|
109
|
+
## Shared state
|
|
110
|
+
|
|
111
|
+
Chrome that wants cross-panel hover / highlighting creates one `SceneState` per scene and threads it through `PanelContext`:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { createSceneState } from 'svelte-p5-viz';
|
|
115
|
+
|
|
116
|
+
const scene = createSceneState();
|
|
117
|
+
// scene.hover — current PanelHit across all panels
|
|
118
|
+
// scene.highlights — ids other panels should emphasize
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Scene state is a Svelte 5 `$state` proxy — mutate it directly, Svelte handles reactivity.
|
|
122
|
+
|
|
123
|
+
## What this package deliberately doesn't ship
|
|
124
|
+
|
|
125
|
+
- **Any panel implementations.** Those belong in app code or a future `svelte-p5-viz-pack`.
|
|
126
|
+
- **A scene runner.** One sample runner lives in [`svelte-p5-components`](../components/) as `<SceneRenderer>`; you can also write your own in ~30 lines.
|
|
127
|
+
- **Runtime schema validation.** `VizPanel.schema` is typed as `unknown` so you can plug zod/valibot/arktype (or none). Validation is a studio concern, not a runtime one.
|
|
128
|
+
- **Persistence.** Serializing a `SceneConfig` is `JSON.stringify`; loading is `JSON.parse`. That's the whole API.
|
|
129
|
+
|
|
130
|
+
## Versioning
|
|
131
|
+
|
|
132
|
+
`0.x` is unstable. Breaking changes bump the minor and are called out with `BREAKING CHANGE:` in the commit. The `version` field on `SceneConfig` will bump when the format itself changes, with a documented migration path.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { Bounds, PanelContext, PanelHit, PanelResult, VizPanel } from './types.js';
|
|
2
|
+
export { createPanelRegistry, type PanelRegistry } from './registry.js';
|
|
3
|
+
export type { GridSceneLayout, PanelInstance, SceneConfig, SceneLayout, SingleSceneLayout, SplitSceneLayout } from './scene.js';
|
|
4
|
+
export { createSceneState, type SceneState } from './scene-state.svelte.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { VizPanel } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Typed lookup table from panel `type` strings to panel implementations.
|
|
4
|
+
* A scene runner resolves a {@link PanelInstance} to a live panel through a
|
|
5
|
+
* registry; the studio discovers available panel types by listing one.
|
|
6
|
+
*/
|
|
7
|
+
export interface PanelRegistry {
|
|
8
|
+
/**
|
|
9
|
+
* Register a panel implementation. Throws if the `type` is already taken —
|
|
10
|
+
* duplicate types would be a silent correctness hazard in the no-code studio.
|
|
11
|
+
*/
|
|
12
|
+
register<TData, TConfig>(panel: VizPanel<TData, TConfig>): void;
|
|
13
|
+
/** Look up a panel by its registered type. `undefined` if not registered. */
|
|
14
|
+
get(type: string): VizPanel | undefined;
|
|
15
|
+
/** Whether a panel with this type is registered. */
|
|
16
|
+
has(type: string): boolean;
|
|
17
|
+
/** Snapshot of all registered panels, in insertion order. */
|
|
18
|
+
list(): readonly VizPanel[];
|
|
19
|
+
/** Remove a panel implementation. Returns whether anything was removed. */
|
|
20
|
+
unregister(type: string): boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create an empty panel registry. A scene runner typically creates one at
|
|
24
|
+
* boot, calls {@link PanelRegistry.register} for each panel the app exposes,
|
|
25
|
+
* and hangs onto it for the session.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { createPanelRegistry } from 'svelte-p5-viz';
|
|
30
|
+
* import { WordCloudPanel, TimelineTrackPanel } from './panels';
|
|
31
|
+
*
|
|
32
|
+
* export const registry = createPanelRegistry();
|
|
33
|
+
* registry.register(WordCloudPanel);
|
|
34
|
+
* registry.register(TimelineTrackPanel);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function createPanelRegistry(): PanelRegistry;
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create an empty panel registry. A scene runner typically creates one at
|
|
3
|
+
* boot, calls {@link PanelRegistry.register} for each panel the app exposes,
|
|
4
|
+
* and hangs onto it for the session.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createPanelRegistry } from 'svelte-p5-viz';
|
|
9
|
+
* import { WordCloudPanel, TimelineTrackPanel } from './panels';
|
|
10
|
+
*
|
|
11
|
+
* export const registry = createPanelRegistry();
|
|
12
|
+
* registry.register(WordCloudPanel);
|
|
13
|
+
* registry.register(TimelineTrackPanel);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function createPanelRegistry() {
|
|
17
|
+
const panels = new Map();
|
|
18
|
+
return {
|
|
19
|
+
register(panel) {
|
|
20
|
+
if (panels.has(panel.type)) {
|
|
21
|
+
throw new Error(`[svelte-p5-viz] panel "${panel.type}" is already registered`);
|
|
22
|
+
}
|
|
23
|
+
panels.set(panel.type, panel);
|
|
24
|
+
},
|
|
25
|
+
get(type) {
|
|
26
|
+
return panels.get(type);
|
|
27
|
+
},
|
|
28
|
+
has(type) {
|
|
29
|
+
return panels.has(type);
|
|
30
|
+
},
|
|
31
|
+
list() {
|
|
32
|
+
return [...panels.values()];
|
|
33
|
+
},
|
|
34
|
+
unregister(type) {
|
|
35
|
+
return panels.delete(type);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PanelHit } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reactive chrome-level state shared across all panels in a scene. Chrome
|
|
4
|
+
* wrappers read/write this; panels receive a read view via {@link PanelContext.highlights}.
|
|
5
|
+
*
|
|
6
|
+
* The ergonomics match Svelte 5's rune model — the returned object is a
|
|
7
|
+
* `$state` proxy; mutate it directly from chrome code.
|
|
8
|
+
*/
|
|
9
|
+
export interface SceneState {
|
|
10
|
+
/** The most-recently-reported hit across all panels this frame. */
|
|
11
|
+
hover: PanelHit | null;
|
|
12
|
+
/**
|
|
13
|
+
* Ids other panels should highlight. Populated by chrome when `hover`
|
|
14
|
+
* changes, cleared when `hover` goes null. Panels read a read-only snapshot
|
|
15
|
+
* via {@link PanelContext.highlights}.
|
|
16
|
+
*/
|
|
17
|
+
highlights: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a fresh reactive scene-state store. Call once per scene instance
|
|
21
|
+
* (typically inside a chrome wrapper's `$effect`).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```svelte
|
|
25
|
+
* <script lang="ts">
|
|
26
|
+
* import { createSceneState } from 'svelte-p5-viz';
|
|
27
|
+
*
|
|
28
|
+
* const scene = createSceneState();
|
|
29
|
+
*
|
|
30
|
+
* function onPanelHit(hit) {
|
|
31
|
+
* scene.hover = hit;
|
|
32
|
+
* scene.highlights = hit ? [hit.id] : [];
|
|
33
|
+
* }
|
|
34
|
+
* </script>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function createSceneState(): SceneState;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a fresh reactive scene-state store. Call once per scene instance
|
|
3
|
+
* (typically inside a chrome wrapper's `$effect`).
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```svelte
|
|
7
|
+
* <script lang="ts">
|
|
8
|
+
* import { createSceneState } from 'svelte-p5-viz';
|
|
9
|
+
*
|
|
10
|
+
* const scene = createSceneState();
|
|
11
|
+
*
|
|
12
|
+
* function onPanelHit(hit) {
|
|
13
|
+
* scene.hover = hit;
|
|
14
|
+
* scene.highlights = hit ? [hit.id] : [];
|
|
15
|
+
* }
|
|
16
|
+
* </script>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function createSceneState() {
|
|
20
|
+
const state = $state({
|
|
21
|
+
hover: null,
|
|
22
|
+
highlights: []
|
|
23
|
+
});
|
|
24
|
+
return state;
|
|
25
|
+
}
|
package/dist/scene.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A panel placed in a scene. The `type` maps to a {@link VizPanel} in the
|
|
3
|
+
* registry; `config` is the per-instance override that gets shallow-merged
|
|
4
|
+
* over the panel's `defaultConfig`.
|
|
5
|
+
*/
|
|
6
|
+
export interface PanelInstance {
|
|
7
|
+
/** Scene-unique id. Layouts reference panels by this. */
|
|
8
|
+
readonly id: string;
|
|
9
|
+
/** Registered panel type (see {@link VizPanel.type}). */
|
|
10
|
+
readonly type: string;
|
|
11
|
+
/** Optional human-readable label for the no-code studio. */
|
|
12
|
+
readonly label?: string;
|
|
13
|
+
/** Per-instance config override. Missing keys fall back to the panel's defaultConfig. */
|
|
14
|
+
readonly config?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Arrange panels in an equal-cell grid. `columns` fixes width; the number of
|
|
18
|
+
* rows is derived from the panel count.
|
|
19
|
+
*/
|
|
20
|
+
export interface GridSceneLayout {
|
|
21
|
+
readonly kind: 'grid';
|
|
22
|
+
/** Number of columns. Must be ≥ 1. */
|
|
23
|
+
readonly columns: number;
|
|
24
|
+
/** CSS gap (px) between cells. Default: 8. */
|
|
25
|
+
readonly gap?: number;
|
|
26
|
+
/** CSS padding (px) inside the canvas. Default: 0. */
|
|
27
|
+
readonly padding?: number;
|
|
28
|
+
/** Panel instance ids in reading order (row-major). */
|
|
29
|
+
readonly panels: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Two-panel split with a draggable divider. Consumers render the divider
|
|
33
|
+
* themselves (see `<SplitPane>` in svelte-p5-components); this type just
|
|
34
|
+
* captures the config.
|
|
35
|
+
*/
|
|
36
|
+
export interface SplitSceneLayout {
|
|
37
|
+
readonly kind: 'split';
|
|
38
|
+
readonly orientation: 'horizontal' | 'vertical';
|
|
39
|
+
/** Divider position as a percentage (0-100) from the top/left. */
|
|
40
|
+
readonly position: number;
|
|
41
|
+
/** Panel instance id in the first slot (top / left). */
|
|
42
|
+
readonly first: string;
|
|
43
|
+
/** Panel instance id in the second slot (bottom / right). */
|
|
44
|
+
readonly second: string;
|
|
45
|
+
}
|
|
46
|
+
/** A single panel filling the entire canvas. */
|
|
47
|
+
export interface SingleSceneLayout {
|
|
48
|
+
readonly kind: 'single';
|
|
49
|
+
readonly panel: string;
|
|
50
|
+
}
|
|
51
|
+
export type SceneLayout = GridSceneLayout | SplitSceneLayout | SingleSceneLayout;
|
|
52
|
+
/**
|
|
53
|
+
* A complete scene description — enough to reconstruct a visualization from
|
|
54
|
+
* JSON on disk, a URL hash, or a no-code studio export.
|
|
55
|
+
*
|
|
56
|
+
* The format is versioned so future non-backward-compatible additions can
|
|
57
|
+
* ship behind a migration step.
|
|
58
|
+
*/
|
|
59
|
+
export interface SceneConfig {
|
|
60
|
+
readonly version: 1;
|
|
61
|
+
/** Optional human-readable name (for tab titles, exports). */
|
|
62
|
+
readonly name?: string;
|
|
63
|
+
/** Panel definitions — referenced by id from the layout. */
|
|
64
|
+
readonly panels: readonly PanelInstance[];
|
|
65
|
+
/** How to arrange the panels spatially. */
|
|
66
|
+
readonly layout: SceneLayout;
|
|
67
|
+
}
|
package/dist/scene.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type p5 from 'p5';
|
|
2
|
+
/**
|
|
3
|
+
* A rectangular region in canvas coordinates. Chrome slices a canvas into
|
|
4
|
+
* these and hands them to panels; panels render inside them and nothing else.
|
|
5
|
+
*/
|
|
6
|
+
export interface Bounds {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A hit emitted by a panel — what's currently under the pointer (or
|
|
14
|
+
* otherwise "selected" by the panel). Chrome surfaces it for tooltips,
|
|
15
|
+
* cross-highlighting, and scene-level selection state.
|
|
16
|
+
*
|
|
17
|
+
* `id` is the contract between panels: two panels agreeing on the same id
|
|
18
|
+
* (e.g. a speaker name, a turn number, a timestamp) is how cross-highlighting
|
|
19
|
+
* works without any bespoke event bus.
|
|
20
|
+
*/
|
|
21
|
+
export interface PanelHit {
|
|
22
|
+
/** Stable identifier. Panels that want to cross-highlight should agree on the id shape. */
|
|
23
|
+
readonly id: string;
|
|
24
|
+
/** Optional panel-specific payload. Consumers of the hit should treat this as untrusted and narrow it themselves. */
|
|
25
|
+
readonly meta?: unknown;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Everything a panel is handed on each render frame. Ambient state only —
|
|
29
|
+
* a panel should never need to reach outside the PanelContext for chrome
|
|
30
|
+
* concerns (bounds, mouse, highlights).
|
|
31
|
+
*/
|
|
32
|
+
export interface PanelContext {
|
|
33
|
+
/** Live p5 instance. Panels draw into its current renderer. */
|
|
34
|
+
readonly sk: p5;
|
|
35
|
+
/** The rectangle this panel owns for the frame. Don't draw outside it. */
|
|
36
|
+
readonly bounds: Bounds;
|
|
37
|
+
/** Pointer position in canvas coordinates, or `null` if the pointer is off-canvas. */
|
|
38
|
+
readonly mouse: {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
} | null;
|
|
42
|
+
/**
|
|
43
|
+
* Ids other panels emitted as hits (or hover) this frame. A panel that
|
|
44
|
+
* renders the same domain object should visually emphasise or dim items
|
|
45
|
+
* whose id matches an entry here. Read-only — write via the returned
|
|
46
|
+
* `PanelResult.hover`.
|
|
47
|
+
*/
|
|
48
|
+
readonly highlights: ReadonlyArray<string>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* What a panel returns after drawing one frame. Only chrome-relevant state
|
|
52
|
+
* belongs here; app-specific side channels live in the panel's own instance
|
|
53
|
+
* state or in the shared data layer.
|
|
54
|
+
*/
|
|
55
|
+
export interface PanelResult {
|
|
56
|
+
/** What this panel is currently hovering over. `null` means nothing is under the cursor. */
|
|
57
|
+
readonly hover: PanelHit | null;
|
|
58
|
+
/** Signals that the panel's content overflowed its bounds — chrome can render an indicator. */
|
|
59
|
+
readonly overflow?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The core contract. A panel is a stateless renderer for one kind of
|
|
63
|
+
* visualization. Instantiated once, called every frame with fresh
|
|
64
|
+
* data, config, and bounds.
|
|
65
|
+
*
|
|
66
|
+
* Panel implementations should avoid storing per-frame state on the panel
|
|
67
|
+
* itself; cache-worthy state (e.g. offscreen buffers) is fine, but anything
|
|
68
|
+
* time-varying belongs in the data the caller passes in.
|
|
69
|
+
*
|
|
70
|
+
* @template TData — the shape of data this panel renders
|
|
71
|
+
* @template TConfig — user-tunable knobs (colors, thresholds, modes)
|
|
72
|
+
*/
|
|
73
|
+
export interface VizPanel<TData = unknown, TConfig = unknown> {
|
|
74
|
+
/** Registry key. Must be unique across all panels a scene uses. */
|
|
75
|
+
readonly type: string;
|
|
76
|
+
/** Config used when a scene instance provides no override. Shallow-merged with per-instance config. */
|
|
77
|
+
readonly defaultConfig: TConfig;
|
|
78
|
+
/**
|
|
79
|
+
* Optional runtime schema for the config. Typed as `unknown` so the contract
|
|
80
|
+
* stays schema-library-agnostic — use zod, valibot, arktype, or plain JSON
|
|
81
|
+
* Schema. The no-code studio will inspect this to generate property panels.
|
|
82
|
+
*/
|
|
83
|
+
readonly schema?: unknown;
|
|
84
|
+
/** Render one frame. Called from inside the p5 draw loop by a scene runner. */
|
|
85
|
+
render(ctx: PanelContext, data: TData, config: TConfig): PanelResult;
|
|
86
|
+
/** Optional cleanup hook — release p5.Graphics buffers or other owned resources here. */
|
|
87
|
+
dispose?(): void;
|
|
88
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "svelte-p5-viz",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Visualization panel contract, registry, and scene-config types for svelte-p5. Composable, no-code-ready building blocks for canvas-based dashboards.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"svelte",
|
|
7
|
+
"svelte5",
|
|
8
|
+
"p5",
|
|
9
|
+
"p5.js",
|
|
10
|
+
"visualization",
|
|
11
|
+
"dashboard",
|
|
12
|
+
"panel",
|
|
13
|
+
"no-code"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/edw1nzhao/svelte-p5.git",
|
|
18
|
+
"directory": "packages/viz"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/edw1nzhao/svelte-p5/tree/main/packages/viz#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/edw1nzhao/svelte-p5/issues"
|
|
23
|
+
},
|
|
24
|
+
"author": "Edwin Zhao",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"svelte": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"main": "./dist/index.js",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"svelte": "./dist/index.js",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"!dist/**/*.test.*",
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
],
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"sideEffects": false,
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "svelte-package -o dist && publint",
|
|
49
|
+
"typecheck": "svelte-check --tsconfig ./tsconfig.json",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"test:coverage": "vitest run --coverage"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"p5": ">=1.11.0 <3",
|
|
56
|
+
"svelte": "^5.0.0",
|
|
57
|
+
"svelte-p5": "workspace:^"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@sveltejs/package": "catalog:",
|
|
61
|
+
"@sveltejs/vite-plugin-svelte": "catalog:",
|
|
62
|
+
"@types/p5": "catalog:",
|
|
63
|
+
"@vitest/coverage-v8": "catalog:",
|
|
64
|
+
"p5": "catalog:",
|
|
65
|
+
"publint": "catalog:",
|
|
66
|
+
"svelte": "catalog:",
|
|
67
|
+
"svelte-check": "catalog:",
|
|
68
|
+
"svelte-p5": "workspace:*",
|
|
69
|
+
"typescript": "catalog:",
|
|
70
|
+
"vite": "catalog:",
|
|
71
|
+
"vitest": "catalog:"
|
|
72
|
+
}
|
|
73
|
+
}
|