fragment-tools 0.2.9 → 0.2.11

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.
Files changed (33) hide show
  1. package/package.json +5 -6
  2. package/src/cli/createConfig.js +38 -19
  3. package/src/cli/plugins/hot-shader-replacement.js +0 -5
  4. package/src/cli/plugins/save.js +7 -2
  5. package/src/cli/run.js +27 -6
  6. package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +3 -1
  7. package/src/client/app/lib/canvas-recorder/GIFRecorder.js +11 -1
  8. package/src/client/app/lib/canvas-recorder/MediaBunnyRecorder.js +97 -0
  9. package/src/client/app/modules/Exports.svelte +28 -4
  10. package/src/client/app/state/exports.svelte.js +40 -2
  11. package/src/client/app/state/rendering.svelte.js +1 -1
  12. package/src/client/app/ui/Field.svelte +7 -3
  13. package/src/client/app/ui/FieldSection.svelte +1 -1
  14. package/src/client/app/ui/LayoutBuild.svelte +1 -1
  15. package/src/client/app/ui/SketchRenderer.svelte +5 -1
  16. package/src/client/app/ui/fields/CheckboxInput.svelte +9 -7
  17. package/src/client/app/ui/fields/ImageInput.svelte +33 -15
  18. package/src/client/app/ui/fields/Select.svelte +2 -1
  19. package/src/client/app/utils/canvas.utils.js +21 -19
  20. package/src/index.js +12 -0
  21. package/src/types/config.d.ts +6 -0
  22. package/src/types/index.d.ts +1 -0
  23. package/src/types/props.d.ts +32 -5
  24. package/.changeset/README.md +0 -8
  25. package/.changeset/config.json +0 -11
  26. package/.prettierignore +0 -5
  27. package/.prettierrc +0 -25
  28. package/CHANGELOG.md +0 -10
  29. package/src/client/app/lib/canvas-recorder/MP4Recorder.js +0 -44
  30. package/src/client/app/lib/canvas-recorder/WebMRecorder.js +0 -29
  31. package/src/client/app/lib/canvas-recorder/mp4.js +0 -1654
  32. package/src/client/app/lib/canvas-recorder/mp4.wasm +0 -0
  33. package/src/client/public/preview.html +0 -59
@@ -4,7 +4,13 @@
4
4
  import FieldInputRow from './FieldInputRow.svelte';
5
5
  import TextInput from './TextInput.svelte';
6
6
 
7
- let { value, context = null, key = '', disabled = false } = $props();
7
+ let {
8
+ value,
9
+ context = null,
10
+ key = '',
11
+ onchange,
12
+ disabled = false,
13
+ } = $props();
8
14
 
9
15
  /** @type {HTMLImageElement} */
10
16
  let img;
@@ -24,7 +30,7 @@
24
30
  }
25
31
 
26
32
  let reader = new FileReader();
27
- let dragover = false;
33
+ let dragover = $state(false);
28
34
 
29
35
  function handleUpload(event) {
30
36
  event.preventDefault();
@@ -44,6 +50,7 @@
44
50
  reader.onload = null;
45
51
 
46
52
  value = e.target.result;
53
+ onchange(value);
47
54
  };
48
55
  reader.readAsDataURL(file);
49
56
 
@@ -67,33 +74,31 @@
67
74
 
68
75
  <div
69
76
  class="img-container"
77
+ role="group"
78
+ aria-label="Drop an image here to upload"
70
79
  class:dragover
71
- on:dragover={handleDragover}
72
- on:dragleave={handleDragleave}
73
- on:drop={handleUpload}
80
+ ondragover={handleDragover}
81
+ ondragleave={handleDragleave}
82
+ ondrop={handleUpload}
74
83
  >
75
84
  <FieldInputRow --grid-template-columns="1fr 0.5fr">
76
85
  <div class="row">
77
- <div class="preview" on:click={handleClick}>
86
+ <div class="preview">
78
87
  <img class="img" src="" alt="" bind:this={img} />
88
+ <button class="preview-button" onclick={handleClick}>
89
+ <span class="visually-hidden">Upload</span>
90
+ </button>
79
91
  <input
80
92
  class="input"
81
93
  type="file"
82
94
  bind:this={input}
83
- on:change={handleUpload}
95
+ onchange={handleUpload}
84
96
  disabled={disabled ? 'disabled' : null}
