Преглед на файлове

Reformat the frontend with Prettier

Output of `npm run format` and nothing else (R2). Every Svelte
component compiles to the same output as before except App.svelte,
where one help paragraph is wrapped onto two lines; its whitespace
collapses to a single space on screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude преди 2 часа
родител
ревизия
5b491092d3

+ 1 - 1
frontend/index.html

@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!doctype html>
 <html lang="en">
   <head>
     <meta charset="UTF-8" />

+ 66 - 12
frontend/src/App.svelte

@@ -10,7 +10,19 @@
   import { transfer, transferKind } from "./lib/pads";
   import SequencePanel from "./lib/SequencePanel.svelte";
   import SlotGrid, { type Cell } from "./lib/SlotGrid.svelte";
-  import { app, exportFolder, exportZip, listen, loadProject, message, newProject, refresh, run, ui, type Tab } from "./lib/store.svelte";
+  import {
+    app,
+    exportFolder,
+    exportZip,
+    listen,
+    loadProject,
+    message,
+    newProject,
+    refresh,
+    run,
+    ui,
+    type Tab,
+  } from "./lib/store.svelte";
   import { BANKS, PER_BANK, bars, gridSlots, tuneText } from "./lib/slots";
 
   onMount(() => {
@@ -35,7 +47,11 @@
 
   // Export would fail on a missing sample file, so it is not offered at all.
   const exportBlocked = $derived((st?.missingCount ?? 0) > 0);
-  const exportBlockedWhy = $derived(exportBlocked ? "Export is disabled while sample files are missing. Replace them, or put the files back and load the project again." : undefined);
+  const exportBlockedWhy = $derived(
+    exportBlocked
+      ? "Export is disabled while sample files are missing. Replace them, or put the files back and load the project again."
+      : undefined,
+  );
 
   const padCounts = $derived(perBank((s) => (pads[s]?.file ?? "") !== ""));
   const seqCounts = $derived(perBank((s) => seqBySlot.has(s)));
@@ -46,7 +62,9 @@
           const p = pads[slot];
           const badges = p.missing
             ? ["missing"]
-            : [tuneText(p.coarse, p.fine), p.muteGroup ? "M" + p.muteGroup : "", p.hasNotes ? "notes" : ""].filter(Boolean);
+            : [tuneText(p.coarse, p.fine), p.muteGroup ? "M" + p.muteGroup : "", p.hasNotes ? "notes" : ""].filter(
+                Boolean,
+              );
           return {
             slot,
             label: p.label,
@@ -119,13 +137,24 @@
     <h1 class="visually-hidden">MPCkit</h1>
     <div class="name">
       <label class="eyebrow" for="project-name">Project name</label>
-      <input id="project-name" type="text" value={st?.name ?? ""} spellcheck="false" autocomplete="off" onchange={rename} />
+      <input
+        id="project-name"
+        type="text"
+        value={st?.name ?? ""}
+        spellcheck="false"
+        autocomplete="off"
+        onchange={rename}
+      />
     </div>
     <div class="actions">
       <button type="button" class="btn" onclick={newProject}>New</button>
       <button type="button" class="btn" onclick={loadProject}>Load</button>
-      <button type="button" class="btn" disabled={exportBlocked} title={exportBlockedWhy} onclick={exportZip}>Export zip</button>
-      <button type="button" class="btn primary" disabled={exportBlocked} title={exportBlockedWhy} onclick={exportFolder}>Export to folder</button>
+      <button type="button" class="btn" disabled={exportBlocked} title={exportBlockedWhy} onclick={exportZip}
+        >Export zip</button
+      >
+      <button type="button" class="btn primary" disabled={exportBlocked} title={exportBlockedWhy} onclick={exportFolder}
+        >Export to folder</button
+      >
     </div>
     <button type="button" class="about-link" onclick={() => (ui.aboutOpen = true)}>About</button>
   </header>
@@ -138,8 +167,26 @@
   {/if}
 
   <div class="tabs" role="tablist" aria-label="Views">
-    <button id="tab-pads" type="button" role="tab" aria-selected={ui.tab === "pads"} aria-controls="view-pads" tabindex={ui.tab === "pads" ? 0 : -1} onclick={() => pickTab("pads")} onkeydown={tabKey}>Pads</button>
-    <button id="tab-sequences" type="button" role="tab" aria-selected={ui.tab === "sequences"} aria-controls="view-sequences" tabindex={ui.tab === "sequences" ? 0 : -1} onclick={() => pickTab("sequences")} onkeydown={tabKey}>Sequences</button>
+    <button
+      id="tab-pads"
+      type="button"
+      role="tab"
+      aria-selected={ui.tab === "pads"}
+      aria-controls="view-pads"
+      tabindex={ui.tab === "pads" ? 0 : -1}
+      onclick={() => pickTab("pads")}
+      onkeydown={tabKey}>Pads</button
+    >
+    <button
+      id="tab-sequences"
+      type="button"
+      role="tab"
+      aria-selected={ui.tab === "sequences"}
+      aria-controls="view-sequences"
+      tabindex={ui.tab === "sequences" ? 0 : -1}
+      onclick={() => pickTab("sequences")}
+      onkeydown={tabKey}>Sequences</button
+    >
   </div>
 
   {#if st}
@@ -161,7 +208,10 @@
             ondragbank={(b) => (ui.padBank = b)}
           />
           <SlotGrid cells={padCells} selected={ui.padSlot} onpick={pickPad} dropTarget ondroppad={dropPad} />
-          <p class="hint grid-hint">Click a pad to select and hear it. Drag a pad to move it, drag onto a filled pad to swap, hold Alt while dragging to copy. Drag over a bank letter to reach another bank. Drop a WAV file on any pad to assign it.</p>
+          <p class="hint grid-hint">
+            Click a pad to select and hear it. Drag a pad to move it, drag onto a filled pad to swap, hold Alt while
+            dragging to copy. Drag over a bank letter to reach another bank. Drop a WAV file on any pad to assign it.
+          </p>
         </section>
         <PadPanel pad={pads[ui.padSlot]} limits={st.limits} />
       </div>
@@ -182,7 +232,9 @@
             }}
           />
           <SlotGrid cells={seqCells} selected={ui.seqSlot} onpick={(s) => (ui.seqSlot = s)} />
-          <p class="hint grid-hint">Each pad is one sequence slot, {st.limits.maxSequences} in total. Click an empty slot to add a sequence there.</p>
+          <p class="hint grid-hint">
+            Each pad is one sequence slot, {st.limits.maxSequences} in total. Click an empty slot to add a sequence there.
+          </p>
         </section>
         <SequencePanel slot={ui.seqSlot} seq={seqBySlot.get(ui.seqSlot)} limits={st.limits} />
       </div>
@@ -192,7 +244,9 @@
       <span>{st.sampleCount} of {st.limits.maxSamples} samples</span>
       <span>{st.sequenceCount} of {st.limits.maxSequences} sequences</span>
       <span>WAV files only</span>
-      {#if st.missingCount}<span class="missing">{st.missingCount} sample {st.missingCount === 1 ? "file" : "files"} missing: export is disabled</span>{/if}
+      {#if st.missingCount}<span class="missing"
+          >{st.missingCount} sample {st.missingCount === 1 ? "file" : "files"} missing: export is disabled</span
+        >{/if}
       {#if st.dirty}<span class="dirty">Unsaved changes: export to keep them</span>{/if}
       {#if app.notice}<span class="notice" title={app.notice}>{app.notice}</span>{/if}
     </footer>
@@ -204,7 +258,7 @@
     <AboutModal onclose={() => (ui.aboutOpen = false)} />
   {/if}
   {#if ui.picker && st}
-    <PadPicker picker={ui.picker} pads={pads} onclose={() => (ui.picker = null)} />
+    <PadPicker picker={ui.picker} {pads} onclose={() => (ui.picker = null)} />
   {/if}
   {#if ui.confirm}
     <ConfirmModal c={ui.confirm} />

+ 3 - 1
frontend/src/lib/AboutModal.svelte

@@ -29,7 +29,9 @@
 <div class="backdrop" onclick={(e) => e.target === e.currentTarget && onclose()}>
   <div class="dialog" role="dialog" aria-modal="true" aria-labelledby="about-title">
     <h2 id="about-title">About MPCkit</h2>
-    <p class="lead">A desktop project and kit editor for the MPC Sample. Build kits on your computer, then export them to the device.</p>
+    <p class="lead">
+      A desktop project and kit editor for the MPC Sample. Build kits on your computer, then export them to the device.
+    </p>
     <div class="facts">
       <span>Version {about?.version ?? ""}</span>
       {#if about?.commit}<span>Build {about.commit}</span>{/if}

+ 10 - 2
frontend/src/lib/ConfirmModal.svelte

@@ -17,7 +17,13 @@
 <svelte:window onkeydown={keydown} />
 
 <div class="backdrop">
-  <div class="dialog" role="alertdialog" aria-modal="true" aria-labelledby="confirm-title" aria-describedby="confirm-message">
+  <div
+    class="dialog"
+    role="alertdialog"
+    aria-modal="true"
+    aria-labelledby="confirm-title"
+    aria-describedby="confirm-message"
+  >
     <h2 id="confirm-title">{c.title}</h2>
     <p id="confirm-message">{c.message}</p>
     {#if c.items.length}
@@ -28,7 +34,9 @@
       </ul>
     {/if}
     <div class="buttons">
-      <button type="button" class="btn secondary" bind:this={cancelButton} onclick={() => c.resolve(false)}>Cancel</button>
+      <button type="button" class="btn secondary" bind:this={cancelButton} onclick={() => c.resolve(false)}
+        >Cancel</button
+      >
       <button type="button" class="btn primary" onclick={() => c.resolve(true)}>{c.action}</button>
     </div>
   </div>

+ 25 - 6
frontend/src/lib/PadPanel.svelte

@@ -43,7 +43,9 @@
   {#if pad.file === ""}
     <div class="empty-box drop" data-file-drop-target data-slot={pad.slot}>
       <p>This pad is empty. Drop a WAV file on it, or choose one.</p>
-      <button type="button" class="btn primary" onclick={() => run(() => Service.AssignSampleDialog(pad.slot))}>Choose WAV file</button>
+      <button type="button" class="btn primary" onclick={() => run(() => Service.AssignSampleDialog(pad.slot))}
+        >Choose WAV file</button
+      >
     </div>
   {:else}
     <div class="stack">
@@ -56,10 +58,21 @@
       {/if}
       <div class="row">
         <button type="button" class="btn primary" disabled={pad.missing} onclick={() => preview(pad)}>
-          <svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"><path d="M3 2 L12 7 L3 12 Z" /></svg>
+          <svg
+            aria-hidden="true"
+            width="14"
+            height="14"
+            viewBox="0 0 14 14"
+            fill="none"
+            stroke="currentColor"
+            stroke-width="2"
+            stroke-linejoin="round"><path d="M3 2 L12 7 L3 12 Z" /></svg
+          >
           Play
         </button>
-        <button type="button" class="btn secondary" onclick={() => run(() => Service.AssignSampleDialog(pad.slot))}>Replace</button>
+        <button type="button" class="btn secondary" onclick={() => run(() => Service.AssignSampleDialog(pad.slot))}
+          >Replace</button
+        >
         <button type="button" class="btn secondary" onclick={() => run(() => Service.ClearPad(pad.slot))}>Clear</button>
       </div>
 
@@ -127,11 +140,17 @@
       </div>
 
       <div class="row divided">
-        <button type="button" class="btn secondary" onclick={() => (ui.picker = { kind: "move", from: pad.slot })}>Move to pad</button>
-        <button type="button" class="btn secondary" onclick={() => (ui.picker = { kind: "copy", from: pad.slot })}>Copy to pad</button>
+        <button type="button" class="btn secondary" onclick={() => (ui.picker = { kind: "move", from: pad.slot })}
+          >Move to pad</button
+        >
+        <button type="button" class="btn secondary" onclick={() => (ui.picker = { kind: "copy", from: pad.slot })}
+          >Copy to pad</button
+        >
       </div>
       {#if pad.hasNotes}
-        <p class="hint notes">Recorded notes in a sequence play this pad. They stay on {pad.label} if the sample moves.</p>
+        <p class="hint notes">
+          Recorded notes in a sequence play this pad. They stay on {pad.label} if the sample moves.
+        </p>
       {/if}
     </div>
   {/if}

+ 12 - 2
frontend/src/lib/PadPicker.svelte

@@ -25,7 +25,15 @@
   const target = $derived(bank * PER_BANK + pad);
   const kind = $derived(transferKind(src, pads[target], picker.kind === "copy"));
   const action = $derived(
-    target === picker.from ? "" : kind === "move" ? "Move" : kind === "swap" ? "Swap" : pads[target].file ? "Copy and replace" : "Copy",
+    target === picker.from
+      ? ""
+      : kind === "move"
+        ? "Move"
+        : kind === "swap"
+          ? "Swap"
+          : pads[target].file
+            ? "Copy and replace"
+            : "Copy",
   );
 
   let firstField: HTMLSelectElement | undefined = $state();
@@ -87,7 +95,9 @@
     </p>
     <div class="buttons">
       <button type="button" class="btn secondary" onclick={onclose}>Cancel</button>
-      <button type="button" class="btn primary" disabled={!action} onclick={go}>{action || (picker.kind === "move" ? "Move" : "Copy")}</button>
+      <button type="button" class="btn primary" disabled={!action} onclick={go}
+        >{action || (picker.kind === "move" ? "Move" : "Copy")}</button
+      >
     </div>
   </div>
 </div>

+ 35 - 3
frontend/src/lib/SequencePanel.svelte

@@ -85,16 +85,48 @@
     <div class="stack">
       <div class="control">
         <label class="label" for="seq-name">Name</label>
-        <input id="seq-name" class="field" type="text" spellcheck="false" autocomplete="off" value={name} oninput={(e) => (name = e.currentTarget.value)} onchange={save} onkeydown={enter} />
+        <input
+          id="seq-name"
+          class="field"
+          type="text"
+          spellcheck="false"
+          autocomplete="off"
+          value={name}
+          oninput={(e) => (name = e.currentTarget.value)}
+          onchange={save}
+          onkeydown={enter}
+        />
       </div>
       <div class="pair">
         <div class="control">
           <label class="label" for="seq-bpm">BPM ({limits.minBPM}–{limits.maxBPM})</label>
-          <input id="seq-bpm" class="field mono" type="number" min={limits.minBPM} max={limits.maxBPM} step="any" value={bpm} oninput={(e) => (bpm = e.currentTarget.valueAsNumber)} onchange={save} onkeydown={enter} />
+          <input
+            id="seq-bpm"
+            class="field mono"
+            type="number"
+            min={limits.minBPM}
+            max={limits.maxBPM}
+            step="any"
+            value={bpm}
+            oninput={(e) => (bpm = e.currentTarget.valueAsNumber)}
+            onchange={save}
+            onkeydown={enter}
+          />
         </div>
         <div class="control">
           <label class="label" for="seq-bars">Length (bars)</label>
-          <input id="seq-bars" class="field mono" type="number" min="1" max={limits.maxBars} step="1" value={bars} oninput={(e) => (bars = e.currentTarget.valueAsNumber)} onchange={save} onkeydown={enter} />
+          <input
+            id="seq-bars"
+            class="field mono"
+            type="number"
+            min="1"
+            max={limits.maxBars}
+            step="1"
+            value={bars}
+            oninput={(e) => (bars = e.currentTarget.valueAsNumber)}
+            onchange={save}
+            onkeydown={enter}
+          />
         </div>
       </div>
       <span class="hint">Notes are recorded on the device. This screen sets name, tempo and length only.</span>

+ 8 - 2
frontend/src/lib/pads.ts

@@ -40,7 +40,9 @@ async function doTransfer(kind: Transfer, from: number, to: number): Promise<voi
   const changed = kind === "copy" ? [dst] : [src, dst];
   for (const p of changed) {
     if (p.hasNotes) {
-      items.push(`${p.label} has recorded notes. They stay on ${p.label} and will play ${soundAfter(kind, p, src, dst)}.`);
+      items.push(
+        `${p.label} has recorded notes. They stay on ${p.label} and will play ${soundAfter(kind, p, src, dst)}.`,
+      );
     }
   }
   if (items.length) {
@@ -54,7 +56,11 @@ async function doTransfer(kind: Transfer, from: number, to: number): Promise<voi
   }
 
   const call =
-    kind === "move" ? () => Service.MovePad(from, to) : kind === "swap" ? () => Service.SwapPads(from, to) : () => Service.CopyPad(from, to);
+    kind === "move"
+      ? () => Service.MovePad(from, to)
+      : kind === "swap"
+        ? () => Service.SwapPads(from, to)
+        : () => Service.CopyPad(from, to);
   if (await run(call)) {
     ui.padBank = Math.floor(to / PER_BANK);
     ui.padSlot = to;

+ 8 - 2
frontend/src/lib/store.svelte.ts

@@ -177,9 +177,15 @@ async function exportWith(
 }
 
 export function exportFolder(): Promise<void> {
-  return exportWith(() => Service.ChooseExportFolder(), (overwrite) => Service.ExportFolder(overwrite));
+  return exportWith(
+    () => Service.ChooseExportFolder(),
+    (overwrite) => Service.ExportFolder(overwrite),
+  );
 }
 
 export function exportZip(): Promise<void> {
-  return exportWith(() => Service.ChooseZipPath(), (overwrite) => Service.ExportZip(overwrite));
+  return exportWith(
+    () => Service.ChooseZipPath(),
+    (overwrite) => Service.ExportZip(overwrite),
+  );
 }

+ 2 - 2
frontend/svelte.config.js

@@ -1,7 +1,7 @@
-import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
 
 export default {
   // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
   // for more information about preprocessors
   preprocess: vitePreprocess(),
-}
+};

+ 1 - 1
frontend/tsconfig.json

@@ -20,5 +20,5 @@
     "checkJs": true,
     "isolatedModules": true
   },
-  "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte", "bindings"],
+  "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte", "bindings"]
 }