Skip to content

Commit b441d80

Browse files
committed
Refactor: Extract core logic, data models, MIDI, and P2P functionality into a new @notater/core package.
1 parent 1f9ca45 commit b441d80

34 files changed

Lines changed: 453 additions & 8832 deletions

apps/web/package-lock.json

Lines changed: 0 additions & 8406 deletions
This file was deleted.

apps/web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"jszip": "^3.10.1",
3232
"lucide-react": "^0.563.0",
3333
"next": "16.1.4",
34-
"notater": "0.1.1",
34+
"@notater/core": "workspace:*",
3535
"peerjs": "^1.5.5",
3636
"react": "19.2.3",
3737
"react-dom": "19.2.3",

apps/web/src/components/CollabMenu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11

22
import { useState } from "react";
3-
import { p2p } from "@/lib/p2p";
3+
import { p2p, type Collaborator } from "@notater/core";
44
import { Copy, Check, Share2, Radio, Users, Loader2, Wifi } from "lucide-react";
55
import { motion, AnimatePresence } from "framer-motion";
66
import { toast } from "sonner";
77
import { useModal } from "./ui/ModalProvider";
88
import { useUser } from "@clerk/nextjs";
9-
import { useStore, type Collaborator } from "@/lib/store";
9+
import { useStore } from "@/lib/store";
1010

1111
const AVATAR_COLORS = [
1212
"bg-red-500", "bg-orange-500", "bg-amber-500", "bg-yellow-500",

apps/web/src/components/DrumRoll.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { useState, useEffect, useRef } from "react";
33
import { motion } from "framer-motion";
44
import { useStore } from "@/lib/store";
5-
import { playDrum, DrumType, DRUM_TYPES } from "@/lib/audio/drums";
5+
import { playDrum, DrumType, DRUM_TYPES } from "@notater/core";
66
// import { useToast } from "./ui/ToastProvider";
77

88
// Simple drum labels

apps/web/src/components/Drums.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22
import { useState, useEffect, useCallback, useRef } from "react";
33
import { motion, AnimatePresence } from "framer-motion";
4-
import { playDrum, DrumType, DRUM_TYPES, setDrumKit, DrumKit, DRUM_KITS } from "@/lib/audio/drums";
4+
import { playDrum, DrumType, DRUM_TYPES, setDrumKit, DrumKit, DRUM_KITS } from "@notater/core";
55
import { useStore } from "@/lib/store";
66
import { LucideIcon, Circle, Triangle, Square, Hexagon, Octagon, Star, Disc, Zap, Save, Trash2, X, Sparkles, Loader2 } from "lucide-react";
77
import { useToast } from "./ui/ToastProvider";

apps/web/src/components/InstrumentSelect.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"use client";
22
import { useStore } from "@/lib/store";
3-
import { SynthPreset } from "@/lib/audio/synth";
4-
import { DRUM_KITS } from "@/lib/audio/drums";
3+
import { DRUM_KITS, SynthPreset } from "@notater/core";
54
import { useState } from "react";
65
import { motion, AnimatePresence } from "framer-motion";
76
import { Piano, Drum } from "lucide-react";

apps/web/src/components/PianoRoll.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useRef, useEffect, useState, useMemo } from "react";
22
import { useStore } from "@/lib/store";
3-
import { getDrumFromPitch } from "@/lib/audio";
3+
import { getDrumFromPitch } from "@notater/core";
44
import { motion, AnimatePresence } from "framer-motion";
55
import { Music, Layers, Grid3X3, Eraser, ChevronsLeftRight, MousePointer2, Pencil, Trash2 } from "lucide-react";
66
import { InstrumentSelect } from "./InstrumentSelect";