85
97
  />
86
98
  </div>
87
99
  <TextInput disabled value={displayUrl} />
88
100
  </div>
89
- <ButtonInput
90
- label="change"
91
- on:click={handleClick}
92
- on:dragover={handleDragover}
93
- on:dragleave={handleDragleave}
94
- on:drop={handleUpload}
95
- {disabled}
96
- />
101
+ <ButtonInput label="change" onclick={handleClick} {disabled} />
97
102
  </FieldInputRow>
98
103
  </div>
99
104
 
@@ -103,6 +108,8 @@
103
108
  }
104
109
 
105
110
  .preview {
111
+ position: relative;
112
+
106
113
  width: calc(var(--fragment-input-height) * 1);
107
114
  height: calc(var(--fragment-input-height) * 1);
108
115
  display: grid;
@@ -116,6 +123,17 @@
116
123
  overflow: hidden;
117
124
  }
118
125
 
126
+ .preview-button {
127
+ position: absolute;
128
+ top: 0;
129
+ left: 0;
130
+
131
+ width: 100%;
132
+ height: 100%;
133
+
134
+ opacity: 0;
135
+ }
136
+
119
137
  .row {
120
138
  display: grid;
121
139
  grid-template-columns: 20px auto;
@@ -116,7 +116,7 @@
116
116
  }
117
117
 
118
118
  .select {
119
- padding: 0 var(--padding, 6px) 0 var(--padding, 6px);
119
+ padding: 0 18px 0 var(--padding, 6px);
120
120
 
121
121
  width: 100%;
122
122
 
@@ -126,6 +126,7 @@
126
126
  outline: 0;
127
127
  background-color: transparent;
128
128
  opacity: 1;
129
+ text-overflow: ellipsis;
129
130
  }
130
131
 
131
132
  .select-input:not(.disabled) .select {
@@ -3,10 +3,9 @@ https://github.com/mattdesl/canvas-sketch/blob/24f6bb2bbdfdfd72a698a0b8a0962ad84
3
3
  */
4
4
 
5
5
  import { VIDEO_FORMATS } from '../state/exports.svelte';
6
- import WebMRecorder from '../lib/canvas-recorder/WebMRecorder';
7
- import MP4Recorder from '../lib/canvas-recorder/MP4Recorder';
8
6
  import GIFRecorder from '../lib/canvas-recorder/GIFRecorder';
9
7
  import FrameRecorder from '../lib/canvas-recorder/FrameRecorder';
8
+ import MediaBunnyRecorder from '../lib/canvas-recorder/MediaBunnyRecorder';
10
9
  import { exportCanvas } from '../lib/canvas-recorder/utils';
11
10
  import { map } from './math.utils';
12
11
  import { createDataURLFromBlob, saveFiles } from './file.utils';
@@ -101,28 +100,21 @@ export async function screenshotCanvas(
101
100
  }
102
101
  }
103
102
 
