Skip to content

⚡ Bolt: [performance improvement] standardize useIsConnectedSelector#2144

Merged
georgi merged 9 commits intomainfrom
bolt-performance-optimization-10620303807128719213
Mar 21, 2026
Merged

⚡ Bolt: [performance improvement] standardize useIsConnectedSelector#2144
georgi merged 9 commits intomainfrom
bolt-performance-optimization-10620303807128719213

Conversation

@georgi
Copy link
Collaborator

@georgi georgi commented Mar 17, 2026

⚡ Bolt: Complete Rollout of useIsConnectedSelector for Node Properties

💡 What

Replaced direct subscriptions to the entire state.edges array with the memoized useIsConnectedSelector hook in the remaining property components:

  • CollectionProperty.tsx
  • StringProperty.tsx (standardized to use the shared hook instead of inline implementation)

🎯 Why

CollectionProperty was previously evaluating state.edges.some(...) via useNodes(state => state.edges) on every single store update (e.g. 60fps during node drag). This means the component would constantly re-render whenever any unrelated edge in the entire graph changed or during any drag operation, wasting CPU cycles and causing UI jank.

📊 Impact

  • Eliminates O(E) filter iterations: Prevents the .some() check from running 60 times a second on all nodes during drag operations.
  • Prevents Unnecessary Re-renders: Properties now only re-render when their specific connection state changes, completely decoupling them from unrelated graph updates.
  • Improves Responsiveness: Smoother graph interactions, particularly on larger workflows with many collection or string inputs.

🔬 Measurement

Verify by adding a console.log inside a CollectionProperty or StringProperty render. Drag an unrelated node across the canvas. Before this patch, it would log on every frame; after this patch, it logs zero times during the drag operation.

🧪 Testing

  • Ran cd web && pnpm run typecheck: Passed.
  • Ran cd web && pnpm run lint: Passed.
  • Ran cd web && pnpm test: All relevant property tests passed.

PR created automatically by Jules for task 10620303807128719213 started by @georgi

Copilot AI and others added 7 commits March 14, 2026 21:11
- TitleBar.tsx: use typed window.api.windowControls directly (from
  window.d.ts); cast WebkitAppRegion style to CSSProperties intersection
- browser.ts: access window.process.type directly (already typed in
  window.d.ts)
- audio.ts, useRealtimeAudioPlayback.ts, useRealtimeAudioStream.ts:
  type webkitAudioContext via 'Window & { webkitAudioContext? }' instead
  of casting window to any
- GlobalChat.tsx, StandaloneChat.tsx: use window.visualViewport directly
  (typed in lib.dom.d.ts as VisualViewport | null)
- EditorController.tsx: add CSSWithHighlights, WindowWithHighlight, and
  DocumentWithFragmentDirective type aliases for experimental APIs
- prismGlobal.ts, CodeHighlightPlugin.tsx: use typed globalThis
  augmentation for Prism assignment

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- outputChunkUtils.ts: use chunk?.content_type directly (Chunk has the property)
- ChunkRenderer.tsx: use chunk.content_type and chunk.content_metadata directly
- NodeOutputs.tsx: use dyn[1].type directly (TypeMetadata has type: string)
- OutputRenderer.tsx:
  - stableKeyForOutputValue: cast object to Record<string, unknown>
  - concatTextChunksSafely: use c.content directly (Chunk.content is string)
  - audio case: cast value to { metadata?: { format?: string } }
  - model_3d case: cast value to Record<string, unknown> for format check
  - chunk rendering: use c.content_type, c.done, c.content directly (c is Chunk)
  - audioChunks[0].content_metadata used directly (audioChunks is Chunk[])
- OutputNode.tsx: remove redundant as any in getCopySource (value is already any)
- PreviewNode.tsx: same fix as OutputNode.tsx

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- useModelsByProvider.ts: Remove `as any` from provider path params
  (ProviderInfo.provider is already Provider type); remove String()
  conversion; fix supported_tasks.includes() and pipelineTask casts
- useRecommendedTaskModels.ts: Replace `as any` with `as Provider`
  for inferProvider() return value; import Provider type
- useEmbeddingModels.ts: Remove `as any` from provider path param
- EmbeddingModelMenuDialog.tsx: Remove unnecessary `as any` cast on
  useEmbeddingModelMenuStore (compatible type)
- ComfyModelSelect.tsx: Replace typed client with fetch + BASE_URL for
  non-spec path; define ComfyModelItem type; fix model.name access
- VideoModelSelect.tsx, ASRModelSelect.tsx, TTSModelSelect.tsx,
  Model3DModelSelect.tsx: Replace client.GET with fetch + BASE_URL for
  non-spec `/api/models/{model_type}` path
