Skip to content

Commit f5120db

Browse files
committed
feat: AI preset generator, Spotify integration, community gallery API
AI Preset Generator (Butterchurn-resistant moat — needs compiler pipeline): - assets/js/milkdrop/preset-prompt.ts — DSL grammar spec, prompt builder for LLMs. Specifies all 37 functions, 6 audio registers, 8 state slots, 4 expression block types. Few-shot prompt format. - assets/js/core/services/preset-generator.ts — Calls Workers AI or fallback procedural generator, compiles result through JIT pipeline, validates diagnostics. - functions/api/generate-preset.ts — POST endpoint with Llama 4 Scout via Workers AI binding. Cleans LLM output, falls back to keyword- matched procedural preset if AI unavailable. Spotify Web Playback SDK: - assets/js/core/services/spotify-service.ts — PKCE OAuth flow, SDK lifecycle (idle→authorizing→connecting→ready), token/refresh management, player controls, createMediaElementSource audio routing. - functions/api/spotify-token.ts — Proxies auth code and refresh token exchanges to Spotify API. - assets/js/frontend/SpotifyPlayerUI.tsx — React component with connect button and now-playing controls. Community Preset Gallery: - functions/api/presets.ts — Full CRUD: list with search/tag/sort, single get with R2 .milk source, upload with R2+D1 storage. CORS. - schema/d1-presets.sql — D1 schema with presets + favorites tables and indexes. - wrangler.d1.example.toml, wrangler-secrets.example.json — config templates for D1, R2, and Spotify OAuth secrets.
1 parent d75b003 commit f5120db

12 files changed

Lines changed: 1136 additions & 2 deletions
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { compileMilkdropPresetSource } from '../../milkdrop/compiler.ts';
2+
import type { MilkdropCompiledPreset } from '../../milkdrop/types.ts';
3+
import { buildGeneratePrompt } from '../../milkdrop/preset-prompt.ts';
4+
5+
export type GenerateStatus =
6+
| { status: 'generating' }
7+
| { status: 'compiling' }
8+
| { status: 'ready'; preset: MilkdropCompiledPreset }
9+
| { status: 'error'; message: string };
10+
11+
export async function generatePreset(
12+
description: string,
13+
options: {
14+
complexity?: 'simple' | 'moderate' | 'complex';
15+
apiEndpoint?: string;
16+
} = {},
17+
): Promise<MilkdropCompiledPreset> {
18+
const endpoint = options.apiEndpoint || '/api/generate-preset';
19+
20+
const response = await fetch(endpoint, {
21+
method: 'POST',
22+
headers: { 'Content-Type': 'application/json' },
23+
body: JSON.stringify({
24+
description,
25+
complexity: options.complexity || 'moderate',
26+
}),
27+
});
28+
29+
if (!response.ok) {
30+
const err = await response.text();
31+
throw new Error(`Generator API error: ${response.status} ${err}`);
32+
}
33+
34+
const data = (await response.json()) as { milkSource: string };
35+
36+
const compiled = compileMilkdropPresetSource(data.milkSource, {
37+
id: `ai-${Date.now()}`,
38+
title: 'AI Generated',
39+
origin: 'generated',
40+
});
41+
42+
if (compiled.diagnostics.filter((d) => d.severity === 'error').length > 0) {
43+
throw new Error(
44+
`Generated preset has compilation errors: ${compiled.diagnostics.map((d) => d.message).join('; ')}`,
45+
);
46+
}
47+
48+
return compiled;
49+
}
50+
51+
export async function generatePresetOnWorker(
52+
description: string,
53+
complexity: 'simple' | 'moderate' | 'complex' = 'moderate',
54+
): Promise<MilkdropCompiledPreset> {
55+
buildGeneratePrompt(description, complexity);
56+
57+
const response = await fetch('/api/generate-preset', {
58+
method: 'POST',
59+
headers: { 'Content-Type': 'application/json' },
60+
body: JSON.stringify({ description, complexity }),
61+
});
62+
63+
if (!response.ok) {
64+
const text = await response.text();
65+
throw new Error(text);
66+
}
67+
68+
const { milkSource } = (await response.json()) as { milkSource: string };
69+
70+
const sourceMeta = {
71+
id: `ai-${Date.now()}`,
72+
title: `AI: ${description}`,
73+
origin: 'generated' as const,
74+
};
75+
const compiled = compileMilkdropPresetSource(milkSource, sourceMeta);
76+
77+
return compiled;
78+
}

0 commit comments

Comments
 (0)