feat(FN-4224): tighten github tracking header to single-row layout in task
Restyles the GitHub tracking header in TaskDetailModal to a compact single-row layout with tighter spacing, adds test coverage for the header layout, and includes a regression verification pass alongside a changeset documenting the change. Fusion-Task-Id: FN-4224
This commit is contained in:
5
.changeset/FN-4185-open-new-room.md
Normal file
5
.changeset/FN-4185-open-new-room.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Auto-open newly created chat rooms in the dashboard so successful room creation immediately reveals the new thread, including collapsing the mobile sidebar.
|
||||||
5
.changeset/fn-4224-github-tracking-header-compact.md
Normal file
5
.changeset/fn-4224-github-tracking-header-compact.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Tighten the Task Detail modal GitHub tracking header into a compact single-row summary with the inline Enable action and disclosure toggle staying aligned across desktop and mobile layouts.
|
||||||
@@ -2867,7 +2867,13 @@ export function ChatView({ projectId, addToast, experimentalFeatures }: ChatView
|
|||||||
existingRoomNames={rooms.rooms.map((room) => room.name)}
|
existingRoomNames={rooms.rooms.map((room) => room.name)}
|
||||||
onCreate={async (draft) => {
|
onCreate={async (draft) => {
|
||||||
await rooms.createRoom({ name: draft.name, memberAgentIds: draft.memberAgentIds });
|
await rooms.createRoom({ name: draft.name, memberAgentIds: draft.memberAgentIds });
|
||||||
|
if (chatScope !== "rooms") {
|
||||||
|
setChatScope("rooms");
|
||||||
|
}
|
||||||
setCreateRoomOpen(false);
|
setCreateRoomOpen(false);
|
||||||
|
if (isMobile) {
|
||||||
|
setSidebarVisible(false);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -439,6 +439,7 @@
|
|||||||
.detail-github-tracking-enable {
|
.detail-github-tracking-enable {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
|
flex: 0 0 auto;
|
||||||
padding-block: calc(var(--space-xs) / 2);
|
padding-block: calc(var(--space-xs) / 2);
|
||||||
padding-inline: var(--space-sm);
|
padding-inline: var(--space-sm);
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
@@ -488,12 +489,38 @@
|
|||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
background: var(--card);
|
background: var(--card);
|
||||||
padding: var(--space-sm) var(--space-md);
|
padding: var(--space-xs) var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-github-tracking-section .detail-source-header {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-github-tracking-section .detail-source-summary {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-github-tracking-section .detail-source-empty {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-github-tracking-section .detail-source-toggle {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-github-tracking-section .detail-github-tracking-enable {
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-github-tracking-content {
|
.detail-github-tracking-content {
|
||||||
margin-top: var(--space-sm);
|
margin-top: var(--space-xs);
|
||||||
padding-top: var(--space-sm);
|
padding-top: var(--space-xs);
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1599,17 +1626,23 @@
|
|||||||
flex: 1 1 100%;
|
flex: 1 1 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-github-tracking-section .detail-source-summary {
|
.detail-github-tracking-section .detail-source-header {
|
||||||
flex: 1 1 auto;
|
flex-wrap: nowrap;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-github-tracking-enable {
|
.detail-github-tracking-section .detail-source-summary {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-github-tracking-section .detail-github-tracking-enable {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-source-header .detail-source-toggle {
|
.detail-github-tracking-section .detail-source-header .detail-source-toggle {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { act, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
|
import { act, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
|
||||||
|
import { useState } from "react";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { userEvent } from "@testing-library/user-event";
|
import { userEvent } from "@testing-library/user-event";
|
||||||
import { ChatView } from "../ChatView";
|
import { ChatView } from "../ChatView";
|
||||||
@@ -181,6 +182,50 @@ function setupMockRooms(overrides: Partial<UseChatRoomsResult> = {}) {
|
|||||||
mockUseChatRooms.mockReturnValue(state);
|
mockUseChatRooms.mockReturnValue(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createRoomFixture(name: string) {
|
||||||
|
return {
|
||||||
|
id: `room-${name}`,
|
||||||
|
projectId: "proj-123",
|
||||||
|
slug: name,
|
||||||
|
name,
|
||||||
|
createdAt: "2026-05-12T00:00:00.000Z",
|
||||||
|
updatedAt: "2026-05-12T00:00:00.000Z",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupStatefulCreateRoomMock(options?: { createRejects?: boolean }) {
|
||||||
|
const createRoom = vi.fn();
|
||||||
|
|
||||||
|
mockUseChatRooms.mockImplementation(() => {
|
||||||
|
const [roomsState, setRoomsState] = useState<UseChatRoomsResult["rooms"]>([]);
|
||||||
|
const [activeRoom, setActiveRoom] = useState<UseChatRoomsResult["activeRoom"]>(null);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...defaultRoomsState,
|
||||||
|
rooms: roomsState,
|
||||||
|
activeRoom,
|
||||||
|
activeRoomMembers: activeRoom
|
||||||
|
? [{ roomId: activeRoom.id, agentId: "agent-001", role: "member", addedAt: "2026-05-12T00:00:00.000Z" }]
|
||||||
|
: [],
|
||||||
|
createRoom: async ({ name, memberAgentIds }) => {
|
||||||
|
createRoom({ name, memberAgentIds });
|
||||||
|
if (options?.createRejects) {
|
||||||
|
throw new Error("Failed to create room.");
|
||||||
|
}
|
||||||
|
const nextRoom = createRoomFixture(name);
|
||||||
|
setRoomsState((previous) => [...previous, nextRoom]);
|
||||||
|
setActiveRoom(nextRoom);
|
||||||
|
return nextRoom;
|
||||||
|
},
|
||||||
|
selectRoom: (roomId) => {
|
||||||
|
setActiveRoom(roomsState.find((room) => room.id === roomId) ?? null);
|
||||||
|
},
|
||||||
|
} satisfies UseChatRoomsResult;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { createRoom };
|
||||||
|
}
|
||||||
|
|
||||||
function ensureMatchMedia() {
|
function ensureMatchMedia() {
|
||||||
if (!window.matchMedia) {
|
if (!window.matchMedia) {
|
||||||
Object.defineProperty(window, "matchMedia", {
|
Object.defineProperty(window, "matchMedia", {
|
||||||
@@ -208,7 +253,10 @@ function mockViewportMode(mode: "mobile" | "desktop") {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
localStorage.clear();
|
||||||
|
_resetInitialViewportHeight();
|
||||||
setupMockRooms();
|
setupMockRooms();
|
||||||
|
mockViewportMode("desktop");
|
||||||
mockFetchDiscoveredSkills.mockResolvedValue([]);
|
mockFetchDiscoveredSkills.mockResolvedValue([]);
|
||||||
mockCreateObjectURL.mockImplementation((file: File) => `blob:${file.name}`);
|
mockCreateObjectURL.mockImplementation((file: File) => `blob:${file.name}`);
|
||||||
Object.defineProperty(URL, "createObjectURL", { value: mockCreateObjectURL, writable: true });
|
Object.defineProperty(URL, "createObjectURL", { value: mockCreateObjectURL, writable: true });
|
||||||
@@ -222,7 +270,8 @@ beforeEach(() => {
|
|||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
localStorage.removeItem("fusion:chat-scope");
|
localStorage.clear();
|
||||||
|
_resetInitialViewportHeight();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("ChatView", () => {
|
describe("ChatView", () => {
|
||||||
@@ -2258,6 +2307,7 @@ describe("ChatView", () => {
|
|||||||
const toggle = screen.getByTestId("chat-thread-render-toggle");
|
const toggle = screen.getByTestId("chat-thread-render-toggle");
|
||||||
const providerIcon = identity.querySelector(".provider-icon");
|
const providerIcon = identity.querySelector(".provider-icon");
|
||||||
const modelTag = identity.querySelector(".chat-model-tag");
|
const modelTag = identity.querySelector(".chat-model-tag");
|
||||||
|
const newChatButton = screen.getByTestId("chat-thread-new-chat-btn");
|
||||||
|
|
||||||
expect(header).toBeInTheDocument();
|
expect(header).toBeInTheDocument();
|
||||||
expect(providerIcon).toBeInTheDocument();
|
expect(providerIcon).toBeInTheDocument();
|
||||||
@@ -2265,7 +2315,8 @@ describe("ChatView", () => {
|
|||||||
expect(modelTag).toBeInTheDocument();
|
expect(modelTag).toBeInTheDocument();
|
||||||
expect(modelTag).toHaveTextContent("Claude Sonnet 4.5");
|
expect(modelTag).toHaveTextContent("Claude Sonnet 4.5");
|
||||||
expect(toggle).toBeInTheDocument();
|
expect(toggle).toBeInTheDocument();
|
||||||
expect(header?.children[header.children.length - 1]).toBe(toggle);
|
expect(header?.children[header.children.length - 2]).toBe(toggle);
|
||||||
|
expect(header?.children[header.children.length - 1]).toBe(newChatButton);
|
||||||
expect(document.querySelectorAll(".chat-thread-header .chat-model-tag")).toHaveLength(1);
|
expect(document.querySelectorAll(".chat-thread-header .chat-model-tag")).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2794,6 +2845,82 @@ describe("ChatView sidebar structure", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("room creation", () => {
|
||||||
|
it("opens the newly created room and collapses the mobile sidebar on success", async () => {
|
||||||
|
const viewportSpy = mockViewportMode("mobile");
|
||||||
|
const { createRoom } = setupStatefulCreateRoomMock();
|
||||||
|
setupMockChat({ sessions: [], filteredSessions: [] });
|
||||||
|
localStorage.setItem("fusion:chat-scope", "rooms");
|
||||||
|
|
||||||
|
render(<ChatView projectId="proj-123" addToast={vi.fn()} experimentalFeatures={{ chatRooms: true }} />);
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByTestId("chat-create-room-btn"));
|
||||||
|
const dialog = await screen.findByRole("dialog", { name: "Create room" });
|
||||||
|
await userEvent.type(within(dialog).getByLabelText("Room name"), "newroom");
|
||||||
|
await userEvent.click(within(screen.getByTestId("create-room-member-list")).getByText("Alpha"));
|
||||||
|
await userEvent.click(within(dialog).getByRole("button", { name: "Create room" }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(createRoom).toHaveBeenCalledWith({ name: "newroom", memberAgentIds: ["agent-001"] });
|
||||||
|
});
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(document.querySelector(".chat-sidebar")).toHaveClass("chat-sidebar--hidden");
|
||||||
|
});
|
||||||
|
expect(screen.queryByRole("dialog", { name: "Create room" })).toBeNull();
|
||||||
|
expect(within(document.querySelector(".chat-room-thread-header") as HTMLElement).getByText("#newroom")).toBeInTheDocument();
|
||||||
|
|
||||||
|
viewportSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("opens the newly created room on desktop without hiding the sidebar", async () => {
|
||||||
|
const viewportSpy = mockViewportMode("desktop");
|
||||||
|
const { createRoom } = setupStatefulCreateRoomMock();
|
||||||
|
setupMockChat({ sessions: [], filteredSessions: [] });
|
||||||
|
localStorage.setItem("fusion:chat-scope", "rooms");
|
||||||
|
|
||||||
|
render(<ChatView projectId="proj-123" addToast={vi.fn()} experimentalFeatures={{ chatRooms: true }} />);
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByTestId("chat-create-room-btn"));
|
||||||
|
const dialog = await screen.findByRole("dialog", { name: "Create room" });
|
||||||
|
await userEvent.type(within(dialog).getByLabelText("Room name"), "newroom");
|
||||||
|
await userEvent.click(within(screen.getByTestId("create-room-member-list")).getByText("Alpha"));
|
||||||
|
await userEvent.click(within(dialog).getByRole("button", { name: "Create room" }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(createRoom).toHaveBeenCalledWith({ name: "newroom", memberAgentIds: ["agent-001"] });
|
||||||
|
});
|
||||||
|
expect(document.querySelector(".chat-sidebar")).not.toHaveClass("chat-sidebar--hidden");
|
||||||
|
expect(screen.queryByRole("dialog", { name: "Create room" })).toBeNull();
|
||||||
|
expect(within(document.querySelector(".chat-room-thread-header") as HTMLElement).getByText("#newroom")).toBeInTheDocument();
|
||||||
|
|
||||||
|
viewportSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps the modal open and sidebar visible when room creation fails", async () => {
|
||||||
|
const viewportSpy = mockViewportMode("mobile");
|
||||||
|
const { createRoom } = setupStatefulCreateRoomMock({ createRejects: true });
|
||||||
|
setupMockChat({ sessions: [], filteredSessions: [] });
|
||||||
|
localStorage.setItem("fusion:chat-scope", "rooms");
|
||||||
|
|
||||||
|
render(<ChatView projectId="proj-123" addToast={vi.fn()} experimentalFeatures={{ chatRooms: true }} />);
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByTestId("chat-create-room-btn"));
|
||||||
|
const dialog = await screen.findByRole("dialog", { name: "Create room" });
|
||||||
|
await userEvent.type(within(dialog).getByLabelText("Room name"), "newroom");
|
||||||
|
await userEvent.click(within(screen.getByTestId("create-room-member-list")).getByText("Alpha"));
|
||||||
|
await userEvent.click(within(dialog).getByRole("button", { name: "Create room" }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(createRoom).toHaveBeenCalledWith({ name: "newroom", memberAgentIds: ["agent-001"] });
|
||||||
|
});
|
||||||
|
expect(screen.getByRole("dialog", { name: "Create room" })).toBeInTheDocument();
|
||||||
|
expect(document.querySelector(".chat-sidebar")).not.toHaveClass("chat-sidebar--hidden");
|
||||||
|
expect(screen.queryByText("#newroom")).toBeNull();
|
||||||
|
|
||||||
|
viewportSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("Direct/Rooms scope toggle", () => {
|
describe("Direct/Rooms scope toggle", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { loadAllAppCss } from "../../test/cssFixture";
|
||||||
|
import { TaskDetailModal } from "../TaskDetailModal";
|
||||||
|
import { makeTask, noop, noopDelete, noopMerge, noopMove, noopOpenDetail, setupTaskDetailModalHooks } from "./TaskDetailModal.test-helpers";
|
||||||
|
|
||||||
|
setupTaskDetailModalHooks();
|
||||||
|
|
||||||
|
describe("FN-4224 GitHub tracking header layout", () => {
|
||||||
|
it("keeps the summary, enable action, and disclosure toggle on one row across desktop and mobile CSS", () => {
|
||||||
|
render(
|
||||||
|
<TaskDetailModal
|
||||||
|
task={makeTask({
|
||||||
|
column: "todo",
|
||||||
|
githubTracking: { enabled: false },
|
||||||
|
})}
|
||||||
|
onClose={noop}
|
||||||
|
onMoveTask={noopMove}
|
||||||
|
onDeleteTask={noopDelete}
|
||||||
|
onMergeTask={noopMerge}
|
||||||
|
onOpenDetail={noopOpenDetail}
|
||||||
|
addToast={noop}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText("GitHub tracking")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Tracking is currently disabled")).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("button", { name: "Enable GitHub tracking" })).toHaveTextContent("Enable");
|
||||||
|
expect(screen.getByRole("button", { name: "Expand GitHub tracking details" })).toBeInTheDocument();
|
||||||
|
|
||||||
|
const css = loadAllAppCss();
|
||||||
|
|
||||||
|
expect(css).toMatch(
|
||||||
|
/\.detail-github-tracking-section\s+\.detail-source-header\s*\{[^}]*flex-wrap:\s*nowrap;[^}]*align-items:\s*center;[^}]*min-width:\s*0;/,
|
||||||
|
);
|
||||||
|
expect(css).toMatch(
|
||||||
|
/\.detail-github-tracking-section\s+\.detail-source-summary\s*\{[^}]*flex:\s*1 1 auto;[^}]*flex-wrap:\s*nowrap;[^}]*min-width:\s*0;/,
|
||||||
|
);
|
||||||
|
expect(css).toMatch(
|
||||||
|
/@media\s*\(max-width:\s*768px\)\s*\{[\s\S]*?\.detail-github-tracking-section\s+\.detail-source-header\s*\{[^}]*flex-wrap:\s*nowrap;[^}]*min-width:\s*0;[^}]*\}[\s\S]*?\.detail-github-tracking-section\s+\.detail-source-summary\s*\{[^}]*flex:\s*1 1 auto;[^}]*flex-wrap:\s*nowrap;[^}]*min-width:\s*0;[^}]*\}/,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -11,7 +11,7 @@ const qualityAppTests = [
|
|||||||
"app/api/**/*.test.ts",
|
"app/api/**/*.test.ts",
|
||||||
// Representative workflow/component coverage. Exhaustive modal/view suites
|
// Representative workflow/component coverage. Exhaustive modal/view suites
|
||||||
// stay available in the full `dashboard-app` project.
|
// stay available in the full `dashboard-app` project.
|
||||||
"app/components/__tests__/{ActiveAgentsPanel,AgentMentionPopup,AgentMetricsBar,AgentReflectionsTab,AgentTokenStatsPanel,AuthTokenRecoveryDialog,Board,board-mobile-view-switch,Column,ConfirmDialog,ConversationHistory,DashboardLoader,DirectoryPicker,ErrorBoundary,ExecutorStatusBar,FileBrowser,FileEditor,InlineCreateCard,LoginInstructions,MemoryView,MessageComposer,MobileNavBar,NewTaskModal,NodeCard,NodeHealthDot,NodeStatusIndicator,ProjectCard,ProjectSelector,ProviderIcon,QuickChatFAB,ResearchView,StashRecoveryView,TaskCard,TaskChangesTab,TaskComments,TaskDocumentsTab,TaskForm,ThemeSelectorSwatchContract,WorkflowResultsTab}.test.tsx",
|
"app/components/__tests__/{ActiveAgentsPanel,AgentMentionPopup,AgentMetricsBar,AgentReflectionsTab,AgentTokenStatsPanel,AuthTokenRecoveryDialog,Board,board-mobile-view-switch,ChatView,Column,ConfirmDialog,ConversationHistory,DashboardLoader,DirectoryPicker,ErrorBoundary,ExecutorStatusBar,FileBrowser,FileEditor,InlineCreateCard,LoginInstructions,MemoryView,MessageComposer,MobileNavBar,NewTaskModal,NodeCard,NodeHealthDot,NodeStatusIndicator,ProjectCard,ProjectSelector,ProviderIcon,QuickChatFAB,ResearchView,StashRecoveryView,TaskCard,TaskChangesTab,TaskComments,TaskDetailModal.github-tracking-header,TaskDocumentsTab,TaskForm,ThemeSelectorSwatchContract,WorkflowResultsTab}.test.tsx",
|
||||||
// Hooks and utilities are fast, user-visible state/formatting behavior.
|
// Hooks and utilities are fast, user-visible state/formatting behavior.
|
||||||
"app/context/**/*.test.tsx",
|
"app/context/**/*.test.tsx",
|
||||||
"app/hooks/__tests__/{useAgents,useAgentLogs,useAppSettings,useAuthOnboarding,useConfirm,useCurrentProject,useNodes,useNodeSettingsSync,useProjects,useQuickChat,useTasks,useTerminalSessions,useTheme,useToast,useUsageData,useViewState}.test.{ts,tsx}",
|
"app/hooks/__tests__/{useAgents,useAgentLogs,useAppSettings,useAuthOnboarding,useConfirm,useCurrentProject,useNodes,useNodeSettingsSync,useProjects,useQuickChat,useTasks,useTerminalSessions,useTheme,useToast,useUsageData,useViewState}.test.{ts,tsx}",
|
||||||
|
|||||||
Reference in New Issue
Block a user