From 44a5c057fcf25420bd1866d56942ff3336ee54d1 Mon Sep 17 00:00:00 2001 From: AgastyaRai Date: Mon, 3 Nov 2025 10:11:14 -0800 Subject: [PATCH 01/37] added frontend state --- backend/shared-logic/src/db.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/shared-logic/src/db.rs b/backend/shared-logic/src/db.rs index b62018b..7b5c7e0 100644 --- a/backend/shared-logic/src/db.rs +++ b/backend/shared-logic/src/db.rs @@ -8,7 +8,7 @@ use log::{info, error, warn}; use chrono::{DateTime, Utc}; use dotenvy::dotenv; use super::models::{User, NewUser, TimeSeriesData, UpdateUser, Session, FrontendState}; -use crate::{lsl::EEGDataPacket}; +use crate::{lsl::EEGData}; use once_cell::sync::OnceCell; use std::sync::Arc; From 65f9a2d21d0a06a1e0f4e2b4033c4e1af863e9e2 Mon Sep 17 00:00:00 2001 From: AgastyaRai Date: Mon, 3 Nov 2025 19:36:31 -0800 Subject: [PATCH 02/37] added migration --- .../20251101120000_add_sessions_and_frontend_state.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/migrations/20251101120000_add_sessions_and_frontend_state.sql b/backend/migrations/20251101120000_add_sessions_and_frontend_state.sql index c3c61a9..8017c0d 100644 --- a/backend/migrations/20251101120000_add_sessions_and_frontend_state.sql +++ b/backend/migrations/20251101120000_add_sessions_and_frontend_state.sql @@ -5,7 +5,6 @@ CREATE TABLE IF NOT EXISTS sessions ( name TEXT NOT NULL UNIQUE ); - CREATE TABLE IF NOT EXISTS frontend_state ( session_id INTEGER PRIMARY KEY REFERENCES sessions(id) ON DELETE CASCADE, From 2423ce4b1fe5624de75983663e34a1a3a355dbed Mon Sep 17 00:00:00 2001 From: AgastyaRai Date: Mon, 3 Nov 2025 19:36:59 -0800 Subject: [PATCH 03/37] added migration --- .../20251101120000_add_sessions_and_frontend_state.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/migrations/20251101120000_add_sessions_and_frontend_state.sql b/backend/migrations/20251101120000_add_sessions_and_frontend_state.sql index 8017c0d..c3c61a9 100644 --- a/backend/migrations/20251101120000_add_sessions_and_frontend_state.sql +++ b/backend/migrations/20251101120000_add_sessions_and_frontend_state.sql @@ -5,6 +5,7 @@ CREATE TABLE IF NOT EXISTS sessions ( name TEXT NOT NULL UNIQUE ); + CREATE TABLE IF NOT EXISTS frontend_state ( session_id INTEGER PRIMARY KEY REFERENCES sessions(id) ON DELETE CASCADE, From 358690f80b6caa50905e1c7b07cb8dd1e4691a4d Mon Sep 17 00:00:00 2001 From: AgastyaRai Date: Mon, 1 Dec 2025 23:16:58 -0800 Subject: [PATCH 04/37] modified websocket flow to wait for a signal config before sending data --- backend/shared-logic/src/db.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/shared-logic/src/db.rs b/backend/shared-logic/src/db.rs index 7b5c7e0..b62018b 100644 --- a/backend/shared-logic/src/db.rs +++ b/backend/shared-logic/src/db.rs @@ -8,7 +8,7 @@ use log::{info, error, warn}; use chrono::{DateTime, Utc}; use dotenvy::dotenv; use super::models::{User, NewUser, TimeSeriesData, UpdateUser, Session, FrontendState}; -use crate::{lsl::EEGData}; +use crate::{lsl::EEGDataPacket}; use once_cell::sync::OnceCell; use std::sync::Arc; From 12bcdfe91af4574749ef692529f429a0d95334d2 Mon Sep 17 00:00:00 2001 From: jsun64 Date: Fri, 9 Jan 2026 13:24:56 -0800 Subject: [PATCH 05/37] added processing config --- .../nodes/filter-node/filter-node.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frontend/components/nodes/filter-node/filter-node.tsx b/frontend/components/nodes/filter-node/filter-node.tsx index 6dd7831..2032775 100644 --- a/frontend/components/nodes/filter-node/filter-node.tsx +++ b/frontend/components/nodes/filter-node/filter-node.tsx @@ -195,6 +195,24 @@ export default function FilterNode({ id }: FilterNodeProps) { isConnected={isConnected} isDataStreamOn={dataStreaming} /> + + {isConnected && ( + <> + setCutoff(Number(e.target.value))} + /> + +

+ {selectedFilter === 'lowpass' + ? 'Frequencies below cutoff will pass through' + : 'Frequencies above cutoff will pass through'} +

+ + )} ); } \ No newline at end of file From d0152b995a2af0ce5b3509bc2aafe91dda359ddf Mon Sep 17 00:00:00 2001 From: Ali Abbas Date: Mon, 12 Jan 2026 16:49:04 -0800 Subject: [PATCH 06/37] Bandpass Filter --- .../nodes/filter-node/combo-box.tsx | 127 +++++++++++++++--- fullstack-moss-app | 1 + 2 files changed, 106 insertions(+), 22 deletions(-) create mode 160000 fullstack-moss-app diff --git a/frontend/components/nodes/filter-node/combo-box.tsx b/frontend/components/nodes/filter-node/combo-box.tsx index ec46d64..9851f58 100644 --- a/frontend/components/nodes/filter-node/combo-box.tsx +++ b/frontend/components/nodes/filter-node/combo-box.tsx @@ -12,6 +12,10 @@ const filters = [ value: 'highpass', label: 'High Pass Filter', }, + { + value: 'bandpass', + label: 'Bandpass Filter', + }, ]; interface ComboBoxProps { @@ -34,6 +38,10 @@ export default function ComboBox({ const [isExpanded, setIsExpanded] = React.useState(false); const titleRef = React.useRef(null); const [sliderValue, setSliderValue] = React.useState([75]); + const [cutoff, setCutoff] = React.useState([75]); + + const [lowCutoff, setLowCutoff] = React.useState([25]); + const [highCutoff, setHighCutoff] = React.useState([75]); const toggleExpanded = () => { setIsExpanded(!isExpanded); @@ -57,7 +65,6 @@ export default function ComboBox({ style={{ width: 'fit-content', minWidth: '396px', // changed width to 396px, same width as source-node - }} > {/* Main button/header */} @@ -73,8 +80,8 @@ export default function ComboBox({ isConnected ? 'border-black' // Connected to source AND data stream on: black border (activated) : isConnected - ? 'border-gray-300' // Connected to source: gray border (non-activated) - : 'border-gray-300' // Disconnected: gray border + ? 'border-gray-300' // Connected to source: gray border (non-activated) + : 'border-gray-300' // Disconnected: gray border )} > {/* No filled circle - always stay empty */} @@ -85,7 +92,8 @@ export default function ComboBox({ className={cn( 'absolute left-16 w-3 h-3 rounded-full', isConnected && isDataStreamOn - ? 'bg-[#509693]' : 'bg-[#D3D3D3]' + ? 'bg-[#509693]' + : 'bg-[#D3D3D3]' )} /> @@ -102,8 +110,11 @@ export default function ComboBox({
{/* Toggle arrow */}
- +
{/* Right connection circle - changes based on connection and data stream */} @@ -113,11 +124,11 @@ export default function ComboBox({ isConnected ? 'border-black' // Connected to source AND data stream on: black border (activated) : isConnected - ? 'border-gray-300' // Connected to source: gray border (non-activated) - : 'border-gray-300' // Disconnected: gray border + ? 'border-gray-300' // Connected to source: gray border (non-activated) + : 'border-gray-300' // Disconnected: gray border )} > - {/* No solid circle inside - always stay empty */} + {/* No solid circle inside */}
@@ -126,26 +137,98 @@ export default function ComboBox({
- + {value != 'bandpass' && ( + + )} + + {/* Single slider for lowpass and highpass */} + {value == 'bandpass' && ( +
+ {/* Low cutoff */} +
+ + + { + // prevent low from going above high + const next = + val[0] >= highCutoff[0] + ? highCutoff[0] - 1 + : val[0]; + setLowCutoff([next]); + }} + min={0} + max={100} + step={1} + className="w-full" + /> +
+ +
+
+ {/* Makeshift spacing */} + + + +
+ 0 + Low Cutoff + 100 +
+ +
+
+
+
+
+
+
+
+ + {/* More makeshift spacing */} + + + + {/* High cutoff */} +
+ + + { + // prevent high from going below low + const next = + val[0] <= lowCutoff[0] + ? lowCutoff[0] + 1 + : val[0]; + setHighCutoff([next]); + }} + min={0} + max={100} + step={1} + className="w-full" + /> +
+
+ )} +
- {/* optional 0 / 100 labels, adjust if you don't want them */} 0 - + High Cutoff 100
- -
{/* Expandable options section */} diff --git a/fullstack-moss-app b/fullstack-moss-app new file mode 160000 index 0000000..42890df --- /dev/null +++ b/fullstack-moss-app @@ -0,0 +1 @@ +Subproject commit 42890df0532dc8ae5234c71e442a51a44f537bd9 From 938a1bb012c1fd8c097e6b113a36920792d20b96 Mon Sep 17 00:00:00 2001 From: MaddieWright Date: Mon, 12 Jan 2026 20:40:39 -0800 Subject: [PATCH 07/37] Fix slider labelling and spacing format --- .../nodes/filter-node/combo-box.tsx | 70 +++++++------------ 1 file changed, 27 insertions(+), 43 deletions(-) diff --git a/frontend/components/nodes/filter-node/combo-box.tsx b/frontend/components/nodes/filter-node/combo-box.tsx index 9851f58..3229082 100644 --- a/frontend/components/nodes/filter-node/combo-box.tsx +++ b/frontend/components/nodes/filter-node/combo-box.tsx @@ -80,8 +80,8 @@ export default function ComboBox({ isConnected ? 'border-black' // Connected to source AND data stream on: black border (activated) : isConnected - ? 'border-gray-300' // Connected to source: gray border (non-activated) - : 'border-gray-300' // Disconnected: gray border + ? 'border-gray-300' // Connected to source: gray border (non-activated) + : 'border-gray-300' // Disconnected: gray border )} > {/* No filled circle - always stay empty */} @@ -111,9 +111,8 @@ export default function ComboBox({ {/* Toggle arrow */}
@@ -124,8 +123,8 @@ export default function ComboBox({ isConnected ? 'border-black' // Connected to source AND data stream on: black border (activated) : isConnected - ? 'border-gray-300' // Connected to source: gray border (non-activated) - : 'border-gray-300' // Disconnected: gray border + ? 'border-gray-300' // Connected to source: gray border (non-activated) + : 'border-gray-300' // Disconnected: gray border )} > {/* No solid circle inside */} @@ -142,14 +141,20 @@ export default function ComboBox({ }} > {value != 'bandpass' && ( - +
+ +
+ 0 + 100 +
+
)} {/* Single slider for lowpass and highpass */} @@ -157,7 +162,6 @@ export default function ComboBox({
{/* Low cutoff */}
-
-
-
- {/* Makeshift spacing */} - - - -
+
0 Low Cutoff 100
-
-
-
-
-
-
-
-
- - {/* More makeshift spacing */} - - - {/* High cutoff */}
- - { @@ -221,14 +204,15 @@ export default function ComboBox({ className="w-full" />
+ +
+ 0 + High Cutoff + 100 +
)} -
- 0 - High Cutoff - 100 -
{/* Expandable options section */} From 592774702531ecf790a4710b4f9db032ff7100ea Mon Sep 17 00:00:00 2001 From: MaddieWright Date: Mon, 12 Jan 2026 20:53:05 -0800 Subject: [PATCH 08/37] bandpass spacing, remove mistake folder --- frontend/components/nodes/filter-node/combo-box.tsx | 6 +++--- fullstack-moss-app | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) delete mode 160000 fullstack-moss-app diff --git a/frontend/components/nodes/filter-node/combo-box.tsx b/frontend/components/nodes/filter-node/combo-box.tsx index 3229082..d062cd1 100644 --- a/frontend/components/nodes/filter-node/combo-box.tsx +++ b/frontend/components/nodes/filter-node/combo-box.tsx @@ -176,11 +176,11 @@ export default function ComboBox({ min={0} max={100} step={1} - className="w-full" + className="w-full mb-1" /> -
+
0 Low Cutoff 100 @@ -201,7 +201,7 @@ export default function ComboBox({ min={0} max={100} step={1} - className="w-full" + className="w-full mb-1" />
diff --git a/fullstack-moss-app b/fullstack-moss-app deleted file mode 160000 index 42890df..0000000 --- a/fullstack-moss-app +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 42890df0532dc8ae5234c71e442a51a44f537bd9 From 6271f098aefad5906f6e1da7da170189ffecfe06 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 18:35:38 -0800 Subject: [PATCH 09/37] added a Pull Request Template --- .github/docs/PULL_REQUEST_TEMPLATE.md | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/docs/PULL_REQUEST_TEMPLATE.md diff --git a/.github/docs/PULL_REQUEST_TEMPLATE.md b/.github/docs/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..a5c4080 --- /dev/null +++ b/.github/docs/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ +# Pull Request Template + +## What? + +**Add a concise description** of the change made. + +**Example:** "Implemented backend API for dataset retrieval." + +--- + +## How? + +**Mention a brief technical explanation** of the change. + +**Example:** "Refactored the API service to handle dataset retrieval with +caching to improve performance." + +--- + +## Testing + +List ways you have tested the code. + +**Example:** "Used console.log() to ensure function ran, UI changes color when +hovering over" + +Attach images or videos if possible. +--- + +### Notes: + +- This template is intended to **guide** your PR submission, but feel free to + modify sections as needed. +- For the PR title, just reference the appropriate Github Issue/Notion Task + - eg. `steve/[Bug Fix] Changed color of background` \ No newline at end of file From 3ea73f6a3ac00b29765a170dbeb5fe2965ef30cd Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 18:38:39 -0800 Subject: [PATCH 10/37] Moved template location --- .github/{docs => }/PULL_REQUEST_TEMPLATE.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{docs => }/PULL_REQUEST_TEMPLATE.md (100%) diff --git a/.github/docs/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from .github/docs/PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md From 146f2968b4d97a588598d4ad4680feed385e54cf Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 19:29:23 -0800 Subject: [PATCH 11/37] PR automation workflow --- .github/CODEOWNERS | 8 +++ .github/workflows/pr-workflow.yml | 112 ++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/pr-workflow.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..91f2aec --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,8 @@ +# Frontend lead +/frontend/ @MaddieWright + +# Backend lead +/backend/ @HeisSteve + +# Infra +/.github/ @HeisSteve @MaddieWright \ No newline at end of file diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml new file mode 100644 index 0000000..328d4f2 --- /dev/null +++ b/.github/workflows/pr-workflow.yml @@ -0,0 +1,112 @@ +name: PR workflow + +on: + pull_request: + types: [opened, synchronize] + +jobs: + pr-automation: + runs-on: ubuntu-latest + + steps: + - name: Get changed files + id: files + uses: tj-actions/changed-files@v44 + + - name: Assign, label, and request reviewers + uses: actions/github-script@v7 + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const prNumber = context.payload.pull_request.number; + const author = context.payload.pull_request.user.login; + + const changedFiles = "${{ steps.files.outputs.all_changed_files }}".split(" "); + + // CONFIGURATION + const rules = { + frontend: { + path: "frontend/", + label: "frontend", + reviewers: ["MaddieWright"] + }, + backend: { + path: "backend/", + label: "backend", + reviewers: ["HeisSteve"] + }, + infra: { + path: ".github/", + label: "infra", + reviewers: ["HeisSteve", "MaddieWright"] + } + }; + + const labelsToAdd = new Set(); + const reviewersToAdd = new Set(); + + // Determine applicable rules + for (const file of changedFiles) { + for (const rule of Object.values(rules)) { + if (file.startsWith(rule.path)) { + labelsToAdd.add(rule.label); + rule.reviewers.forEach(r => reviewersToAdd.add(r)); + } + } + } + + // Assign PR author + await github.rest.issues.addAssignees({ + owner, + repo, + issue_number: prNumber, + assignees: [author] + }); + + // Get existing labels + const existingLabels = await github.rest.issues.listLabelsOnIssue({ + owner, + repo, + issue_number: prNumber + }); + + const managedLabels = Object.values(rules).map(r => r.label); + const existingManagedLabels = existingLabels.data + .map(l => l.name) + .filter(l => managedLabels.includes(l)); + + // Remove labels that no longer apply + for (const label of existingManagedLabels) { + if (!labelsToAdd.has(label)) { + await github.rest.issues.removeLabel({ + owner, + repo, + issue_number: prNumber, + name: label + }); + } + } + + // Add labels + if (labelsToAdd.size > 0) { + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: prNumber, + labels: [...labelsToAdd] + }); + } + + // Remove PR author from reviewers if present + reviewersToAdd.delete(author); + + // Request reviewers + if (reviewersToAdd.size > 0) { + await github.rest.pulls.requestReviewers({ + owner, + repo, + pull_number: prNumber, + reviewers: [...reviewersToAdd] + }); + } From da938e6eaa609bfbbe30a84833c6a384d3030266 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 20:09:24 -0800 Subject: [PATCH 12/37] updated approval restriction --- .github/workflows/pr-workflow.yml | 110 ++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 21 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index 328d4f2..f5cfe03 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -2,18 +2,20 @@ name: PR workflow on: pull_request: - types: [opened, synchronize] + types: [opened, synchronize, ready_for_review] # include ready_for_review for draft PRs jobs: pr-automation: runs-on: ubuntu-latest steps: + # Step 1: Get changed files - name: Get changed files id: files uses: tj-actions/changed-files@v44 - - name: Assign, label, and request reviewers + # Step 2: Assign, label, request reviewers, and enforce approvals + - name: Assign, label, request reviewers, enforce approvals uses: actions/github-script@v7 with: script: | @@ -22,36 +24,35 @@ jobs: const prNumber = context.payload.pull_request.number; const author = context.payload.pull_request.user.login; - const changedFiles = "${{ steps.files.outputs.all_changed_files }}".split(" "); + if (context.payload.pull_request.draft) { + console.log("PR is a draft — skipping workflow steps."); + return; + } + + const changedFiles = "${{ steps.files.outputs.all_changed_files }}".split(" ").filter(f => f); + // ------------------- // CONFIGURATION + // ------------------- const rules = { - frontend: { - path: "frontend/", - label: "frontend", - reviewers: ["MaddieWright"] - }, - backend: { - path: "backend/", - label: "backend", - reviewers: ["HeisSteve"] - }, - infra: { - path: ".github/", - label: "infra", - reviewers: ["HeisSteve", "MaddieWright"] - } + frontend: { path: "frontend/", label: "frontend", lead: "MaddieWright", team: [] }, + backend: { path: "backend/", label: "backend", lead: "HeisSteve", team: [] }, + infra: { path: ".github/", label: "infra", lead: ["HeisSteve","MaddieWright" ], team: [] } }; const labelsToAdd = new Set(); const reviewersToAdd = new Set(); + const leadsToCheck = new Set(); + const teamToCheck = new Set(); // Determine applicable rules for (const file of changedFiles) { for (const rule of Object.values(rules)) { if (file.startsWith(rule.path)) { labelsToAdd.add(rule.label); - rule.reviewers.forEach(r => reviewersToAdd.add(r)); + rule.team.forEach(u => reviewersToAdd.add(u)); + leadsToCheck.add(rule.lead); + rule.team.forEach(u => teamToCheck.add(u)); } } } @@ -98,8 +99,9 @@ jobs: }); } - // Remove PR author from reviewers if present - reviewersToAdd.delete(author); + // Remove PR author from reviewers + ![...leadsToCheck].includes(author) && reviewersToAdd.delete(author); + // Request reviewers if (reviewersToAdd.size > 0) { @@ -110,3 +112,69 @@ jobs: reviewers: [...reviewersToAdd] }); } + + + // ------------------- + // CUSTOM APPROVAL CHECK WITH SINGLE COMMENT + // ------------------- + const { data: reviews } = await github.rest.pulls.listReviews({ + owner, + repo, + pull_number: prNumber + }); + + // Set of users who approved + const approvedUsers = new Set( + reviews + .filter(r => r.state === "APPROVED") + .map(r => r.user.login) + ); + + // Check if at least one lead approved (include author if they are a lead) + const leadApproved = [...leadsToCheck].some(u => approvedUsers.has(u)); + + // Count how many team members approved (exclude PR author) + let teamApprovals = 0; + for (const member of teamToCheck) { + if (approvedUsers.has(member) && member !== author) teamApprovals++; + } + + // Determine if approval rules are satisfied + const approvalSatisfied = leadApproved || teamApprovals >= 2; + + // Prepare the comment body with an identifier + const commentBody = approvalSatisfied + ? "✅ PR approval conditions satisfied (1 lead OR 2 team members approved). " + : "❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging. "; + + // Find existing comment from the bot with our identifier + const { data: comments } = await github.rest.issues.listComments({ + owner, + repo, + issue_number: prNumber + }); + + const existingComment = comments.find(c => c.body.includes("")); + + if (existingComment) { + // Update the existing comment + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: existingComment.id, + body: commentBody + }); + } else { + // Create a new comment + await github.rest.issues.createComment({ + owner, + repo, + issue_number: prNumber, + body: commentBody + }); + } + + // Fail workflow if approvals are not satisfied + if (!approvalSatisfied) { + process.exit(1); + } From 4cfb5141c37086c8bb7a1c520cfe462516c4ec33 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 20:27:21 -0800 Subject: [PATCH 13/37] Reviewer logic improvement --- .github/workflows/pr-workflow.yml | 38 +++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index f5cfe03..0d74583 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -35,25 +35,39 @@ jobs: // CONFIGURATION // ------------------- const rules = { - frontend: { path: "frontend/", label: "frontend", lead: "MaddieWright", team: [] }, - backend: { path: "backend/", label: "backend", lead: "HeisSteve", team: [] }, - infra: { path: ".github/", label: "infra", lead: ["HeisSteve","MaddieWright" ], team: [] } + frontend: { path: "frontend/", label: "frontend", lead: ["MaddieWright"], team: [] }, + backend: { path: "backend/", label: "backend", lead: ["HeisSteve"], team: [] }, + infra: { path: ".github/", label: "infra", lead: ["HeisSteve", "MaddieWright" ], team: [] } }; const labelsToAdd = new Set(); - const reviewersToAdd = new Set(); - const leadsToCheck = new Set(); - const teamToCheck = new Set(); + const reviewersToAdd = new Set(); + const leadsToCheck = new Set(); + const teamToCheck = new Set(); + + // Determine applicable rules for (const file of changedFiles) { for (const rule of Object.values(rules)) { - if (file.startsWith(rule.path)) { - labelsToAdd.add(rule.label); - rule.team.forEach(u => reviewersToAdd.add(u)); - leadsToCheck.add(rule.lead); - rule.team.forEach(u => teamToCheck.add(u)); - } + if (!file.startsWith(rule.path)) continue; + + labelsToAdd.add(rule.label); + + // Normalize leads to an array + const leads = Array.isArray(rule.lead) ? rule.lead : [rule.lead]; + + // Add leads for approval & reviewers + leads.forEach(u => { + leadsToCheck.add(u); + reviewersToAdd.add(u); + }); + + // Add team members for approval & reviewers + rule.team.forEach(u => { + teamToCheck.add(u); + reviewersToAdd.add(u); + }); } } From 8bd504abcbac9f9389a13854fa18923fb20187da Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 20:35:01 -0800 Subject: [PATCH 14/37] review removal logic refactor --- .github/workflows/pr-workflow.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index 0d74583..a870ffd 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -114,8 +114,9 @@ jobs: } // Remove PR author from reviewers - ![...leadsToCheck].includes(author) && reviewersToAdd.delete(author); - + if (!leadsToCheck.has(author)) { + reviewersToAdd.delete(author); + } // Request reviewers if (reviewersToAdd.size > 0) { From 3275b177d8158cf8f940e5a55bfee3c11ea06bb2 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 20:54:23 -0800 Subject: [PATCH 15/37] timestamp on comments --- .github/workflows/pr-workflow.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index a870ffd..8ddd5d9 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -158,9 +158,11 @@ jobs: const approvalSatisfied = leadApproved || teamApprovals >= 2; // Prepare the comment body with an identifier + const now = new Date().toISOString(); const commentBody = approvalSatisfied - ? "✅ PR approval conditions satisfied (1 lead OR 2 team members approved). " - : "❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging. "; + ? `✅ PR approval conditions satisfied (1 lead OR 2 team members approved). [${now}]` + : `❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging. [${now}]`; + // Find existing comment from the bot with our identifier const { data: comments } = await github.rest.issues.listComments({ From 386b5673598e7798d6b11cee79681a7249e9b3ca Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 20:58:39 -0800 Subject: [PATCH 16/37] comment fix --- .github/workflows/pr-workflow.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index 8ddd5d9..2b82f15 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -157,14 +157,13 @@ jobs: // Determine if approval rules are satisfied const approvalSatisfied = leadApproved || teamApprovals >= 2; - // Prepare the comment body with an identifier + + // Prepare the comment body with a timestamp and identifier const now = new Date().toISOString(); const commentBody = approvalSatisfied - ? `✅ PR approval conditions satisfied (1 lead OR 2 team members approved). [${now}]` - : `❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging. [${now}]`; - + ? `✅ PR approval conditions satisfied (1 lead OR 2 team members approved).\n[Last checked: ${now}] ` + : `❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging.\n[Last checked: ${now}] `; - // Find existing comment from the bot with our identifier const { data: comments } = await github.rest.issues.listComments({ owner, repo, @@ -194,4 +193,4 @@ jobs: // Fail workflow if approvals are not satisfied if (!approvalSatisfied) { process.exit(1); - } + } \ No newline at end of file From 4c768a6097cb013a844890a42bb9a08577639131 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 21:01:14 -0800 Subject: [PATCH 17/37] added permissions --- .github/workflows/pr-workflow.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index 2b82f15..e63f777 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -1,5 +1,10 @@ name: PR workflow +permissions: + contents: read # for git operations + pull-requests: write # to request reviewers + issues: write # to create/update PR comments + on: pull_request: types: [opened, synchronize, ready_for_review] # include ready_for_review for draft PRs From 1ebe65135bb724c090497c0029288e3383979dca Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 21:09:25 -0800 Subject: [PATCH 18/37] automation name --- .github/workflows/pr-workflow.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index e63f777..4207a2d 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -11,6 +11,7 @@ on: jobs: pr-automation: + name: "PR Check" runs-on: ubuntu-latest steps: From 9fc2a4c09afb3576ac85b2b7be651803f8c4276e Mon Sep 17 00:00:00 2001 From: jsun64 Date: Tue, 10 Feb 2026 11:29:02 -0800 Subject: [PATCH 19/37] setting cutoffs for non bandpass filters --- frontend/components/nodes/filter-node/combo-box.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/components/nodes/filter-node/combo-box.tsx b/frontend/components/nodes/filter-node/combo-box.tsx index d062cd1..513540e 100644 --- a/frontend/components/nodes/filter-node/combo-box.tsx +++ b/frontend/components/nodes/filter-node/combo-box.tsx @@ -144,7 +144,17 @@ export default function ComboBox({
{ + setSliderValue(val); + + if (value === 'lowpass') { + setHighCutoff(val); + } + + if (value === 'highpass') { + setLowCutoff(val); + } + }} max={100} min={0} step={1} From ce863f56f2f72b52b0738982d6480918d4da844e Mon Sep 17 00:00:00 2001 From: Hassan ElGhayaty <114446784+hsn200406@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:22:44 -0800 Subject: [PATCH 20/37] Add MIT License to the MOSS Project This is the first push of adding an open source licensing option. MIT License has been chosen as the open source License. --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..63c399e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 UBC MINT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 6347b0cd7b052f2744819094ec205fd558200455 Mon Sep 17 00:00:00 2001 From: AgastyaRai Date: Mon, 3 Nov 2025 10:11:14 -0800 Subject: [PATCH 21/37] added frontend state --- backend/shared-logic/src/db.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/shared-logic/src/db.rs b/backend/shared-logic/src/db.rs index b62018b..7b5c7e0 100644 --- a/backend/shared-logic/src/db.rs +++ b/backend/shared-logic/src/db.rs @@ -8,7 +8,7 @@ use log::{info, error, warn}; use chrono::{DateTime, Utc}; use dotenvy::dotenv; use super::models::{User, NewUser, TimeSeriesData, UpdateUser, Session, FrontendState}; -use crate::{lsl::EEGDataPacket}; +use crate::{lsl::EEGData}; use once_cell::sync::OnceCell; use std::sync::Arc; From 47c84c2fcbec33571ab4079a5e25c533a95c7d49 Mon Sep 17 00:00:00 2001 From: AgastyaRai Date: Mon, 1 Dec 2025 23:16:58 -0800 Subject: [PATCH 22/37] modified websocket flow to wait for a signal config before sending data --- backend/shared-logic/src/db.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/shared-logic/src/db.rs b/backend/shared-logic/src/db.rs index 7b5c7e0..b62018b 100644 --- a/backend/shared-logic/src/db.rs +++ b/backend/shared-logic/src/db.rs @@ -8,7 +8,7 @@ use log::{info, error, warn}; use chrono::{DateTime, Utc}; use dotenvy::dotenv; use super::models::{User, NewUser, TimeSeriesData, UpdateUser, Session, FrontendState}; -use crate::{lsl::EEGData}; +use crate::{lsl::EEGDataPacket}; use once_cell::sync::OnceCell; use std::sync::Arc; From 94486866b966ca75e49f1379fcb529e15caf2c2c Mon Sep 17 00:00:00 2001 From: Ali Abbas Date: Mon, 12 Jan 2026 16:49:04 -0800 Subject: [PATCH 23/37] Bandpass Filter --- .../nodes/filter-node/combo-box.tsx | 84 ++++++++++--------- fullstack-moss-app | 1 + 2 files changed, 46 insertions(+), 39 deletions(-) create mode 160000 fullstack-moss-app diff --git a/frontend/components/nodes/filter-node/combo-box.tsx b/frontend/components/nodes/filter-node/combo-box.tsx index 513540e..9851f58 100644 --- a/frontend/components/nodes/filter-node/combo-box.tsx +++ b/frontend/components/nodes/filter-node/combo-box.tsx @@ -80,8 +80,8 @@ export default function ComboBox({ isConnected ? 'border-black' // Connected to source AND data stream on: black border (activated) : isConnected - ? 'border-gray-300' // Connected to source: gray border (non-activated) - : 'border-gray-300' // Disconnected: gray border + ? 'border-gray-300' // Connected to source: gray border (non-activated) + : 'border-gray-300' // Disconnected: gray border )} > {/* No filled circle - always stay empty */} @@ -111,8 +111,9 @@ export default function ComboBox({ {/* Toggle arrow */}
@@ -123,8 +124,8 @@ export default function ComboBox({ isConnected ? 'border-black' // Connected to source AND data stream on: black border (activated) : isConnected - ? 'border-gray-300' // Connected to source: gray border (non-activated) - : 'border-gray-300' // Disconnected: gray border + ? 'border-gray-300' // Connected to source: gray border (non-activated) + : 'border-gray-300' // Disconnected: gray border )} > {/* No solid circle inside */} @@ -141,30 +142,14 @@ export default function ComboBox({ }} > {value != 'bandpass' && ( -
- { - setSliderValue(val); - - if (value === 'lowpass') { - setHighCutoff(val); - } - - if (value === 'highpass') { - setLowCutoff(val); - } - }} - max={100} - min={0} - step={1} - className="w-full" - /> -
- 0 - 100 -
-
+ )} {/* Single slider for lowpass and highpass */} @@ -172,6 +157,7 @@ export default function ComboBox({
{/* Low cutoff */}
+
-
+
+
+ {/* Makeshift spacing */} + + + +
0 Low Cutoff 100
+
+
+
+
+
+
+
+
+ + {/* More makeshift spacing */} + + + {/* High cutoff */}
+ + { @@ -211,18 +218,17 @@ export default function ComboBox({ min={0} max={100} step={1} - className="w-full mb-1" + className="w-full" />
- -
- 0 - High Cutoff - 100 -
)} +
+ 0 + High Cutoff + 100 +
{/* Expandable options section */} diff --git a/fullstack-moss-app b/fullstack-moss-app new file mode 160000 index 0000000..42890df --- /dev/null +++ b/fullstack-moss-app @@ -0,0 +1 @@ +Subproject commit 42890df0532dc8ae5234c71e442a51a44f537bd9 From 0f682dc40ba116c9a8fc88fec3fbf2b0c70a55a9 Mon Sep 17 00:00:00 2001 From: MaddieWright Date: Mon, 12 Jan 2026 20:40:39 -0800 Subject: [PATCH 24/37] Fix slider labelling and spacing format --- .../nodes/filter-node/combo-box.tsx | 70 +++++++------------ 1 file changed, 27 insertions(+), 43 deletions(-) diff --git a/frontend/components/nodes/filter-node/combo-box.tsx b/frontend/components/nodes/filter-node/combo-box.tsx index 9851f58..3229082 100644 --- a/frontend/components/nodes/filter-node/combo-box.tsx +++ b/frontend/components/nodes/filter-node/combo-box.tsx @@ -80,8 +80,8 @@ export default function ComboBox({ isConnected ? 'border-black' // Connected to source AND data stream on: black border (activated) : isConnected - ? 'border-gray-300' // Connected to source: gray border (non-activated) - : 'border-gray-300' // Disconnected: gray border + ? 'border-gray-300' // Connected to source: gray border (non-activated) + : 'border-gray-300' // Disconnected: gray border )} > {/* No filled circle - always stay empty */} @@ -111,9 +111,8 @@ export default function ComboBox({ {/* Toggle arrow */}
@@ -124,8 +123,8 @@ export default function ComboBox({ isConnected ? 'border-black' // Connected to source AND data stream on: black border (activated) : isConnected - ? 'border-gray-300' // Connected to source: gray border (non-activated) - : 'border-gray-300' // Disconnected: gray border + ? 'border-gray-300' // Connected to source: gray border (non-activated) + : 'border-gray-300' // Disconnected: gray border )} > {/* No solid circle inside */} @@ -142,14 +141,20 @@ export default function ComboBox({ }} > {value != 'bandpass' && ( - +
+ +
+ 0 + 100 +
+
)} {/* Single slider for lowpass and highpass */} @@ -157,7 +162,6 @@ export default function ComboBox({
{/* Low cutoff */}
-
-
-
- {/* Makeshift spacing */} - - - -
+
0 Low Cutoff 100
-
-
-
-
-
-
-
-
- - {/* More makeshift spacing */} - - - {/* High cutoff */}
- - { @@ -221,14 +204,15 @@ export default function ComboBox({ className="w-full" />
+ +
+ 0 + High Cutoff + 100 +
)} -
- 0 - High Cutoff - 100 -
{/* Expandable options section */} From 5810bc1c95a6267825efe8f20a86dbe04063547b Mon Sep 17 00:00:00 2001 From: MaddieWright Date: Mon, 12 Jan 2026 20:53:05 -0800 Subject: [PATCH 25/37] bandpass spacing, remove mistake folder --- frontend/components/nodes/filter-node/combo-box.tsx | 6 +++--- fullstack-moss-app | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) delete mode 160000 fullstack-moss-app diff --git a/frontend/components/nodes/filter-node/combo-box.tsx b/frontend/components/nodes/filter-node/combo-box.tsx index 3229082..d062cd1 100644 --- a/frontend/components/nodes/filter-node/combo-box.tsx +++ b/frontend/components/nodes/filter-node/combo-box.tsx @@ -176,11 +176,11 @@ export default function ComboBox({ min={0} max={100} step={1} - className="w-full" + className="w-full mb-1" />
-
+
0 Low Cutoff 100 @@ -201,7 +201,7 @@ export default function ComboBox({ min={0} max={100} step={1} - className="w-full" + className="w-full mb-1" />
diff --git a/fullstack-moss-app b/fullstack-moss-app deleted file mode 160000 index 42890df..0000000 --- a/fullstack-moss-app +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 42890df0532dc8ae5234c71e442a51a44f537bd9 From 87ddd89e9f3657e8a36912cad68031adb618b6c5 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 18:35:38 -0800 Subject: [PATCH 26/37] added a Pull Request Template --- .github/docs/PULL_REQUEST_TEMPLATE.md | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/docs/PULL_REQUEST_TEMPLATE.md diff --git a/.github/docs/PULL_REQUEST_TEMPLATE.md b/.github/docs/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..a5c4080 --- /dev/null +++ b/.github/docs/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ +# Pull Request Template + +## What? + +**Add a concise description** of the change made. + +**Example:** "Implemented backend API for dataset retrieval." + +--- + +## How? + +**Mention a brief technical explanation** of the change. + +**Example:** "Refactored the API service to handle dataset retrieval with +caching to improve performance." + +--- + +## Testing + +List ways you have tested the code. + +**Example:** "Used console.log() to ensure function ran, UI changes color when +hovering over" + +Attach images or videos if possible. +--- + +### Notes: + +- This template is intended to **guide** your PR submission, but feel free to + modify sections as needed. +- For the PR title, just reference the appropriate Github Issue/Notion Task + - eg. `steve/[Bug Fix] Changed color of background` \ No newline at end of file From 1423ef5785069433c3bbd564a381b30b2fdde565 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 18:38:39 -0800 Subject: [PATCH 27/37] Moved template location --- .github/docs/PULL_REQUEST_TEMPLATE.md | 35 --------------------------- 1 file changed, 35 deletions(-) delete mode 100644 .github/docs/PULL_REQUEST_TEMPLATE.md diff --git a/.github/docs/PULL_REQUEST_TEMPLATE.md b/.github/docs/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index a5c4080..0000000 --- a/.github/docs/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,35 +0,0 @@ -# Pull Request Template - -## What? - -**Add a concise description** of the change made. - -**Example:** "Implemented backend API for dataset retrieval." - ---- - -## How? - -**Mention a brief technical explanation** of the change. - -**Example:** "Refactored the API service to handle dataset retrieval with -caching to improve performance." - ---- - -## Testing - -List ways you have tested the code. - -**Example:** "Used console.log() to ensure function ran, UI changes color when -hovering over" - -Attach images or videos if possible. ---- - -### Notes: - -- This template is intended to **guide** your PR submission, but feel free to - modify sections as needed. -- For the PR title, just reference the appropriate Github Issue/Notion Task - - eg. `steve/[Bug Fix] Changed color of background` \ No newline at end of file From b7c3c16ac3ecee0fade9bf70a8c900d0126ed3d3 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 19:29:23 -0800 Subject: [PATCH 28/37] PR automation workflow --- .github/workflows/pr-workflow.yml | 140 ++++++------------------------ 1 file changed, 25 insertions(+), 115 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index 4207a2d..328d4f2 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -1,27 +1,19 @@ name: PR workflow -permissions: - contents: read # for git operations - pull-requests: write # to request reviewers - issues: write # to create/update PR comments - on: pull_request: - types: [opened, synchronize, ready_for_review] # include ready_for_review for draft PRs + types: [opened, synchronize] jobs: pr-automation: - name: "PR Check" runs-on: ubuntu-latest steps: - # Step 1: Get changed files - name: Get changed files id: files uses: tj-actions/changed-files@v44 - # Step 2: Assign, label, request reviewers, and enforce approvals - - name: Assign, label, request reviewers, enforce approvals + - name: Assign, label, and request reviewers uses: actions/github-script@v7 with: script: | @@ -30,50 +22,37 @@ jobs: const prNumber = context.payload.pull_request.number; const author = context.payload.pull_request.user.login; - if (context.payload.pull_request.draft) { - console.log("PR is a draft — skipping workflow steps."); - return; - } - - const changedFiles = "${{ steps.files.outputs.all_changed_files }}".split(" ").filter(f => f); + const changedFiles = "${{ steps.files.outputs.all_changed_files }}".split(" "); - // ------------------- // CONFIGURATION - // ------------------- const rules = { - frontend: { path: "frontend/", label: "frontend", lead: ["MaddieWright"], team: [] }, - backend: { path: "backend/", label: "backend", lead: ["HeisSteve"], team: [] }, - infra: { path: ".github/", label: "infra", lead: ["HeisSteve", "MaddieWright" ], team: [] } + frontend: { + path: "frontend/", + label: "frontend", + reviewers: ["MaddieWright"] + }, + backend: { + path: "backend/", + label: "backend", + reviewers: ["HeisSteve"] + }, + infra: { + path: ".github/", + label: "infra", + reviewers: ["HeisSteve", "MaddieWright"] + } }; const labelsToAdd = new Set(); - const reviewersToAdd = new Set(); + const reviewersToAdd = new Set(); - const leadsToCheck = new Set(); - const teamToCheck = new Set(); - - // Determine applicable rules for (const file of changedFiles) { for (const rule of Object.values(rules)) { - if (!file.startsWith(rule.path)) continue; - - labelsToAdd.add(rule.label); - - // Normalize leads to an array - const leads = Array.isArray(rule.lead) ? rule.lead : [rule.lead]; - - // Add leads for approval & reviewers - leads.forEach(u => { - leadsToCheck.add(u); - reviewersToAdd.add(u); - }); - - // Add team members for approval & reviewers - rule.team.forEach(u => { - teamToCheck.add(u); - reviewersToAdd.add(u); - }); + if (file.startsWith(rule.path)) { + labelsToAdd.add(rule.label); + rule.reviewers.forEach(r => reviewersToAdd.add(r)); + } } } @@ -119,10 +98,8 @@ jobs: }); } - // Remove PR author from reviewers - if (!leadsToCheck.has(author)) { - reviewersToAdd.delete(author); - } + // Remove PR author from reviewers if present + reviewersToAdd.delete(author); // Request reviewers if (reviewersToAdd.size > 0) { @@ -133,70 +110,3 @@ jobs: reviewers: [...reviewersToAdd] }); } - - - // ------------------- - // CUSTOM APPROVAL CHECK WITH SINGLE COMMENT - // ------------------- - const { data: reviews } = await github.rest.pulls.listReviews({ - owner, - repo, - pull_number: prNumber - }); - - // Set of users who approved - const approvedUsers = new Set( - reviews - .filter(r => r.state === "APPROVED") - .map(r => r.user.login) - ); - - // Check if at least one lead approved (include author if they are a lead) - const leadApproved = [...leadsToCheck].some(u => approvedUsers.has(u)); - - // Count how many team members approved (exclude PR author) - let teamApprovals = 0; - for (const member of teamToCheck) { - if (approvedUsers.has(member) && member !== author) teamApprovals++; - } - - // Determine if approval rules are satisfied - const approvalSatisfied = leadApproved || teamApprovals >= 2; - - - // Prepare the comment body with a timestamp and identifier - const now = new Date().toISOString(); - const commentBody = approvalSatisfied - ? `✅ PR approval conditions satisfied (1 lead OR 2 team members approved).\n[Last checked: ${now}] ` - : `❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging.\n[Last checked: ${now}] `; - - const { data: comments } = await github.rest.issues.listComments({ - owner, - repo, - issue_number: prNumber - }); - - const existingComment = comments.find(c => c.body.includes("")); - - if (existingComment) { - // Update the existing comment - await github.rest.issues.updateComment({ - owner, - repo, - comment_id: existingComment.id, - body: commentBody - }); - } else { - // Create a new comment - await github.rest.issues.createComment({ - owner, - repo, - issue_number: prNumber, - body: commentBody - }); - } - - // Fail workflow if approvals are not satisfied - if (!approvalSatisfied) { - process.exit(1); - } \ No newline at end of file From cde8d8edb8393138a054b8b255e82f504a629229 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 20:09:24 -0800 Subject: [PATCH 29/37] updated approval restriction --- .github/workflows/pr-workflow.yml | 110 ++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 21 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index 328d4f2..f5cfe03 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -2,18 +2,20 @@ name: PR workflow on: pull_request: - types: [opened, synchronize] + types: [opened, synchronize, ready_for_review] # include ready_for_review for draft PRs jobs: pr-automation: runs-on: ubuntu-latest steps: + # Step 1: Get changed files - name: Get changed files id: files uses: tj-actions/changed-files@v44 - - name: Assign, label, and request reviewers + # Step 2: Assign, label, request reviewers, and enforce approvals + - name: Assign, label, request reviewers, enforce approvals uses: actions/github-script@v7 with: script: | @@ -22,36 +24,35 @@ jobs: const prNumber = context.payload.pull_request.number; const author = context.payload.pull_request.user.login; - const changedFiles = "${{ steps.files.outputs.all_changed_files }}".split(" "); + if (context.payload.pull_request.draft) { + console.log("PR is a draft — skipping workflow steps."); + return; + } + + const changedFiles = "${{ steps.files.outputs.all_changed_files }}".split(" ").filter(f => f); + // ------------------- // CONFIGURATION + // ------------------- const rules = { - frontend: { - path: "frontend/", - label: "frontend", - reviewers: ["MaddieWright"] - }, - backend: { - path: "backend/", - label: "backend", - reviewers: ["HeisSteve"] - }, - infra: { - path: ".github/", - label: "infra", - reviewers: ["HeisSteve", "MaddieWright"] - } + frontend: { path: "frontend/", label: "frontend", lead: "MaddieWright", team: [] }, + backend: { path: "backend/", label: "backend", lead: "HeisSteve", team: [] }, + infra: { path: ".github/", label: "infra", lead: ["HeisSteve","MaddieWright" ], team: [] } }; const labelsToAdd = new Set(); const reviewersToAdd = new Set(); + const leadsToCheck = new Set(); + const teamToCheck = new Set(); // Determine applicable rules for (const file of changedFiles) { for (const rule of Object.values(rules)) { if (file.startsWith(rule.path)) { labelsToAdd.add(rule.label); - rule.reviewers.forEach(r => reviewersToAdd.add(r)); + rule.team.forEach(u => reviewersToAdd.add(u)); + leadsToCheck.add(rule.lead); + rule.team.forEach(u => teamToCheck.add(u)); } } } @@ -98,8 +99,9 @@ jobs: }); } - // Remove PR author from reviewers if present - reviewersToAdd.delete(author); + // Remove PR author from reviewers + ![...leadsToCheck].includes(author) && reviewersToAdd.delete(author); + // Request reviewers if (reviewersToAdd.size > 0) { @@ -110,3 +112,69 @@ jobs: reviewers: [...reviewersToAdd] }); } + + + // ------------------- + // CUSTOM APPROVAL CHECK WITH SINGLE COMMENT + // ------------------- + const { data: reviews } = await github.rest.pulls.listReviews({ + owner, + repo, + pull_number: prNumber + }); + + // Set of users who approved + const approvedUsers = new Set( + reviews + .filter(r => r.state === "APPROVED") + .map(r => r.user.login) + ); + + // Check if at least one lead approved (include author if they are a lead) + const leadApproved = [...leadsToCheck].some(u => approvedUsers.has(u)); + + // Count how many team members approved (exclude PR author) + let teamApprovals = 0; + for (const member of teamToCheck) { + if (approvedUsers.has(member) && member !== author) teamApprovals++; + } + + // Determine if approval rules are satisfied + const approvalSatisfied = leadApproved || teamApprovals >= 2; + + // Prepare the comment body with an identifier + const commentBody = approvalSatisfied + ? "✅ PR approval conditions satisfied (1 lead OR 2 team members approved). " + : "❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging. "; + + // Find existing comment from the bot with our identifier + const { data: comments } = await github.rest.issues.listComments({ + owner, + repo, + issue_number: prNumber + }); + + const existingComment = comments.find(c => c.body.includes("")); + + if (existingComment) { + // Update the existing comment + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: existingComment.id, + body: commentBody + }); + } else { + // Create a new comment + await github.rest.issues.createComment({ + owner, + repo, + issue_number: prNumber, + body: commentBody + }); + } + + // Fail workflow if approvals are not satisfied + if (!approvalSatisfied) { + process.exit(1); + } From a8a73a5adfa14051d0113954d731874bce1ddae0 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 20:27:21 -0800 Subject: [PATCH 30/37] Reviewer logic improvement --- .github/workflows/pr-workflow.yml | 38 +++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index f5cfe03..0d74583 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -35,25 +35,39 @@ jobs: // CONFIGURATION // ------------------- const rules = { - frontend: { path: "frontend/", label: "frontend", lead: "MaddieWright", team: [] }, - backend: { path: "backend/", label: "backend", lead: "HeisSteve", team: [] }, - infra: { path: ".github/", label: "infra", lead: ["HeisSteve","MaddieWright" ], team: [] } + frontend: { path: "frontend/", label: "frontend", lead: ["MaddieWright"], team: [] }, + backend: { path: "backend/", label: "backend", lead: ["HeisSteve"], team: [] }, + infra: { path: ".github/", label: "infra", lead: ["HeisSteve", "MaddieWright" ], team: [] } }; const labelsToAdd = new Set(); - const reviewersToAdd = new Set(); - const leadsToCheck = new Set(); - const teamToCheck = new Set(); + const reviewersToAdd = new Set(); + const leadsToCheck = new Set(); + const teamToCheck = new Set(); + + // Determine applicable rules for (const file of changedFiles) { for (const rule of Object.values(rules)) { - if (file.startsWith(rule.path)) { - labelsToAdd.add(rule.label); - rule.team.forEach(u => reviewersToAdd.add(u)); - leadsToCheck.add(rule.lead); - rule.team.forEach(u => teamToCheck.add(u)); - } + if (!file.startsWith(rule.path)) continue; + + labelsToAdd.add(rule.label); + + // Normalize leads to an array + const leads = Array.isArray(rule.lead) ? rule.lead : [rule.lead]; + + // Add leads for approval & reviewers + leads.forEach(u => { + leadsToCheck.add(u); + reviewersToAdd.add(u); + }); + + // Add team members for approval & reviewers + rule.team.forEach(u => { + teamToCheck.add(u); + reviewersToAdd.add(u); + }); } } From 2d2f0c5168055fc5c19220acca05339df50b99f5 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 20:35:01 -0800 Subject: [PATCH 31/37] review removal logic refactor --- .github/workflows/pr-workflow.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index 0d74583..a870ffd 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -114,8 +114,9 @@ jobs: } // Remove PR author from reviewers - ![...leadsToCheck].includes(author) && reviewersToAdd.delete(author); - + if (!leadsToCheck.has(author)) { + reviewersToAdd.delete(author); + } // Request reviewers if (reviewersToAdd.size > 0) { From c4064465ee2a781f8250da9b56c0b83c05a0ddd2 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 20:54:23 -0800 Subject: [PATCH 32/37] timestamp on comments --- .github/workflows/pr-workflow.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index a870ffd..8ddd5d9 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -158,9 +158,11 @@ jobs: const approvalSatisfied = leadApproved || teamApprovals >= 2; // Prepare the comment body with an identifier + const now = new Date().toISOString(); const commentBody = approvalSatisfied - ? "✅ PR approval conditions satisfied (1 lead OR 2 team members approved). " - : "❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging. "; + ? `✅ PR approval conditions satisfied (1 lead OR 2 team members approved). [${now}]` + : `❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging. [${now}]`; + // Find existing comment from the bot with our identifier const { data: comments } = await github.rest.issues.listComments({ From 49e1053e46e5c8ce4ae7cb27e398ed0fde824a9f Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 20:58:39 -0800 Subject: [PATCH 33/37] comment fix --- .github/workflows/pr-workflow.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index 8ddd5d9..2b82f15 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -157,14 +157,13 @@ jobs: // Determine if approval rules are satisfied const approvalSatisfied = leadApproved || teamApprovals >= 2; - // Prepare the comment body with an identifier + + // Prepare the comment body with a timestamp and identifier const now = new Date().toISOString(); const commentBody = approvalSatisfied - ? `✅ PR approval conditions satisfied (1 lead OR 2 team members approved). [${now}]` - : `❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging. [${now}]`; - + ? `✅ PR approval conditions satisfied (1 lead OR 2 team members approved).\n[Last checked: ${now}] ` + : `❌ PR approval conditions NOT satisfied. At least 1 team lead OR 2 team members must approve before merging.\n[Last checked: ${now}] `; - // Find existing comment from the bot with our identifier const { data: comments } = await github.rest.issues.listComments({ owner, repo, @@ -194,4 +193,4 @@ jobs: // Fail workflow if approvals are not satisfied if (!approvalSatisfied) { process.exit(1); - } + } \ No newline at end of file From d9d3292df2a4f50e2d8a2035354a06f31ac38982 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 21:01:14 -0800 Subject: [PATCH 34/37] added permissions --- .github/workflows/pr-workflow.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index 2b82f15..e63f777 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -1,5 +1,10 @@ name: PR workflow +permissions: + contents: read # for git operations + pull-requests: write # to request reviewers + issues: write # to create/update PR comments + on: pull_request: types: [opened, synchronize, ready_for_review] # include ready_for_review for draft PRs From 964896bcefbbba91a39736c4b28f6038ae6cd4f8 Mon Sep 17 00:00:00 2001 From: Steve He Date: Sat, 31 Jan 2026 21:09:25 -0800 Subject: [PATCH 35/37] automation name --- .github/workflows/pr-workflow.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index e63f777..4207a2d 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -11,6 +11,7 @@ on: jobs: pr-automation: + name: "PR Check" runs-on: ubuntu-latest steps: From 7e051fbac4fec6111f0da00810b58827f971be3a Mon Sep 17 00:00:00 2001 From: jsun64 Date: Tue, 10 Feb 2026 11:29:02 -0800 Subject: [PATCH 36/37] setting cutoffs for non bandpass filters --- frontend/components/nodes/filter-node/combo-box.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/components/nodes/filter-node/combo-box.tsx b/frontend/components/nodes/filter-node/combo-box.tsx index d062cd1..513540e 100644 --- a/frontend/components/nodes/filter-node/combo-box.tsx +++ b/frontend/components/nodes/filter-node/combo-box.tsx @@ -144,7 +144,17 @@ export default function ComboBox({
{ + setSliderValue(val); + + if (value === 'lowpass') { + setHighCutoff(val); + } + + if (value === 'highpass') { + setLowCutoff(val); + } + }} max={100} min={0} step={1} From b0eb7e0fd7064d4711ccc6ac2ab76601f40ba930 Mon Sep 17 00:00:00 2001 From: jsun64 Date: Sat, 7 Mar 2026 13:39:31 -0800 Subject: [PATCH 37/37] settings bar update --- .../components/ui-header/settings-bar.tsx | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/frontend/components/ui-header/settings-bar.tsx b/frontend/components/ui-header/settings-bar.tsx index 5b642bc..3bed852 100644 --- a/frontend/components/ui-header/settings-bar.tsx +++ b/frontend/components/ui-header/settings-bar.tsx @@ -21,6 +21,27 @@ export default function SettingsBar() { const [leftTimerSeconds, setLeftTimerSeconds] = useState(0); const intervalRef = useRef(null); const [isResetDialogOpen, setIsResetDialogOpen] = useState(false); + const [sessionId, setSessionId] = useState(null); + + useEffect(() => { + async function fetchSessions() { + try { + const res = await fetch('/api/sessions'); + const sessions = await res.json(); + + console.log('sessions response:', sessions); + + if (sessions.length > 0) { + setSessionId(sessions[0].id); + } + } catch (err) { + console.error('Failed to fetch sessions', err); + } + } + + fetchSessions(); + }, []); + // useEffect(() => { // console.log('dataStreaming:', dataStreaming); // }); @@ -88,13 +109,12 @@ export default function SettingsBar() { {/* System Control Panel, Filters, Settings */} - System Control Panel - - - Filters + + Session {sessionId ? sessionId.slice(0, 8) : 'ID'} + - Settings + Tutorials