PageSourceSearch

https://www.hamienet.com/assets/AlgorithmEditor-BuTd-7oq.js

js hamienet.com collected 2026-09-24 18:59:37 UTC 20,995 bytes, 414 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js";
2import { S as ScoreMusicXmlViewer } from "./ScoreMusicXmlViewer-2usqegaw.js";
3import { aX as ArrowLeft, cc as Save, e9 as CloudUpload, dP as GitBranch, e2 as Bot, eu as Terminal, bi as Sparkles, bU as Play, b1 as CircleAlert } from "./vendor-dVNaLJx_.js";
4import "./vendor-i18n-R-bMoYp_.js";
5function AlgorithmEditor({ currentUser, onAuthRequested, id, onNavigate }) {
6  const [algo, setAlgo] = reactExports.useState(null);
7  const [name, setName] = reactExports.useState("");
8  const [description, setDescription] = reactExports.useState("");
9  const [content, setContent] = reactExports.useState("");
10  const [benchmarks, setBenchmarks] = reactExports.useState([]);
11  const [selectedBenchmark, setSelectedBenchmark] = reactExports.useState("basic_scale");
12  const [customMidiId, setCustomMidiId] = reactExports.useState("");
13  const [generatedXml, setGeneratedXml] = reactExports.useState("");
14  const [testDiagnostics, setTestDiagnostics] = reactExports.useState(null);
15  const [isTesting, setIsTesting] = reactExports.useState(false);
16  const [isSaving, setIsSaving] = reactExports.useState(false);
17  const isNew = id === "new";
18  reactExports.useEffect(() => {
19    if (!currentUser) return onAuthRequested();
20    fetch("/api/algorithms/benchmarks").then((r) => r.json()).then((data) => {
21      if (Array.isArray(data)) setBenchmarks(data);
22    }).catch((err) => console.error("Failed to load benchmarks:", err));
23    if (isNew) {
24      setName("My Quantization Standard");
25      setDescription("Custom community quantization logic for unquantized MIDIs.");
26      setContent(`function processScore(midiData) {
27    // 1. You receive midiData as a parsed JSON object (via @tonejs/midi format)
28    // 2. You must return a valid MusicXML 3.1 string
29    
30    // Example: Generate basic score with staves
31    return \`<?xml version="1.0" encoding="UTF-8"?>
32<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
33<score-partwise version="3.1">
34  <part-list>
35    <score-part id="P1"><part-name>Piano</part-name></score-part>
36  </part-list>
37  <part id="P1">
38    <measure number="1">
39      <attributes>
40        <divisions>4</divisions>
41        <key><fifths>0</fifths></key>
42        <time><beats>4</beats><beat-type>4</beat-type></time>
43        <clef><sign>G</sign><line>2</line></clef>
44      </attributes>
45      <note>
46        <pitch><step>C</step><octave>4</octave></pitch>
47        <duration>4</duration>
48        <type>quarter</type>
49      </note>
50      <note>
51        <pitch><step>E</step><octave>4</octave></pitch>
52        <duration>4</duration>
53        <type>quarter</type>
54      </note>
55      <note>
56        <pitch><step>G</step><octave>4</octave></pitch>
57        <duration>4</duration>
58        <type>quarter</type>
59      </note>
60      <note>
61        <pitch><step>C</step><octave>5</octave></pitch>
62        <duration>4</duration>
63        <type>quarter</type>
64      </note>
65    </measure>
66  </part>
67</score-partwise>\`;
68}`);
69    } else {
70      fetch(`/api/algorithms/${id}`).then((r) => r.json()).then((data) => {
71        if (data.error) {
72          alert(`Error: ${data.error}`);
73          return onNavigate("algorithm-studio");
74        }
75        setAlgo(data);
76        setName(data.name || "");
77        setDescription(data.description || "");
78        setContent(data.content || "");
79      }).catch((err) => console.error("Failed to load algorithm:", err));
80    }
81  }, [id, currentUser, isNew, onAuthRequested, onNavigate]);
82  const isOwner = isNew ? true : algo ? String(algo.authorId) === String(currentUser == null ? void 0 : currentUser.id) : true;
83  const isDraft = isNew || algo && algo.status === "DRAFT";
84  const isReadOnly = !isDraft || !isOwner;
85  const handleSave = async () => {
86    if (!name.trim()) return alert("Algorithm name is required");
87    setIsSaving(true);
88    try {
89      if (isNew) {
90        const res = await fetch("/api/algorithms", {
91          method: "POST",
92          headers: { "Content-Type": "application/json" },
93          body: JSON.stringify({ name, description, content })
94        });
95        const data = await res.json();
96        if (data.error) {
97          alert(`Error creating draft: ${data.error}`);
98          return;
99        }
100        alert("Draft created successfully!");
101        onNavigate("algorithm-editor", data.id);
102      } else {
103        const res = await fetch(`/api/algorithms/${id}`, {
104          method: "PUT",
105          headers: { "Content-Type": "application/json" },
106          body: JSON.stringify({ name, description, content })
107        });
108        const data = await res.json();
109        if (data.error) {
110          alert(`Error saving draft: ${data.error}`);
111          return;
112        }
113        alert("Draft saved successfully!");
114      }
115    } catch (e) {
116      alert(`Save failed: ${e.message}`);
117    } finally {
118      setIsSaving(false);
119    }
120  };
121  const handlePublish = async () => {
122    if (!name.trim()) return alert("Algorithm name is required before publishing");
123    if (!window.confirm("Publish to Phase 1 Proposals? This will submit your draft to the community and open upvoting.")) return;
124    setIsSaving(true);
125    try {
126      let targetId = id;
127      if (isNew) {
128        const saveRes = await fetch("/api/algorithms", {
129          method: "POST",
130          headers: { "Content-Type": "application/json" },
131          body: JSON.stringify({ name, description, content })
132        });
133        const saveData = await saveRes.json();
134        if (saveData.error) {
135          alert(`Error saving draft: ${saveData.error}`);
136          return;
137        }
138        targetId = saveData.id;
139      } else {
140        await fetch(`/api/algorithms/${id}`, {
141          method: "PUT",
142          headers: { "Content-Type": "application/json" },
143          body: JSON.stringify({ name, description, content })
144        });
145      }
146      const pubRes = await fetch(`/api/algorithms/${targetId}/publish`, {
147        method: "POST",
148        headers: { "Content-Type": "application/json" }
149      });
150      const pubData = await pubRes.json();
151      if (pubData.error) {
152        alert(`Error publishing: ${pubData.error}`);
153        return;
154      }
155      alert("Successfully published to Phase 1 Proposals!");
156      onNavigate("algorithm-studio");
157    } catch (e) {
158      alert(`Publish failed: ${e.message}`);
159    } finally {
160      setIsSaving(false);
161    }
162  };
163  const handleBranch = async () => {
164    if (!currentUser) return onAuthRequested();
165    try {
166      const res = await fetch("/api/algorithms", {
167        method: "POST",
168        headers: { "Content-Type": "application/json" },
169        body: JSON.stringify({
170          name: `${name} (Branch)`,
171          description: `Branched from ${name}`,
172          content,
173          parentAlgorithmId: algo == null ? void 0 : algo.id
174        })
175      });
176      const data = await res.json();
177      if (data.error) return alert(`Branch error: ${data.error}`);
178      onNavigate("algorithm-editor", data.id);
179    } catch (e) {
180      alert(`Branch failed: ${e.message}`);
181    }
182  };
183  const handleTest = async () => {
184    setIsTesting(true);
185    setTestDiagnostics(null);
186    const startTime = Date.now();
187    try {
188      let payload = { scriptContent: content };
189      if (selectedBenchmark === "custom") {
190        if (!customMidiId.trim()) {
191          alert("Please enter a valid MIDI track ID to test.");
192          setIsTesting(false);
193          return;
194        }
195        const trackNum = parseInt(customMidiId.trim(), 10);
196        const groupFolder = Math.floor(trackNum / 1e3);
197        const midiRes = await fetch(`/storage/tracks/midi/${groupFolder}/${trackNum}.mid`);
198        if (!midiRes.ok) throw new Error(`MIDI track #${trackNum} not found in public storage`);
199        const arrayBuffer = await midiRes.arrayBuffer();
200        const base64String = btoa(
201          new Uint8Array(arrayBuffer).reduce((data2, byte) => data2 + String.fromCharCode(byte), "")
202        );
203        payload.midiBase64 = base64String;
204      } else {
205        payload.benchmarkId = selectedBenchmark;
206      }
207      const res = await fetch("/api/algorithms/sandbox-test", {
208        method: "POST",
209        headers: { "Content-Type": "application/json" },
210        body: JSON.stringify(payload)
211      });
212      const data = await res.json();
213      const elapsed = Date.now() - startTime;
214      if (data.error) {
215        setTestDiagnostics({ elapsedMs: elapsed, error: data.error });
216        setGeneratedXml("");
217      } else {
218        setGeneratedXml(data.xml || "");
219        setTestDiagnostics({ elapsedMs: elapsed });
220      }
221    } catch (e) {
222      setTestDiagnostics({ elapsedMs: Date.now() - startTime, error: e.message });
223      setGeneratedXml("");
224    } finally {
225      setIsTesting(false);
226    }
227  };
228  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-7xl mx-auto p-6 h-screen flex flex-col animate-in fade-in", children: [
229    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-4 shrink-0 flex-wrap gap-4", children: [
230      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [
231        /* @__PURE__ */ jsxRuntimeExports.jsx(
232          "button",
233          {
234            type: "button",
235            onClick: () => onNavigate("algorithm-studio"),
236            className: "p-2 bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700 rounded-full transition-colors",
237            title: "Back to Studio",
238            children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 20 })
239          }
240        ),
241        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
242          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5", children: [
243            /* @__PURE__ */ jsxRuntimeExports.jsx(
244              "input",
245              {
246                value: name,
247                onChange: (e) => setName(e.target.value),
248                placeholder: "Algorithm Name",
249                disabled: isReadOnly,
250                className: "text-2xl font-black bg-transparent outline-none placeholder-zinc-400 text-zinc-900 dark:text-zinc-100"
251              }
252            ),
253            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2.5 py-0.5 rounded-full text-[11px] font-bold uppercase tracking-wider ${isNew || (algo == null ? void 0 : algo.status) === "DRAFT" ? "bg-amber-500/10 text-amber-600 dark:text-amber-400 border border-amber-500/30" : (algo == null ? void 0 : algo.status) === "PHASE1" ? "bg-orange-500/10 text-orange-600 dark:text-orange-400 border border-orange-500/30" : (algo == null ? void 0 : algo.status) === "PHASE2" ? "bg-blue-500/10 text-blue-600 dark:text-blue-400 border border-blue-500/30" : "bg-green-500/10 text-green-600 dark:text-green-400 border border-green-500/30"}`, children: isNew ? "New Draft" : (algo == null ? void 0 : algo.status) || "Draft" })
254          ] }),
255          /* @__PURE__ */ jsxRuntimeExports.jsx(
256            "input",
257            {
258              value: description,
259              onChange: (e) => setDescription(e.target.value),
260              placeholder: "Brief description of the quantization approach...",
261              disabled: isReadOnly,
262              className: "block w-full text-zinc-500 bg-transparent outline-none text-sm mt-1"
263            }
264          )
265        ] })
266      ] }),
267      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
268        isDraft && isOwner && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
269          /* @__PURE__ */ jsxRuntimeExports.jsxs(
270            "button",
271            {
272              type: "button",
273              onClick: handleSave,
274              disabled: isSaving,
275              className: "px-4 py-2 bg-zinc-200 hover:bg-zinc-300 dark:bg-zinc-800 dark:hover:bg-zinc-700 text-zinc-800 dark:text-zinc-200 rounded-xl font-bold flex items-center gap-2 text-sm transition-colors",
276              children: [
277                /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { size: 16 }),
278                " ",
279                isSaving ? "Saving..." : "Save Draft"
280              ]
281            }
282          ),
283          /* @__PURE__ */ jsxRuntimeExports.jsxs(
284            "button",
285            {
286              type: "button",
287              onClick: handlePublish,
288              disabled: isSaving,
289              className: "px-5 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-xl font-bold flex items-center gap-2 text-sm shadow-md shadow-indigo-600/20 transition-colors",
290              children: [
291                /* @__PURE__ */ jsxRuntimeExports.jsx(CloudUpload, { size: 16 }),
292                " Submit to Phase 1"
293              ]
294            }
295          )
296        ] }),
297        !isDraft && algo && /* @__PURE__ */ jsxRuntimeExports.jsxs(
298          "button",
299          {
300            type: "button",
301            onClick: handleBranch,
302            className: "px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-xl font-bold flex items-center gap-2 text-sm shadow-md transition-colors",
303            children: [
304              /* @__PURE__ */ jsxRuntimeExports.jsx(GitBranch, { size: 16 }),
305              " Branch New Draft"
306            ]
307          }
308        )
309      ] })
310    ] }),
311    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-indigo-50 dark:bg-indigo-950/20 border border-indigo-200 dark:border-indigo-900/40 rounded-xl p-3 mb-4 shrink-0 flex items-start gap-3", children: [
312      /* @__PURE__ */ jsxRuntimeExports.jsx(Bot, { className: "text-indigo-500 shrink-0 mt-0.5", size: 18 }),
313      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
314        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-indigo-900 dark:text-indigo-300 font-semibold mb-1", children: "Develop with AI Assistance:" }),
315        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-white dark:bg-zinc-900 border border-indigo-100 dark:border-indigo-900/50 p-2 rounded text-[11px] font-mono text-zinc-600 dark:text-zinc-400 select-all overflow-x-auto", children: "Write a Javascript function called `processScore(midiData)` that takes a parsed @tonejs/midi JSON object and returns a valid MusicXML 3.1 string. The function should iterate over `midiData.tracks`, align notes to the nearest 16th note grid, and group durations into standard musical measures assuming 4/4 meter." })
316      ] })
317    ] }),
318    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-4 flex-1 min-h-0", children: [
319      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-1/2 flex flex-col bg-zinc-950 rounded-xl border border-zinc-800 overflow-hidden shadow-inner", children: [
320        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 px-4 py-2 border-b border-zinc-800 text-xs font-bold text-zinc-400 font-mono flex items-center justify-between", children: [
321          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
322            /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 14, className: "text-indigo-400" }),
323            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "index.js" })
324          ] }),
325          isReadOnly && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500 uppercase tracking-wider font-semibold", children: "Read-Only (Published)" })
326        ] }),
327        /* @__PURE__ */ jsxRuntimeExports.jsx(
328          "textarea",
329          {
330            value: content,
331            onChange: (e) => setContent(e.target.value),
332            disabled: isReadOnly,
333            className: "flex-1 w-full bg-transparent text-zinc-300 font-mono text-xs p-4 outline-none resize-none leading-relaxed select-text",
334            spellCheck: false
335          }
336        )
337      ] }),
338      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-1/2 flex flex-col gap-3 min-h-0", children: [
339        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 rounded-xl border border-zinc-200 dark:border-zinc-800 p-3 shrink-0 shadow-sm", children: [
340          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-3 mb-2", children: [
341            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-bold text-zinc-700 dark:text-zinc-200 flex items-center gap-1.5", children: [
342              /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14, className: "text-amber-500" }),
343              "Benchmark Test Runner"
344            ] }),
345            (testDiagnostics == null ? void 0 : testDiagnostics.elapsedMs) !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] font-mono text-zinc-500", children: [
346              "Latency: ",
347              /* @__PURE__ */ jsxRuntimeExports.jsxs("strong", { className: "text-zinc-700 dark:text-zinc-300", children: [
348                testDiagnostics.elapsedMs,
349                "ms"
350              ] })
351            ] })
352          ] }),
353          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
354            /* @__PURE__ */ jsxRuntimeExports.jsxs(
355              "select",
356              {
357                value: selectedBenchmark,
358                onChange: (e) => setSelectedBenchmark(e.target.value),
359                className: "flex-1 px-3 py-1.5 bg-zinc-100 dark:bg-zinc-950 text-zinc-800 dark:text-zinc-200 border border-zinc-300 dark:border-zinc-800 rounded-lg outline-none text-xs font-medium cursor-pointer",
360                children: [
361                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "basic_scale", className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", children: "Standard: C Major Scale Baseline (4/4)" }),
362                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "twinkle_twinkle", className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", children: "Standard: Twinkle Twinkle (Melody Benchmark)" }),
363                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "polyphonic_invention", className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", children: "Standard: Two-Voice Counterpoint (Polyphonic)" }),
364                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "syncopated_rhythm", className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", children: "Standard: Syncopated Rhythm Test" }),
365                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "custom", className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", children: "Custom MIDI Track ID..." })
366                ]
367              }
368            ),
369            selectedBenchmark === "custom" && /* @__PURE__ */ jsxRuntimeExports.jsx(
370              "input",
371              {
372                type: "text",
373                placeholder: "MIDI ID (e.g. 878)",
374                value: customMidiId,
375                onChange: (e) => setCustomMidiId(e.target.value),
376                className: "w-32 px-2.5 py-1.5 bg-zinc-100 dark:bg-zinc-950 text-zinc-800 dark:text-zinc-200 border border-zinc-300 dark:border-zinc-800 rounded-lg outline-none text-xs font-mono"
377              }
378            ),
379            /* @__PURE__ */ jsxRuntimeExports.jsxs(
380              "button",
381              {
382                type: "button",
383                onClick: handleTest,
384                disabled: isTesting,
385                className: "px-4 py-1.5 bg-green-600 hover:bg-green-700 disabled:opacity-50 text-white rounded-lg font-bold flex items-center gap-1.5 text-xs transition-colors shrink-0 shadow-sm",
386                children: [
387                  /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 13 }),
388                  " ",
389                  isTesting ? "Running..." : "Run Sandbox"
390                ]
391              }
392            )
393          ] }),
394          (testDiagnostics == null ? void 0 : testDiagnostics.error) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 p-2 rounded-lg bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-900/50 text-red-600 dark:text-red-400 text-xs flex items-start gap-2", children: [
395            /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 14, className: "shrink-0 mt-0.5" }),
396            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 font-mono text-[11px] break-words", children: testDiagnostics.error })
397          ] })
398        ] }),
399        /* @__PURE__ */ jsxRuntimeExports.jsx(
400          ScoreMusicXmlViewer,
401          {
402            xmlString: generatedXml,
403            isLoading: isTesting,
404            title: "Sandbox Output",
405            className: "flex-1 min-h-0"
406          }
407        )
408      ] })
409    ] })
410  ] });
411}
412export {
413  AlgorithmEditor as default
414};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.