docs: add dock supercontrols plan

This commit is contained in:
Ivan Kovalev 2026-01-26 14:36:25 +03:00
parent 9449f88321
commit 317d926668
1 changed files with 466 additions and 0 deletions

View File

@ -0,0 +1,466 @@
# Dockable Supercontrols Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Replace Generate/Edit sidebar tabs with a unified dock system and add a filesystem-backed Tools dock as the primary entry point for tool discovery and execution.
**Architecture:** Introduce a small dock layout framework (layout + column + panel + registry) and refactor existing sidebar sections into dock panels. The dock state (order/open/column) is persisted locally and panels are shown/hidden based on app context.
**Tech Stack:** React + TypeScript, existing UI component library, localStorage.
---
### Task 0: Add minimal test tooling for UI
**Files:**
- Modify: `packages/comfystudio-ui/package.json`
- Create: `packages/comfystudio-ui/vitest.config.ts`
- Create: `packages/comfystudio-ui/src/test/setup.ts`
**Step 1: Write the failing test**
```ts
import { describe, expect, it } from "vitest";
describe("test setup", () => {
it("runs vitest", () => {
expect(true).toBe(true);
});
});
```
**Step 2: Run test to verify it fails**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: FAIL with "Unknown command 'test'".
**Step 3: Write minimal implementation**
```json
{
"scripts": {
"test": "vitest"
},
"devDependencies": {
"jsdom": "^24.0.0",
"vitest": "^2.0.0",
"@testing-library/react": "^16.0.0",
"@testing-library/jest-dom": "^6.0.0"
}
}
```
```ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [react(), tsconfigPaths()],
test: {
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"],
},
});
```
```ts
import "@testing-library/jest-dom";
```
**Step 4: Run test to verify it passes**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: PASS.
**Step 5: Commit**
```bash
git add packages/comfystudio-ui/package.json packages/comfystudio-ui/vitest.config.ts packages/comfystudio-ui/src/test/setup.ts
git commit -m "test(ui): add vitest setup"
```
---
### Task 1: Create dock layout primitives
**Files:**
- Create: `packages/comfystudio-ui/src/Dock/index.tsx`
- Create: `packages/comfystudio-ui/src/Dock/DockLayout.tsx`
- Create: `packages/comfystudio-ui/src/Dock/Column.tsx`
- Create: `packages/comfystudio-ui/src/Dock/Panel.tsx`
- Create: `packages/comfystudio-ui/src/Dock/State.ts`
**Step 1: Write the failing test**
```ts
import { describe, expect, it } from "vitest";
import { DockState } from "~/Dock/State";
describe("DockState", () => {
it("initializes with default panels", () => {
const state = DockState.createDefault();
expect(state.panels.length).toBeGreaterThan(0);
});
});
```
**Step 2: Run test to verify it fails**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: FAIL with "test script not found" (will be added in a later task).
**Step 3: Write minimal implementation**
```ts
export type DockPanelState = {
id: string;
column: "left" | "right";
order: number;
open: boolean;
};
export type DockLayoutState = {
panels: DockPanelState[];
};
export namespace DockState {
export const createDefault = (): DockLayoutState => ({
panels: [],
});
}
```
**Step 4: Run test to verify it passes**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: Still FAIL until test tooling is added (resolved in Task 0).
**Step 5: Commit**
```bash
git add packages/comfystudio-ui/src/Dock
git commit -m "feat(ui): add dock layout primitives"
```
---
### Task 2: Add dock layout rendering and persistence
**Files:**
- Modify: `packages/comfystudio-ui/src/Dock/State.ts`
- Modify: `packages/comfystudio-ui/src/Dock/DockLayout.tsx`
- Modify: `packages/comfystudio-ui/src/Dock/Panel.tsx`
**Step 1: Write the failing test**
```ts
import { describe, expect, it } from "vitest";
import { DockState } from "~/Dock/State";
describe("DockState persistence", () => {
it("hydrates from localStorage", () => {
localStorage.setItem("dock-layout.v1", JSON.stringify({ panels: [{ id: "tools", column: "left", order: 0, open: true }] }));
const state = DockState.load();
expect(state.panels[0].id).toBe("tools");
});
});
```
**Step 2: Run test to verify it fails**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: FAIL until `DockState.load()` is implemented and test tooling exists.
**Step 3: Write minimal implementation**
```ts
const STORAGE_KEY = "dock-layout.v1";
export namespace DockState {
export const load = (): DockLayoutState => {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return createDefault();
try {
return JSON.parse(raw) as DockLayoutState;
} catch {
return createDefault();
}
};
export const save = (state: DockLayoutState): void => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
};
}
```
**Step 4: Run test to verify it passes**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: Still FAIL until Task 0 adds testing setup.
**Step 5: Commit**
```bash
git add packages/comfystudio-ui/src/Dock/State.ts
git commit -m "feat(ui): persist dock layout state"
```
---
### Task 3: Extract existing sidebar content into dock panels
**Files:**
- Create: `packages/comfystudio-ui/src/Dock/Panels/PromptPanel.tsx`
- Create: `packages/comfystudio-ui/src/Dock/Panels/InputPanel.tsx`
- Create: `packages/comfystudio-ui/src/Dock/Panels/AdvancedPanel.tsx`
- Create: `packages/comfystudio-ui/src/Dock/Panels/EditorToolPanel.tsx`
- Create: `packages/comfystudio-ui/src/Dock/Panels/LayersPanel.tsx`
- Modify: `packages/comfystudio-ui/src/Generation/Image/Sidebar/index.tsx`
- Modify: `packages/comfystudio-ui/src/Editor/Sidebar/index.tsx`
**Step 1: Write the failing test**
```tsx
import React from "react";
import { describe, expect, it } from "vitest";
import { PromptPanel } from "~/Dock/Panels/PromptPanel";
describe("PromptPanel", () => {
it("returns a React element", () => {
const element = PromptPanel({ inputId: "test" });
expect(React.isValidElement(element)).toBe(true);
});
});
```
**Step 2: Run test to verify it fails**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: FAIL until panel components and testing setup exist.
**Step 3: Write minimal implementation**
```tsx
export function PromptPanel({ inputId }: { inputId: ID }) {
return <Generation.Image.Prompt.Sidebar.Section id={inputId} />;
}
```
**Step 4: Run test to verify it passes**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: Still FAIL until Task 0 adds testing setup.
**Step 5: Commit**
```bash
git add packages/comfystudio-ui/src/Dock/Panels
git commit -m "feat(ui): extract sidebar panels for docking"
```
---
### Task 4: Add Tools dock (filesystem-backed list stub)
**Files:**
- Create: `packages/comfystudio-ui/src/Dock/Panels/ToolsPanel.tsx`
- Create: `packages/comfystudio-ui/src/Tools/Registry.ts`
- Create: `packages/comfystudio-ui/src/Tools/Types.ts`
**Step 1: Write the failing test**
```ts
import { describe, expect, it } from "vitest";
import { ToolRegistry } from "~/Tools/Registry";
describe("ToolRegistry", () => {
it("returns an empty list when no backend is configured", async () => {
const tools = await ToolRegistry.list();
expect(tools).toEqual([]);
});
});
```
**Step 2: Run test to verify it fails**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: FAIL until registry exists and test tooling exists.
**Step 3: Write minimal implementation**
```ts
export namespace ToolRegistry {
export const list = async () => [];
}
```
**Step 4: Run test to verify it passes**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: Still FAIL until Task 0 adds testing setup.
**Step 5: Commit**
```bash
git add packages/comfystudio-ui/src/Tools packages/comfystudio-ui/src/Dock/Panels/ToolsPanel.tsx
git commit -m "feat(ui): add tools dock stub"
```
---
### Task 5: Replace sidebar tabs with unified dock layout
**Files:**
- Modify: `packages/comfystudio-ui/src/Dock/DockLayout.tsx`
- Modify: `packages/comfystudio-ui/src/App/Sidebar/Sidebars.tsx`
- Modify: `packages/comfystudio-ui/src/App/index.tsx`
- Modify: `packages/comfystudio-ui/src/App/BottomBar/index.tsx`
- Modify: `packages/comfystudio-ui/src/Generation/Image/Sidebar/index.tsx`
- Modify: `packages/comfystudio-ui/src/Editor/Sidebar/index.tsx`
**Step 1: Write the failing test**
```tsx
import { describe, expect, it } from "vitest";
import { render } from "@testing-library/react";
import { DockLayout } from "~/Dock/DockLayout";
describe("DockLayout", () => {
it("renders the tools panel", () => {
const { getByText } = render(<DockLayout />);
expect(getByText(/tools/i)).toBeTruthy();
});
});
```
**Step 2: Run test to verify it fails**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: FAIL until DockLayout is wired and testing setup exists.
**Step 3: Write minimal implementation**
```tsx
export function DockLayout() {
return (
<Dock.Column position="left">
<Dock.Panel title="Tools"><ToolsPanel /></Dock.Panel>
</Dock.Column>
);
}
```
**Step 4: Run test to verify it passes**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: Still FAIL until Task 0 adds testing setup.
**Step 5: Commit**
```bash
git add packages/comfystudio-ui/src/Dock packages/comfystudio-ui/src/App
git commit -m "feat(ui): replace sidebar tabs with dock layout"
```
---
### Task 6: Wire dock panels to context and visibility rules
**Files:**
- Modify: `packages/comfystudio-ui/src/Dock/DockLayout.tsx`
- Modify: `packages/comfystudio-ui/src/Dock/State.ts`
- Modify: `packages/comfystudio-ui/src/Dock/Panels/EditorToolPanel.tsx`
**Step 1: Write the failing test**
```ts
import { describe, expect, it } from "vitest";
import { isPanelVisible } from "~/Dock/State";
describe("Dock visibility", () => {
it("hides editor tool panel when no brush tool is active", () => {
const visible = isPanelVisible("editor-tool", { activeTool: "select" });
expect(visible).toBe(false);
});
});
```
**Step 2: Run test to verify it fails**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: FAIL until `isPanelVisible` exists and testing setup exists.
**Step 3: Write minimal implementation**
```ts
export const isPanelVisible = (
panelId: string,
ctx: { activeTool?: string }
) => {
if (panelId === "editor-tool") return ctx.activeTool === "brush";
return true;
};
```
**Step 4: Run test to verify it passes**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: Still FAIL until Task 0 adds testing setup.
**Step 5: Commit**
```bash
git add packages/comfystudio-ui/src/Dock/State.ts
git commit -m "feat(ui): add dock panel visibility rules"
```
---
### Task 8: Remove sidebar tab wiring and tidy references
**Files:**
- Modify: `packages/comfystudio-ui/src/Generation/Image/Sidebar/index.tsx`
- Modify: `packages/comfystudio-ui/src/Editor/Sidebar/index.tsx`
- Modify: `packages/comfystudio-ui/src/App/Sidebar/Tab/*`
- Modify: `packages/comfystudio-ui/src/App/Sidebar/Sidebars.tsx`
**Step 1: Write the failing test**
```ts
import { describe, expect, it } from "vitest";
import { render } from "@testing-library/react";
import { App } from "~/App";
describe("Dock-only sidebar", () => {
it("renders without sidebar tabs", () => {
const { queryByText } = render(<App />);
expect(queryByText(/Generate/i)).toBeNull();
});
});
```
**Step 2: Run test to verify it fails**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: FAIL until tab UI is removed/hidden.
**Step 3: Write minimal implementation**
```tsx
export function Sidebars() {
return <DockLayout />;
}
```
**Step 4: Run test to verify it passes**
Run: `yarn workspace @comfystudio/ui test --run`
Expected: PASS.
**Step 5: Commit**
```bash
git add packages/comfystudio-ui/src/App/Sidebar packages/comfystudio-ui/src/Generation/Image/Sidebar packages/comfystudio-ui/src/Editor/Sidebar
git commit -m "refactor(ui): remove sidebar tabs in favor of docks"
```