@stevejtrettel/shader-sandbox 0.1.2 → 0.1.4

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 (113) hide show
  1. package/README.md +259 -235
  2. package/bin/cli.js +106 -14
  3. package/dist-lib/app/App.d.ts +143 -15
  4. package/dist-lib/app/App.d.ts.map +1 -1
  5. package/dist-lib/app/App.js +1343 -108
  6. package/dist-lib/app/app.css +349 -24
  7. package/dist-lib/app/types.d.ts +48 -5
  8. package/dist-lib/app/types.d.ts.map +1 -1
  9. package/dist-lib/editor/EditorPanel.d.ts +2 -2
  10. package/dist-lib/editor/EditorPanel.d.ts.map +1 -1
  11. package/dist-lib/editor/EditorPanel.js +1 -1
  12. package/dist-lib/editor/editor-panel.css +55 -32
  13. package/dist-lib/editor/prism-editor.css +16 -16
  14. package/dist-lib/embed.js +1 -1
  15. package/dist-lib/engine/{ShadertoyEngine.d.ts → ShaderEngine.d.ts} +134 -10
  16. package/dist-lib/engine/ShaderEngine.d.ts.map +1 -0
  17. package/dist-lib/engine/ShaderEngine.js +1523 -0
  18. package/dist-lib/engine/glHelpers.d.ts +24 -0
  19. package/dist-lib/engine/glHelpers.d.ts.map +1 -1
  20. package/dist-lib/engine/glHelpers.js +88 -0
  21. package/dist-lib/engine/std140.d.ts +47 -0
  22. package/dist-lib/engine/std140.d.ts.map +1 -0
  23. package/dist-lib/engine/std140.js +119 -0
  24. package/dist-lib/engine/types.d.ts +55 -5
  25. package/dist-lib/engine/types.d.ts.map +1 -1
  26. package/dist-lib/engine/types.js +1 -1
  27. package/dist-lib/index.d.ts +4 -3
  28. package/dist-lib/index.d.ts.map +1 -1
  29. package/dist-lib/index.js +2 -1
  30. package/dist-lib/layouts/SplitLayout.d.ts +2 -1
  31. package/dist-lib/layouts/SplitLayout.d.ts.map +1 -1
  32. package/dist-lib/layouts/SplitLayout.js +3 -0
  33. package/dist-lib/layouts/TabbedLayout.d.ts.map +1 -1
  34. package/dist-lib/layouts/UILayout.d.ts +55 -0
  35. package/dist-lib/layouts/UILayout.d.ts.map +1 -0
  36. package/dist-lib/layouts/UILayout.js +147 -0
  37. package/dist-lib/layouts/default.css +2 -2
  38. package/dist-lib/layouts/index.d.ts +11 -1
  39. package/dist-lib/layouts/index.d.ts.map +1 -1
  40. package/dist-lib/layouts/index.js +17 -1
  41. package/dist-lib/layouts/split.css +33 -31
  42. package/dist-lib/layouts/tabbed.css +127 -74
  43. package/dist-lib/layouts/types.d.ts +14 -3
  44. package/dist-lib/layouts/types.d.ts.map +1 -1
  45. package/dist-lib/main.js +33 -0
  46. package/dist-lib/project/configHelpers.d.ts +45 -0
  47. package/dist-lib/project/configHelpers.d.ts.map +1 -0
  48. package/dist-lib/project/configHelpers.js +196 -0
  49. package/dist-lib/project/generatedLoader.d.ts +2 -2
  50. package/dist-lib/project/generatedLoader.d.ts.map +1 -1
  51. package/dist-lib/project/generatedLoader.js +23 -5
  52. package/dist-lib/project/loadProject.d.ts +6 -6
  53. package/dist-lib/project/loadProject.d.ts.map +1 -1
  54. package/dist-lib/project/loadProject.js +396 -144
  55. package/dist-lib/project/loaderHelper.d.ts +4 -4
  56. package/dist-lib/project/loaderHelper.d.ts.map +1 -1
  57. package/dist-lib/project/loaderHelper.js +278 -116
  58. package/dist-lib/project/types.d.ts +292 -13
  59. package/dist-lib/project/types.d.ts.map +1 -1
  60. package/dist-lib/project/types.js +13 -1
  61. package/dist-lib/styles/base.css +5 -1
  62. package/dist-lib/uniforms/UniformControls.d.ts +60 -0
  63. package/dist-lib/uniforms/UniformControls.d.ts.map +1 -0
  64. package/dist-lib/uniforms/UniformControls.js +518 -0
  65. package/dist-lib/uniforms/UniformStore.d.ts +74 -0
  66. package/dist-lib/uniforms/UniformStore.d.ts.map +1 -0
  67. package/dist-lib/uniforms/UniformStore.js +145 -0
  68. package/dist-lib/uniforms/UniformsPanel.d.ts +53 -0
  69. package/dist-lib/uniforms/UniformsPanel.d.ts.map +1 -0
  70. package/dist-lib/uniforms/UniformsPanel.js +124 -0
  71. package/dist-lib/uniforms/index.d.ts +11 -0
  72. package/dist-lib/uniforms/index.d.ts.map +1 -0
  73. package/dist-lib/uniforms/index.js +8 -0
  74. package/package.json +16 -1
  75. package/src/app/App.ts +1469 -126
  76. package/src/app/app.css +349 -24
  77. package/src/app/types.ts +53 -5
  78. package/src/editor/EditorPanel.ts +5 -5
  79. package/src/editor/editor-panel.css +55 -32
  80. package/src/editor/prism-editor.css +16 -16
  81. package/src/embed.ts +1 -1
  82. package/src/engine/ShaderEngine.ts +1934 -0
  83. package/src/engine/glHelpers.ts +117 -0
  84. package/src/engine/std140.ts +136 -0
  85. package/src/engine/types.ts +69 -5
  86. package/src/index.ts +4 -3
  87. package/src/layouts/SplitLayout.ts +8 -3
  88. package/src/layouts/TabbedLayout.ts +3 -3
  89. package/src/layouts/UILayout.ts +185 -0
  90. package/src/layouts/default.css +2 -2
  91. package/src/layouts/index.ts +20 -1
  92. package/src/layouts/split.css +33 -31
  93. package/src/layouts/tabbed.css +127 -74
  94. package/src/layouts/types.ts +19 -3
  95. package/src/layouts/ui.css +289 -0
  96. package/src/main.ts +39 -1
  97. package/src/project/configHelpers.ts +225 -0
  98. package/src/project/generatedLoader.ts +27 -6
  99. package/src/project/loadProject.ts +459 -173
  100. package/src/project/loaderHelper.ts +377 -130
  101. package/src/project/types.ts +360 -14
  102. package/src/styles/base.css +5 -1
  103. package/src/styles/theme.css +292 -0
  104. package/src/uniforms/UniformControls.ts +660 -0
  105. package/src/uniforms/UniformStore.ts +166 -0
  106. package/src/uniforms/UniformsPanel.ts +163 -0
  107. package/src/uniforms/index.ts +13 -0
  108. package/src/uniforms/uniform-controls.css +342 -0
  109. package/src/uniforms/uniforms-panel.css +277 -0
  110. package/templates/shaders/example-buffer/config.json +1 -0
  111. package/dist-lib/engine/ShadertoyEngine.d.ts.map +0 -1
  112. package/dist-lib/engine/ShadertoyEngine.js +0 -704
  113. package/src/engine/ShadertoyEngine.ts +0 -929
