Skip to content

feat(js): add graph change callback and set tags api - #230

Open
heypoom wants to merge 5 commits into
mainfrom
graph-change-callbacks-and-object-tags
Open

feat(js): add graph change callback and set tags api#230
heypoom wants to merge 5 commits into
mainfrom
graph-change-callbacks-and-object-tags

Conversation

@heypoom

@heypoom heypoom commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Adds the onGraphChange({ tags }, callback) API for listening to Patchies graph changes, and a setTags API for setting tags onto objects.

Summary by CodeRabbit

  • New Features
    • JavaScript nodes can dynamically update tags with setTags.
    • Added onGraphChange subscriptions for matching graph nodes and connections.
    • Added runtime JavaScript support for callbacks, timers, console output, and lifecycle cleanup.
    • Added completion hints and guidance for graph-related functions.
  • Bug Fixes
    • Graph subscriptions no longer emit empty snapshots or react to nonmatching changes.
    • Tag handling trims, deduplicates, and excludes invalid or reserved tags.
  • Tests
    • Added coverage for graph updates, tag filtering, JavaScript callbacks, and runtime behavior.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds tag-filtered graph subscriptions and exposes them to JavaScript runtime objects. It wires graph notifications through PatchRuntime, adds setTags to supported nodes, registers JSObject, and adds tests for graph callbacks, tags, messaging, and cleanup.

Changes

Graph runtime and JavaScript APIs

