Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 10, 2025

Complete implementation of Plan Code in Coding Assistant

Summary

Successfully implemented the Plan Code (Plugin Scaffold) feature for the AI Coding Assistant in the FDO editor. This feature allows users to generate complete plugin project structures with multiple files from a single AI prompt, optionally including UI mockup analysis.

Latest Changes (Bug Fixes)

Bug #1: handleRefine Not Being Used

  • Fixed: "Refine Response" button now properly calls handleRefine function instead of inline handler
  • Impact: Consistent function reuse and better code maintainability

Bug #2: Plan Code Prompt Incorrectly Suggesting React

  • Fixed: Updated plan code prompt to accurately reflect FDO plugin development constraints
  • Key Changes:
    • Removed all React/JSX references (plugins use vanilla TypeScript + SDK DOM classes)
    • Added explicit constraint: "NO React/JSX: FDO plugins do NOT use React"
    • Documented global plugin host functions (window.createBackendReq, window.executeInjectedScript, etc.)
    • Removed .tsx file examples, replaced with SDK DOM class examples
    • Added example render() method showing proper SDK usage with DOMTable, DOMButton
    • Emphasized goober CSS-in-JS for styling (not CSS files)
    • Clarified that render() must return HTML strings, not JSX

Previous Changes

Critical Fix: File Creation Loop

  • Fixed: Replaced manual Monaco model creation with createVirtualFile utility (as used in FileDialogComponent)
  • Impact: Proper language detection, model management, and VirtualFS registration
  • Why: Previous implementation was inefficient (importing monaco in loop) and didn't follow FDO patterns

Backend LLM Message Consistency

  • Fixed: Consistent message handling for both image and non-image paths
  • Why: Ensures LLM conversation state is properly set up regardless of input type

Performance & UX Improvements

  • Regex Safety: Added 50,000 character limit to prevent catastrophic backtracking on malformed AI responses
  • Auto-Clear Timeout: Increased from 3s to 10s to give users time to review plan before clearing
  • Code Simplification: Removed redundant getLanguageFromPath helper (handled by createVirtualFile)

Completed Tasks

  • Explore repository structure and understand existing code
  • Identify missing components for Plan Code feature
  • Implement backend handler (handlePlanCode) in src/ipc/ai_coding_agent.js
  • Implement frontend execution logic in AiCodingAgentPanel.jsx
  • Register the handler in IPC handlers
  • Build and test for regressions
  • Update documentation in AI_CODING_AGENT.md
  • Address PR review feedback
  • Fix file creation to use proper FDO patterns
  • Run security checks (CodeQL - no issues)
  • Fix handleRefine not being used
  • Fix plan code prompt to remove React and use SDK-only approach

Implementation Details

Backend Handler (handlePlanCode)

  • Accepts: requestId, prompt, image (base64), assistantId
  • Generates structured plans with AI using comprehensive prompt
  • Updated: Prompt now correctly instructs AI to use SDK DOM classes instead of React
  • Updated: Documents plugin host environment functions (createBackendReq, executeInjectedScript, etc.)
  • Supports vision models (GPT-4 Vision, Claude 3+) for UI mockup analysis
  • Consistent LLM message handling for both vision and text-only modes
  • Streams response back to frontend

Frontend Execution (handleExecutePlan)

  • Creates snapshot before applying changes (undo support)
  • Parses AI response to extract file paths and content using safe regex
  • Creates folder structure in VirtualFS
  • Uses createVirtualFile utility for proper file creation (follows FDO patterns)
  • Provides detailed success/error feedback
  • Auto-clears after 10s (increased from 3s for better UX)
  • Fixed: "Refine Response" button properly uses handleRefine function

Plan Format