@@ -1,24 +1,38 @@
1
1
  /**
2
2
  * Layouts - Modular layout system for Shadertoy viewer
3
3
  *
4
- * Provides four layout modes:
4
+ * Provides five layout modes:
5
5
  * - Default: Canvas centered with styling
6
6
  * - Fullscreen: Canvas fills entire viewport
7
7
  * - Split: Canvas on left, code viewer on right
8
8
  * - Tabbed: Single window with tabs for shader and code
9
+ * - UI: Canvas on left, uniforms panel on right
9
10
  */
10
11
 
11
12
  export { FullscreenLayout } from './FullscreenLayout';
12
13
  export { DefaultLayout } from './DefaultLayout';
13
14
  export { SplitLayout } from './SplitLayout';
14
15
  export { TabbedLayout } from './TabbedLayout';
16
+ export { UILayout } from './UILayout';
15
17
  export type { BaseLayout, LayoutOptions, LayoutMode } from './types';
16
18
 
17
19
  import { FullscreenLayout } from './FullscreenLayout';
18
20
  import { DefaultLayout } from './DefaultLayout';
19
21
  import { SplitLayout } from './SplitLayout';
20
22
  import { TabbedLayout } from './TabbedLayout';
23
+ import { UILayout } from './UILayout';
21
24
  import { BaseLayout, LayoutOptions, LayoutMode } from './types';