Layer / File(s) Summary
Graph observation and tag normalization
ui/src/lib/runtime/services/GraphObserver.ts, ui/src/lib/runtime/services/graph-tags.ts, ui/src/lib/runtime/services/*test.ts, docs/design-docs/specs/...md
Adds filtered graph snapshots, wildcard tag matching, batched notifications, duplicate suppression, unsubscribe support, and normalized user tags. Empty snapshots do not trigger callbacks.
Runtime object context and graph lifecycle
ui/src/lib/objects/v2/*, ui/src/lib/runtime/adapters/MessageAdapter.ts, ui/src/lib/runtime/services/PatchRuntime.ts, ui/src/lib/runtime/utils/runtime-test-utils.ts
Passes graph subscription options through object creation, registers JSObject, synchronizes message connections, notifies observers after graph mutations, and cleans up observers during runtime destruction.
JavaScript graph callbacks and execution lifecycle
ui/src/objects/js/JSObject.ts, ui/src/objects/js/JSObject.test.ts, ui/src/lib/js-runner/*, ui/src/objects/js/JSBlockNode.svelte, ui/src/objects/code/CodeBlockBase.svelte, ui/src/lib/runtime/services/OnGraphChange.integration.test.ts
Adds JavaScript support for setTags and onGraphChange. JSObject manages subscriptions, callbacks, timers, messages, console output, execution state, and cleanup.
Tag APIs and editor availability
ui/src/objects/canvas/CanvasDom.svelte, ui/src/objects/dom/DomRuntimeNode.svelte, ui/src/objects/three/ThreeDom.svelte, ui/src/lib/codemirror/patchies-completions.ts, docs/design-docs/specs/...md
Adds normalized setTags support to visual nodes and documents completion signatures, top-level restrictions, and node-type availability.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant JavaScript
  participant JSObject
  participant PatchRuntime
  participant GraphObserver
  participant MessageNode
  JavaScript->>JSObject: subscribe with onGraphChange
  JSObject->>PatchRuntime: subscribeGraph(query, callback)
  PatchRuntime->>GraphObserver: register subscription
  PatchRuntime->>GraphObserver: notify after graph mutation
  GraphObserver->>JSObject: deliver matching snapshot
  JSObject->>MessageNode: send callback data
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the two main changes: the graph change callback and the setTags API.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch graph-change-callbacks-and-object-tags

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 2, 2026

Copy link
Copy Markdown

Deploying patchies with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4b80bee
Status: ✅  Deploy successful!
Preview URL: https://ed5eec76.patchies.pages.dev
Branch Preview URL: https://graph-change-callbacks-and-o.patchies.pages.dev

View logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (6)
ui/src/lib/runtime/services/GraphObserver.ts (1)

67-90: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Cache the graph reference to avoid a redundant call.

getSnapshot calls this.getGraph() twice, once at Line 68 and once at Line 74. getSnapshot runs once per subscription on every notify cycle, so caching the result in a local variable avoids doing the same work twice per call.

♻️ Proposed refactor to cache the graph reference
   private getSnapshot(query: GraphChangeQuery): GraphSnapshot {
-    const nodes = this.getGraph().objects.flatMap((node) => {
+    const graph = this.getGraph();
+    const nodes = graph.objects.flatMap((node) => {
       const tags = getUserTags(node.data.tags);

       return nodeMatchesTags(tags, query.tags) ? [{ ...node, tags }] : [];
     });
     const matchingNodeIds = new Set(nodes.map((node) => node.id));
-    const edges = (this.getGraph().connections ?? []).flatMap((edge) =>
+    const edges = (graph.connections ?? []).flatMap((edge) =>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/lib/runtime/services/GraphObserver.ts` around lines 67 - 90, Update
getSnapshot to store this.getGraph() in a local graph variable once, then use
that variable for both objects and connections instead of calling getGraph()
twice.
ui/src/objects/canvas/CanvasDom.svelte (1)

404-406: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

setTags fully replaces data.tags; verify this preserves derived core tags.

All three sites implement setTags the same way: they overwrite data.tags with only getUserTags(tags), the newly supplied list. The design doc states setTags "does not change its derived core tags," which implies data.tags can hold both user tags and reserved core/-prefixed tags together (confirmed by GraphObserver.getSnapshot, which reads getUserTags(node.data.tags) to strip reserved tags out of that same field). None of these three sites preserve any core/-prefixed tag that might already be present in data.tags before the overwrite.

No code in this batch currently writes a core/-prefixed tag into data.tags, so there is no observable effect today. Confirm whether any current or planned mechanism ever writes reserved tags into data.tags. If it does, or will, merge with the existing reserved tags instead of replacing the whole array.

  • ui/src/objects/canvas/CanvasDom.svelte#L404-L406: merge existing core/-prefixed entries from data.tags with getUserTags(tags) instead of replacing the array outright.
  • ui/src/objects/dom/DomRuntimeNode.svelte#L267-L269: apply the same merge fix.
  • ui/src/objects/three/ThreeDom.svelte#L396-L398: apply the same merge fix.
🛡️ Proposed fix pattern (apply at each site)
-        setTags(tags: string[]) {
-          updateNodeData(nodeId, { tags: getUserTags(tags) });
-        },
+        setTags(tags: string[]) {
+          const coreTags = (data.tags ?? []).filter((tag) => tag.startsWith('core/'));
+          updateNodeData(nodeId, { tags: [...coreTags, ...getUserTags(tags)] });
+        },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/objects/canvas/CanvasDom.svelte` around lines 404 - 406, Update
setTags in ui/src/objects/canvas/CanvasDom.svelte#L404-L406,
ui/src/objects/dom/DomRuntimeNode.svelte#L267-L269, and
ui/src/objects/three/ThreeDom.svelte#L396-L398 to preserve existing
core/-prefixed entries from data.tags while replacing the user-tag portion with
getUserTags(tags). Apply the same merge behavior at all three sites.
ui/src/lib/runtime/services/GraphObserver.test.ts (1)

7-19: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Await the queued microtask before asserting on notify().

notify() defers its work with queueMicrotask. These three tests call observer.notify() and assert immediately, with no await anywhere in the test body. Because the test function body has no await, it runs synchronously to completion, including expect(...), before the queued microtask executes.

As a result, each assertion only reflects the state set by subscribe()'s own synchronous immediate notification, not by the notify() call each test is meant to exercise. The expected values happen to match what subscribe() alone produces, so the tests pass without validating notify()'s deferred behavior at all. A regression that makes the deferred microtask push an incorrect snapshot would not be caught by any of these three tests.

Await a resolved promise after observer.notify() so the queued microtask flushes before the assertion runs.

✅ Proposed fix to flush the queued microtask before asserting
     observer.subscribe({ tags: ['shader/foo/*'] }, (snapshot) => snapshots.push(snapshot));
     observer.notify();
+    await Promise.resolve();

     expect(snapshots).toEqual([]);

Apply the same await Promise.resolve(); after each observer.notify() call in the other two affected tests.

Also applies to: 21-38, 40-71

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/lib/runtime/services/GraphObserver.test.ts` around lines 7 - 19,
Update all three affected tests around GraphObserver.notify to await a resolved
promise immediately after each observer.notify() call, before asserting
snapshots. This flushes the queued microtask and ensures the assertions validate
notify’s deferred behavior rather than only subscribe’s synchronous
notification.
ui/src/lib/runtime/services/PatchRuntime.ts (1)

299-324: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reuse syncMessageConnections inside syncConnections.

Both methods map the graph connections to editor edges and call this.message.updateEdges(...). Call syncMessageConnections() from syncConnections() to keep one mapping site.

♻️ Proposed refactor
   private syncConnections(): void {
-    const edges = this.graph.getConnections().map(getEditorEdgeFromRuntimeConnection);
+    const edges = this.graph.getConnections().map(getEditorEdgeFromRuntimeConnection);
@@
-    this.message.updateEdges(edges);
+    this.syncMessageConnections(edges);
     this.audio.audioService.updateEdges(edges);
@@
-  private syncMessageConnections(): void {
-    this.message.updateEdges(this.graph.getConnections().map(getEditorEdgeFromRuntimeConnection));
+  private syncMessageConnections(
+    edges = this.graph.getConnections().map(getEditorEdgeFromRuntimeConnection)
+  ): void {
+    this.message.updateEdges(edges);
   }

As per coding guidelines: "Extract shared functions instead of duplicating the same logic across multiple places".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/lib/runtime/services/PatchRuntime.ts` around lines 299 - 324, Update
syncConnections() to call the existing syncMessageConnections() method instead
of independently mapping graph connections and invoking
this.message.updateEdges. Keep the remaining system edge updates unchanged and
retain syncMessageConnections() as the single message-connection mapping site.

Source: Coding guidelines

ui/src/lib/runtime/utils/runtime-test-utils.ts (1)

152-159: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse getObjectClass for class selection.

The nested ternary repeats the mapping in getObjectClass. Each new test object type must be added in three places. Derive the class from getObjectClass instead.

♻️ Proposed refactor
-    const ObjectClass: TextObjectClass =
-      objectType === ButtonObject.type
-        ? ButtonObject
-        : objectType === JSObject.type
-          ? JSObject
-          : PatchRuntimeTestObject;
+    const ObjectClass: TextObjectClass = this.getObjectClass(objectType) ?? PatchRuntimeTestObject;

As per coding guidelines: "Extract shared functions instead of duplicating the same logic across multiple places".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/lib/runtime/utils/runtime-test-utils.ts` around lines 152 - 159,
Replace the nested ternary in the class-selection logic with a call to the
existing getObjectClass method, preserving the current objectType mapping and
ObjectClass typing while ensuring future test object types use the shared
mapping.

Source: Coding guidelines

ui/src/lib/runtime/services/OnGraphChange.integration.test.ts (1)

12-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Unregister the compiler node in afterEach.

MessageSystem.getInstance() is a shared singleton across test files. The runtime registers compilerId when it creates the js object. afterEach unregisters only glslId, so a stale shader-compiler registration can remain and affect later tests.

♻️ Proposed change
   afterEach(() => {
+    messageSystem.unregisterNode(compilerId);
     messageSystem.unregisterNode(glslId);
     messageSystem.updateEdges([]);
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/lib/runtime/services/OnGraphChange.integration.test.ts` around lines
12 - 15, Update the afterEach cleanup to also unregister the compilerId node
registered by the js object, while retaining the existing glslId unregister and
edge reset through the shared messageSystem singleton.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/src/lib/runtime/services/GraphObserver.ts`:
- Around line 40-61: Update notifySubscription to wrap
subscription.callback(snapshot) in a try/catch, ensuring callback failures are
isolated and do not interrupt notification of other subscriptions. Apply this in
notifySubscription so both subscribe and notify retain their existing behavior
while continuing to process unrelated subscriptions.

In `@ui/src/objects/code/CodeBlockBase.svelte`:
- Line 52: Update the default data-triggered execution handler in CodeBlockBase
so it uses the local executeCode() flow instead of onExecute, preserving
clearConsole() and the lineErrors/hasError resets for consumers that do not
provide an override. Ensure the data-triggered call site around the affected
execution path continues to invoke that default behavior.

In `@ui/src/objects/js/JSBlockNode.svelte`:
- Around line 52-54: Update setSetting in JSBlockNode to route node setting
changes through the SettingsManager from extraContext.settings by calling
settingsManager.setValue instead of directly merging data.settings. For kv
fields, also invoke sendSettingsValueChanged so persisted values and change
notifications remain synchronized.

---

Nitpick comments:
In `@ui/src/lib/runtime/services/GraphObserver.test.ts`:
- Around line 7-19: Update all three affected tests around GraphObserver.notify
to await a resolved promise immediately after each observer.notify() call,
before asserting snapshots. This flushes the queued microtask and ensures the
assertions validate notify’s deferred behavior rather than only subscribe’s
synchronous notification.

In `@ui/src/lib/runtime/services/GraphObserver.ts`:
- Around line 67-90: Update getSnapshot to store this.getGraph() in a local
graph variable once, then use that variable for both objects and connections
instead of calling getGraph() twice.

In `@ui/src/lib/runtime/services/OnGraphChange.integration.test.ts`:
- Around line 12-15: Update the afterEach cleanup to also unregister the
compilerId node registered by the js object, while retaining the existing glslId
unregister and edge reset through the shared messageSystem singleton.

In `@ui/src/lib/runtime/services/PatchRuntime.ts`:
- Around line 299-324: Update syncConnections() to call the existing
syncMessageConnections() method instead of independently mapping graph
connections and invoking this.message.updateEdges. Keep the remaining system
edge updates unchanged and retain syncMessageConnections() as the single
message-connection mapping site.

In `@ui/src/lib/runtime/utils/runtime-test-utils.ts`:
- Around line 152-159: Replace the nested ternary in the class-selection logic
with a call to the existing getObjectClass method, preserving the current
objectType mapping and ObjectClass typing while ensuring future test object
types use the shared mapping.

In `@ui/src/objects/canvas/CanvasDom.svelte`:
- Around line 404-406: Update setTags in
ui/src/objects/canvas/CanvasDom.svelte#L404-L406,
ui/src/objects/dom/DomRuntimeNode.svelte#L267-L269, and
ui/src/objects/three/ThreeDom.svelte#L396-L398 to preserve existing
core/-prefixed entries from data.tags while replacing the user-tag portion with
getUserTags(tags). Apply the same merge behavior at all three sites.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 55956b99-62a9-451c-9be1-000647b8f61b

📥 Commits

Reviewing files that changed from the base of the PR and between 1bf87cd and 3427e06.

⛔ Files ignored due to path filters (1)
  • ui/src/lib/generated/object-schemas.generated.ts is excluded by !**/*.generated.*, !**/generated/**
📒 Files selected for processing (23)
  • docs/design-docs/specs/171-graph-systems-and-composable-code-representations.md
  • ui/src/lib/codemirror/patchies-completions.ts
  • ui/src/lib/js-runner/JSRunner.test.ts
  • ui/src/lib/js-runner/JSRunner.ts
  • ui/src/lib/objects/v2/ObjectContext.ts
  • ui/src/lib/objects/v2/ObjectService.ts
  • ui/src/lib/objects/v2/nodes/index.ts
  • ui/src/lib/runtime/adapters/MessageAdapter.ts
  • ui/src/lib/runtime/services/GraphObserver.test.ts
  • ui/src/lib/runtime/services/GraphObserver.ts
  • ui/src/lib/runtime/services/GraphSubscription.test.ts
  • ui/src/lib/runtime/services/OnGraphChange.integration.test.ts
  • ui/src/lib/runtime/services/PatchRuntime.ts
  • ui/src/lib/runtime/services/graph-tags.test.ts
  • ui/src/lib/runtime/services/graph-tags.ts
  • ui/src/lib/runtime/utils/runtime-test-utils.ts
  • ui/src/objects/canvas/CanvasDom.svelte
  • ui/src/objects/code/CodeBlockBase.svelte
  • ui/src/objects/dom/DomRuntimeNode.svelte
  • ui/src/objects/js/JSBlockNode.svelte
  • ui/src/objects/js/JSObject.test.ts
  • ui/src/objects/js/JSObject.ts
  • ui/src/objects/three/ThreeDom.svelte

Comment on lines +40 to +61
subscribe(query: GraphChangeQuery, callback: GraphChangeCallback): () => void {
const subscription = { query, callback };

this.subscriptions.add(subscription);
this.notifySubscription(subscription);

return () => this.subscriptions.delete(subscription);
}

notify(): void {
if (this.notificationQueued) return;

this.notificationQueued = true;

queueMicrotask(() => {
this.notificationQueued = false;

for (const subscription of this.subscriptions) {
this.notifySubscription(subscription);
}
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Isolate subscription callbacks from each other.

notifySubscription calls subscription.callback(snapshot) at Line 105 without a try/catch. GraphObserver is a single shared instance for the whole patch (see PatchRuntime.ts: private graphObserver = new GraphObserver(...)). If one subscription's callback throws, the for loop in notify() (Line 57-59) stops. Other, unrelated subscriptions do not receive their pending notification in that batch. The same unguarded call happens in subscribe() at Line 44.

The design doc states each subscription must reconcile independently and a failing callback must not block other graph systems. Add a try/catch around the callback invocation in notifySubscription so this applies to both call sites.

🔒 Proposed fix to isolate subscription callback failures
     subscription.lastSnapshotKey = snapshotKey;
-    subscription.callback(snapshot);
+
+    try {
+      subscription.callback(snapshot);
+    } catch (error) {
+      console.error('GraphObserver subscription callback failed', error);
+    }
   }

Also applies to: 92-106

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/lib/runtime/services/GraphObserver.ts` around lines 40 - 61, Update
notifySubscription to wrap subscription.callback(snapshot) in a try/catch,
ensuring callback failures are isolated and do not interrupt notification of
other subscriptions. Apply this in notifySubscription so both subscribe and
notify retain their existing behavior while continuing to process unrelated
subscriptions.

Comment thread ui/src/objects/code/CodeBlockBase.svelte Outdated
Comment on lines +52 to 54
function setSetting(key: string, value: unknown) {
updateNodeData(nodeId, { settings: { ...data.settings, [key]: value } });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect SettingsManager persistence behavior and other setting-change call sites.
fd -t f -g 'SettingsManager*' --exec rg -n -C6 'persist|kv|set\(' {}
rg -n -C4 'onSettingsValueChange' --type=svelte

Repository: heypoom/patchies

Length of output: 6563


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate candidate files =="
fd -t f 'JSBlockNode.svelte|JSObject|SettingsManager|updateNodeData|createKVStore|createSettingsAPI' .

echo
echo "== JSBlockNode outline =="
ast-grep outline ui/src/objects/js/JSBlockNode.svelte --view expanded || true

echo
echo "== JSBlockNode relevant content =="
cat -n ui/src/objects/js/JSBlockNode.svelte | sed -n '1,180p'

echo
echo "== SettingsManager full file =="
fd -t f 'SettingsManager.*' -x sh -c 'echo "--- $1"; cat -n "$1"' sh {}

echo
echo "== search settingsAPI and createKVStore/createKVStore usages =="
rg -n -C4 'createSetting|createSettings|createKVStore|getSettings|setSetting|SettingsManager|data.settings|updateNodeData' --glob '*.ts' --glob '*.svelte' .

Repository: heypoom/patchies

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== JSBlockNode around setSetting and JSObject usage =="
sed -n '1,130p' ui/src/objects/js/JSBlockNode.svelte | cat -n

echo
echo "== JSBlockNode calls to JSObject settings events =="
rg -n -C3 'JSObject|onSettingsValueChange|onSettingsRevertAll|settingsValues|settingsSchema|setSetting|updateNodeData' ui/src/objects/js/JSBlockNode.svelte ui/src/objects/js -g '*.svelte' -g '*.ts'

echo
echo "== JSObject implementation/files =="
fd -t f 'JSObject.*' ui/src/objects/js -x sh -c 'echo "--- $1"; wc -l "$1"; sed -n "1,260p" "$1" | cat -n' sh {}

echo
echo "== SettingsManager full implementation/files =="
fd -t f 'SettingsManager.*' ui/src -x sh -c 'echo "--- $1"; wc -l "$1"; sed -n "1,260p" "$1" | cat -n' sh {}

Repository: heypoom/patchies

Length of output: 35983


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== settings schema/API files =="
fd -t f 'settings.*|create-settings-api.*|types.*' ui/src/lib/settings
echo
for f in $(fd -t f . ui/src/lib/settings); do
  echo "--- $f"; wc -l "$f"; cat -n "$f";
done

echo
echo "== JSBlockNode imports/props/usages relevant to settings =="
rg -n -C4 'import .*JSObject|JSObject|settingsSchema|settingsValues|onSettingsValueChange|onSettingsRevertAll|setSetting|execut' ui/src/objects/js/JSBlockNode.svelte ui/src -g '*.svelte' -g '*.ts' --max-count 200

Repository: heypoom/patchies

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== settings lib files only =="
for f in $(find ui/src/lib/settings -maxdepth 1 -type f); do
  echo "--- $f"; wc -l "$f"; cat -n "$f";
done

echo
echo "== CodeBlockBase settings events and JSObject registration/ref =="
rg -n -C5 'CodeBlockBase|ObjectSettings|JSObject|JSObjectNode|createSettingsAPI|sendSettingsValueChanged|sendSettingsValue' ui/src \
  --glob '*.svelte' --glob '*.ts' --glob '*.tsx' --max-count 250

echo
echo "== settings persistence fields =="
rg -n -C3 'settingsSchema|settings:|persistence:|type: .*settings|onSettingsValueChange|settingsValues' ui/src/lib/ui ui/src/components ui/src/objects js -g '*.svelte' -g '*.ts' --max-count 250

Repository: heypoom/patchies

Length of output: 50373


Route JS view setting changes through SettingsManager.

setSetting updates data.settings, but JSObject only uses its SettingsManager from extraContext.settings; the view is not connected as a JSObject setting callback. Use settingsManager.setValue(...) for node settings updates and also fire sendSettingsValueChanged(...) for kv fields, so persisted values and setting change notifications stay in sync.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/objects/js/JSBlockNode.svelte` around lines 52 - 54, Update setSetting
in JSBlockNode to route node setting changes through the SettingsManager from
extraContext.settings by calling settingsManager.setValue instead of directly
merging data.settings. For kv fields, also invoke sendSettingsValueChanged so
persisted values and change notifications remain synchronized.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/src/lib/runtime/services/GraphObserver.test.ts`:
- Around line 78-98: Update the GraphObserver callback-failure test to either
assert logger.warn was called with the expected message and thrown error, or
remove the warn spy and related cleanup if logging is not part of the contract;
preserve the existing assertion that later subscriptions still receive the
snapshot.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7c4e632a-7311-4dc5-9574-0a791c6ccd43

📥 Commits

Reviewing files that changed from the base of the PR and between 3427e06 and 4b80bee.

📒 Files selected for processing (10)
  • ui/src/lib/js-runner/JSRunner.ts
  • ui/src/lib/runtime/index.ts
  • ui/src/lib/runtime/services/GraphObserver.test.ts
  • ui/src/lib/runtime/services/GraphObserver.ts
  • ui/src/lib/runtime/services/OnGraphChange.integration.test.ts
  • ui/src/lib/runtime/services/PatchRuntime.ts
  • ui/src/lib/runtime/services/graph-tags.ts
  • ui/src/lib/runtime/utils/runtime-test-utils.ts
  • ui/src/objects/code/CodeBlockBase.svelte
  • ui/src/objects/js/JSObject.test.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • ui/src/lib/runtime/services/graph-tags.ts
  • ui/src/lib/runtime/services/OnGraphChange.integration.test.ts
  • ui/src/lib/runtime/utils/runtime-test-utils.ts
  • ui/src/objects/js/JSObject.test.ts
  • ui/src/lib/runtime/services/GraphObserver.ts
  • ui/src/lib/js-runner/JSRunner.ts
  • ui/src/lib/runtime/services/PatchRuntime.ts

Comment on lines +78 to +98
it('continues notifying subscriptions when a callback fails', () => {
const observer = new GraphObserver(() => ({
objects: [{ id: 'fragment', type: 'js', data: { tags: ['shader/foo/function'] } }],
connections: []
}));
const warn = vi.spyOn(logger, 'warn').mockImplementation(() => {});
const snapshots: string[][] = [];

try {
observer.subscribe({ tags: ['shader/foo/*'] }, () => {
throw new Error('broken callback');
});
observer.subscribe({ tags: ['shader/foo/*'] }, ({ nodes }) =>
snapshots.push(nodes.map(({ id }) => id))
);

expect(snapshots).toEqual([['fragment']]);
} finally {
warn.mockRestore();
}
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files | rg 'GraphObserver\.(test\.)?ts|logger' || true

echo
echo "Test outline:"
ast-grep outline ui/src/lib/runtime/services/GraphObserver.test.ts || true
echo
echo "Test relevant lines:"
cat -n ui/src/lib/runtime/services/GraphObserver.test.ts | sed -n '1,140p'

echo
echo "Source outline:"
ast-grep outline ui/src/lib/runtime/services/GraphObserver.ts || true
echo
echo "Source relevant lines:"
cat -n ui/src/lib/runtime/services/GraphObserver.ts | sed -n '1,220p'

Repository: heypoom/patchies

Length of output: 10284


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Other logger.warn tests in GraphObserver files:"
rg -n "logger|warn|onGraphChange" ui/src/lib/runtime/services/GraphObserver* || true

Repository: heypoom/patchies

Length of output: 705


Assert the warning call or remove the spy.

GraphObserver logs callback failures with logger.warn('Error in onGraphChange() handler:', error), but the test only asserts that the second subscription still runs. Check the expected warning message plus an error argument, or remove the logger.warn spy if logging is not part of the caller contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/lib/runtime/services/GraphObserver.test.ts` around lines 78 - 98,
Update the GraphObserver callback-failure test to either assert logger.warn was
called with the expected message and thrown error, or remove the warn spy and
related cleanup if logging is not part of the contract; preserve the existing
assertion that later subscriptions still receive the snapshot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant