Skip to content

Conversation

iremozturk
Copy link

@iremozturk iremozturk commented Aug 7, 2025

Description

This PR implements Task #2: Matrix Runner for the DreamLayer coding challenge. The Matrix Runner allows users to create parameter sweeps across multiple generation settings including seeds, steps, samplers, and CFG values, with pause/resume functionality and deterministic job execution.

Changes Made

  • Matrix Runner UI Component - Complete form interface with parameter inputs
  • Job Generation System - Expands parameter ranges into deterministic job lists
  • Progress Tracking - Visual grid showing job status and completion
  • Pause/Resume Functionality - State persistence across page refreshes
  • Integration Tests - Comprehensive test suite covering Task 2 requirements
  • TypeScript Types - Proper type definitions for matrix runner functionality

Evidence Required

UI Screenshot

UI_SS_DreamLayer

Generated Image

Note: Due to local development environment limitations, I wasn't able to capture AI-generated images. However, the Matrix Runner functionality is fully implemented and tested as shown in the logs and UI screenshot above.

Logs

PASS src/features/MatrixRunner/tests/MatrixRunner.integration.test.tsx
Matrix Runner Integration Tests
Task #2 Requirements
✓ 3×2 sweep completes jobs exactly once (593 ms)
✓ Pause and resume operate without creating duplicates (146 ms)
✓ State survives page refresh (16 ms)
✓ Deterministic job list generation (1 ms)
✓ Parameter parsing handles various formats (18 ms)
Advanced Features
✓ Auto batching groups similar jobs

Test Suites: 1 passed, 1 total
Tests: 6 passed, 6 total

Tests (Optional)

✓ Form accepts lists or ranges for seeds, steps, samplers and similar fields
✓ Expands to a deterministic job list and shows the total count
✓ Pause and resume operate without creating duplicates and survive page refresh
✓ Integration test runs a 3 by 2 sweep that completes each job exactly once

Checklist

  • UI screenshot provided
  • Generated image provided
  • Logs provided
  • Tests added
  • Code follows project style
  • Self-review completed

Summary by Sourcery

Implement the Matrix Runner feature for parameter sweeping and integrate direct Stability AI API support, enhance backend workflows and utilities for STABILITY_API_KEY, update documentation, and add integration tests with Jest for robust end-to-end coverage.

New Features:

  • Add Matrix Runner feature with UI, state management, job generation, pause/resume, progress grid, and job list for systematic parameter sweeps
  • Integrate direct Stability AI API support in ComfyUI nodes and backend with STABILITY_API_KEY handling
  • Expose a new “Matrix Runner” tab in the frontend navigation

Enhancements:

  • Extend backend and workflow loader to recognize Stability AI models and STABILITY_API_KEY
  • Refactor txt2img and img2img workflows to map closed-source and Stability models
  • Improve environment key management and API key injection logic for multiple services
  • Clean up and unify path handling, formatting, and code style across various modules

CI:

  • Configure Jest and related testing dependencies with a new test script in package.json

Documentation:

  • Document STABILITY_API_KEY usage and Stability models in README, installation, usage, and API reference
  • Update utils documentation and examples to reflect new Stability AI integration and code changes

Tests:

  • Add comprehensive integration tests for Matrix Runner covering sweep generation, pause/resume, persistence, deterministic job IDs, and auto-batching

…mentation

- Complete parameter parser with range/list support
- Deterministic job queue with content-addressed IDs
- Pause/resume functionality with IndexedDB persistence
- Integration tests for 3×2 sweep (6/6 tests passing)
- Advanced features: progress visualization, smart batching, ETA calculator
- Production quality: TypeScript, comprehensive testing, documentation

Closes: Task DreamLayer-AI#2 Matrix Runner
Copy link
Contributor

sourcery-ai bot commented Aug 7, 2025

Reviewer's Guide

This PR introduces a full-featured Matrix Runner for DreamLayer, enabling users to define ranges or lists of generation parameters, generate a deterministic job queue, and execute jobs sequentially with pause/resume and real-time progress tracking. The implementation uses a persisted Zustand store backed by IndexedDB, new utility functions for parsing and job orchestration, and a tabbed UI with Parameter Inputs, Progress Grid, and Job List, all covered by comprehensive integration tests.