25
+ import { ThemeMode } from '../project/types';
26
+
27
+ /**
28
+ * Apply theme to the document.
29
+ * Sets the data-theme attribute on the html element.
30
+ *
31
+ * @param theme - Theme mode to apply ('light', 'dark', or 'system')
32
+ */
33
+ export function applyTheme(theme: ThemeMode): void {
34
+ document.documentElement.setAttribute('data-theme', theme);
35
+ }
22
36
 
23
37
  /**
24
38
  * Factory function to create the appropriate layout based on mode.
@@ -31,6 +45,9 @@ export function createLayout(
31
45
  mode: LayoutMode,
32
46
  options: LayoutOptions
33
47
  ): BaseLayout {
48
+ // Apply theme from project configuration
49
+ applyTheme(options.project.theme);
50
+
34
51
  switch (mode) {
35
52
  case 'fullscreen':
36
53
  return new FullscreenLayout(options);
@@ -40,5 +57,7 @@ export function createLayout(
40
57
  return new SplitLayout(options);
41
58
  case 'tabbed':
42
59
  return new TabbedLayout(options);
60
+ case 'ui':
61
+ return new UILayout(options);
43
62
  }
44
63
  }
@@ -16,9 +16,9 @@
16
16
  .layout-split .canvas-container {
17
17
  position: relative;
18
18
  flex: 1;
19
- background: #000;
19
+ background: var(--bg-canvas);
20
20
  border-radius: 8px;
21
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2), 0 3px 8px rgba(0, 0, 0, 0.12);
21
+ box-shadow: var(--shadow-md), var(--shadow-sm);
22
22
  overflow: hidden;
23
23
  }
24
24
 
@@ -27,17 +27,17 @@
27
27
  flex: 1;
28
28
  display: flex;
29
29
  flex-direction: column;
30
- background: white;
30
+ background: var(--bg-secondary);
31
31
  border-radius: 8px;
32
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2), 0 3px 8px rgba(0, 0, 0, 0.12);
32
+ box-shadow: var(--shadow-md), var(--shadow-sm);
33
33
  overflow: hidden;
34
34
  }
35
35
 
36
36
  /* ===== Code Panel ===== */
37
37
  .tab-bar {
38
38
  display: flex;
39
- background: #f8f8f8;
40
- border-bottom: 1px solid #e0e0e0;
39
+ background: var(--tab-bg);
40
+ border-bottom: 1px solid var(--border-primary);
41
41
  padding: 8px 8px 0 8px;
42
42
  gap: 4px;
43
43
  }
@@ -50,17 +50,18 @@
50
50
  font-size: 13px;
51
51
  font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
52
52
  cursor: pointer;
53
- transition: background 0.2s;
54
- color: #666;
53
+ transition: background 0.2s, color 0.2s;
54
+ color: var(--tab-text);
55
55
  }
56
56
 
57
57
  .tab-button:hover {
58
- background: #e8e8e8;
58
+ background: var(--button-bg-hover);
59
+ color: var(--tab-text-hover);
59
60
  }
60
61
 
61
62
  .tab-button.active {
62
- background: white;
63
- color: #000;
63
+ background: var(--bg-secondary);
64
+ color: var(--tab-text-active);
64
65
  font-weight: 500;
65
66
  }
66
67
 
@@ -69,10 +70,10 @@
69
70
  top: 12px;
70
71
  right: 12px;
71
72
  padding: 6px;
72
- background: transparent;
73
+ background: var(--button-bg);
73
74
  border: none;
74
75
  border-radius: 4px;
