@stevejtrettel/shader-sandbox 0.1.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 +391 -0
- package/bin/cli.js +389 -0
- package/dist-lib/app/App.d.ts +134 -0
- package/dist-lib/app/App.d.ts.map +1 -0
- package/dist-lib/app/App.js +570 -0
- package/dist-lib/app/types.d.ts +32 -0
- package/dist-lib/app/types.d.ts.map +1 -0
- package/dist-lib/app/types.js +6 -0
- package/dist-lib/editor/EditorPanel.d.ts +39 -0
- package/dist-lib/editor/EditorPanel.d.ts.map +1 -0
- package/dist-lib/editor/EditorPanel.js +274 -0
- package/dist-lib/editor/prism-editor.css +99 -0
- package/dist-lib/editor/prism-editor.d.ts +19 -0
- package/dist-lib/editor/prism-editor.d.ts.map +1 -0
- package/dist-lib/editor/prism-editor.js +96 -0
- package/dist-lib/embed.d.ts +17 -0
- package/dist-lib/embed.d.ts.map +1 -0
- package/dist-lib/embed.js +35 -0
- package/dist-lib/engine/ShadertoyEngine.d.ts +160 -0
- package/dist-lib/engine/ShadertoyEngine.d.ts.map +1 -0
- package/dist-lib/engine/ShadertoyEngine.js +704 -0
- package/dist-lib/engine/glHelpers.d.ts +79 -0
- package/dist-lib/engine/glHelpers.d.ts.map +1 -0
- package/dist-lib/engine/glHelpers.js +298 -0
- package/dist-lib/engine/types.d.ts +77 -0
- package/dist-lib/engine/types.d.ts.map +1 -0
- package/dist-lib/engine/types.js +7 -0
- package/dist-lib/index.d.ts +12 -0
- package/dist-lib/index.d.ts.map +1 -0
- package/dist-lib/index.js +9 -0
- package/dist-lib/layouts/DefaultLayout.d.ts +17 -0
- package/dist-lib/layouts/DefaultLayout.d.ts.map +1 -0
- package/dist-lib/layouts/DefaultLayout.js +27 -0
- package/dist-lib/layouts/FullscreenLayout.d.ts +17 -0
- package/dist-lib/layouts/FullscreenLayout.d.ts.map +1 -0
- package/dist-lib/layouts/FullscreenLayout.js +27 -0
- package/dist-lib/layouts/SplitLayout.d.ts +26 -0
- package/dist-lib/layouts/SplitLayout.d.ts.map +1 -0
- package/dist-lib/layouts/SplitLayout.js +61 -0
- package/dist-lib/layouts/TabbedLayout.d.ts +38 -0
- package/dist-lib/layouts/TabbedLayout.d.ts.map +1 -0
- package/dist-lib/layouts/TabbedLayout.js +305 -0
- package/dist-lib/layouts/index.d.ts +24 -0
- package/dist-lib/layouts/index.d.ts.map +1 -0
- package/dist-lib/layouts/index.js +36 -0
- package/dist-lib/layouts/split.css +196 -0
- package/dist-lib/layouts/tabbed.css +345 -0
- package/dist-lib/layouts/types.d.ts +48 -0
- package/dist-lib/layouts/types.d.ts.map +1 -0
- package/dist-lib/layouts/types.js +4 -0
- package/dist-lib/main.d.ts +15 -0
- package/dist-lib/main.d.ts.map +1 -0
- package/dist-lib/main.js +102 -0
- package/dist-lib/project/generatedLoader.d.ts +3 -0
- package/dist-lib/project/generatedLoader.d.ts.map +1 -0
- package/dist-lib/project/generatedLoader.js +17 -0
- package/dist-lib/project/loadProject.d.ts +22 -0
- package/dist-lib/project/loadProject.d.ts.map +1 -0
- package/dist-lib/project/loadProject.js +350 -0
- package/dist-lib/project/loaderHelper.d.ts +7 -0
- package/dist-lib/project/loaderHelper.d.ts.map +1 -0
- package/dist-lib/project/loaderHelper.js +240 -0
- package/dist-lib/project/types.d.ts +192 -0
- package/dist-lib/project/types.d.ts.map +1 -0
- package/dist-lib/project/types.js +7 -0
- package/dist-lib/styles/base.css +29 -0
- package/package.json +48 -0
- package/src/app/App.ts +699 -0
- package/src/app/app.css +208 -0
- package/src/app/types.ts +36 -0
- package/src/editor/EditorPanel.ts +340 -0
- package/src/editor/editor-panel.css +175 -0
- package/src/editor/prism-editor.css +99 -0
- package/src/editor/prism-editor.ts +124 -0
- package/src/embed.ts +55 -0
- package/src/engine/ShadertoyEngine.ts +929 -0
- package/src/engine/glHelpers.ts +432 -0
- package/src/engine/types.ts +118 -0
- package/src/index.ts +13 -0
- package/src/layouts/DefaultLayout.ts +40 -0
- package/src/layouts/FullscreenLayout.ts +40 -0
- package/src/layouts/SplitLayout.ts +81 -0
- package/src/layouts/TabbedLayout.ts +371 -0
- package/src/layouts/default.css +22 -0
- package/src/layouts/fullscreen.css +15 -0
- package/src/layouts/index.ts +44 -0
- package/src/layouts/split.css +196 -0
- package/src/layouts/tabbed.css +345 -0
- package/src/layouts/types.ts +58 -0
- package/src/main.ts +114 -0
- package/src/project/generatedLoader.ts +23 -0
- package/src/project/loadProject.ts +421 -0
- package/src/project/loaderHelper.ts +300 -0
- package/src/project/types.ts +243 -0
- package/src/styles/base.css +29 -0
- package/src/styles/embed.css +14 -0
- package/src/vite-env.d.ts +1 -0
- package/templates/index.html +28 -0
- package/templates/main.ts +126 -0
- package/templates/package.json +12 -0
- package/templates/shaders/example-buffer/bufferA.glsl +14 -0
- package/templates/shaders/example-buffer/config.json +10 -0
- package/templates/shaders/example-buffer/image.glsl +5 -0
- package/templates/shaders/example-gradient/config.json +4 -0
- package/templates/shaders/example-gradient/image.glsl +7 -0
- package/templates/vite.config.js +35 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tabbed Layout Styles
|
|
3
|
+
* Single window with tabs switching between shader and code views
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* ===== Theme Variables (for code viewer) ===== */
|
|
7
|
+
.layout-tabbed {
|
|
8
|
+
--tab-border: #e0e0e0;
|
|
9
|
+
--code-bg: white;
|
|
10
|
+
--code-text: #000;
|
|
11
|
+
--line-number-text: #999;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.layout-tabbed {
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: 100%;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
padding: 60px;
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.tabbed-wrapper {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
width: 800px;
|
|
29
|
+
max-width: 100%;
|
|
30
|
+
height: 650px; /* 600px content + ~50px tabs */
|
|
31
|
+
max-height: 100%;
|
|
32
|
+
border-radius: 8px;
|
|
33
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25), 0 5px 15px rgba(0, 0, 0, 0.15);
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ===== Toolbar (contains tabs + buttons) ===== */
|
|
38
|
+
.tabbed-toolbar {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
flex-shrink: 0;
|
|
42
|
+
background: #f8f8f8;
|
|
43
|
+
border-bottom: 1px solid #e0e0e0;
|
|
44
|
+
padding-right: 8px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* ===== Tab Bar ===== */
|
|
48
|
+
.tabbed-tab-bar {
|
|
49
|
+
display: flex;
|
|
50
|
+
flex: 1;
|
|
51
|
+
gap: 4px;
|
|
52
|
+
overflow-x: auto;
|
|
53
|
+
overflow-y: hidden;
|
|
54
|
+
scrollbar-width: thin;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Hide scrollbar on webkit but keep functionality */
|
|
58
|
+
.tabbed-tab-bar::-webkit-scrollbar {
|
|
59
|
+
height: 4px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.tabbed-tab-bar::-webkit-scrollbar-thumb {
|
|
63
|
+
background: #ccc;
|
|
64
|
+
border-radius: 2px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.tabbed-tab-button {
|
|
68
|
+
padding: 10px 16px;
|
|
69
|
+
background: transparent;
|
|
70
|
+
border: none;
|
|
71
|
+
border-bottom: 2px solid transparent;
|
|
72
|
+
font-size: 12px;
|
|
73
|
+
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
transition: color 0.15s, border-color 0.15s;
|
|
76
|
+
color: #666;
|
|
77
|
+
white-space: nowrap;
|
|
78
|
+
flex-shrink: 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.tabbed-tab-button:hover {
|
|
82
|
+
color: #333;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.tabbed-tab-button.active {
|
|
86
|
+
color: #000;
|
|
87
|
+
border-bottom-color: #4a9eff;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.tabbed-tab-button.shader-tab {
|
|
91
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.tabbed-tab-button.image-tab {
|
|
95
|
+
color: #7c4dff;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.tabbed-tab-button.image-tab.active {
|
|
99
|
+
color: #7c4dff;
|
|
100
|
+
border-bottom-color: #7c4dff;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* ===== Content Area ===== */
|
|
104
|
+
.tabbed-content {
|
|
105
|
+
flex: 1;
|
|
106
|
+
min-height: 0; /* Critical for flexbox scrolling */
|
|
107
|
+
position: relative;
|
|
108
|
+
background: #000;
|
|
109
|
+
overflow: hidden;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.tabbed-canvas-container {
|
|
113
|
+
position: absolute;
|
|
114
|
+
top: 0;
|
|
115
|
+
left: 0;
|
|
116
|
+
width: 100%;
|
|
117
|
+
height: 100%;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* ===== Code Viewer ===== */
|
|
121
|
+
.tabbed-code-viewer {
|
|
122
|
+
position: absolute;
|
|
123
|
+
top: 0;
|
|
124
|
+
left: 0;
|
|
125
|
+
width: 100%;
|
|
126
|
+
height: 100%;
|
|
127
|
+
overflow: auto;
|
|
128
|
+
background: var(--code-bg);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.tabbed-code-viewer pre {
|
|
132
|
+
margin: 0;
|
|
133
|
+
padding: 16px 16px 16px 0;
|
|
134
|
+
font-size: 13px;
|
|
135
|
+
line-height: 1.6;
|
|
136
|
+
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
|
|
137
|
+
background: var(--code-bg);
|
|
138
|
+
color: var(--code-text);
|
|
139
|
+
display: flex;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.tabbed-code-viewer code {
|
|
143
|
+
font-family: inherit;
|
|
144
|
+
font-size: inherit;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Line numbers */
|
|
148
|
+
.tabbed-line-numbers {
|
|
149
|
+
text-align: right;
|
|
150
|
+
padding-right: 16px;
|
|
151
|
+
margin-right: 16px;
|
|
152
|
+
border-right: 1px solid var(--tab-border);
|
|
153
|
+
color: var(--line-number-text);
|
|
154
|
+
user-select: none;
|
|
155
|
+
flex-shrink: 0;
|
|
156
|
+
padding-left: 16px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.tabbed-code-content {
|
|
160
|
+
flex: 1;
|
|
161
|
+
overflow-x: auto;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* ===== Image Viewer ===== */
|
|
165
|
+
.tabbed-image-viewer {
|
|
166
|
+
position: absolute;
|
|
167
|
+
top: 0;
|
|
168
|
+
left: 0;
|
|
169
|
+
width: 100%;
|
|
170
|
+
height: 100%;
|
|
171
|
+
display: flex;
|
|
172
|
+
align-items: center;
|
|
173
|
+
justify-content: center;
|
|
174
|
+
background: #f5f5f5;
|
|
175
|
+
padding: 20px;
|
|
176
|
+
box-sizing: border-box;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.tabbed-image-viewer img {
|
|
180
|
+
max-width: 100%;
|
|
181
|
+
max-height: 100%;
|
|
182
|
+
object-fit: contain;
|
|
183
|
+
border-radius: 4px;
|
|
184
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* ===== Prism Theme (Light) ===== */
|
|
188
|
+
.tabbed-code-viewer .token.comment { color: #6a9955; }
|
|
189
|
+
.tabbed-code-viewer .token.keyword { color: #0000ff; }
|
|
190
|
+
.tabbed-code-viewer .token.string { color: #a31515; }
|
|
191
|
+
.tabbed-code-viewer .token.number { color: #098658; }
|
|
192
|
+
.tabbed-code-viewer .token.operator { color: #000000; }
|
|
193
|
+
.tabbed-code-viewer .token.function { color: #795e26; }
|
|
194
|
+
.tabbed-code-viewer .token.class-name { color: #267f99; }
|
|
195
|
+
.tabbed-code-viewer .token.punctuation { color: #000000; }
|
|
196
|
+
|
|
197
|
+
/* ===== Prism Theme (Dark) ===== */
|
|
198
|
+
[data-bs-theme="dark"] .tabbed-code-viewer .token.comment,
|
|
199
|
+
.dark .tabbed-code-viewer .token.comment { color: #6a9955; }
|
|
200
|
+
[data-bs-theme="dark"] .tabbed-code-viewer .token.keyword,
|
|
201
|
+
.dark .tabbed-code-viewer .token.keyword { color: #569cd6; }
|
|
202
|
+
[data-bs-theme="dark"] .tabbed-code-viewer .token.string,
|
|
203
|
+
.dark .tabbed-code-viewer .token.string { color: #ce9178; }
|
|
204
|
+
[data-bs-theme="dark"] .tabbed-code-viewer .token.number,
|
|
205
|
+
.dark .tabbed-code-viewer .token.number { color: #b5cea8; }
|
|
206
|
+
[data-bs-theme="dark"] .tabbed-code-viewer .token.operator,
|
|
207
|
+
.dark .tabbed-code-viewer .token.operator { color: #d4d4d4; }
|
|
208
|
+
[data-bs-theme="dark"] .tabbed-code-viewer .token.function,
|
|
209
|
+
.dark .tabbed-code-viewer .token.function { color: #dcdcaa; }
|
|
210
|
+
[data-bs-theme="dark"] .tabbed-code-viewer .token.class-name,
|
|
211
|
+
.dark .tabbed-code-viewer .token.class-name { color: #4ec9b0; }
|
|
212
|
+
[data-bs-theme="dark"] .tabbed-code-viewer .token.punctuation,
|
|
213
|
+
.dark .tabbed-code-viewer .token.punctuation { color: #d4d4d4; }
|
|
214
|
+
|
|
215
|
+
/* ===== Responsive ===== */
|
|
216
|
+
@media (max-width: 1200px) {
|
|
217
|
+
.layout-tabbed {
|
|
218
|
+
padding: 40px 60px;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@media (max-width: 900px) {
|
|
223
|
+
.layout-tabbed {
|
|
224
|
+
padding: 30px 40px;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@media (max-width: 600px) {
|
|
229
|
+
.layout-tabbed {
|
|
230
|
+
padding: 20px;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.tabbed-tab-button {
|
|
234
|
+
padding: 8px 12px;
|
|
235
|
+
font-size: 12px;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* ===== Editor Mode ===== */
|
|
240
|
+
.tabbed-editor-container {
|
|
241
|
+
position: absolute;
|
|
242
|
+
top: 0;
|
|
243
|
+
left: 0;
|
|
244
|
+
width: 100%;
|
|
245
|
+
height: 100%;
|
|
246
|
+
overflow: hidden;
|
|
247
|
+
background: #ffffff;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* Button container for copy and recompile (in toolbar) */
|
|
251
|
+
.tabbed-button-container {
|
|
252
|
+
display: flex;
|
|
253
|
+
align-items: center;
|
|
254
|
+
gap: 6px;
|
|
255
|
+
flex-shrink: 0;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/* Copy button (icon only) */
|
|
259
|
+
.tabbed-copy-button {
|
|
260
|
+
display: flex;
|
|
261
|
+
align-items: center;
|
|
262
|
+
justify-content: center;
|
|
263
|
+
background: transparent;
|
|
264
|
+
border: 1px solid #ccc;
|
|
265
|
+
color: #666;
|
|
266
|
+
width: 32px;
|
|
267
|
+
height: 32px;
|
|
268
|
+
border-radius: 4px;
|
|
269
|
+
cursor: pointer;
|
|
270
|
+
transition: background 0.15s, border-color 0.15s, color 0.15s;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.tabbed-copy-button:hover {
|
|
274
|
+
background: #f0f0f0;
|
|
275
|
+
border-color: #999;
|
|
276
|
+
color: #333;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.tabbed-copy-button:active {
|
|
280
|
+
background: #e0e0e0;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.tabbed-copy-button.copied {
|
|
284
|
+
background: #e8f5e9;
|
|
285
|
+
border-color: #4caf50;
|
|
286
|
+
color: #4caf50;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.tabbed-recompile-button {
|
|
290
|
+
display: flex;
|
|
291
|
+
align-items: center;
|
|
292
|
+
gap: 6px;
|
|
293
|
+
background: #4a9eff;
|
|
294
|
+
border: none;
|
|
295
|
+
color: #fff;
|
|
296
|
+
padding: 6px 12px;
|
|
297
|
+
border-radius: 4px;
|
|
298
|
+
cursor: pointer;
|
|
299
|
+
font-family: inherit;
|
|
300
|
+
font-size: 12px;
|
|
301
|
+
font-weight: 500;
|
|
302
|
+
transition: background 0.15s;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.tabbed-recompile-button:hover {
|
|
306
|
+
background: #3a8eef;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.tabbed-recompile-button:active {
|
|
310
|
+
background: #2a7edf;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.tabbed-recompile-button svg {
|
|
314
|
+
flex-shrink: 0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.tabbed-error-display {
|
|
318
|
+
position: absolute;
|
|
319
|
+
bottom: 0;
|
|
320
|
+
left: 0;
|
|
321
|
+
right: 0;
|
|
322
|
+
background: #fff0f0;
|
|
323
|
+
color: #c00;
|
|
324
|
+
padding: 10px 14px;
|
|
325
|
+
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
|
|
326
|
+
font-size: 12px;
|
|
327
|
+
white-space: pre-wrap;
|
|
328
|
+
overflow: auto;
|
|
329
|
+
max-height: 120px;
|
|
330
|
+
border-top: 1px solid #fcc;
|
|
331
|
+
z-index: 10;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.tabbed-fallback-textarea {
|
|
335
|
+
width: 100%;
|
|
336
|
+
height: 100%;
|
|
337
|
+
background: #ffffff;
|
|
338
|
+
color: #000;
|
|
339
|
+
border: none;
|
|
340
|
+
padding: 12px;
|
|
341
|
+
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
|
|
342
|
+
font-size: 13px;
|
|
343
|
+
resize: none;
|
|
344
|
+
outline: none;
|
|
345
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout Types - Common interface for all layout modes
|
|
3
|
+
*/
|
|
4
|
+
import { ShadertoyProject, PassName } from '../project/types';
|
|
5
|
+
/**
|
|
6
|
+
* Result of a recompilation attempt.
|
|
7
|
+
*/
|
|
8
|
+
export interface RecompileResult {
|
|
9
|
+
success: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Callback for recompiling shader code.
|
|
14
|
+
* @param passName - 'common' for common.glsl, or a PassName for individual passes
|
|
15
|
+
* @param newSource - New GLSL source code
|
|
16
|
+
* @returns Result indicating success or failure with error message
|
|
17
|
+
*/
|
|
18
|
+
export type RecompileHandler = (passName: 'common' | PassName, newSource: string) => RecompileResult;
|
|
19
|
+
/**
|
|
20
|
+
* Base interface that all layouts must implement.
|
|
21
|
+
*/
|
|
22
|
+
export interface BaseLayout {
|
|
23
|
+
/**
|
|
24
|
+
* Get the canvas container element where App should render.
|
|
25
|
+
*/
|
|
26
|
+
getCanvasContainer(): HTMLElement;
|
|
27
|
+
/**
|
|
28
|
+
* Set the recompile handler for editor mode.
|
|
29
|
+
* Called by App after initialization to wire up recompilation.
|
|
30
|
+
*/
|
|
31
|
+
setRecompileHandler?(handler: RecompileHandler): void;
|
|
32
|
+
/**
|
|
33
|
+
* Clean up all DOM elements and resources.
|
|
34
|
+
*/
|
|
35
|
+
dispose(): void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Options for creating a layout.
|
|
39
|
+
*/
|
|
40
|
+
export interface LayoutOptions {
|
|
41
|
+
container: HTMLElement;
|
|
42
|
+
project: ShadertoyProject;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Available layout modes.
|
|
46
|
+
*/
|
|
47
|
+
export type LayoutMode = 'fullscreen' | 'default' | 'split' | 'tabbed';
|
|
48
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/layouts/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,QAAQ,EAAE,QAAQ,GAAG,QAAQ,EAC7B,SAAS,EAAE,MAAM,KACd,eAAe,CAAC;AAErB;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,kBAAkB,IAAI,WAAW,CAAC;IAElC;;;OAGG;IACH,mBAAmB,CAAC,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAEtD;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,WAAW,CAAC;IACvB,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Entry Point
|
|
3
|
+
*
|
|
4
|
+
* Loads a demo project from the demos/ folder and starts the App.
|
|
5
|
+
*
|
|
6
|
+
* To run a specific demo:
|
|
7
|
+
* npm run dev:demo <demo-name>
|
|
8
|
+
* npm run build:demo <demo-name>
|
|
9
|
+
*
|
|
10
|
+
* Examples:
|
|
11
|
+
* npm run dev:demo keyboard-test
|
|
12
|
+
* npm run build:demo simple-gradient
|
|
13
|
+
*/
|
|
14
|
+
import './styles/base.css';
|
|
15
|
+
//# sourceMappingURL=main.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,mBAAmB,CAAC"}
|
package/dist-lib/main.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Entry Point
|
|
3
|
+
*
|
|
4
|
+
* Loads a demo project from the demos/ folder and starts the App.
|
|
5
|
+
*
|
|
6
|
+
* To run a specific demo:
|
|
7
|
+
* npm run dev:demo <demo-name>
|
|
8
|
+
* npm run build:demo <demo-name>
|
|
9
|
+
*
|
|
10
|
+
* Examples:
|
|
11
|
+
* npm run dev:demo keyboard-test
|
|
12
|
+
* npm run build:demo simple-gradient
|
|
13
|
+
*/
|
|
14
|
+
import './styles/base.css';
|
|
15
|
+
import { App } from './app/App';
|
|
16
|
+
import { createLayout } from './layouts';
|
|
17
|
+
import { loadDemoProject, DEMO_NAME } from './project/generatedLoader';
|
|
18
|
+
async function main() {
|
|
19
|
+
try {
|
|
20
|
+
console.log(`Loading demo: ${DEMO_NAME}`);
|
|
21
|
+
// Load the demo project from demos/ folder
|
|
22
|
+
// The demo is determined by the generated loader (created by dev-demo.cjs or build-demo.cjs)
|
|
23
|
+
const project = await loadDemoProject();
|
|
24
|
+
console.log(`Loaded project: ${project.meta.title}`);
|
|
25
|
+
console.log(`Passes:`, Object.keys(project.passes).filter(k => project.passes[k]));
|
|
26
|
+
// Get root container element
|
|
27
|
+
const rootContainer = document.getElementById('app');
|
|
28
|
+
if (!rootContainer) {
|
|
29
|
+
throw new Error('Container element #app not found');
|
|
30
|
+
}
|
|
31
|
+
// Create layout
|
|
32
|
+
const layout = createLayout(project.layout, {
|
|
33
|
+
container: rootContainer,
|
|
34
|
+
project,
|
|
35
|
+
});
|
|
36
|
+
// Get canvas container from layout
|
|
37
|
+
const canvasContainer = layout.getCanvasContainer();
|
|
38
|
+
// Create app
|
|
39
|
+
const app = new App({
|
|
40
|
+
container: canvasContainer,
|
|
41
|
+
project,
|
|
42
|
+
pixelRatio: window.devicePixelRatio,
|
|
43
|
+
});
|
|
44
|
+
// Wire up recompile handler for layouts that support it (split, tabbed)
|
|
45
|
+
if (layout.setRecompileHandler) {
|
|
46
|
+
layout.setRecompileHandler((passName, newSource) => {
|
|
47
|
+
const engine = app.getEngine();
|
|
48
|
+
if (!engine) {
|
|
49
|
+
return { success: false, error: 'Engine not initialized' };
|
|
50
|
+
}
|
|
51
|
+
if (passName === 'common') {
|
|
52
|
+
const result = engine.recompileCommon(newSource);
|
|
53
|
+
if (result.success) {
|
|
54
|
+
return { success: true };
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// Return first error
|
|
58
|
+
const firstError = result.errors[0];
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: firstError ? `${firstError.passName}: ${firstError.error}` : 'Unknown error',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
return engine.recompilePass(passName, newSource);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Only start animation loop if there are no compilation errors
|
|
71
|
+
// If there are errors, the error overlay is already shown by App constructor
|
|
72
|
+
if (!app.hasErrors()) {
|
|
73
|
+
app.start();
|
|
74
|
+
console.log('App started!');
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.warn('App not started due to shader compilation errors');
|
|
78
|
+
}
|
|
79
|
+
// Expose for debugging
|
|
80
|
+
window.app = app;
|
|
81
|
+
window.layout = layout;
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error('Failed to initialize:', error);
|
|
85
|
+
const container = document.getElementById('app');
|
|
86
|
+
if (container) {
|
|
87
|
+
container.innerHTML = `
|
|
88
|
+
<div style="color: red; padding: 20px; font-family: monospace;">
|
|
89
|
+
<h2>Error</h2>
|
|
90
|
+
<pre>${error instanceof Error ? error.message : String(error)}</pre>
|
|
91
|
+
</div>
|
|
92
|
+
`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Start when DOM is ready
|
|
97
|
+
if (document.readyState === 'loading') {
|
|
98
|
+
document.addEventListener('DOMContentLoaded', main);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
main();
|
|
102
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generatedLoader.d.ts","sourceRoot":"","sources":["../../src/project/generatedLoader.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,SAAS,iCAAiC,CAAC;AAExD,wBAAsB,eAAe,gDAgBpC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Auto-generated - DO NOT EDIT
|
|
2
|
+
import { loadDemo } from './loaderHelper';
|
|
3
|
+
export const DEMO_NAME = 'course/day5/torus-analytical';
|
|
4
|
+
export async function loadDemoProject() {
|
|
5
|
+
const glslFiles = import.meta.glob('/demos/course/day5/torus-analytical/**/*.glsl', {
|
|
6
|
+
query: '?raw',
|
|
7
|
+
import: 'default',
|
|
8
|
+
});
|
|
9
|
+
const jsonFiles = import.meta.glob('/demos/course/day5/torus-analytical/**/*.json', {
|
|
10
|
+
import: 'default',
|
|
11
|
+
});
|
|
12
|
+
const imageFiles = import.meta.glob('/demos/course/day5/torus-analytical/**/*.{jpg,jpeg,png,gif,webp,bmp}', {
|
|
13
|
+
query: '?url',
|
|
14
|
+
import: 'default',
|
|
15
|
+
});
|
|
16
|
+
return loadDemo(DEMO_NAME, glslFiles, jsonFiles, imageFiles);
|
|
17
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Layer - Config Loader
|
|
3
|
+
*
|
|
4
|
+
* Loads Shadertoy projects from disk into normalized ShadertoyProject representation.
|
|
5
|
+
* Handles both single-pass (no config) and multi-pass (with config) projects.
|
|
6
|
+
*
|
|
7
|
+
* Based on docs/project-spec.md
|
|
8
|
+
*/
|
|
9
|
+
import { ShadertoyProject } from './types';
|
|
10
|
+
/**
|
|
11
|
+
* Load a Shadertoy project from disk.
|
|
12
|
+
*
|
|
13
|
+
* Automatically detects:
|
|
14
|
+
* - Single-pass mode (no config, just image.glsl)
|
|
15
|
+
* - Multi-pass mode (config.json present)
|
|
16
|
+
*
|
|
17
|
+
* @param root - Absolute path to project directory
|
|
18
|
+
* @returns Fully normalized ShadertoyProject
|
|
19
|
+
* @throws Error with descriptive message if project is invalid
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadProject(root: string): Promise<ShadertoyProject>;
|
|
22
|
+
//# sourceMappingURL=loadProject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadProject.d.ts","sourceRoot":"","sources":["../../src/project/loadProject.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAQL,gBAAgB,EAGjB,MAAM,SAAS,CAAC;AAmEjB;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAoBzE"}
|