Sequence diagram for Matrix Runner job execution and pause/resume

sequenceDiagram
    actor User
    participant MatrixRunnerStore as Store
    participant IndexedDB
    participant Backend as DreamLayer API

    User->>Store: Clicks 'Start Matrix'
    Store->>Store: Set isRunning=true, isPaused=false
    loop For each job
      Store->>Store: Set job.status = running
      Store->>Backend: POST /api/txt2img (job parameters)
      Backend-->>Store: Generation result
      Store->>Store: Update job.status = completed/failed
      Store->>IndexedDB: Persist state
      alt User clicks Pause
        User->>Store: Clicks 'Pause'
        Store->>Store: Set isPaused=true, isRunning=false
        Store-->>IndexedDB: Persist state
        User->>Store: Clicks 'Resume'
        Store->>Store: Set isPaused=false, isRunning=true
      end
    end
    Store->>User: Update progress grid and job list
Loading

Class diagram for Matrix Runner state and job orchestration

classDiagram
    class MatrixRunnerStore {
      +MatrixParameters parameters
      +Txt2ImgCoreSettings baseSettings
      +MatrixJob[] jobs
      +int currentJobIndex
      +bool isRunning
      +bool isPaused
      +int totalJobs
      +int completedJobs
      +int failedJobs
      +int pendingJobs
      +bool showProgressGrid
      +bool autoBatch
      +setParameter(param, range)
      +setBaseSettings(settings)
      +generateJobs()
      +startMatrix()
      +pauseMatrix()
      +resumeMatrix()
      +stopMatrix()
      +clearJobs()
      +executeNextJob()
      +updateJobStatus(jobId, status, result, error, duration)
      +toggleProgressGrid()
      +toggleAutoBatch()
    }
    class MatrixJob {
      +string id
      +"pending|running|completed|failed|paused" status
      +Txt2ImgCoreSettings parameters
      +number createdAt
      +number startedAt
      +number completedAt
      +string error
      +result
    }
    class ParameterRange {
      +"list|range" type
      +string|number[] values
      +string original
    }
    class MatrixParameters {
      +ParameterRange seeds
      +ParameterRange samplers
      +ParameterRange steps
      +ParameterRange cfg_scale
      +ParameterRange width
      +ParameterRange height
      +ParameterRange batch_size
      +ParameterRange batch_count
    }
    MatrixRunnerStore "1" o-- "*" MatrixJob : manages
    MatrixRunnerStore "1" o-- "1" MatrixParameters : configures
    MatrixParameters "1" o-- "*" ParameterRange : contains
    MatrixJob "1" o-- "1" Txt2ImgCoreSettings : uses
Loading

File-Level Changes

Change Details Files
Matrix Runner UI with parameter inputs and tabbed interface
  • Added MatrixRunnerPage component with tabs for Parameters, Progress Grid, and Job List
  • Created MatrixParameterInput, MatrixProgressGrid, and MatrixJobList UI components
  • Updated navigation (TabsNav.tsx) and page routing (Index.tsx) to include the Matrix Runner tab
src/features/MatrixRunner/MatrixRunnerPage.tsx
src/features/MatrixRunner/MatrixParameterInput.tsx
src/features/MatrixRunner/MatrixProgressGrid.tsx
src/features/MatrixRunner/MatrixJobList.tsx
src/components/Navigation/TabsNav.tsx
src/pages/Index.tsx
State management with persisted Zustand store
  • Implemented useMatrixRunnerStore with actions for job lifecycle (generate, start, pause, resume, stop)
  • Added IndexedDB adapter for state persistence across refreshes
  • Included job execution logic and real-time updates in the store
src/stores/useMatrixRunnerStore.ts
Utility library for parameter parsing and job generation
  • Added parseParameter, generateJobId, and generateJobs functions
  • Included helpers for ETA calculation, average time updates, and smart batching
  • Implemented formatDuration and calculateTotalJobs utilities
src/utils/matrixUtils.ts
TypeScript types for Matrix Runner feature
  • Defined ParameterRange, MatrixParameters, and MatrixJob interfaces
  • Outlined MatrixRunnerState and MatrixRunnerActions in store types
  • Created ParsedParameter and MatrixGenerationResult types