75
- color: #666;
76
+ color: var(--button-text);
76
77
  cursor: pointer;
77
78
  transition: all 0.2s;
78
79
  z-index: 10;
@@ -82,8 +83,8 @@
82
83
  }
83
84
 
84
85
  .copy-button:hover {
85
- background: rgba(0, 0, 0, 0.05);
86
- color: #333;
86
+ background: var(--button-bg-hover);
87
+ color: var(--button-text-hover);
87
88
  }
88
89
 
89
90
  .copy-button:active {
@@ -91,7 +92,7 @@
91
92
  }
92
93
 
93
94
  .copy-button.copied {
94
- color: #4caf50;
95
+ color: var(--success-text);
95
96
  }
96
97
 
97
98
  .code-viewer {
@@ -99,7 +100,7 @@
99
100
  min-height: 0; /* Allow shrinking below content size in flexbox */
100
101
  overflow: auto;
101
102
  position: relative;
102
- background: white;
103
+ background: var(--code-bg);
103
104
  }
104
105
 
105
106
  /* Prism.js syntax highlighting */
@@ -109,7 +110,8 @@
109
110
  font-size: 13px;
110
111
  line-height: 1.5;
111
112
  font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
112
- background: white;
113
+ background: var(--code-bg);
114
+ color: var(--code-text);
113
115
  }
114
116
 
115
117
  .code-viewer code {
@@ -117,23 +119,23 @@
117
119
  font-size: inherit;
118
120
  }
119
121
 