### File: /package.json
\`\`\`json
{ ... }
\`\`\`

### File: /index.ts
\`\`\`typescript
import { FDO_SDK, FDOInterface, PluginMetadata } from "@anikitenko/fdo-sdk";

export default class MyPlugin extends FDO_SDK implements FDOInterface {
    render(): string {
        const table = new DOMTable()
            .addRow([new DOMText('Col 1'), new DOMText('Col 2')])
            .build();
        return \`<div>\${table}</div>\`;
    }
}
\`\`\`

Test Results

  • ✅ Build successful (no errors or new warnings)
  • ✅ All unit tests pass (90/92 - same 2 unrelated failures as baseline)
  • ✅ No regressions in existing features
Original prompt

Complete implementation of Plan Code in Coding assistant in Editor


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits November 10, 2025 12:24
Co-authored-by: anikitenko <12380460+anikitenko@users.noreply.github.com>
Co-authored-by: anikitenko <12380460+anikitenko@users.noreply.github.com>
Copilot AI changed the title [WIP] Complete implementation of plan code in coding assistant Implement Plan Code feature for AI Coding Assistant Nov 10, 2025
Copilot AI requested a review from anikitenko November 10, 2025 12:32
@anikitenko anikitenko marked this pull request as ready for review November 10, 2025 13:14
Copilot AI review requested due to automatic review settings November 10, 2025 13:14
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds a new "Plan Code (Plugin Scaffold)" feature to the AI Coding Agent that enables generation of complete multi-file plugin projects. The feature leverages vision-capable AI models to analyze UI mockups and generate matching code structures, automatically creating files and folders in the editor's VirtualFS.

Key changes:

  • New handlePlanCode IPC handler with vision/image support for analyzing UI mockups
  • Plan parsing and execution logic that extracts file paths and content from AI-generated markdown
  • Comprehensive documentation covering the feature's workflow, format, and troubleshooting

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
src/ipc/ai_coding_agent.js Adds handlePlanCode handler that generates structured plugin plans with vision model support for UI mockup analysis
src/components/editor/AiCodingAgentPanel.jsx Implements plan execution workflow including parsing AI responses, creating folder structures, and generating Monaco models with snapshot protection
docs/AI_CODING_AGENT.md Documents the Plan Code feature including format specifications, usage examples, troubleshooting guidance, and vision model integration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 433 to 438
// Clear response and image after a delay
setTimeout(() => {
setResponse('');
responseRef.current = '';
handleRemoveImage();
}, 3000);
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

[nitpick] After successfully creating files from the plan, the response and image are automatically cleared after 3 seconds (lines 434-438). However, if the user wants to copy the plan or review the response, they have only 3 seconds to do so. Consider either:

  1. Not auto-clearing on success (let the user manually clear)
  2. Increasing the timeout to give users more time (e.g., 10 seconds)
  3. Only clearing if the user manually dismisses, or providing a "dismiss" button

The current 3-second auto-clear might be too aggressive and could frustrate users who want to review the plan after execution.

Copilot uses AI. Check for mistakes.
Copy link
Owner

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Increased timeout from 3s to 10s in commit a256c98 to give users more time to review the plan after execution.

const files = [];

// Match file sections: ### File: /path/to/file followed by code block
const filePattern = /###\s+File:\s+(\/[^\n]+)\s*\n\s*```(\w+)?\s*\n([\s\S]*?)```/g;
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

[nitpick] The regex uses the global flag (/g) with a greedy quantifier [\s\S]*? which can lead to catastrophic backtracking on malformed input (e.g., if there's a file section without a closing code fence). While *? is lazy, the regex engine may still need to backtrack significantly on large responses. Consider:

  1. Adding a reasonable maximum length limit to the content capture group
  2. Using a more specific pattern instead of [\s\S]*?, such as [^]+` if backticks aren't expected in code
  3. Implementing a timeout or length limit for the parsing operation

For typical AI responses this should be fine, but it's worth considering edge cases where the AI generates malformed output.

Suggested change
const filePattern = /###\s+File:\s+(\/[^\n]+)\s*\n\s*```(\w+)?\s*\n([\s\S]*?)```/g;
// Limit code block content to 10,000 characters to avoid catastrophic backtracking on malformed input
const filePattern = /###\s+File:\s+(\/[^\n]+)\s*\n\s*```(\w+)?\s*\n([^]{0,10000}?)```/g;

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added 50,000 character limit to the regex capture group in commit a256c98 to prevent catastrophic backtracking on malformed AI responses.

