Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ jobs:
libappindicator3-dev \
librsvg2-dev \
patchelf \
libudev-dev
libudev-dev \
libasound2-dev

- name: Install dependencies
run: bun install
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ jobs:
libappindicator3-dev \
librsvg2-dev \
patchelf \
libudev-dev
libudev-dev \
libasound2-dev

- name: Install dependencies
run: bun install
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ jobs:
libappindicator3-dev \
librsvg2-dev \
patchelf \
libudev-dev
libudev-dev \
libasound2-dev

# Whole workspace: app_lib plus the extracted microflow-core and
# microflow-codegen-wasm crates (the browser code generator).
Expand Down Expand Up @@ -62,7 +63,8 @@ jobs:
libappindicator3-dev \
librsvg2-dev \
patchelf \
libudev-dev
libudev-dev \
libasound2-dev

- name: Test
run: cargo test --workspace --lib --tests
12 changes: 12 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,15 @@ Bidirectional reconciler between a `FlowDocument` (Y.Doc CRDT) and the [ReactFlo
- **`scheduleFlush` / `flush`** — RAF-batched structural writes. Multiple `applyNodeChanges` / `applyEdgeChanges` calls in one frame coalesce into one `transact("local")` and therefore one `UndoManager` entry. `flush()` is public so tests and callers needing a write barrier (e.g. before navigation) can force the flush synchronously.

Each invariant is independently unit-testable (28 vitest cases in `__tests__/react-flow-bridge.test.ts`, including a convergence-via-`RecordingSyncAdapter` headline test that proves CRDT replay end-to-end without a React renderer). Drag-during positions are **not** sent over the doc today — the right channel for ephemeral peer state is Yjs awareness, not the doc, and a future enhancement will broadcast `draggingNode: { id, position }` via `RemoteSyncAdapter.updateCursor`-style awareness so live collaborators see smooth drag motion without polluting undo history.

## Board Bring-Up

Sans-IO state machine owning the board bring-up policy: probe → flash StandardFirmata if missing → connect → auto-reconnect, and the `disconnected → connecting → flashing → connected → error` phase transitions. Lives in `crates/microflow-core/src/bringup.rs` (value-tested transition table); events in (`PortReady`, `ProbeOk`/`ProbeFailed`, `FlashOk`/`FlashFailed`, `ConnectionLost`, `PortGone`, `DisconnectRequested`) → `Phase` + host actions out (`Probe`, `Flash`, `ClosePort`, `ScheduleRetry`, `Notify`), mirroring the [Effects](#effects)/[EffectsSink](#effectssink) discipline. Both [Runtime Hosts](#runtime-host) are adapters: the browser drives it through `BringUpMachine` in `microflow-firmata-wasm` from `apps/web/src/lib/firmata/board-controller.ts`; the desktop drives it from `src-tauri/src/hardware/mod.rs`. Hosts own I/O and timing only (serial ops, flash transport, boot/settle sleeps, toasts keyed off `Notify` phases); the decisions live in the machine.

## Flow Role

The access level a user has on a cloud flow: **Owner** (the `flow.ownerId` user — full control including delete, sharing, and role changes), **Editor** (a `flowCollaborator` row with `role: "editor"` — may edit the document), or **Viewer** (`role: "viewer"` — read-only). Ranked `viewer < editor < owner`. The type and the pure resolution/enforcement helpers (`resolveFlowRole`, `assertFlowRole`) live in `packages/api/src/routers/flow-role.ts`; the access matrix is table-tested in `flow-access.test.ts`.

## Flow Access seam

`requireFlowAccess(flowId, userId, minRole)` in `packages/api/src/routers/flow-access.ts` — the single choke point where tRPC flow procedures resolve a [Flow Role](#flow-role) and enforce a minimum. Fetches the flow row, resolves owner-or-collaborator role via `resolveFlowRole`, throws `"Flow not found"` / `"Access denied"`, and returns `{ flow, role }` so procedures never re-implement the check. `flow.get` keeps its richer eager-loaded query but routes role resolution through the same pure helpers, so the two access notions cannot drift.
144 changes: 129 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion apps/web/node-components.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@
"name": "Matrix",
"impl": "Matrix"
},
{
"name": "Midi",
"impl": "Midi"
},
{
"name": "Monitor",
"impl": "Monitor"
Expand Down Expand Up @@ -232,6 +236,11 @@
"category": "output",
"requiresHardware": true
},
{
"name": "Midi",
"category": "external",
"requiresHardware": false
},
{
"name": "Monitor",
"category": "output",
Expand Down Expand Up @@ -319,4 +328,4 @@
"requiresHardware": false
}
]
}
}
1 change: 1 addition & 0 deletions apps/web/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ tauri-plugin-process = "2"
tauri-plugin-opener = "2"
tauri-plugin-deep-link = "2"
serialport = "4"
midir = "0.10"
tokio = { version = "1", features = ["sync", "rt", "macros", "time", "net"] }
mqtt-endpoint-tokio = { version = "0.6", default-features = false, features = ["tls", "ws"] }
uuid = { version = "1", features = ["v4"] }
Expand Down
5 changes: 0 additions & 5 deletions apps/web/src-tauri/src/hardware/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,6 @@ impl EventEmitter {
});
}

/// Emit error for port without Firmata
pub fn no_firmata_error(&self, port_name: &str) {
self.board_error(&format!("No Firmata detected on {port_name}"));
}

// ========================================================================
// Port Events
// ========================================================================
Expand Down
Loading
Loading