120
- /* Prism theme - simple light theme */
121
- .token.comment { color: #6a9955; }
122
- .token.keyword { color: #0000ff; }
123
- .token.string { color: #a31515; }
124
- .token.number { color: #098658; }
125
- .token.operator { color: #000000; }
126
- .token.function { color: #795e26; }
127
- .token.class-name { color: #267f99; }
128
- .token.punctuation { color: #000000; }
122
+ /* Prism theme - uses CSS variables */
123
+ .token.comment { color: var(--syntax-comment); }
124
+ .token.keyword { color: var(--syntax-keyword); }
125
+ .token.string { color: var(--syntax-string); }
126
+ .token.number { color: var(--syntax-number); }
127
+ .token.operator { color: var(--syntax-operator); }
128
+ .token.function { color: var(--syntax-function); }
129
+ .token.class-name { color: var(--syntax-class); }
130
+ .token.punctuation { color: var(--syntax-punctuation); }
129
131
 
130
132
  /* Image tab styling */
131
133
  .tab-button.image-tab {
132
- color: #7c4dff;
134
+ color: var(--accent-secondary);
133
135
  }
134
136
 
135
137
  .tab-button.image-tab.active {
136
- color: #7c4dff;
138
+ color: var(--accent-secondary);
137
139
  }
138
140
 
139
141
  /* Image viewer */
@@ -143,7 +145,7 @@
143
145
  justify-content: center;
144
146
  height: 100%;
145
147
  padding: 16px;
146
- background: #f5f5f5;
148
+ background: var(--image-viewer-bg);
147
149
  }
148
150
 
149
151
  .image-viewer img {
@@ -151,7 +153,7 @@
151
153
  max-height: 100%;
152
154
  object-fit: contain;
153
155
  border-radius: 4px;
154
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
156
+ box-shadow: var(--shadow-sm);
155
157
  }
156
158
 
157
159
  /* ===== Responsive adjustments ===== */
@@ -3,14 +3,6 @@
3
3
  * Single window with tabs switching between shader and code views
4
4
  */
5
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
6
  .layout-tabbed {
15
7
  width: 100%;
16
8
  height: 100%;
@@ -30,7 +22,7 @@
30
22
  height: 650px; /* 600px content + ~50px tabs */
31
23
  max-height: 100%;
32
24
  border-radius: 8px;
33
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25), 0 5px 15px rgba(0, 0, 0, 0.15);
25
+ box-shadow: var(--shadow-lg), var(--shadow-sm);
34
26
  overflow: hidden;
35
27
  }
36
28
 
@@ -39,8 +31,8 @@
39
31
  display: flex;
40
32
  align-items: center;
41
33
  flex-shrink: 0;
42
- background: #f8f8f8;
43
- border-bottom: 1px solid #e0e0e0;
34
+ background: var(--tab-bg);
35
+ border-bottom: 1px solid var(--border-primary);
44
36
  padding-right: 8px;
45
37
  }
46
38
 
@@ -60,7 +52,7 @@
60
52
  }
61
53
 
62
54
  .tabbed-tab-bar::-webkit-scrollbar-thumb {
63
- background: #ccc;
55
+ background: var(--border-secondary);
64
56
  border-radius: 2px;
65
57
  }
66
58
 
@@ -73,18 +65,18 @@
73
65
  font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
74
66
  cursor: pointer;
75
67
  transition: color 0.15s, border-color 0.15s;
76
- color: #666;
68
+ color: var(--tab-text);
77
69
  white-space: nowrap;
78
70
  flex-shrink: 0;
79
71
  }
80
72
 
81
73
  .tabbed-tab-button:hover {
82
- color: #333;
74
+ color: var(--tab-text-hover);
83
75
  }
84
76
 
85
77
  .tabbed-tab-button.active {
86
- color: #000;
87
- border-bottom-color: #4a9eff;
78
+ color: var(--tab-text-active);
79
+ border-bottom-color: var(--tab-border-active);
88
80
  }
89
81
 
90
82
  .tabbed-tab-button.shader-tab {
@@ -92,12 +84,28 @@
92
84
  }
93
85
 
94
86
  .tabbed-tab-button.image-tab {
95
- color: #7c4dff;
87
+ color: var(--accent-secondary);
96
88
  }
97
89
 
98
90
  .tabbed-tab-button.image-tab.active {
99
- color: #7c4dff;
100
- border-bottom-color: #7c4dff;
91
+ color: var(--accent-secondary);
92
+ border-bottom-color: var(--accent-secondary);
93
+ }
94
+
95
+ .tabbed-tab-button.uniforms-tab {
96
+ color: var(--accent-tertiary, var(--accent-primary));
97
+ padding: 8px 12px;
98
+ }
99
+
100
+ .tabbed-tab-button.uniforms-tab.active {
101
+ color: var(--accent-tertiary, var(--accent-primary));
102
+ border-bottom-color: var(--accent-tertiary, var(--accent-primary));
103
+ }
104
+
105
+ .tabbed-tab-button .uniforms-icon {
106
+ width: 18px;
107
+ height: 18px;
108
+ display: block;
101
109
  }
102
110
 
103
111
  /* ===== Content Area ===== */
@@ -105,7 +113,7 @@
105
113
  flex: 1;
106
114
  min-height: 0; /* Critical for flexbox scrolling */
107
115
  position: relative;
108
- background: #000;
116
+ background: var(--bg-canvas);
109
117
  overflow: hidden;
110
118
  }
111
119
 
@@ -149,8 +157,8 @@
149
157
  text-align: right;
150
158
  padding-right: 16px;
151
159
  margin-right: 16px;
152
- border-right: 1px solid var(--tab-border);
153
- color: var(--line-number-text);
160
+ border-right: 1px solid var(--code-line-border);
161
+ color: var(--code-line-number);
154
162
  user-select: none;
155
163
  flex-shrink: 0;
156
164
  padding-left: 16px;
@@ -171,7 +179,7 @@
171
179
  display: flex;
172
180
  align-items: center;
173
181
  justify-content: center;
174
- background: #f5f5f5;
182
+ background: var(--image-viewer-bg);
175
183
  padding: 20px;
176
184
  box-sizing: border-box;
177
185
  }
@@ -181,36 +189,18 @@
181
189
  max-height: 100%;
182
190
  object-fit: contain;
183
191
  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; }
192
+ box-shadow: var(--shadow-sm);
193
+ }
194
+
195
+ /* ===== Prism Theme (uses CSS variables) ===== */
196
+ .tabbed-code-viewer .token.comment { color: var(--syntax-comment); }
197
+ .tabbed-code-viewer .token.keyword { color: var(--syntax-keyword); }
198
+ .tabbed-code-viewer .token.string { color: var(--syntax-string); }
199
+ .tabbed-code-viewer .token.number { color: var(--syntax-number); }
200
+ .tabbed-code-viewer .token.operator { color: var(--syntax-operator); }
201
+ .tabbed-code-viewer .token.function { color: var(--syntax-function); }
202
+ .tabbed-code-viewer .token.class-name { color: var(--syntax-class); }
203
+ .tabbed-code-viewer .token.punctuation { color: var(--syntax-punctuation); }
214
204
 
