Skip to content

Commit 7fd9770

Browse files
georgiCopilotCopilotgoogle-labs-jules[bot]
authored
Standardize useIsConnectedSelector across property components
* Initial plan * fix: replace 'as any' with proper TypeScript types in web app - 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> * Replace `as any` type assertions with proper TypeScript types - 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> * Replace `as any` type assertions with proper TypeScript types - 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> * Replace `as any` type assertions with proper TypeScript types - 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> * fix: replace `as any` with proper TypeScript types across web app - 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> * Fix as any type assertions with proper TypeScript types 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> * perf: standardize useIsConnectedSelector for node properties 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> * fix: resolve remaining tests broken by loglevel migration 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> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: georgi <19498+georgi@users.noreply.github.com>
1 parent 573b887 commit 7fd9770

85 files changed

Lines changed: 360 additions & 277 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

web/src/__tests__/components/chat/containers/ChatView.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import log from "loglevel";
12
import React from "react";
23
import "@testing-library/jest-dom";
34
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
@@ -350,7 +351,7 @@ describe("ChatView", () => {
350351

351352
it("handles sendMessage errors gracefully", async () => {
352353
const consoleSpy = jest
353-
.spyOn(console, "error")
354+
.spyOn(log, "error")
354355
.mockImplementation(() => {});
355356
mockSendMessage.mockRejectedValueOnce(new Error("Send failed"));
356357

web/src/components/TitleBar.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,20 @@ const closeHoverStyle: React.CSSProperties = {
3232

3333
const TitleBar: React.FC = memo(function TitleBar() {
3434
const minimize = useCallback(() => {
35-
(window as any)?.api?.windowControls?.minimize?.();
35+
window.api?.windowControls?.minimize?.();
3636
}, []);
3737

3838
const maximize = useCallback(() => {
39-
(window as any)?.api?.windowControls?.maximize?.();
39+
window.api?.windowControls?.maximize?.();
4040
}, []);
4141

4242
const close = useCallback(() => {
43-
(window as any)?.api?.windowControls?.close?.();
43+
window.api?.windowControls?.close?.();
4444
}, []);
4545

4646
return (
4747
<div
48-
style={{ ...(containerStyle as any), WebkitAppRegion: "no-drag" } as any}
48+
style={{ ...containerStyle, WebkitAppRegion: "no-drag" } as React.CSSProperties & { WebkitAppRegion: string }}
4949
>
5050
<button
5151
style={baseButtonStyle}

web/src/components/assets/AssetDeleteConfirmation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ const AssetDeleteConfirmation: React.FC<AssetDeleteConfirmationProps> = ({
127127
if (response === undefined) {
128128
log.error("Received undefined response from server");
129129
} else if (typeof response === "object" && response !== null) {
130-
log.info("Deleted asset IDs:", (response as any).deleted_asset_ids);
130+
log.info("Deleted asset IDs:", (response as { deleted_asset_ids?: string[] }).deleted_asset_ids);
131131
}
132132
// Blur focused element to prevent aria-hidden focus warning
133133
if (document.activeElement instanceof HTMLElement) {

web/src/components/assets/AssetGrid.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ import {
3535
IDockviewPanelProps,
3636
IDockviewPanel
3737
} from "dockview";
38+
39+
/** IDockviewPanel exposes an internal `group` property not present in its public type */
40+
type IDockviewPanelWithGroup = IDockviewPanel & {
41+
group?: { api?: { setSize: (size: { width?: number; height?: number }) => void } } & { setSize?: (size: { width?: number; height?: number }) => void };
42+
};
3843
import PanelErrorBoundary from "../common/PanelErrorBoundary";
3944
import log from "loglevel";
4045

@@ -160,7 +165,7 @@ const AssetGrid: React.FC<AssetGridProps> = ({
160165
(event: DockviewReadyEvent) => {
161166
const { api } = event;
162167
// Add folders panel first with an initial size
163-
const foldersPanel: IDockviewPanel = api.addPanel({
168+
const foldersPanel: IDockviewPanelWithGroup = api.addPanel({
164169
id: "asset-folders",
165170
component: "asset-folders",
166171
title: "Folders",
@@ -184,7 +189,7 @@ const AssetGrid: React.FC<AssetGridProps> = ({
184189
// Enforce initial size
185190
const applyInitialSize = () => {
186191
const groupApi =
187-
(foldersPanel as any)?.group?.api ?? (foldersPanel as any)?.group;
192+
foldersPanel?.group?.api ?? foldersPanel?.group;
188193
if (groupApi && typeof groupApi.setSize === "function") {
189194
if (isFullscreenAssets) {
190195
groupApi.setSize({ width: FOLDERS_PANEL_WIDTH });

web/src/components/assets/StorageAnalytics.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const StorageAnalytics: React.FC<StorageAnalyticsProps> = ({
6666

6767
const { totalSize, fileCount, folderCount } = useMemo(() => {
6868
const total = assets.reduce((sum, asset) => {
69-
const assetSize = (asset as any).size as number | undefined;
69+
const assetSize = asset.size as number | undefined;
7070
return sum + (assetSize || 0);
7171
}, 0);
7272

web/src/components/chat/containers/GlobalChat.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,13 +265,14 @@ const GlobalChat: React.FC = () => {
265265
};
266266

267267
// Use Visual Viewport API for better keyboard handling
268-
if ((window as any).visualViewport) {
269-
(window as any).visualViewport.addEventListener(
268+
const vv = window.visualViewport;
269+
if (vv) {
270+
vv.addEventListener(
270271
"resize",
271272
handleViewportChange
272273
);
273274
return () => {
274-
(window as any).visualViewport.removeEventListener(
275+
vv.removeEventListener(
275276
"resize",
276277
handleViewportChange
277278
);

web/src/components/chat/containers/StandaloneChat.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,13 +287,14 @@ const StandaloneChat: React.FC = () => {
287287
};
288288

289289
// Use Visual Viewport API for better keyboard handling
290-
if ((window as any).visualViewport) {
291-
(window as any).visualViewport.addEventListener(
290+
const vv = window.visualViewport;
291+
if (vv) {
292+
vv.addEventListener(
292293
"resize",
293294
handleViewportChange
294295
);
295296
return () => {
296-
(window as any).visualViewport.removeEventListener(
297+
vv.removeEventListener(
297298
"resize",
298299
handleViewportChange
299300
);

web/src/components/chat/message/MessageView.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ import {
99
TaskUpdate,
1010
StepResult
1111
} from "../../../stores/ApiTypes";
12+
13+
/** Shape of a parsed execution-event content object. */
14+
type ExecutionEventContent = {
15+
type?: string;
16+
severity?: string;
17+
content?: React.ReactNode;
18+
[key: string]: unknown;
19+
};
1220
import ChatMarkdown from "./ChatMarkdown";
1321
import { useEditorInsertion } from "../../../contexts/EditorInsertionContext";
1422
import { ThoughtSection } from "./thought/ThoughtSection";
@@ -68,7 +76,7 @@ const ToolCallCard: React.FC<{
6876
const [open, setOpen] = useState(false);
6977
const runningToolCallId = useGlobalChatStore((s) => s.currentRunningToolCallId);
7078
const runningToolMessage = useGlobalChatStore((s) => s.currentToolMessage);
71-
const hasArgs = (tc as any)?.args && Object.keys((tc as any).args).length > 0;
79+
const hasArgs = tc.args && Object.keys(tc.args).length > 0;
7280
const hasDetails = !!hasArgs;
7381
const isRunning = runningToolCallId && tc.id && runningToolCallId === tc.id;
7482

@@ -110,7 +118,7 @@ const ToolCallCard: React.FC<{
110118
<Typography variant="caption" className="tool-section-title">
111119
Arguments
112120
</Typography>
113-
<PrettyJson value={(tc as any).args} />
121+
<PrettyJson value={tc.args} />
114122
</Box>
115123
)}
116124
</Collapse>
@@ -143,7 +151,7 @@ export const MessageView: React.FC<
143151
// Memoize JSON parsing to avoid repeated parsing on every render
144152
// Use string comparison to avoid re-parsing identical content
145153
const { executionContent, executionEventType } = useMemo(() => {
146-
let executionContent = message.content as any;
154+
let executionContent: ExecutionEventContent | string | null = message.content as ExecutionEventContent | string | null;
147155
let executionEventType = message.execution_event_type;
148156

149157
// Fast path: if content is not a string, no parsing needed
@@ -154,7 +162,7 @@ export const MessageView: React.FC<
154162
typeof executionContent === "object" &&
155163
"type" in executionContent
156164
) {
157-
executionEventType = (executionContent as any).type;
165+
executionEventType = executionContent.type;
158166
}
159167
return { executionContent, executionEventType };
160168
}
@@ -180,7 +188,7 @@ export const MessageView: React.FC<
180188
typeof executionContent === "object" &&
181189
"type" in executionContent
182190
) {
183-
executionEventType = (executionContent as any).type;
191+
executionEventType = executionContent.type;
184192
}
185193

186194
return { executionContent, executionEventType };

web/src/components/chat/thread/ChatThreadView.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -442,11 +442,10 @@ const ChatThreadView: React.FC<ChatThreadViewProps> = ({
442442
const map: Record<string, { name?: string | null; content: any }> = {};
443443
for (const m of messages) {
444444
// Tool result messages carry tool_call_id to link back to the originating tool call
445-
const anyMsg: any = m as any;
446-
if (m.role === "tool" && anyMsg.tool_call_id) {
447-
map[String(anyMsg.tool_call_id)] = {
448-
name: anyMsg.name ?? undefined,
449-
content: m.content as any
445+
if (m.role === "tool" && m.tool_call_id) {
446+
map[String(m.tool_call_id)] = {
447+
name: m.name ?? undefined,
448+
content: m.content
450449
};
451450
}
452451
}

web/src/components/chat/thread/ThreadItem.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const ThreadItemBase: React.FC<ThreadItemProps> = ({
5151
</Typography>
5252
{showDate && (
5353
<Typography className="date">
54-
{relativeTime((thread as any).updated_at || thread.updatedAt)}
54+
{relativeTime(thread.updatedAt)}
5555
</Typography>
5656
)}
5757
<DeleteButton
@@ -68,9 +68,7 @@ export const ThreadItem = memo(ThreadItemBase, (prevProps, nextProps) => {
6868
prevProps.previewText === nextProps.previewText &&
6969
prevProps.showDate === nextProps.showDate &&
7070
prevProps.thread.title === nextProps.thread.title &&
71-
// Check both potential update fields
72-
((prevProps.thread as any).updated_at || prevProps.thread.updatedAt) ===
73-
((nextProps.thread as any).updated_at || nextProps.thread.updatedAt)
71+
prevProps.thread.updatedAt === nextProps.thread.updatedAt
7472
);
7573
});
7674

0 commit comments

Comments
 (0)