apps/web/src/components/ProjectControl.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
"use client";
12
import { useState, useRef, useEffect } from "react";
23
import { useStore } from "@/lib/store";
34
import { useProjects } from "@/lib/db-hooks";
4-
import { exportProjectPackage, importProjectPackage } from "@/lib/sync";
5+
import { db, saveProject, getProject, exportProjectPackage, importProjectPackage } from "@notater/core";
56
import { pushProjectToCloud, pullProjectsFromCloud } from "@/lib/cloud-sync";
67
import { loginToGoogle, logoutFromGoogle, listDriveProjects, downloadFromDrive, syncProjectToDrive } from "@/lib/drive";
78
import { motion, AnimatePresence } from "framer-motion";
@@ -96,8 +97,8 @@ export function ProjectControl() {
9697
const handleSyncPush = async () => {
9798
try {
9899
await saveProject(); // Save local first
99-
const db = await import("@/lib/db");
100-
const dbProject = await db.getProject(project.id);
100+
await saveProject(); // Save local first
101+
const dbProject = await getProject(project.id);
101102
if (dbProject) {
102103
await pushProjectToCloud(dbProject);
103104
success("Project pushed to cloud!");
@@ -141,7 +142,8 @@ export function ProjectControl() {
141142
try {
142143
setIsDriveSyncing(true);
143144
await saveProject(); // Save local
144-
const projectData = await import("@/lib/db").then(m => m.getProject(project.id));
145+
await saveProject(); // Save local
146+
const projectData = await getProject(project.id);
145147
if (projectData) {
146148
await syncProjectToDrive(projectData);
147149
success("Synced to 'Notater Projects' in Drive!");

apps/web/src/hooks/useMidi.ts

Lines changed: 14 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,24 @@
1-
21
import { useEffect } from "react";
32
import { useStore } from "@/lib/store";
4-
5-
// Map MIDI note numbers to Note names
6-
const noteMap = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
7-
8-
function midiToNote(midi: number) {
9-
const octave = Math.floor(midi / 12) - 1;
10-
const note = noteMap[midi % 12];
11-
return `${note}${octave}`;
12-
}
3+
import { Utils } from "@notater/core";
134

145
export function useMidi() {
15-
// We access the synth via the store's audio engine reference if possible,
16-
// but the store keeps audio logic encapsulated.
17-
// Ideally, we add a `triggerNote` action to the store or access the exported synth.
18-
// The previous implementation had `globalSynth` exported from `lib/audio/index.ts`?
19-
// Let's check imports. `lib/store.ts` imports `createSynth` but `globalSynth` is lazy.
20-
21-
// Actually, `store.ts` has `setSynthPreset` but maybe not direct trigger actions exposed appropriately for real-time play
22-
// without React overhead.
23-
24-
// For low latency, direct access is better.
25-
// Let's assume we can import `globalSynth` or use a store action.
26-
27-
// If I use `useStore` actions, it might trigger state updates which is fine for UI but maybe slow for audio?
28-
// Not necessarily.
29-
30-
// However, `lib/audio/index.ts` exports `globalSynth` variable?
31-
// Let's verify. If not, I'll update store to handle "noteOn" / "noteOff".
32-
336
useEffect(() => {
34-
if (!navigator.requestMIDIAccess) return;
35-
36-
const handleMidiMessage = (e: Event) => {
37-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
38-
const event = e as any; // Cast to any because WebMIDI types are not standard in this env
39-
if (!event.data) return;
40-
41-
const [command, note, velocity] = event.data;
42-
43-
// Note On
44-
if (command === 144 && velocity > 0) {
45-
const noteName = midiToNote(note);
46-
// Trigger Store Action
47-
useStore.getState().triggerAttack(noteName);
48-
}
49-
50-
// Note Off
51-
if (command === 128 || (command === 144 && velocity === 0)) {
52-
const noteName = midiToNote(note);
53-
useStore.getState().triggerRelease(noteName);
7+
// Initialize Core MIDI Manager
8+
const midi = Utils.midiManager;
9+
midi.initialize();
10+
11+
// Set Handler to trigger Store Actions
12+
midi.setHandler({
13+
onNoteOn: (note, velocity) => {
14+
useStore.getState().triggerAttack(note);
15+
},
16+
onNoteOff: (note) => {
17+
useStore.getState().triggerRelease(note);
5418
}
55-
};
56-
57-
navigator.requestMIDIAccess().then((access) => {
58-
const inputs = access.inputs.values();
59-
for (const input of inputs) {
60-
input.onmidimessage = handleMidiMessage;
61-
}
62-
63-
access.onstatechange = (e: Event) => {
64-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
65-
const event = e as any;
66-
// creating/removing connections
67-
console.log(event.port.name, event.port.state, event.port.connection);
68-
};
6919
});
7020

21+
// Cleanup not strictly necessary for singleton, but good practice if we had unmount logic
22+
// For now, we leave the handler attached or allow it to be overwritten.
7123
}, []);
7224
}

apps/web/src/lib/cloud-sync.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { StoredProject } from "./db";
1+
import { StoredProject } from "@notater/core";
22
import { PutBlobResult } from "@vercel/blob";
33

44
// Simple sync hook (placeholder for future expansion)
@@ -65,7 +65,7 @@ export async function pullProjectsFromCloud(): Promise<StoredProject[]> {
6565
export async function saveAndPushToCloud(currentProjectId: string, options?: { keepalive?: boolean }) {
6666
// We dynamically import db to avoid server-side issues (though this runs on client)
6767
try {
68-
const db = await import("./db");
68+
const db = await import("@notater/core");
6969
// We assume the store has already saved to DB via persistence,
7070
// but to be safe we might want to trigger a save.
7171
// However, useStore.getState().saveProject() is a hook function usually.

0 commit comments

Comments
 (0)