215
205
  /* ===== Responsive ===== */
216
206
  @media (max-width: 1200px) {
@@ -244,7 +234,7 @@
244
234
  width: 100%;
245
235
  height: 100%;
246
236
  overflow: hidden;
247
- background: #ffffff;
237
+ background: var(--code-bg);
248
238
  }
249
239
 
250
240
  /* Button container for copy and recompile (in toolbar) */
@@ -260,9 +250,9 @@
260
250
  display: flex;
261
251
  align-items: center;
262
252
  justify-content: center;
263
- background: transparent;
264
- border: 1px solid #ccc;
265
- color: #666;
253
+ background: var(--button-bg);
254
+ border: 1px solid var(--button-border);
255
+ color: var(--button-text);
266
256
  width: 32px;
267
257
  height: 32px;
268
258
  border-radius: 4px;
@@ -271,43 +261,43 @@
271
261
  }
272
262
 
273
263
  .tabbed-copy-button:hover {
274
- background: #f0f0f0;
275
- border-color: #999;
276
- color: #333;
264
+ background: var(--button-bg-hover);
265
+ border-color: var(--button-border-hover);
266
+ color: var(--button-text-hover);
277
267
  }
278
268
 
279
269
  .tabbed-copy-button:active {
280
- background: #e0e0e0;
270
+ background: var(--button-bg-hover);
281
271
  }
282
272
 
283
273
  .tabbed-copy-button.copied {
284
- background: #e8f5e9;
285
- border-color: #4caf50;
286
- color: #4caf50;
274
+ background: var(--success-bg);
275
+ border-color: var(--success-border);
276
+ color: var(--success-text);
287
277
  }
288
278
 
289
279
  .tabbed-recompile-button {
290
280
  display: flex;
291
281
  align-items: center;
292
282
  gap: 6px;
293
- background: #4a9eff;
283
+ background: var(--recompile-bg);
294
284
  border: none;
295
- color: #fff;
285
+ color: var(--recompile-text);
296
286
  padding: 6px 12px;
297
287
  border-radius: 4px;
298
288
  cursor: pointer;
299
289
  font-family: inherit;
300
290
  font-size: 12px;
301
291
  font-weight: 500;
302
- transition: background 0.15s;
292
+ transition: background 0.15s, color 0.15s;
303
293
  }
304
294
 
305
295
  .tabbed-recompile-button:hover {
306
- background: #3a8eef;
296
+ background: var(--recompile-bg-hover);
307
297
  }
308
298
 
309
299
  .tabbed-recompile-button:active {
310
- background: #2a7edf;
300
+ background: var(--recompile-bg-active);
311
301
  }
312
302
 
313
303
  .tabbed-recompile-button svg {
@@ -319,23 +309,23 @@
319
309
  bottom: 0;
320
310
  left: 0;
321
311
  right: 0;
322
- background: #fff0f0;
323
- color: #c00;
312
+ background: var(--error-bg);
313
+ color: var(--error-text);
324
314
  padding: 10px 14px;
325
315
  font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
326
316
  font-size: 12px;
327
317
  white-space: pre-wrap;
328
318
  overflow: auto;
329
319
  max-height: 120px;
330
- border-top: 1px solid #fcc;
320
+ border-top: 1px solid var(--error-border);
331
321
  z-index: 10;
332
322
  }
333
323
 
334
324
  .tabbed-fallback-textarea {
335
325
  width: 100%;
336
326
  height: 100%;
337
- background: #ffffff;
338
- color: #000;
327
+ background: var(--code-bg);
328
+ color: var(--code-text);
339
329
  border: none;
340
330
  padding: 12px;
341
331
  font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
@@ -343,3 +333,66 @@
343
333
  resize: none;
344
334
  outline: none;
345
335
  }