104
- function recordCanvasWebM(canvas, options) {
105
- let recorder = new WebMRecorder(canvas, options);
103
+ function record(canvas, options) {
104
+ let recorder = new MediaBunnyRecorder(canvas, options);
106
105
  recorder.start();
107
106
 
108
107
  return recorder;
109
108
  }
110
109
 
111
- function recordCanvasMp4(canvas, options) {
112
- let recorder = new MP4Recorder(canvas, options);
113
- recorder.start();
114
-
115
- return recorder;
116
- }
117
-
118
- function recordCanvasGIF(canvas, options) {
110
+ function recordGIF(canvas, options) {
119
111
  let recorder = new GIFRecorder(canvas, options);
120
112
  recorder.start();
121
113
 
122
114
  return recorder;
123
115
  }
124
116
 
125
- function recordCanvasFrames(canvas, options) {
117
+ function recordFrames(canvas, options) {
126
118
  let recorder = new FrameRecorder(canvas, options);
127
119
  recorder.start();
128
120
 
@@ -138,6 +130,7 @@ export function recordCanvas(
138
130
  duration = Infinity,
139
131
  quality = 100,
140
132
  pattern = defaultFilenamePattern,
133
+ codec,
141
134
  exportDir,
142
135
  imageEncoding,
143
136
  onStart = () => {},
@@ -189,6 +182,7 @@ export function recordCanvas(
189
182
 
190
183
  const options = {
191
184
  framerate,
185
+ format,
192
186
  duration,
193
187
  quality,
194
188
  onStart,
@@ -198,14 +192,22 @@ export function recordCanvas(
198
192
 
199
193
  let recorder;
200
194
 
201
- if (format === VIDEO_FORMATS.WEBM) {
202
- recorder = recordCanvasWebM(canvas, options);
203
- } else if (format === VIDEO_FORMATS.MP4) {
204
- recorder = recordCanvasMp4(canvas, options);
195
+ if (
196
+ [
197
+ VIDEO_FORMATS.MKV,
198
+ VIDEO_FORMATS.MOV,
199
+ VIDEO_FORMATS.MP4,
200
+ VIDEO_FORMATS.WEBM,
201
+ ].includes(format)
202
+ ) {
203
+ recorder = record(canvas, {
204
+ ...options,
205
+ codec,
206
+ });
205
207
  } else if (format === VIDEO_FORMATS.GIF) {
206
- recorder = recordCanvasGIF(canvas, options);
208
+ recorder = recordGIF(canvas, options);
207
209
  } else if (format === VIDEO_FORMATS.FRAMES) {
208
- recorder = recordCanvasFrames(canvas, {
210
+ recorder = recordFrames(canvas, {
209
211
  ...options,
210
212
  imageEncoding,
211
213
  });
package/src/index.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @typedef {import('./types/config').Config} Config
3
+ */
4
+
5
+ /**
6
+ * Type helper to make it easier to use fragment.config.js
7
+ * @param {Config} config
8
+ * @returns {Config}
9
+ */
10
+ export function defineConfig(config = {}) {
11
+ return config;
12
+ }
@@ -0,0 +1,6 @@
1
+ import { UserConfig } from 'vite';
2
+
3
+ export interface Config {
4
+ vite?: UserConfig;
5
+ [key: string]: any;
6
+ }
@@ -1,5 +1,6 @@
1
1
  export type * from './renderers';
2
2
  export type * from './sketch';
3
+ export type * from './config';
3
4
  export type * from './props';
4
5
  export type * from './helpers';
5
6
  export type * from './hooks';
@@ -3,7 +3,7 @@ type BaseProp<Value, Params, Type> = {
3
3
  params?: Params;
4
4
  type?: Type;
5
5
  hidden?: boolean;
6
- displayName?: string;
6
+ displayName?: string | null;
7
7
  folder?: string;
8
8
  group?: string;
9
9
  onChange?: PropOnChange<Value, Params>;
@@ -25,11 +25,37 @@ type NumberProp = BaseProp<
25
25
  { disabled?: boolean; step?: number } | { min: number; max: number },
26
26
  'number'
27
27
  >;
28
- type VecProp = BaseProp<
28
+
29
+ type VecArray =
29
30
  | [number, number]
30
31
  | [number, number, number]
31
- | [number, number, number, number],
32
- { locked?: boolean },
32
+ | [number, number, number, number];
33
+
34
+ type VecObject = Record<string, number> & { [key: number]: never };
35
+
36
+ type VecValue = VecArray | VecObject;
37
+
38
+ type VecArrayParams<V extends VecArray> = {
39
+ min: { [K in keyof V]: number };
40
+ max: { [K in keyof V]: number };
41
+ step?: { [K in keyof V]: number };
42
+ };
43
+
44
+ type VecObjectParams<V extends VecObject> = {
45
+ min: { [K in keyof V]: number };
46
+ max: { [K in keyof V]: number };
47
+ step?: { [K in keyof V]: number };
48
+ };
49
+
50
+ type VecParams<V extends VecValue> = V extends readonly number[]
51
+ ? VecArrayParams<V>
52
+ : V extends Record<string, number>
53
+ ? VecObjectParams<V>
54
+ : never;
55
+
56
+ type VecProp<V extends VecValue = VecValue> = BaseProp<
57
+ V,
58
+ { locked?: boolean } | VecParams<V>,
33
59
  'vec'
34
60
  >;
35
61
  type CheckboxProp = BaseProp<boolean, never, 'checkbox'>;
@@ -50,7 +76,8 @@ type ImageProp = BaseProp<string, never, 'image'>;
50
76
  type Prop =
51
77
  | SelectProp
52
78
  | NumberProp
53
- | VecProp
79
+ | VecProp<VecObject>
80
+ | VecProp<VecArray>
54
81
  | CheckboxProp
55
82
  | TextProp
56
83
  | ListProp
@@ -1,8 +0,0 @@
1
- # Changesets
2
-
3
- Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4
- with multi-package repos, or single-package repos to help you version and publish your code. You can
5
- find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6
-
7
- We have a quick list of common questions to get you started engaging with this project in
8
- [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
@@ -1,11 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
3
- "changelog": ["@svitejs/changesets-changelog-github-compact", { "repo": "raphaelameaume/fragment"}],
4
- "commit": false,
5
- "fixed": [],
6
- "linked": [],
7
- "access": "restricted",
8
- "baseBranch": "main",
9
- "updateInternalDependencies": "patch",
10
- "ignore": []
11
- }
package/.prettierignore DELETED
@@ -1,5 +0,0 @@
1
- pnpm-lock.yaml
2
- pnpm-workspace.yaml
3
- package-lock.json
4
- .changeset/config.json
5
- /docs
package/.prettierrc DELETED
@@ -1,25 +0,0 @@
1
- {
2
- "semi": true,
3
- "tabWidth": 4,
4
- "useTabs": true,
5
- "singleQuote": true,
6
- "printWidth": 80,
7
- "bracketSpacing": true,
8
- "trailingComma": "all",
9
- "plugins": ["prettier-plugin-svelte"],
10
- "overrides": [
11
- {
12
- "files": ["README.md", "packages/*/README.md", "**/package.json"],
13
- "options": {
14
- "useTabs": false,
15
- "tabWidth": 2
16
- }
17
- },
18
- {
19
- "files": ["docs/**/*.md"],
20
- "options": {
21
- "printWidth": 60
22
- }
23
- }
24
- ]
25
- }
package/CHANGELOG.md DELETED
@@ -1,10 +0,0 @@
1
- # fragment-tools
2
-
3
- ## 0.2.9
4
-
5
- ### Patch Changes
6
-
7
- - Prevent state mutations and warnings
8
- - Start templates on a green screen
9
- - Update examples
10
- - Prevent build layout re-render if layout is not persistent
@@ -1,44 +0,0 @@
1
- import loadMP4Module, { isWebCodecsSupported } from './mp4.js';
2
- import CanvasRecorder from './CanvasRecorder';
3
-
4
- let MP4;
5
-
6
- class MP4Recorder extends CanvasRecorder {
7
- static loaded = false;
8
- static isSupported = true;
9
-
10
- constructor(canvas, options) {
11
- super(canvas, options);
12
- }
13
-
14
- async load() {
15
- if (!MP4Recorder.loaded) {
16
- MP4 = await loadMP4Module();
17
-
18
- MP4Recorder.loaded = true;
19
- }
20
-
21
- this.encoder = MP4.createWebCodecsEncoder({
22
- width: this.canvas.width,
23
- height: this.canvas.height,
24
- fps: this.framerate,
25
- });
26
- }
27
-
28
- async tick() {
29
- const bitmap = await window.createImageBitmap(this.canvas);
30
-
31
- // Add bitmap to encoder
32
- await this.encoder.addFrame(bitmap);
33
- }
34
-
35
- async end() {
36
- const buffer = await this.encoder.end();
37
-
38
- this.result = new Blob([buffer], { type: 'video/mp4' });
39
-
40
- super.end();
41
- }
42
- }
43
-
44
- export default MP4Recorder;
@@ -1,29 +0,0 @@
1
- import WebMWriter from 'webm-writer';
2
- import { map } from '../../utils/math.utils';
3
- import CanvasRecorder from './CanvasRecorder';
4
-
5
- class WebMRecorder extends CanvasRecorder {
6
- start() {
7
- const quality = map(this.quality, 1, 100, 0, 0.99999);
8
-
9
- this.writer = new WebMWriter({
10
- quality,
11
- frameRate: this.framerate,
12
- });
13
-
14
- super.start();
15
- }
16
-
17
- tick() {
18
- this.writer.addFrame(this.canvas);
19
- }
20
-
21
- async end() {
22
- this.result = await this.writer.complete();
23
- this.writer = null;
24
-
25
- super.end();
26
- }
27
- }
28
-
29
- export default WebMRecorder;