Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions public/shaders/gen-crystalline-chrono-dyson.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ struct Uniforms {
ripples: array<vec4<f32>, 50>,
};

// Custom mod function
fn mod(x: f32, y: f32) -> f32 {
// Custom fmod function (avoids reserved keyword 'mod')
fn fmod(x: f32, y: f32) -> f32 {
return x - y * floor(x / y);
}

Expand Down
11 changes: 10 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ const SHADER_DEFAULTS: Record<string, number[]> = {
'bloom': [0.50, 0.40, 0.55, 0.35],
'dynamic-lens-flares': [0.45, 0.50, 0.40, 0.45],
'chromatic-crawler': [0.40, 0.45, 0.50, 0.35],

// Image processing effects
'digital-haze': [0.50, 0.40, 0.45, 0.35],

// Generative: Crystalline Chrono-Dyson (Panel Density, Quasar Glow, Flux Speed, Swarm Count)
'gen-crystalline-chrono-dyson': [0.40, 0.55, 0.50, 0.45],
};

// Helper to get shader defaults - tries multiple ID variations for matching
Expand Down Expand Up @@ -365,7 +371,7 @@ function MainApp() {

// Record play event (fire-and-forget)
if (ok) {
fetch(`${SHADER_WGSL_URL}/${shaderEntry.id}/play`, { method: 'POST' }).catch(() => {});
fetch(`${SHADER_WGSL_URL}/${shaderEntry.id}/play`, { method: 'GET' }).catch(() => {});
}
} catch (error) {
console.error(`❌ Failed to load shader ${shaderEntry.id}:`, error);
Expand Down Expand Up @@ -406,6 +412,9 @@ function MainApp() {
const response = await fetch(IMAGE_MANIFEST_URL);
if (response.ok) {
const data = await response.json();
if (!Array.isArray(data)) {
throw new TypeError(`API response is not an array, received: ${typeof data}`);
}
manifest = data.map((item: any) => ({
url: item.url,
tags: item.description ? item.description.toLowerCase().split(/[\s,]+/) : [],
Expand Down
33 changes: 33 additions & 0 deletions src/services/contentLoader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,37 @@ describe('fetchContentManifest', () => {
});
expect(result.videos).toEqual(FALLBACK_VIDEOS);
});

it('falls back to the local manifest when the API returns a non-array', async () => {
fetchMock.mockImplementation((url: RequestInfo | URL) => {
if (url === IMAGE_MANIFEST_URL) {
return Promise.resolve({
ok: true,
json: async () => ({ error: 'unexpected object' }),
});
}

if (url === LOCAL_MANIFEST_URL) {
return Promise.resolve({
ok: true,
json: async () => ({
images: [{ url: 'gallery/fallback.png', tags: ['fallback'] }],
videos: [],
}),
});
}

return Promise.reject(new Error(`Unexpected url: ${String(url)}`));
});

const result = await fetchContentManifest();

expect(result.manifest).toEqual([
{
url: `${BUCKET_BASE_URL}/gallery/fallback.png`,
tags: ['fallback'],
description: 'fallback',
},
]);
});
});
3 changes: 3 additions & 0 deletions src/services/contentLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export async function fetchContentManifest(): Promise<LoadedContent> {
const response = await fetch(IMAGE_MANIFEST_URL);
if (response.ok) {
const data = await response.json() as ApiManifestItem[];
if (!Array.isArray(data)) {
throw new TypeError(`API response is not an array, received: ${typeof data}`);
}
manifest = data.map((item) => ({
url: item.url,
tags: item.description ? item.description.toLowerCase().split(/[\s,]+/) : [],
Expand Down
3 changes: 3 additions & 0 deletions src/services/shaderApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,9 @@ class ShaderApiService {
const response = await fetch(url);
if (!response.ok) throw new Error(`API ${response.status}`);
const data: ApiShaderEntry[] = await response.json();
if (!Array.isArray(data)) {
throw new TypeError(`API response is not an array, received: ${typeof data}`);
}

console.log(`[ShaderApi] Received ${data.length} shaders from API`);

Expand Down