src/types/matrixRunner.ts
Integration tests and test configuration
  • Wrote end-to-end integration tests covering sweep execution, pause/resume, persistence, deterministic IDs, and batching
  • Mocked fetch and IndexedDB for test isolation
  • Configured Jest (jest.config.js) and setupTests.ts for React Testing Library
src/features/MatrixRunner/__tests__/MatrixRunner.integration.test.tsx
jest.config.js
src/setupTests.ts
Project dependencies and build configuration updates
  • Added zustand, @testing-library/react, jest and related types/devDependencies
  • Updated package.json scripts to include test command
  • Configured ts-jest transformer for TypeScript tests
dream_layer_frontend/package.json
dream_layer_frontend/src/components/AliasKeyInputs.tsx
dream_layer_frontend/jest.config.js
Documentation and feature guide for Matrix Runner
  • Added comprehensive README in features/MatrixRunner with architecture, usage, and testing details
  • Included setup and quick-start instructions for demo
  • Documented parameter formats and validation rules
src/features/MatrixRunner/README.md
SETUP_GUIDE.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

coderabbitai bot commented Aug 7, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @iremozturk - I've reviewed your changes - here's some feedback:

  • Consider refactoring the heavy matrix execution loop into a Web Worker (or background thread) to keep the UI responsive for large job batches.
  • For very large matrices, the progress grid and job list components may become slow—consider adding virtualization (e.g. react-window) to optimize rendering massive lists/grids.
  • The IndexedDB storage adapter is embedded in the store configuration—consider moving it to a separate module to simplify the store definition and improve testability.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider refactoring the heavy matrix execution loop into a Web Worker (or background thread) to keep the UI responsive for large job batches.
- For very large matrices, the progress grid and job list components may become slow—consider adding virtualization (e.g. react-window) to optimize rendering massive lists/grids.
- The IndexedDB storage adapter is embedded in the store configuration—consider moving it to a separate module to simplify the store definition and improve testability.

## Individual Comments

### Comment 1
<location> `dream_layer_frontend/src/stores/useMatrixRunnerStore.ts:13` </location>
<code_context>
+// IndexedDB storage adapter for Zustand
</code_context>

<issue_to_address>
Consider fallback or error handling for environments without IndexedDB support.

IndexedDB may not be available in all environments. Add a fallback (e.g., localStorage or memory) or provide clear error messaging if persistence fails.

Suggested implementation:

```typescript
/**
 * IndexedDB storage adapter for Zustand with fallback to localStorage or in-memory storage.
 */
const createIndexedDBStorage = () => {
  const dbName = 'dreamlayer-matrix-runner';
  const storeName = 'matrix-runner-state';
  const version = 1;

  // In-memory fallback
  const memoryStore: Record<string, string> = {};

  // Check for IndexedDB support
  const hasIndexedDB = typeof window !== 'undefined' && 'indexedDB' in window;
  const hasLocalStorage = typeof window !== 'undefined' && 'localStorage' in window;

  // IndexedDB methods
  const indexedDBMethods = {
    getItem: async (name: string): Promise<string | null> => {
      return new Promise((resolve) => {
        const request = indexedDB.open(dbName, version);


```

```typescript
  // localStorage methods
  const localStorageMethods = {
    getItem: async (name: string): Promise<string | null> => {
      try {
        return localStorage.getItem(name);
      } catch (e) {
        console.warn('localStorage getItem failed', e);
        return null;
      }
    },
    setItem: async (name: string, value: string): Promise<void> => {
      try {
        localStorage.setItem(name, value);
      } catch (e) {
        console.warn('localStorage setItem failed', e);
      }
    },
    removeItem: async (name: string): Promise<void> => {
      try {
        localStorage.removeItem(name);
      } catch (e) {
        console.warn('localStorage removeItem failed', e);
      }
    }
  };

  // In-memory methods
  const memoryMethods = {
    getItem: async (name: string): Promise<string | null> => {
      return memoryStore[name] ?? null;
    },
    setItem: async (name: string, value: string): Promise<void> => {
      memoryStore[name] = value;
    },
    removeItem: async (name: string): Promise<void> => {
      delete memoryStore[name];
    }
  };

  // Select storage backend
  let storageBackend;
  if (hasIndexedDB) {
    storageBackend = indexedDBMethods;
  } else if (hasLocalStorage) {
    console.warn('IndexedDB not available, falling back to localStorage for persistence.');
    storageBackend = localStorageMethods;
  } else {
    console.warn('Neither IndexedDB nor localStorage available, falling back to in-memory storage. Persistence will not survive reloads.');
    storageBackend = memoryMethods;
  }

  return storageBackend;

```
</issue_to_address>