Comment on lines 392 to 394
for (const file of files) {
try {
const monaco = await import('monaco-editor');
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

The dynamic import of monaco-editor happens inside a loop for each file. This is inefficient because:

  1. The import statement will be executed multiple times (once per file)
  2. While the module is cached after the first import, the await import() overhead still occurs on each iteration

Move the await import('monaco-editor') outside the loop (before line 392) to import it once and reuse the reference for all files. This will improve performance when creating multiple files.

Suggested change
for (const file of files) {
try {
const monaco = await import('monaco-editor');
const monaco = await import('monaco-editor');
for (const file of files) {
try {

Copilot uses AI. Check for mistakes.
Copy link
Owner

Choose a reason for hiding this comment

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

@copilot this loop is completely incorrect. Consider checking FileDialogComponent.js file about file creation

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit a256c98. Replaced the incorrect loop with proper file creation using createVirtualFile utility, following the pattern from FileDialogComponent.js. This eliminates the monaco import inside the loop and ensures proper language detection and VirtualFS registration.

Comment on lines 554 to 568
const messages = [];
if (image) {
messages.push({
role: 'user',
content: [
{ type: 'text', text: fullPrompt },
{
type: 'image_url',
image_url: { url: image }
}
]
});
} else {
llm.user(fullPrompt);
}
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

The llm.user() call is only made in the else branch, but when image is provided, the LLM instance doesn't have the prompt added through the usual API. The messages array is created locally but the LLM's internal conversation state may not be set up correctly. This inconsistency in how messages are prepared could lead to unexpected behavior. Consider using a consistent pattern for both cases.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit a256c98. Refactored to use consistent message handling - when image is provided, messages are prepared before the chat call, otherwise llm.user() is called before chat. Both paths now properly set up the LLM conversation state.

anikitenko and others added 3 commits November 10, 2025 15:28
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions
Copy link

github-actions bot commented Nov 10, 2025

Qodana for JS

111 new problems were found

Inspection name Severity Problems
Unused local symbol 🔶 Warning 19
Comma expression 🔶 Warning 16
Unnecessary label 🔶 Warning 11
Webpack config compliance with JSON Schema 🔶 Warning 10
'with' statement 🔶 Warning 7
Exception used for local control-flow 🔶 Warning 5
Unused import 🔶 Warning 4
Infinite loop statement 🔶 Warning 3
Redundant character escape 🔶 Warning 3
Unused assignment 🔶 Warning 2
Syntax errors and unresolved references in JSDoc 🔶 Warning 2
Unreachable code 🔶 Warning 2
'this' expression which references the global object 🔶 Warning 1
Result of method call returning a promise is ignored ◽️ Notice 10
Expression statement which is not assignment or call ◽️ Notice 6
Duplicated code fragment ◽️ Notice 6
Deprecated symbol used ◽️ Notice 4

☁️ View the detailed Qodana report

Contact Qodana team

Contact us at qodana-support@jetbrains.com

Co-authored-by: anikitenko <12380460+anikitenko@users.noreply.github.com>
@anikitenko
Copy link
Owner

@copilot and now there are 2 bugs:

  1. handleRefine is not used anymore

  2. AI coding assistant during planning is not fully utilizing SDK. React is not allowed. Please checkout plugin_host.html and see what plugins can use. Also here is a code that injects with plugin during UI rendering:
    window.createBackendReq = function(type, data) {
    return new Promise((resolve) => {
    const message = { type: "UI_MESSAGE", message: {handler: type, content: data} };
    window.parent.postMessage(message, "*");

                 const listener = (event) => {
                     if (event.data?.type === "UI_MESSAGE") {
                         window.removeEventListener("message", listener);
                         resolve(event.data.content);
                     }
                 };
                 window.removeEventListener("message", listener);
                 window.addEventListener("message", listener);
             });
         };
         
         // Function to check if the element exists
         window.waitForElement = function(selector, callback, timeout = 5000) {
             const start = Date.now();
             
             const checkExist = setInterval(() => {
                 const element = document.querySelector(selector);
                 if (element && element.parentNode) {
                     clearInterval(checkExist);
                     callback(element);
                 }
                 if (Date.now() - start > timeout) {
                     clearInterval(checkExist);
                     console.error("Timeout: Element not found:", selector);
                 }
             }, 100);
         }
         
         window.executeInjectedScript = function (scriptContent) {
             const existingPlaceholder = document.getElementById("plugin-script-placeholder");
             if (!existingPlaceholder) {
                 return;
             }
             const scriptTag = document.createElement("script");
             scriptTag.type = "text/javascript";
             scriptTag.id = "plugin-script-placeholder";
             scriptTag.nonce = "plugin-script-inject";
             scriptTag.textContent = scriptContent;
             existingPlaceholder.replaceWith(scriptTag);
         }
         
         window.addGlobalEventListener = function(eventType, callback) {
             window.addEventListener(eventType, callback);
         }
         
         window.removeGlobalEventListener = function(eventType, callback) {
             window.removeEventListener(eventType, callback);
         }
         
         window.applyClassToSelector = function(className, selector) {
             const el = document.querySelector(selector);
             if (el && !el.classList.contains(className)) {
                 el.classList.add(className);
             }
         }
    

…opment

Co-authored-by: anikitenko <12380460+anikitenko@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 10, 2025

@copilot and now there are 2 bugs:

  1. handleRefine is not used anymore
  2. AI coding assistant during planning is not fully utilizing SDK. React is not allowed. Please checkout plugin_host.html and see...

Fixed both bugs in commit c040749:

  1. handleRefine bug: Changed "Refine Response" button to use onClick={handleRefine} instead of inline handler
  2. React/SDK bug: Updated plan code prompt to remove all React/JSX references and correctly describe FDO plugin development:
    • Added explicit "NO React/JSX" constraint
    • Removed .tsx file examples
    • Documented plugin host functions (createBackendReq, executeInjectedScript, etc.)
    • Added SDK DOM class usage examples (DOMTable, DOMButton, etc.)
    • Clarified that render() returns HTML strings via SDK classes
    • Emphasized goober CSS-in-JS for styling

@anikitenko anikitenko merged commit 9ebf3f2 into main Nov 11, 2025
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