- LlamaModelSelect.tsx: Replace `as any` with typed cast for error detail
- ModelListIndex.tsx: Define ApiErrorShape interface instead of `as any`
- ModelListItem.tsx: Access model.provider directly (now typed)
- ApiTypes.ts: Extend UnifiedModel with `provider?: string | null`

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- threadUtils.ts: use ThreadInfo.updatedAt directly (dead-code fallback removed)
- ThreadItem.tsx: same – use thread.updatedAt in render and memo comparison
- ThreadList.tsx: same – use thread.updatedAt for date grouping
- ChatThreadView.tsx: use m.tool_call_id / m.name from the Message type; drop anyMsg local
- MessageView.tsx: add ExecutionEventContent type, use tc.args directly (ToolCall already has args), replace executionContent as any
- RecentChats.tsx: drop redundant `as any[]` cast on empty array

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- window.d.ts: Add isProduction, isLocalhost, isElectron, setForceLocalhost
  as optional properties to the Window interface
- ApiClient.ts: Remove `as any` window assignments, use typed Window properties
- ModelPreferencesStore.ts: Use typed intermediate cast for Set rehydration
  migration code
- graphNodeToReactFlowNode.ts: Use `Record<string, unknown>` cast instead of
  `as any` when reading stale workflow_id from node.data
- reactFlowNodeToGraphNode.ts: Remove `as any` from node.style.width/height —
  CSSProperties already types them as string | number, typeof narrows to number
- GlobalChatStore.ts: Annotate partialize return type explicitly as
  Pick<GlobalChatState, ...> instead of `as any`
- dockviewLayout.ts: Define local PanelsMap and SerializedGrid types for
  dockview internal structure access; remove all `as any` casts
- createAssetFile.ts: Use `{ data: unknown }` / `{ content: unknown }` casts
  instead of `as any` in toUint8Array object branch
- getAssetThumbUrl.ts: Cast asset.data to `Record<string, number>` instead
  of `as any` for Object.values call
- useWorkflow.ts: Remove redundant `as any` from options spread — the type
  already satisfies UseQueryOptions
- useJobReconnection.ts: Define RunStateInfo in ApiTypes and use typed
  intersection `Job & { run_state?: RunStateInfo | null }` cast

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace as any with proper types across the web app:
- PlaceholderNode.tsx: extend NodeData interface with originalType/node_type/title/properties fields
- LayoutMenu.tsx: add SerializedPanel interface for dockview panel serialization
- panelComponents.tsx: extend DockviewApi type for getPanel
- AssetDeleteConfirmation.tsx: cast response to { deleted_asset_ids?: string[] }
- AssetGrid.tsx: add IDockviewPanelWithGroup type for group.api access
- StorageAnalytics.tsx: Asset.size already typed, remove as any
- GettingStartedPanel.tsx: typed ollama response shape
- NodeEditor.tsx: use string key directly for CSS custom property
- PropertyContextMenu.tsx: NodeData.dynamic_inputs already typed
- typeFilterUtils.ts: use TypeMetadata[] instead of any[]
- PropertyInput.tsx: use intersection type for dynamic schema extras
- NodeInputs.tsx: use intersection type for dynamic schema enum field
- KieSchemaLoader.tsx/FalSchemaLoader.tsx: intersection type for enum field
- useProcessedEdges.ts: cast nodes to Node<NodeData> instead of any
- useChatIntegration.ts: selectedModel is LanguageModel, fix sendMessage/content types
- useModalResize.ts: properly type debounce return with cancel method
- PlotlyRenderer.tsx: import plotly.js Data/Layout/Config/Frame types
- TableActions.tsx: use proper casts instead of any
- WorkflowListView.tsx: cast to React.UIEvent<HTMLDivElement>
- CompareImagesNode.tsx: cast to { type?: string } instead of any

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 17, 2026

Deploying nodetool-staging with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1ddd9b3
Status:⚡️  Build in progress...

View logs

@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 17, 2026

Deploying nodetool with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4005712
Status: ✅  Deploy successful!
Preview URL: https://acea91ea.nodetool.pages.dev
Branch Preview URL: https://bolt-performance-optimizatio.nodetool.pages.dev

View logs

Copy link
Collaborator Author

georgi commented Mar 21, 2026

Code changes reviewed and approved - standardizing useIsConnectedSelector across CollectionProperty and StringProperty is correct and eliminates unnecessary re-renders.

However, this PR has merge conflicts (likely in lock files) that prevent merging. Please rebase on latest main to resolve the conflicts, then this can be merged.

@georgi georgi force-pushed the bolt-performance-optimization-10620303807128719213 branch from 4005712 to 2fb8d84 Compare March 21, 2026 08:31
google-labs-jules bot and others added 2 commits March 21, 2026 08:36
Replaced direct subscriptions to the entire state.edges array with the memoized useIsConnectedSelector hook in CollectionProperty and StringProperty components. This eliminates expensive O(E) filter iterations during store updates and prevents unnecessary re-renders when unrelated edges change in the graph.

Co-authored-by: georgi <19498+georgi@users.noreply.github.com>
Fixed remaining test failures that were asserting against `console.error` and `console.warn` after the codebase migrated to the `loglevel` package. Additionally resolved eslint warnings related to unused imports introduced during the fix process.

Co-authored-by: georgi <19498+georgi@users.noreply.github.com>
@georgi georgi force-pushed the bolt-performance-optimization-10620303807128719213 branch from 2fb8d84 to 1ddd9b3 Compare March 21, 2026 08:36
@georgi georgi merged commit 7fd9770 into main Mar 21, 2026
8 of 12 checks passed
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.

2 participants