fragment-tools 0.2.13 → 0.2.15
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 +70 -32
- package/package.json +4 -8
- package/screenshot.jpg +0 -0
- package/src/cli/createConfig.js +6 -1
- package/src/client/app/modules/Params.svelte +34 -32
- package/src/client/app/renderers/FragmentRenderer.js +1 -1
- package/src/client/app/renderers/P5GLRenderer.js +9 -3
- package/src/client/app/renderers/P5Renderer.js +8 -6
- package/src/client/app/renderers/THREERenderer.js +1 -1
- package/src/client/app/state/Sketch.svelte.js +7 -1
- package/src/client/app/triggers/shared.js +26 -16
package/README.md
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
<h1 align="center">Fragment</h1>
|
|
2
|
-
<div align="center">A
|
|
2
|
+
<div align="center">A modern toolkit for creative coding</div>
|
|
3
3
|
<br/>
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Fragment lets you sketch, render, and export canvas-based graphics with ease, from quick experiments to production-ready visuals.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
11
|
+
- Start instantly using built-in templates for [Canvas 2D](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API), [p5.js](https://github.com/processing/p5.js/), and [three.js](https://github.com/mrdoob/three.js/)
|
|
12
|
+
- Generate live GUI controls directly from your sketch parameters
|
|
13
|
+
- Reload GLSL shaders on the fly
|
|
14
|
+
- Attach mouse, keyboard, and MIDI inputs directly from the interface or your code
|
|
15
|
+
- Organize your creative workspace with flexible window layouts
|
|
16
|
+
- Export your sketches as images or videos in standard formats
|
|
17
|
+
- Build static bundles ready for production deployment
|
|
18
|
+
|
|
19
|
+
## Specifications
|
|
20
|
+
|
|
21
|
+
- Fast development and production builds powered by [Vite](https://vite.dev/)
|
|
22
|
+
- Interactive command-line interface on top of [sade](https://github.com/lukeed/sade) and [clack](https://www.clack.cc/)
|
|
23
|
+
- Video encoding in the browser with [Mediabunny](https://mediabunny.dev/)
|
|
17
24
|
- TypeScript support
|
|
18
25
|
|
|
19
26
|
## Installation
|
|
@@ -33,14 +40,13 @@ npm install fragment-tools --global
|
|
|
33
40
|
You should now be able to run `fragment` from your command line.
|
|
34
41
|
|
|
35
42
|
```bash
|
|
36
|
-
fragment
|
|
43
|
+
fragment -v
|
|
37
44
|
```
|
|
38
45
|
|
|
39
46
|
### Installing locally
|
|
40
47
|
|
|
41
48
|
```bash
|
|
42
49
|
npm install fragment-tools
|
|
43
|
-
npx fragment sketch.js --new
|
|
44
50
|
```
|
|
45
51
|
|
|
46
52
|
## Usage
|
|
@@ -53,10 +59,13 @@ mkdir sketches
|
|
|
53
59
|
cd sketches
|
|
54
60
|
|
|
55
61
|
# Create a sketch from a template
|
|
56
|
-
npx fragment
|
|
62
|
+
npx fragment sketch.js --new --template=2d
|
|
57
63
|
|
|
58
64
|
# or with TypeScript
|
|
59
|
-
npx fragment
|
|
65
|
+
npx fragment sketch.ts --new --template=2d --typescript
|
|
66
|
+
|
|
67
|
+
## Run an existing sketch
|
|
68
|
+
npx fragment sketch.js
|
|
60
69
|
```
|
|
61
70
|
|
|
62
71
|
Learn more about the available flag options in the [CLI docs](./docs/api/CLI.md).
|
|
@@ -67,37 +76,66 @@ This is an example of a sketch drawing a blue circle on a black background with
|
|
|
67
76
|
|
|
68
77
|
```js
|
|
69
78
|
export let props = {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
background: {
|
|
80
|
+
value: '#0057B8', // create a color input GUI
|
|
81
|
+
},
|
|
82
|
+
fill: {
|
|
83
|
+
value: 'rgba(255, 215, 0, 1)', // create a color input GUI
|
|
84
|
+
},
|
|
85
|
+
size: {
|
|
86
|
+
value: 256, // create a number input GUI
|
|
87
|
+
params: {
|
|
88
|
+
min: 64, // add a range slider
|
|
89
|
+
max: 512,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
shape: {
|
|
93
|
+
value: 'circle', // create a string input
|
|
94
|
+
params: {
|
|
95
|
+
options: ['circle', 'square'], // turns the input into a select with different options
|
|
96
|
+
},
|
|
97
|
+
},
|
|
77
98
|
};
|
|
78
99
|
|
|
79
|
-
export let update = ({ context, width, height }) => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
100
|
+
export let update = ({ context, width, height, pixelRatio }) => {
|
|
101
|
+
// draw background
|
|
102
|
+
context.fillStyle = props.background.value;
|
|
103
|
+
|
|
104
|
+
const w = width * pixelRatio;
|
|
105
|
+
const h = height * pixelRatio;
|
|
83
106
|
|
|
84
|
-
|
|
85
|
-
|
|
107
|
+
context.fillRect(0, 0, w, h);
|
|
108
|
+
|
|
109
|
+
// draw circle
|
|
110
|
+
const shape = props.shape.value;
|
|
111
|
+
const size = props.size.value * pixelRatio;
|
|
112
|
+
|
|
113
|
+
context.fillStyle = props.fill.value;
|
|
114
|
+
|
|
115
|
+
if (shape === 'circle') {
|
|
116
|
+
context.beginPath();
|
|
117
|
+
context.arc(w * 0.5, h * 0.5, size, 0, 2 * Math.PI, false);
|
|
118
|
+
context.fill();
|
|
119
|
+
} else if (shape === 'square') {
|
|
120
|
+
context.fillRect(w * 0.5 - size, h * 0.5 - size, size * 2, size * 2);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
86
123
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
124
|
+
export let rendering = '2d';
|
|
125
|
+
export let fps = 0;
|
|
126
|
+
export let name = 'Canvas2D Shape Example';
|
|
127
|
+
export let buildConfig = {
|
|
128
|
+
resizing: 'window',
|
|
91
129
|
};
|
|
92
130
|
```
|
|
93
131
|
|
|
94
|
-
Learn how to write your own sketch by following the [Getting started](./docs/introduction/getting-started.md) guide, reading the [API docs](./docs/api/sketch.md) or the [examples](./examples/).
|
|
132
|
+
Learn how to write your own sketch by following the [Getting started](./docs/introduction/getting-started.md) guide, reading the [API docs](./docs/api/sketch.md) or the other [examples](./examples/).
|
|
95
133
|
|
|
96
134
|
## Contributing
|
|
97
135
|
|
|
98
136
|
If you find issues, please [file one](https://github.com/raphaelameaume/fragment/issues) with details on how to reproduce it.
|
|
99
137
|
|
|
100
|
-
Feel free to reach out on [
|
|
138
|
+
Feel free to reach out on [Bluesky](https://bsky.app/profile/raphaelameaume.com) if you want to discuss the project.
|
|
101
139
|
|
|
102
140
|
## Running it locally
|
|
103
141
|
|
package/package.json
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fragment-tools",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "A web development environment for creative coding",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"fragment": "bin/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"format": "prettier . --write"
|
|
11
|
-
"changeset:add": "changeset add",
|
|
12
|
-
"changeset:version": "changeset version",
|
|
13
|
-
"changeset:publish": "changeset publish"
|
|
10
|
+
"format": "prettier . --write"
|
|
14
11
|
},
|
|
15
12
|
"type": "module",
|
|
16
13
|
"repository": {
|
|
@@ -36,11 +33,10 @@
|
|
|
36
33
|
"milliparsec": "^5.1.0",
|
|
37
34
|
"sade": "^1.8.1",
|
|
38
35
|
"svelte": "^5.55.9",
|
|
39
|
-
"vite": "^8.0
|
|
40
|
-
"ws": "^8.
|
|
36
|
+
"vite": "^8.1.0",
|
|
37
|
+
"ws": "^8.21.0"
|
|
41
38
|
},
|
|
42
39
|
"devDependencies": {
|
|
43
|
-
"@changesets/cli": "^2.29.7",
|
|
44
40
|
"@svitejs/changesets-changelog-github-compact": "^1.2.0",
|
|
45
41
|
"@types/node": "^25.9.1",
|
|
46
42
|
"@types/p5": "^1.7.6",
|
package/screenshot.jpg
ADDED
|
Binary file
|
package/src/cli/createConfig.js
CHANGED
|
@@ -129,7 +129,12 @@ export async function createConfig(
|
|
|
129
129
|
__DEV__: !build,
|
|
130
130
|
},
|
|
131
131
|
optimizeDeps: {
|
|
132
|
-
include: [
|
|
132
|
+
include: [
|
|
133
|
+
'convert-length',
|
|
134
|
+
'changedpi',
|
|
135
|
+
'gifenc',
|
|
136
|
+
'mediabunny',
|
|
137
|
+
],
|
|
133
138
|
},
|
|
134
139
|
}),
|
|
135
140
|
config.vite ?? {},
|
|
@@ -131,38 +131,40 @@
|
|
|
131
131
|
{/if}
|
|
132
132
|
{#if typeof sketchProps === 'object'}
|
|
133
133
|
{#snippet sketchField(index, key, prop)}
|
|
134
|
-
{
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
v
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
134
|
+
{#if prop}
|
|
135
|
+
{@const {
|
|
136
|
+
hidden,
|
|
137
|
+
displayName,
|
|
138
|
+
value,
|
|
139
|
+
type,
|
|
140
|
+
disabled,
|
|
141
|
+
__initialValue: initialValue,
|
|
142
|
+
} = prop}
|
|
143
|
+
{#if !hidden}
|
|
144
|
+
<Field
|
|
145
|
+
context={sketch.key}
|
|
146
|
+
{key}
|
|
147
|
+
{displayName}
|
|
148
|
+
{value}
|
|
149
|
+
{initialValue}
|
|
150
|
+
{type}
|
|
151
|
+
{index}
|
|
152
|
+
{disabled}
|
|
153
|
+
bind:params={sketchProps[key].params}
|
|
154
|
+
bind:triggers={prop.triggers}
|
|
155
|
+
trackChanges
|
|
156
|
+
onclick={(event) => {
|
|
157
|
+
sketch.version++;
|
|
158
|
+
// value(event, sketch.params);
|
|
159
|
+
}}
|
|
160
|
+
onchange={(v) => {
|
|
161
|
+
if (v?.currentTarget && v?.type === 'change') {
|
|
162
|
+
v = v.currentTarget.value;
|
|
163
|
+
}
|
|
164
|
+
sketch.updateProp(key, v);
|
|
165
|
+
}}
|
|
166
|
+
/>
|
|
167
|
+
{/if}
|
|
166
168
|
{/if}
|
|
167
169
|
{/snippet}
|
|
168
170
|
{#snippet sketchPropItem(index, item)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import p5 from 'p5';
|
|
2
|
-
import { client } from '
|
|
2
|
+
import { client } from '../client';
|
|
3
3
|
import { getShaderPath } from '../utils/glsl.utils';
|
|
4
4
|
import { clearError } from '../state/errors.svelte';
|
|
5
5
|
|
|
@@ -38,9 +38,15 @@ export let onMountPreview = ({
|
|
|
38
38
|
pixelRatio,
|
|
39
39
|
}) => {
|
|
40
40
|
const p = new p5((sketch) => {
|
|
41
|
-
sketch.pixelDensity(pixelRatio);
|
|
42
41
|
sketch.setup = () => {
|
|
43
|
-
|
|
42
|
+
const dpr = window.devicePixelRatio;
|
|
43
|
+
sketch.pixelDensity(pixelRatio);
|
|
44
|
+
sketch.createCanvas(
|
|
45
|
+
(width / dpr) * pixelRatio,
|
|
46
|
+
(height / dpr) * pixelRatio,
|
|
47
|
+
'webgl',
|
|
48
|
+
canvas,
|
|
49
|
+
);
|
|
44
50
|
};
|
|
45
51
|
}, container);
|
|
46
52
|
|
|
@@ -34,9 +34,14 @@ export let onMountPreview = ({
|
|
|
34
34
|
pixelRatio,
|
|
35
35
|
}) => {
|
|
36
36
|
const p = new p5((sketch) => {
|
|
37
|
-
sketch.pixelDensity(pixelRatio);
|
|
38
37
|
sketch.setup = () => {
|
|
39
|
-
|
|
38
|
+
const dpr = window.devicePixelRatio;
|
|
39
|
+
sketch.pixelDensity(pixelRatio);
|
|
40
|
+
sketch.createCanvas(
|
|
41
|
+
(width / dpr) * pixelRatio,
|
|
42
|
+
(height / dpr) * pixelRatio,
|
|
43
|
+
canvas,
|
|
44
|
+
);
|
|
40
45
|
};
|
|
41
46
|
}, container);
|
|
42
47
|
|
|
@@ -52,10 +57,7 @@ export let onMountPreview = ({
|
|
|
52
57
|
};
|
|
53
58
|
|
|
54
59
|
/**
|
|
55
|
-
* @param {MountParamsP5Renderer} params
|
|
56
|
-
* @param {number} params.id
|
|
57
|
-
* @param {HTMLCanvasElement} params.canvas
|
|
58
|
-
* @param {HTMLDivElement} params.container
|
|
60
|
+
* @param {MountParamsP5Renderer & PreviewP5Renderer} params
|
|
59
61
|
*/
|
|
60
62
|
export let onBeforeUpdatePreview = ({ id }) => {
|
|
61
63
|
const preview = previews.find((p) => p.id === id);
|
|
@@ -409,7 +409,13 @@ class Sketch {
|
|
|
409
409
|
collection.push(fieldgroup);
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
-
if (
|
|
412
|
+
if (
|
|
413
|
+
fieldgroup &&
|
|
414
|
+
isCurrent &&
|
|
415
|
+
!fieldgroup.children.some(
|
|
416
|
+
(c) => c.type === 'field' && c.key === key,
|
|
417
|
+
)
|
|
418
|
+
) {
|
|
413
419
|
fieldgroup.children.push({
|
|
414
420
|
type: 'field',
|
|
415
421
|
key,
|
|
@@ -1,37 +1,47 @@
|
|
|
1
|
+
import { getFileExtension, getFilename } from '../utils/file.utils';
|
|
2
|
+
|
|
1
3
|
export const wildcard = '*';
|
|
4
|
+
/** @type string[] */
|
|
2
5
|
export let sketchFiles = [];
|
|
3
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @param {string[]} files
|
|
9
|
+
*/
|
|
4
10
|
export function assignSketchFiles(files) {
|
|
5
11
|
sketchFiles.push(...files);
|
|
6
12
|
}
|
|
7
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Retrieves sketch key based on error stack traces
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
8
18
|
export function getContext() {
|
|
9
|
-
let context;
|
|
19
|
+
let context = wildcard;
|
|
10
20
|
|
|
11
21
|
const { stack } = new Error();
|
|
12
22
|
const { url } = import.meta;
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
|
|
24
|
+
if (stack) {
|
|
25
|
+
const callstack = stack.split('\n');
|
|
26
|
+
const index = callstack.findIndex((call) => call.includes(url));
|
|
16
27
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
if (index >= 0) {
|
|
29
|
+
callstack.splice(0, index + 1);
|
|
30
|
+
}
|
|
20
31
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
for (let i = 0; i < callstack.length; i++) {
|
|
33
|
+
for (let j = 0; j < sketchFiles.length; j++) {
|
|
34
|
+
const sketchFile = getFilename(sketchFiles[j]);
|
|
35
|
+
const extension = getFileExtension(sketchFile);
|
|
36
|
+
const filename = sketchFile.split(`.${extension}`)[0];
|
|
24
37
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
38
|
+
if (callstack[i].includes(filename)) {
|
|
39
|
+
context = sketchFile;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
28
42
|
}
|
|
29
43
|
}
|
|
30
44
|
}
|
|
31
45
|
|
|
32
|
-
if (!context) {
|
|
33
|
-
context = wildcard;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
46
|
return context;
|
|
37
47
|
}
|