336
+
337
+ /* ===== Uniforms Container (semi-transparent overlay) ===== */
338
+ .tabbed-uniforms-container {
339
+ position: absolute;
340
+ top: 0;
341
+ left: 0;
342
+ width: 100%;
343
+ height: 100%;
344
+ overflow-y: auto;
345
+ background: rgba(0, 0, 0, 0.5);
346
+ backdrop-filter: blur(8px);
347
+ display: flex;
348
+ justify-content: center;
349
+ padding: 20px;
350
+ box-sizing: border-box;
351
+ }
352
+
353
+ /* Center the uniform controls and constrain width */
354
+ .tabbed-uniforms-container .uniform-controls {
355
+ max-width: 400px;
356
+ width: 100%;
357
+ background: rgba(30, 30, 35, 0.95);
358
+ border-radius: 12px;
359
+ padding: 20px;
360
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
361
+ height: fit-content;
362
+ }
363
+
364
+ /* Ensure text is visible in the overlay */
365
+ .tabbed-uniforms-container .uniform-control-label {
366
+ color: #e0e0e0;
367
+ }
368
+
369
+ .tabbed-uniforms-container .uniform-control-value {
370
+ color: #a0a0a0;
371
+ background: rgba(0, 0, 0, 0.3);
372
+ }
373
+
374
+ .tabbed-uniforms-container .uniform-controls-header {
375
+ color: #909090;
376
+ border-bottom-color: rgba(255, 255, 255, 0.1);
377
+ }
378
+
379
+ .tabbed-uniforms-container .uniform-control-slider {
380
+ background: rgba(255, 255, 255, 0.1);
381
+ }
382
+
383
+ .tabbed-uniforms-container .uniform-control-slider::-webkit-slider-thumb {
384
+ background: #ffffff;
385
+ }
386
+
387
+ .tabbed-uniforms-container .uniform-control-slider::-moz-range-thumb {
388
+ background: #ffffff;
389
+ }
390
+
391
+ .tabbed-uniforms-container .uniform-control-vec-component {
392
+ color: #909090;
393
+ }
394
+
395
+ .tabbed-uniforms-container .uniform-control-vec-value {
396
+ color: #a0a0a0;
397
+ background: rgba(0, 0, 0, 0.3);
398
+ }
@@ -2,7 +2,7 @@
2
2
  * Layout Types - Common interface for all layout modes
3
3
  */
4
4
 
5
- import { ShadertoyProject, PassName } from '../project/types';
5
+ import { ShaderProject, PassName, UniformValue } from '../project/types';
6
6
 
7
7
  /**
8
8
  * Result of a recompilation attempt.
@@ -23,6 +23,16 @@ export type RecompileHandler = (
23
23
  newSource: string
24
24
  ) => RecompileResult;
25
25
 
26
+ /**
27
+ * Callback for uniform value changes.
28
+ * @param name - Uniform name
29
+ * @param value - New uniform value
30
+ */
31
+ export type UniformChangeHandler = (
32
+ name: string,
33
+ value: UniformValue
34
+ ) => void;
35
+
26
36
  /**
27
37
  * Base interface that all layouts must implement.
28
38
  */
@@ -38,6 +48,12 @@ export interface BaseLayout {
38
48
  */
39
49
  setRecompileHandler?(handler: RecompileHandler): void;
40
50
 
51
+ /**
52
+ * Set the uniform change handler for uniform controls.
53
+ * Called by App after initialization to wire up uniform changes.
54
+ */
55
+ setUniformHandler?(handler: UniformChangeHandler): void;
56
+
41
57
  /**
42
58
  * Clean up all DOM elements and resources.
43
59
  */
@@ -49,10 +65,10 @@ export interface BaseLayout {
49
65
  */
50
66
  export interface LayoutOptions {
51
67
  container: HTMLElement;
52
- project: ShadertoyProject;
68
+ project: ShaderProject;
53
69
  }
54
70
 
55
71
  /**
56
72
  * Available layout modes.
57
73
  */
58
- export type LayoutMode = 'fullscreen' | 'default' | 'split' | 'tabbed';
74
+ export type LayoutMode = 'fullscreen' | 'default' | 'split' | 'tabbed' | 'ui';