Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maybe generation of //... so the replace algorithm can match any number of lines here #80

Open
cielecki opened this issue Jun 11, 2023 · 3 comments
Labels
wishlist Something that is not a priority and is optional

Comments

@cielecki
Copy link
Contributor

No description provided.

@cielecki
Copy link
Contributor Author

Situation in the wild:

REPLACE
function MinionTaskComponent(...) {
...
}
WITH
function MinionTaskComponent(...) {
...

function adjustTextAreaHeight(target: HTMLTextAreaElement) {
target.style.height = "auto";
target.style.height = target.scrollHeight + "px";
}

...
}
END_REPLACE

@cielecki
Copy link
Contributor Author

It matched with

    minionTaskId: minionTask.id,
  });
}

@cielecki
Copy link
Contributor Author

In this file

import { ChevronDownIcon, ChevronUpIcon, XMarkIcon } from "@heroicons/react/24/solid";
import * as React from "react";
import { forwardRef } from "react";
import { blendWithForeground, getBaseColor } from "../utils/blendColors";
import { ALL_MINION_ICONS_FILL } from "./MinionIconsFill";
import { FINISHED_STAGE_NAME, MinionTaskUIInfo } from "./MinionTaskUIInfo";
import { ProgressBar } from "./ProgressBar";
import { postMessageToVsCode } from "./SideBarWebViewInnerComponent";

function adjustTextAreaHeight(target: HTMLTextAreaElement) {
target.style.height = "auto";
target.style.height = target.scrollHeight + "px";
}

// Constants
const MAX_PREVIEW_LENGTH = 100;

function getUserQueryPreview(userQuery: string) {
const lines = userQuery.split("\n");
let preview = lines[0].substring(0, MAX_PREVIEW_LENGTH);

// Add ellipsis if the query exceeds the preview length or has multiple lines
if (lines.length > 1 || lines[0].length > MAX_PREVIEW_LENGTH) {
preview += "…";
}

return preview;
}