### Comment 2
<location> `dream_layer_frontend/src/stores/useMatrixRunnerStore.ts:240` </location>
<code_context>
+          // Execute the job
+          const startTime = Date.now();
+          
+          const response = await fetch('http://localhost:5001/api/txt2img', {
+            method: 'POST',
+            headers: {
+              'Content-Type': 'application/json',
+            },
+            body: JSON.stringify(currentJob.parameters),
+          });
+
+          if (!response.ok) {
</code_context>

<issue_to_address>
Hardcoded API endpoint may limit deployment flexibility.

Consider making the API endpoint configurable through environment variables or a global config to allow independent deployment of frontend and backend.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
          const response = await fetch('http://localhost:5001/api/txt2img', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify(currentJob.parameters),
          });
=======
          // Use environment variable for API base URL, fallback to localhost if not set
          const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5001';
          const response = await fetch(`${API_BASE_URL}/api/txt2img`, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify(currentJob.parameters),
          });
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `dream_layer_frontend/src/stores/useMatrixRunnerStore.ts:256` </location>
<code_context>
+          const endTime = Date.now();
+          const jobDuration = (endTime - startTime) / 1000; // Convert to seconds
+
+          if (data.comfy_response?.generated_images) {
+            const images = data.comfy_response.generated_images.map((img: any) => ({
+              id: `${Date.now()}-${Math.random()}`,
+              url: img.url,
+              prompt: currentJob.parameters.prompt,
+              negativePrompt: currentJob.parameters.negative_prompt,
+              timestamp: Date.now(),
+              settings: currentJob.parameters
+            }));
+
+            // Update job with success result
</code_context>

<issue_to_address>
Assumes a specific backend response structure.

Consider adding error handling or schema validation to handle potential changes in the backend response format.

Suggested implementation:

```typescript
          const data = await response.json();
          const endTime = Date.now();
          const jobDuration = (endTime - startTime) / 1000; // Convert to seconds

          // Schema validation for backend response
          // If zod is not available, replace with manual checks as needed
          import { z } from "zod";
          const ComfyResponseSchema = z.object({
            comfy_response: z.object({
              generated_images: z.array(
                z.object({
                  url: z.string().url()
                })
              )
            })
          });

          const parseResult = ComfyResponseSchema.safeParse(data);

          if (!parseResult.success) {
            console.error('Backend response schema validation failed:', parseResult.error);
            throw new Error('Unexpected backend response format');
          }

          const generatedImages = parseResult.data.comfy_response.generated_images;

          if (generatedImages && generatedImages.length > 0) {
            const images = generatedImages.map((img: any) => ({
              id: `${Date.now()}-${Math.random()}`,
              url: img.url,
              prompt: currentJob.parameters.prompt,
              negativePrompt: currentJob.parameters.negative_prompt,
              timestamp: Date.now(),
              settings: currentJob.parameters
            }));

            // Update job with success result
            get().updateJobStatus(currentJob.id, 'completed', { images }, undefined, jobDuration);
          } else {
            throw new Error('No images were generated');
          }

```

- If `zod` is not already installed, run `npm install zod` and ensure the import is at the top of your file.
- If you do not want to use `zod`, replace the schema validation block with manual checks for the presence and type of `comfy_response.generated_images`.
- Move the `import { z } from "zod";` statement to the top of the file if you prefer to keep imports together.
</issue_to_address>

### Comment 4
<location> `dream_layer_frontend/src/features/MatrixRunner/MatrixProgressGrid.tsx:30` </location>
<code_context>
+  const getGridDimensions = () => {
</code_context>

<issue_to_address>
Grid layout logic may not generalize for all parameter combinations.

Currently, the grid only handles specific two-parameter combinations. If more than two parameters have multiple values, the visualization may become unclear. Consider adding a warning or supporting more flexible layouts for higher-dimensional cases.

Suggested implementation:

```typescript
  const getGridDimensions = () => {
    const paramCounts = {
      seeds: parameters.seeds?.values.length || 1,
      samplers: parameters.samplers?.values.length || 1,
      steps: parameters.steps?.values.length || 1,
      cfg_scale: parameters.cfg_scale?.values.length || 1,
    };

    // Count how many parameters have more than one value
    const multiValueParams = Object.entries(paramCounts).filter(([_, count]) => count > 1);

    // Warn if more than two parameters have multiple values
    if (multiValueParams.length > 2) {
      return {
        warning: `Warning: More than two parameters (${multiValueParams.map(([k]) => k).join(', ')}) have multiple values. The grid layout may not visualize all combinations clearly.`,
        rows: 1,
        cols: 1,
        xAxis: null,
        yAxis: null,
      };
    }

    // Determine grid layout based on parameter combinations
    if (paramCounts.seeds > 1 && paramCounts.samplers > 1) {
      return { rows: paramCounts.seeds, cols: paramCounts.samplers, xAxis: 'samplers', yAxis: 'seeds' };
    } else if (paramCounts.steps > 1 && paramCounts.cfg_scale > 1) {
      return { rows: paramCounts.steps, cols: paramCounts.cfg_scale, xAxis: 'cfg_scale', yAxis: 'steps' };

```

You will need to update the component that calls `getGridDimensions()` to check for and display the `warning` property if present. For example, after calling `const grid = getGridDimensions();`, you could render an alert or message if `grid.warning` exists.
</issue_to_address>

### Comment 5
<location> `README.md:164` </location>
<code_context>
-
+| 🔍 Feature                      | 🚀 How it’s better                                                                                          |
+| ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
+| **Familiar Layout**             | If you’ve used A1111 or Forge, you’ll feel at home in sec. Zero learning curve                              |
+| **Modern UX**                   | Responsive design with light & dark themes and a clutter-free interface that lets you work faster           |
+| **ComfyUI Engine Inside**       | All generation runs on a proven, modular, stable ComfyUI backend. Ready for custom nodes and advanced hacks |
</code_context>

<issue_to_address>
Possible typo: 'in sec.' should likely be 'in seconds'.

Consider updating 'in sec.' to 'in seconds' for clarity.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
| **Familiar Layout**             | If you’ve used A1111 or Forge, you’ll feel at home in sec. Zero learning curve                              |
=======
| **Familiar Layout**             | If you’ve used A1111 or Forge, you’ll feel at home in seconds. Zero learning curve                              |
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +13 to +22
// IndexedDB storage adapter for Zustand
const createIndexedDBStorage = () => {
const dbName = 'dreamlayer-matrix-runner';
const storeName = 'matrix-runner-state';
const version = 1;

return {
getItem: async (name: string): Promise<string | null> => {
return new Promise((resolve) => {
const request = indexedDB.open(dbName, version);
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Consider fallback or error handling for environments without IndexedDB support.

IndexedDB may not be available in all environments. Add a fallback (e.g., localStorage or memory) or provide clear error messaging if persistence fails.

Suggested implementation:

/**
 * IndexedDB storage adapter for Zustand with fallback to localStorage or in-memory storage.
 */
const createIndexedDBStorage = () => {
  const dbName = 'dreamlayer-matrix-runner';
  const storeName = 'matrix-runner-state';
  const version = 1;

  // In-memory fallback
  const memoryStore: Record<string, string> = {};

  // Check for IndexedDB support
  const hasIndexedDB = typeof window !== 'undefined' && 'indexedDB' in window;
  const hasLocalStorage = typeof window !== 'undefined' && 'localStorage' in window;

  // IndexedDB methods
  const indexedDBMethods = {
    getItem: async (name: string): Promise<string | null> => {
      return new Promise((resolve) => {
        const request = indexedDB.open(dbName, version);
  // localStorage methods
  const localStorageMethods = {
    getItem: async (name: string): Promise<string | null> => {
      try {
        return localStorage.getItem(name);
      } catch (e) {
        console.warn('localStorage getItem failed', e);
        return null;
      }
    },
    setItem: async (name: string, value: string): Promise<void> => {
      try {
        localStorage.setItem(name, value);
      } catch (e) {
        console.warn('localStorage setItem failed', e);
      }
    },
    removeItem: async (name: string): Promise<void> => {
      try {
        localStorage.removeItem(name);
      } catch (e) {
        console.warn('localStorage removeItem failed', e);
      }
    }
  };

  // In-memory methods
  const memoryMethods = {
    getItem: async (name: string): Promise<string | null> => {
      return memoryStore[name] ?? null;
    },
    setItem: async (name: string, value: string): Promise<void> => {
      memoryStore[name] = value;
    },
    removeItem: async (name: string): Promise<void> => {
      delete memoryStore[name];
    }
  };

  // Select storage backend
  let storageBackend;
  if (hasIndexedDB) {
    storageBackend = indexedDBMethods;
  } else if (hasLocalStorage) {
    console.warn('IndexedDB not available, falling back to localStorage for persistence.');
    storageBackend = localStorageMethods;
  } else {
    console.warn('Neither IndexedDB nor localStorage available, falling back to in-memory storage. Persistence will not survive reloads.');
    storageBackend = memoryMethods;
  }

  return storageBackend;

Comment on lines +240 to +246
const response = await fetch('http://localhost:5001/api/txt2img', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(currentJob.parameters),
});
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Hardcoded API endpoint may limit deployment flexibility.

Consider making the API endpoint configurable through environment variables or a global config to allow independent deployment of frontend and backend.

Suggested change
const response = await fetch('http://localhost:5001/api/txt2img', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(currentJob.parameters),
});
// Use environment variable for API base URL, fallback to localhost if not set
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5001';
const response = await fetch(`${API_BASE_URL}/api/txt2img`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(currentJob.parameters),
});

...state.parameters,
[param]: range
}
}));
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): Assumes a specific backend response structure.

Consider adding error handling or schema validation to handle potential changes in the backend response format.

Suggested implementation:

          const data = await response.json();
          const endTime = Date.now();
          const jobDuration = (endTime - startTime) / 1000; // Convert to seconds

          // Schema validation for backend response
          // If zod is not available, replace with manual checks as needed
          import { z } from "zod";
          const ComfyResponseSchema = z.object({
            comfy_response: z.object({
              generated_images: z.array(
                z.object({
                  url: z.string().url()
                })
              )
            })
          });

          const parseResult = ComfyResponseSchema.safeParse(data);

          if (!parseResult.success) {
            console.error('Backend response schema validation failed:', parseResult.error);
            throw new Error('Unexpected backend response format');
          }

          const generatedImages = parseResult.data.comfy_response.generated_images;

          if (generatedImages && generatedImages.length > 0) {
            const images = generatedImages.map((img: any) => ({
              id: `${Date.now()}-${Math.random()}`,
              url: img.url,
              prompt: currentJob.parameters.prompt,
              negativePrompt: currentJob.parameters.negative_prompt,
              timestamp: Date.now(),
              settings: currentJob.parameters
            }));

            // Update job with success result
            get().updateJobStatus(currentJob.id, 'completed', { images }, undefined, jobDuration);
          } else {
            throw new Error('No images were generated');
          }
  • If zod is not already installed, run npm install zod and ensure the import is at the top of your file.
  • If you do not want to use zod, replace the schema validation block with manual checks for the presence and type of comfy_response.generated_images.
  • Move the import { z } from "zod"; statement to the top of the file if you prefer to keep imports together.

</CardContent>
</Card>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Grid layout logic may not generalize for all parameter combinations.

Currently, the grid only handles specific two-parameter combinations. If more than two parameters have multiple values, the visualization may become unclear. Consider adding a warning or supporting more flexible layouts for higher-dimensional cases.

Suggested implementation:

  const getGridDimensions = () => {
    const paramCounts = {
      seeds: parameters.seeds?.values.length || 1,
      samplers: parameters.samplers?.values.length || 1,
      steps: parameters.steps?.values.length || 1,
      cfg_scale: parameters.cfg_scale?.values.length || 1,
    };

    // Count how many parameters have more than one value
    const multiValueParams = Object.entries(paramCounts).filter(([_, count]) => count > 1);

    // Warn if more than two parameters have multiple values
    if (multiValueParams.length > 2) {
      return {
        warning: `Warning: More than two parameters (${multiValueParams.map(([k]) => k).join(', ')}) have multiple values. The grid layout may not visualize all combinations clearly.`,
        rows: 1,
        cols: 1,
        xAxis: null,
        yAxis: null,
      };
    }

    // Determine grid layout based on parameter combinations
    if (paramCounts.seeds > 1 && paramCounts.samplers > 1) {
      return { rows: paramCounts.seeds, cols: paramCounts.samplers, xAxis: 'samplers', yAxis: 'seeds' };
    } else if (paramCounts.steps > 1 && paramCounts.cfg_scale > 1) {
      return { rows: paramCounts.steps, cols: paramCounts.cfg_scale, xAxis: 'cfg_scale', yAxis: 'steps' };

You will need to update the component that calls getGridDimensions() to check for and display the warning property if present. For example, after calling const grid = getGridDimensions();, you could render an alert or message if grid.warning exists.


| 🔍 Feature | 🚀 How it’s better |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| **Familiar Layout** | If you’ve used A1111 or Forge, you’ll feel at home in sec. Zero learning curve |
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (typo): Possible typo: 'in sec.' should likely be 'in seconds'.

Consider updating 'in sec.' to 'in seconds' for clarity.

Suggested change
| **Familiar Layout** | If you’ve used A1111 or Forge, you’ll feel at home in sec. Zero learning curve |
| **Familiar Layout** | If you’ve used A1111 or Forge, you’ll feel at home in seconds. Zero learning curve |

Comment on lines 640 to 641
raise Exception(
f"Stability Upscale Fast failed: {response_api.finish_reason}.")
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Raise a specific error instead of the general Exception or BaseException (raise-specific-error)

ExplanationIf a piece of code raises a specific exception type rather than the generic [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) or [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), the calling code can:
  • get more information about what type of error it is
  • define specific exception handling for it

This way, callers of the code can handle the error appropriately.

How can you solve this?

So instead of having code raising Exception or BaseException like

if incorrect_input(value):
    raise Exception("The input is incorrect")

you can have code raising a specific error like

if incorrect_input(value):
    raise ValueError("The input is incorrect")

or

class IncorrectInputError(Exception):
    pass


if incorrect_input(value):
    raise IncorrectInputError("The input is incorrect")

"Content-Type": "application/json"
}

try:
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): We've found these issues:

Comment on lines 81 to 89
api_key = os.getenv(env_key)
if api_key:
api_keys[env_key] = api_key
# Safely truncate for display without assuming length
display_key = api_key[:8] + "..." + api_key[-4:] if len(api_key) > 12 else api_key
display_key = api_key[:8] + "..." + \
api_key[-4:] if len(api_key) > 12 else api_key
print(f"[DEBUG] Found {env_key}: {display_key}")
else:
print(f"[DEBUG] No {env_key} found in environment")
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): We've found these issues:

Comment on lines 238 to 241
# Try to find name parameter in the data URL
name_match = re.search(r';name=(.*?);', data_url)
if name_match:
return name_match.group(1)
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (code-quality): We've found these issues:

Suggested change
# Try to find name parameter in the data URL
name_match = re.search(r';name=(.*?);', data_url)
if name_match:
return name_match.group(1)
if name_match := re.search(r';name=(.*?);', data_url):
return name_match[1]

from shared_utils import SAMPLER_NAME_MAP


def transform_to_txt2img_workflow(data):
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): We've found these issues:


Explanation

The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.

How can you solve this?

It might be worth refactoring this function to make it shorter and more readable.

  • Reduce the function length by extracting pieces of functionality out into
    their own functions. This is the most important thing you can do - ideally a
    function should be less than 10 lines.
  • Reduce nesting, perhaps by introducing guard clauses to return early.
  • Ensure that variables are tightly scoped, so that code using related concepts
    sits together within the function rather than being scattered.

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