export const MinionTaskComponent = forwardRef(
({ minionTask, ...props }: { minionTask: MinionTaskUIInfo } & React.HTMLAttributes, ref: React.ForwardedRef) => {
const { className, ...propsWithoutClassName } = props;

const userQueryPreview = getUserQueryPreview(minionTask.userQuery);
const [isExpanded, setIsExpanded] = React.useState(false);

// State variables for managing the input field state
const [isInputOpen, setIsInputOpen] = React.useState(false);
const [updatedPrompt, setUpdatedPrompt] = React.useState(minionTask.userQuery);

React.useEffect(() => {
  if (minionTask.classification === "AnswerQuestion") {
    setIsExpanded(true);
  }
}, [minionTask.classification]);

React.useEffect(() => {
  setUpdatedPrompt(minionTask.userQuery);
}, [minionTask.userQuery]);

React.useLayoutEffect(() => {
  if (isInputOpen) {
    const textAreaElement = document.querySelector<HTMLTextAreaElement>(".execution textarea");
    if (textAreaElement) {
      adjustTextAreaHeight(textAreaElement);
    }
  }
}, [isInputOpen]);

function handleKeyDown(event: React.KeyboardEvent<HTMLTextAreaElement>) {
  // Only call handleRun() when Enter key is pressed without Shift
  if (event.key === "Enter" && !event.shiftKey) {
    event.preventDefault();
    setIsInputOpen(false);

    handleRun();
  }
}

function handleRun() {
  if (updatedPrompt !== minionTask.userQuery) {
    // Stop the current execution
    postMessageToVsCode({
      type: "stopExecution",
      minionTaskId: minionTask.id,
    });

    // Retry the execution with the updated prompt
    postMessageToVsCode({
      type: "reRunExecution",
      minionTaskId: minionTask.id,
      newUserQuery: updatedPrompt, // Pass the updated prompt value
    });
  }
}

function handleClickShowDiff() {
  postMessageToVsCode({
    type: "showDiff",
    minionTaskId: minionTask.id,
  });
}

let RobotIcon = ALL_MINION_ICONS_FILL[minionTask.minionIndex];

const forceButton = (
  <button
    title="Force execution anyway"
    onClick={() => {
      postMessageToVsCode({
        type: "forceExecution",
        minionTaskId: minionTask.id,
      });
    }}
    style={{
      borderColor: "var(--vscode-button-separator)",
    }}
    className="cursor-pointer border rounded px-2 ml-2"
  >
    Force
  </button>
);

const stopButton = (
  <button
    title="Stop Execution"
    onClick={() => {
      postMessageToVsCode({
        type: "stopExecution",
        minionTaskId: minionTask.id,
      });
    }}
    style={{
      borderColor: "var(--vscode-button-separator)",
    }}
    className="cursor-pointer border rounded px-2 ml-2"
  >
    Stop
  </button>
);

const chevronButton = isExpanded ? <ChevronDownIcon className="h-6 w-6 min-w-min ml-2" /> : <ChevronUpIcon className="h-6 w-6 min-w-min ml-2" />;

const closeButton = (
  <XMarkIcon
    title="Close Execution"
    onClick={() => {
      postMessageToVsCode({
        type: "closeExecution",
        minionTaskId: minionTask.id,
      });
    }}
    className="h-6 w-6 min-w-min cursor-pointer ml-2"
  />
);

const diffButton = (
  <button
    title="Show Diff"
    style={{
      borderColor: "var(--vscode-button-separator)",
    }}
    className="cursor-pointer border rounded px-2 ml-2"
    onClick={handleClickShowDiff}
  >
    Diff
  </button>
);

const retryButton = (
  <button
    title="Retry Execution"
    onClick={() => {
      postMessageToVsCode({
        type: "reRunExecution",
        minionTaskId: minionTask.id,
      });
    }}
    style={{
      borderColor: "var(--vscode-button-separator)",
    }}
    className="cursor-pointer border rounded px-2 ml-2"
  >
    Retry
  </button>
);

const openLogFileButton = (
  <button
    title="Open Log"
    className="cursor-pointer mb-2 border rounded px-2"
    onClick={() => {
      postMessageToVsCode({
        type: "openLog",
        minionTaskId: minionTask.id,
      });
    }}
    style={{
      borderColor: "var(--vscode-button-separator)",
    }}
  >
    Open Log file
  </button>
);

return (
  <div
    ref={ref}
    style={{
      backgroundColor: "var(--vscode-editor-background)",
      color: "var(--vscode-editor-foreground)",
      borderColor: "var(--vscode-focusBorder)",
    }}
    key={minionTask.id}
    className={`execution mb-4 overflow-hidden rounded flex flex-col ${className}`}
    {...propsWithoutClassName}
  >
    <div className="pl-3 pr-3 pt-3 pb-3">
      <div
        className="flex justify-between cursor-pointer"
        title={!isExpanded ? "Click for more info" : "Click to hide"}
        onClick={() => {
          setIsExpanded(!isExpanded);
        }}
      >
        <div
          className={`w-6 h-6 mr-2 transition-color ${!minionTask.stopped && !minionTask.waiting ? "busy-robot" : ""}`}
          style={{
            color: blendWithForeground(getBaseColor(minionTask)),
          }}
        >
          <RobotIcon className={`w-6 h-6 inline-flex ${!minionTask.stopped && !minionTask.waiting ? "busy-robot-extra" : ""}`} />
        </div>
        <div className="text-base font-semibold flex-grow flex-shrink truncate">
          <span className="truncate">{minionTask.shortName}</span>
        </div>
        {minionTask.waiting && !minionTask.stopped && forceButton}

        {!minionTask.stopped ? stopButton : <> </>}
        {chevronButton}
        {closeButton}
      </div>

      {isExpanded && (
        <>
          <div className="grid grid-cols-[auto,1fr] gap-x-4 mt-4 mb-2">
            <div className="mb-2">Log:</div>
            <span className="mb-2">{openLogFileButton}</span>

            <div className="mb-2">File:</div>

            <span className="mb-2">
              <span
                title="Open Document"
                className="cursor-pointer"
                onClick={() => {
                  postMessageToVsCode({
                    type: "openDocument",
                    minionTaskId: minionTask.id,
                  });
                }}
              >
                {minionTask.documentName}
              </span>

              {minionTask.executionStage === FINISHED_STAGE_NAME && diffButton}
            </span>

            <div className="mb-2">Status:</div>

            <span className="mb-2">
              <span
                title="Open Log"
                className="cursor-pointer mb-2"
                onClick={() => {
                  postMessageToVsCode({
                    type: "openLog",
                    minionTaskId: minionTask.id,
                  });
                }}
              >
                {minionTask.executionStage}
              </span>
            </span>

            <div className="mb-2">Task:</div>

            <div className="mb-2 overflow-x-auto">
              {isInputOpen ? (
                <textarea
                  style={{
                    backgroundColor: "inherit",
                    color: "inherit",
                    border: "none",
                    outline: "none",
                    width: "100%", // Make it span the entire line
                    resize: "none", // Disable the resizing of the textarea
                    margin: 0,
                    padding: 0,
                  }}
                  value={updatedPrompt}
                  onChange={(event) => setUpdatedPrompt(event.target.value)}
                  onKeyDown={handleKeyDown}
                  onBlur={() => {
                    setIsInputOpen(false);
                    handleRun();
                  }}
                  autoFocus
                  onInput={(event: React.FormEvent<HTMLTextAreaElement>) => {
                    adjustTextAreaHeight(event.target as HTMLTextAreaElement);
                  }}
                />
              ) : (
                // Wrap the userQueryPreview inside a div with the same line-height as the textarea
                // Apply required padding and margin in this div
                <div>
                  <span title="Edit task" onClick={() => setIsInputOpen(true)}>
                    {userQueryPreview}{" "}
                  </span>
                  {minionTask.stopped && retryButton}
                </div>
              )}
            </div>

            {minionTask.selectedText && <div>Selection:</div>}
            {minionTask.selectedText && (
              <div
                className="text-xs overflow-auto"
                style={{
                  whiteSpace: "pre",
                }}
              >
                <pre>{minionTask.selectedText}</pre>
              </div>
            )}
          </div>
          <div>
            {minionTask.classification === "AnswerQuestion" ? <pre style={{ whiteSpace: "pre-wrap" }}>{minionTask.modificationDescription}</pre> : <></>}
          </div>
        </>
      )}
    </div>
    <div className="flex items-center">
      <div className="w-full" title="Task Progress">
        <ProgressBar execution={minionTask} />
      </div>
    </div>
  </div>
);

}
);

@cielecki cielecki added the wishlist Something that is not a priority and is optional label Jun 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wishlist Something that is not a priority and is optional
Projects
None yet
Development

No branches or pull requests

1 participant