summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--continuedev/src/continuedev/core/abstract_sdk.py4
-rw-r--r--continuedev/src/continuedev/core/autopilot.py9
-rw-r--r--continuedev/src/continuedev/core/config.py34
-rw-r--r--continuedev/src/continuedev/core/main.py6
-rw-r--r--continuedev/src/continuedev/core/policy.py73
-rw-r--r--continuedev/src/continuedev/core/sdk.py6
-rw-r--r--continuedev/src/continuedev/libs/util/step_name_to_steps.py27
-rw-r--r--continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py2
-rw-r--r--continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py3
-rw-r--r--continuedev/src/continuedev/server/gui.py7
-rw-r--r--continuedev/src/continuedev/server/gui_protocol.py6
-rw-r--r--continuedev/src/continuedev/steps/main.py4
-rw-r--r--continuedev/src/continuedev/steps/steps_on_startup.py24
-rw-r--r--extension/package-lock.json64
-rw-r--r--extension/package.json3
-rw-r--r--extension/react-app/package-lock.json60
-rw-r--r--extension/react-app/package.json1
-rw-r--r--extension/react-app/src/components/ComboBox.tsx146
-rw-r--r--extension/react-app/src/hooks/ContinueGUIClientProtocol.ts4
-rw-r--r--extension/react-app/src/hooks/useContinueGUIProtocol.ts10
-rw-r--r--extension/react-app/src/tabs/gui.tsx296
-rw-r--r--extension/scripts/continuedev-0.1.1-py3-none-any.whlbin74791 -> 78916 bytes
22 files changed, 547 insertions, 242 deletions
diff --git a/continuedev/src/continuedev/core/abstract_sdk.py b/continuedev/src/continuedev/core/abstract_sdk.py
index 3b85708d..0658f1b8 100644
--- a/continuedev/src/continuedev/core/abstract_sdk.py
+++ b/continuedev/src/continuedev/core/abstract_sdk.py
@@ -76,8 +76,8 @@ class AbstractContinueSDK(ABC):
async def get_user_secret(self, env_var: str, prompt: str) -> str:
pass
- @abstractmethod
- async def get_config(self) -> ContinueConfig:
+ @abstractproperty
+ def config(self) -> ContinueConfig:
pass
@abstractmethod
diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py
index 1642003c..0874bbc5 100644
--- a/continuedev/src/continuedev/core/autopilot.py
+++ b/continuedev/src/continuedev/core/autopilot.py
@@ -40,6 +40,9 @@ class Autopilot(ContinueBaseModel):
def get_full_state(self) -> FullState:
return FullState(history=self.history, active=self._active, user_input_queue=self._main_user_input_queue)
+ async def get_available_slash_commands(self) -> List[Dict]:
+ return list(map(lambda x: {"name": x.name, "description": x.description}, self.continue_sdk.config.slash_commands)) or []
+
async def clear_history(self):
self.history = History.from_empty()
self._main_user_input_queue = []
@@ -202,7 +205,7 @@ class Autopilot(ContinueBaseModel):
await self._run_singular_step(next_step, is_future_step)
- if next_step := self.policy.next(self.history):
+ if next_step := self.policy.next(self.continue_sdk.config, self.history):
is_future_step = False
elif next_step := self.history.take_next_step():
is_future_step = True
@@ -215,11 +218,11 @@ class Autopilot(ContinueBaseModel):
await self.update_subscribers()
async def run_from_observation(self, observation: Observation):
- next_step = self.policy.next(self.history)
+ next_step = self.policy.next(self.continue_sdk.config, self.history)
await self.run_from_step(next_step)
async def run_policy(self):
- first_step = self.policy.next(self.history)
+ first_step = self.policy.next(self.continue_sdk.config, self.history)
await self.run_from_step(first_step)
async def _request_halt(self):
diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py
index 8ed41a82..cf723984 100644
--- a/continuedev/src/continuedev/core/config.py
+++ b/continuedev/src/continuedev/core/config.py
@@ -1,9 +1,18 @@
import json
import os
-from pydantic import BaseModel
+from pydantic import BaseModel, validator
from typing import List, Optional, Dict
import yaml
+from .main import Step
+
+
+class SlashCommand(BaseModel):
+ name: str
+ description: str
+ step_name: str
+ params: Optional[Dict] = {}
+
class ContinueConfig(BaseModel):
"""
@@ -12,6 +21,29 @@ class ContinueConfig(BaseModel):
steps_on_startup: Optional[Dict[str, Dict]] = {}
server_url: Optional[str] = None
allow_anonymous_telemetry: Optional[bool] = True
+ slash_commands: Optional[List[SlashCommand]] = [
+ # SlashCommand(
+ # name="pytest",
+ # description="Write pytest unit tests for the current file",
+ # step_name="WritePytestsRecipe",
+ # params=??)
+
+ SlashCommand(
+ name="dlt",
+ description="Create a dlt pipeline",
+ step_name="CreatePipelineRecipe",
+ ),
+ SlashCommand(
+ name="ddtobq",
+ description="Create a dlt pipeline to load data from a data source into BigQuery",
+ step_name="DDtoBQRecipe",
+ ),
+ SlashCommand(
+ name="deployairflow",
+ description="Deploy a dlt pipeline to Airflow",
+ step_name="DeployPipelineAirflowRecipe",
+ ),
+ ]
def load_config(config_file: str) -> ContinueConfig:
diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py
index 81aaaf2e..f6b26d69 100644
--- a/continuedev/src/continuedev/core/main.py
+++ b/continuedev/src/continuedev/core/main.py
@@ -118,11 +118,15 @@ class Models:
pass
+class ContinueConfig:
+ pass
+
+
class Policy(ContinueBaseModel):
"""A rule that determines which step to take next"""
# Note that history is mutable, kinda sus
- def next(self, history: History = History.from_empty()) -> "Step":
+ def next(self, config: ContinueConfig, history: History = History.from_empty()) -> "Step":
raise NotImplementedError
diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py
index 00b5427c..37a10e36 100644
--- a/continuedev/src/continuedev/core/policy.py
+++ b/continuedev/src/continuedev/core/policy.py
@@ -1,5 +1,6 @@
from typing import List, Tuple, Type
+from .config import ContinueConfig
from ..steps.chroma import AnswerQuestionChroma, EditFileChroma, CreateCodebaseIndexChroma
from ..steps.steps_on_startup import StepsOnStartupStep
from ..recipes.CreatePipelineRecipe.main import CreatePipelineRecipe
@@ -15,12 +16,13 @@ from ..steps.react import NLDecisionStep
from ..steps.chat import SimpleChatStep
from ..recipes.DDtoBQRecipe.main import DDtoBQRecipe
from ..steps.core.core import MessageStep
+from ..libs.util.step_name_to_steps import get_step_from_name
class DemoPolicy(Policy):
ran_code_last: bool = False
- def next(self, history: History) -> Step:
+ def next(self, config: ContinueConfig, history: History) -> Step:
# At the very start, run initial Steps spcecified in the config
if history.get_current() is None:
return (
@@ -33,20 +35,18 @@ class DemoPolicy(Policy):
if observation is not None and isinstance(observation, UserInputObservation):
# This could be defined with ObservationTypePolicy. Ergonomics not right though.
user_input = observation.user_input
+
+ if user_input.startswith("/"):
+ command_name = user_input.split(" ")[0]
+ after_command = " ".join(user_input.split(" ")[1:])
+ for slash_command in config.slash_commands:
+ if slash_command.name == command_name[1:]:
+ return get_step_from_name(slash_command.step_name, slash_command.params)
+
if "/pytest" in user_input.lower():
return WritePytestsRecipe(instructions=user_input)
- elif "/dlt" in user_input.lower() or " dlt" in user_input.lower():
- return CreatePipelineRecipe()
if "/pytest" in observation.user_input.lower():
return WritePytestsRecipe(instructions=observation.user_input)
- elif "/dlt" in observation.user_input.lower():
- return CreatePipelineRecipe()
- elif "/ddtobq" in observation.user_input.lower():
- return DDtoBQRecipe()
- elif "/airflow" in observation.user_input.lower():
- return DeployPipelineAirflowRecipe()
- elif "/transform" in observation.user_input.lower():
- return AddTransformRecipe()
elif "/comment" in observation.user_input.lower():
return CommentCodeStep()
elif "/ask" in user_input:
@@ -72,54 +72,3 @@ class DemoPolicy(Policy):
return SolveTracebackStep(traceback=observation.traceback)
else:
return None
-
-
-class ObservationTypePolicy(Policy):
- def __init__(self, base_policy: Policy, observation_type: Type[Observation], step_type: Type[Step]):
- self.observation_type = observation_type
- self.step_type = step_type
- self.base_policy = base_policy
-
- def next(self, history: History) -> Step:
- observation = history.last_observation()
- if observation is not None and isinstance(observation, self.observation_type):
- return self.step_type(observation)
- return self.base_policy.next(history)
-
-
-class PolicyWrappedWithValidators(Policy):
- """Default is to stop, unless the validator tells what to do next"""
- index: int
- stage: int
-
- def __init__(self, base_policy: Policy, pairs: List[Tuple[Validator, Type[Step]]]):
- # Want to pass Type[Validator], or just the Validator? Question of where params are coming from.
- self.pairs = pairs
- self.index = len(pairs)
- self.validating = 0
- self.base_policy = base_policy
-
- def next(self, history: History) -> Step:
- if self.index == len(self.pairs):
- self.index = 0
- return self.base_policy.next(history)
-
- if self.stage == 0:
- # Running the validator at the current index for the first time
- validator, step = self.pairs[self.index]
- self.stage = 1
- return validator
- elif self.stage == 1:
- # Previously ran the validator at the current index, now receiving its ValidatorObservation
- observation = history.last_observation()
- if observation.passed:
- self.stage = 0
- self.index += 1
- if self.index == len(self.pairs):
- self.index = 0
- return self.base_policy.next(history)
- else:
- return self.pairs[self.index][0]
- else:
- _, step_type = self.pairs[self.index]
- return step_type(observation)
diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py
index 2849b0c8..1f4cdfb2 100644
--- a/continuedev/src/continuedev/core/sdk.py
+++ b/continuedev/src/continuedev/core/sdk.py
@@ -1,4 +1,3 @@
-from abc import ABC, abstractmethod
import asyncio
from functools import cached_property
from typing import Coroutine, Union
@@ -119,8 +118,9 @@ class ContinueSDK(AbstractContinueSDK):
async def get_user_secret(self, env_var: str, prompt: str) -> str:
return await self.ide.getUserSecret(env_var)
- async def get_config(self) -> ContinueConfig:
- dir = await self.ide.getWorkspaceDirectory()
+ @property
+ def config(self) -> ContinueConfig:
+ dir = self.ide.workspace_directory
yaml_path = os.path.join(dir, '.continue', 'config.yaml')
json_path = os.path.join(dir, '.continue', 'config.json')
if os.path.exists(yaml_path):
diff --git a/continuedev/src/continuedev/libs/util/step_name_to_steps.py b/continuedev/src/continuedev/libs/util/step_name_to_steps.py
new file mode 100644
index 00000000..4023b73b
--- /dev/null
+++ b/continuedev/src/continuedev/libs/util/step_name_to_steps.py
@@ -0,0 +1,27 @@
+from typing import Dict
+
+from ...core.main import Step
+from ...steps.core.core import UserInputStep
+from ...recipes.CreatePipelineRecipe.main import CreatePipelineRecipe
+from ...recipes.DDtoBQRecipe.main import DDtoBQRecipe
+from ...recipes.DeployPipelineAirflowRecipe.main import DeployPipelineAirflowRecipe
+from ...recipes.DDtoBQRecipe.main import DDtoBQRecipe
+from ...recipes.AddTransformRecipe.main import AddTransformRecipe
+
+step_name_to_step_class = {
+ "UserInputStep": UserInputStep,
+ "CreatePipelineRecipe": CreatePipelineRecipe,
+ "DDtoBQRecipe": DDtoBQRecipe,
+ "DeployPipelineAirflowRecipe": DeployPipelineAirflowRecipe,
+ "AddTransformRecipe": AddTransformRecipe,
+ "DDtoBQRecipe": DDtoBQRecipe
+}
+
+
+def get_step_from_name(step_name: str, params: Dict) -> Step:
+ try:
+ return step_name_to_step_class[step_name](**params)
+ except:
+ print(
+ f"Incorrect parameters for step {step_name}. Parameters provided were: {params}")
+ raise
diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py
index 818168ba..92bddc98 100644
--- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py
+++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py
@@ -1,7 +1,7 @@
from textwrap import dedent
-from ...core.main import Step
from ...core.sdk import ContinueSDK
+from ...core.main import Step
from ...steps.core.core import WaitForUserInputStep
from ...steps.core.core import MessageStep
from .steps import SetupPipelineStep, ValidatePipelineStep, RunQueryStep
diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
index e59cc51c..ea4607da 100644
--- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
+++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
@@ -6,11 +6,10 @@ import time
from ...models.main import Range
from ...models.filesystem import RangeInFile
from ...steps.core.core import MessageStep
-from ...core.sdk import Models
from ...core.observation import DictObservation, InternalErrorObservation
from ...models.filesystem_edit import AddFile, FileEdit
from ...core.main import Step
-from ...core.sdk import ContinueSDK
+from ...core.sdk import ContinueSDK, Models
AI_ASSISTED_STRING = "(✨ AI-Assisted ✨)"
diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py
index e8b52004..cf046734 100644
--- a/continuedev/src/continuedev/server/gui.py
+++ b/continuedev/src/continuedev/server/gui.py
@@ -90,6 +90,12 @@ class GUIProtocolServer(AbstractGUIProtocolServer):
"state": state
})
+ async def send_available_slash_commands(self):
+ commands = await self.session.autopilot.get_available_slash_commands()
+ await self._send_json("available_slash_commands", {
+ "commands": commands
+ })
+
def on_main_input(self, input: str):
# Do something with user input
asyncio.create_task(self.session.autopilot.accept_user_input(input))
@@ -127,6 +133,7 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we
protocol.websocket = websocket
# Update any history that may have happened before connection
+ await protocol.send_available_slash_commands()
await protocol.send_state_update()
while AppStatus.should_exit is False:
diff --git a/continuedev/src/continuedev/server/gui_protocol.py b/continuedev/src/continuedev/server/gui_protocol.py
index 889c6761..d9506c6f 100644
--- a/continuedev/src/continuedev/server/gui_protocol.py
+++ b/continuedev/src/continuedev/server/gui_protocol.py
@@ -1,4 +1,4 @@
-from typing import Any
+from typing import Any, Dict, List
from abc import ABC, abstractmethod
@@ -28,6 +28,10 @@ class AbstractGUIProtocolServer(ABC):
"""Send a state update to the client"""
@abstractmethod
+ async def send_available_slash_commands(self, commands: List[Dict]):
+ """Send a list of available slash commands to the client"""
+
+ @abstractmethod
def on_retry_at_index(self, index: int):
"""Called when the user requests a retry at a previous index"""
diff --git a/continuedev/src/continuedev/steps/main.py b/continuedev/src/continuedev/steps/main.py
index 9634c726..36e4f519 100644
--- a/continuedev/src/continuedev/steps/main.py
+++ b/continuedev/src/continuedev/steps/main.py
@@ -63,10 +63,10 @@ class RunPolicyUntilDoneStep(Step):
policy: "Policy"
async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]:
- next_step = self.policy.next(sdk.history)
+ next_step = self.policy.next(sdk.config, sdk.history)
while next_step is not None:
observation = await sdk.run_step(next_step)
- next_step = self.policy.next(sdk.history)
+ next_step = self.policy.next(sdk.config, sdk.history)
return observation
diff --git a/continuedev/src/continuedev/steps/steps_on_startup.py b/continuedev/src/continuedev/steps/steps_on_startup.py
index eae8b558..365cbe1a 100644
--- a/continuedev/src/continuedev/steps/steps_on_startup.py
+++ b/continuedev/src/continuedev/steps/steps_on_startup.py
@@ -1,19 +1,12 @@
-from ..core.main import ContinueSDK, Models, Step
+from ..core.main import Step
+from ..core.sdk import Models, ContinueSDK
from .main import UserInputStep
from ..recipes.CreatePipelineRecipe.main import CreatePipelineRecipe
from ..recipes.DDtoBQRecipe.main import DDtoBQRecipe
from ..recipes.DeployPipelineAirflowRecipe.main import DeployPipelineAirflowRecipe
from ..recipes.DDtoBQRecipe.main import DDtoBQRecipe
from ..recipes.AddTransformRecipe.main import AddTransformRecipe
-
-step_name_to_step_class = {
- "UserInputStep": UserInputStep,
- "CreatePipelineRecipe": CreatePipelineRecipe,
- "DDtoBQRecipe": DDtoBQRecipe,
- "DeployPipelineAirflowRecipe": DeployPipelineAirflowRecipe,
- "AddTransformRecipe": AddTransformRecipe,
- "DDtoBQRecipe": DDtoBQRecipe
-}
+from ..libs.util.step_name_to_steps import get_step_from_name
class StepsOnStartupStep(Step):
@@ -23,13 +16,8 @@ class StepsOnStartupStep(Step):
return "Running steps on startup"
async def run(self, sdk: ContinueSDK):
- steps_descriptions = (await sdk.get_config()).steps_on_startup
+ steps_on_startup = sdk.config.steps_on_startup
- for step_name, step_params in steps_descriptions.items():
- try:
- step = step_name_to_step_class[step_name](**step_params)
- except:
- print(
- f"Incorrect parameters for step {step_name}. Parameters provided were: {step_params}")
- continue
+ for step_name, step_params in steps_on_startup.items():
+ step = get_step_from_name(step_name, step_params)
await sdk.run_step(step)
diff --git a/extension/package-lock.json b/extension/package-lock.json
index aebd0803..b02c4544 100644
--- a/extension/package-lock.json
+++ b/extension/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "continue",
- "version": "0.0.34",
+ "version": "0.0.35",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "continue",
- "version": "0.0.34",
+ "version": "0.0.35",
"license": "Apache-2.0",
"dependencies": {
"@electron/rebuild": "^3.2.10",
@@ -15,6 +15,7 @@
"@styled-icons/heroicons-outline": "^10.47.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"axios": "^1.2.5",
+ "downshift": "^7.6.0",
"highlight.js": "^11.7.0",
"posthog-js": "^1.63.3",
"react-markdown": "^8.0.7",
@@ -2845,6 +2846,11 @@
"integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==",
"dev": true
},
+ "node_modules/compute-scroll-into-view": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz",
+ "integrity": "sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g=="
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -3261,6 +3267,31 @@
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
+ "node_modules/downshift": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/downshift/-/downshift-7.6.0.tgz",
+ "integrity": "sha512-VSoTVynTAsabou/hbZ6HJHUVhtBiVOjQoBsCPcQq5eAROIGP+9XKMp9asAKQ3cEcUP4oe0fFdD2pziUjhFY33Q==",
+ "dependencies": {
+ "@babel/runtime": "^7.14.8",
+ "compute-scroll-into-view": "^2.0.4",
+ "prop-types": "^15.7.2",
+ "react-is": "^17.0.2",
+ "tslib": "^2.3.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.12.0"
+ }
+ },
+ "node_modules/downshift/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+ },
+ "node_modules/downshift/node_modules/tslib": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
+ "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
+ },
"node_modules/dset": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz",
@@ -10615,6 +10646,11 @@
"integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==",
"dev": true
},
+ "compute-scroll-into-view": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz",
+ "integrity": "sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g=="
+ },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -10911,6 +10947,30 @@
"domhandler": "^5.0.1"
}
},
+ "downshift": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/downshift/-/downshift-7.6.0.tgz",
+ "integrity": "sha512-VSoTVynTAsabou/hbZ6HJHUVhtBiVOjQoBsCPcQq5eAROIGP+9XKMp9asAKQ3cEcUP4oe0fFdD2pziUjhFY33Q==",
+ "requires": {
+ "@babel/runtime": "^7.14.8",
+ "compute-scroll-into-view": "^2.0.4",
+ "prop-types": "^15.7.2",
+ "react-is": "^17.0.2",
+ "tslib": "^2.3.0"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+ },
+ "tslib": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
+ "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
+ }
+ }
+ },
"dset": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz",
diff --git a/extension/package.json b/extension/package.json
index 8ee8cb4c..8ccb4b13 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -14,7 +14,7 @@
"displayName": "Continue",
"pricing": "Free",
"description": "Refine code 10x faster",
- "version": "0.0.34",
+ "version": "0.0.35",
"publisher": "Continue",
"engines": {
"vscode": "^1.74.0"
@@ -134,6 +134,7 @@
"@styled-icons/heroicons-outline": "^10.47.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"axios": "^1.2.5",
+ "downshift": "^7.6.0",
"highlight.js": "^11.7.0",
"posthog-js": "^1.63.3",
"react-markdown": "^8.0.7",
diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json
index dbcbc5cc..64440da6 100644
--- a/extension/react-app/package-lock.json
+++ b/extension/react-app/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"@styled-icons/heroicons-outline": "^10.47.0",
"@types/vscode-webview": "^1.57.1",
+ "downshift": "^7.6.0",
"posthog-js": "^1.58.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -1288,6 +1289,11 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/compute-scroll-into-view": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz",
+ "integrity": "sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g=="
+ },
"node_modules/css-color-keywords": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
@@ -1405,6 +1411,26 @@
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"dev": true
},
+ "node_modules/downshift": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/downshift/-/downshift-7.6.0.tgz",
+ "integrity": "sha512-VSoTVynTAsabou/hbZ6HJHUVhtBiVOjQoBsCPcQq5eAROIGP+9XKMp9asAKQ3cEcUP4oe0fFdD2pziUjhFY33Q==",
+ "dependencies": {
+ "@babel/runtime": "^7.14.8",
+ "compute-scroll-into-view": "^2.0.4",
+ "prop-types": "^15.7.2",
+ "react-is": "^17.0.2",
+ "tslib": "^2.3.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.12.0"
+ }
+ },
+ "node_modules/downshift/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+ },
"node_modules/electron-to-chromium": {
"version": "1.4.311",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.311.tgz",
@@ -3003,6 +3029,11 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/tslib": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
+ "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
+ },
"node_modules/typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
@@ -4050,6 +4081,11 @@
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
"integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="
},
+ "compute-scroll-into-view": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz",
+ "integrity": "sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g=="
+ },
"css-color-keywords": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
@@ -4131,6 +4167,25 @@
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"dev": true
},
+ "downshift": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/downshift/-/downshift-7.6.0.tgz",
+ "integrity": "sha512-VSoTVynTAsabou/hbZ6HJHUVhtBiVOjQoBsCPcQq5eAROIGP+9XKMp9asAKQ3cEcUP4oe0fFdD2pziUjhFY33Q==",
+ "requires": {
+ "@babel/runtime": "^7.14.8",
+ "compute-scroll-into-view": "^2.0.4",
+ "prop-types": "^15.7.2",
+ "react-is": "^17.0.2",
+ "tslib": "^2.3.0"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+ }
+ }
+ },
"electron-to-chromium": {
"version": "1.4.311",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.311.tgz",
@@ -5138,6 +5193,11 @@
"resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz",
"integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g=="
},
+ "tslib": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
+ "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
+ },
"typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
diff --git a/extension/react-app/package.json b/extension/react-app/package.json
index 7d1211de..a53fbec8 100644
--- a/extension/react-app/package.json
+++ b/extension/react-app/package.json
@@ -11,6 +11,7 @@
"dependencies": {
"@styled-icons/heroicons-outline": "^10.47.0",
"@types/vscode-webview": "^1.57.1",
+ "downshift": "^7.6.0",
"posthog-js": "^1.58.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx
new file mode 100644
index 00000000..1b7c60e6
--- /dev/null
+++ b/extension/react-app/src/components/ComboBox.tsx
@@ -0,0 +1,146 @@
+import React, { useCallback } from "react";
+import { useCombobox } from "downshift";
+import styled from "styled-components";
+import {
+ buttonColor,
+ defaultBorderRadius,
+ secondaryDark,
+ vscBackground,
+} from ".";
+
+const mainInputFontSize = 16;
+const MainTextInput = styled.input`
+ padding: 8px;
+ font-size: ${mainInputFontSize}px;
+ border-radius: ${defaultBorderRadius};
+ border: 1px solid #ccc;
+ margin: 8px auto;
+ width: 100%;
+ background-color: ${vscBackground};
+ color: white;
+ outline: 1px solid orange;
+`;
+
+const UlMaxHeight = 200;
+const Ul = styled.ul<{
+ hidden: boolean;
+ showAbove: boolean;
+ ulHeightPixels: number;
+}>`
+ ${(props) =>
+ props.showAbove
+ ? `transform: translateY(-${props.ulHeightPixels + 8}px);`
+ : `transform: translateY(${2 * mainInputFontSize}px);`}
+ position: absolute;
+ background: ${vscBackground};
+ background-color: ${secondaryDark};
+ color: white;
+ font-family: "Fira Code", monospace;
+ max-height: ${UlMaxHeight}px;
+ overflow: scroll;
+ padding: 0;
+ ${({ hidden }) => hidden && "display: none;"}
+ border-radius: ${defaultBorderRadius};
+ overflow: hidden;
+ border: 0.5px solid gray;
+`;
+
+const Li = styled.li<{
+ highlighted: boolean;
+ selected: boolean;
+ isLastItem: boolean;
+}>`
+ ${({ highlighted }) => highlighted && "background: #aa0000;"}
+ ${({ selected }) => selected && "font-weight: bold;"}
+ padding: 0.5rem 0.75rem;
+ display: flex;
+ flex-direction: column;
+ ${({ isLastItem }) => isLastItem && "border-bottom: 1px solid gray;"}
+ border-top: 1px solid gray;
+ cursor: pointer;
+`;
+
+interface ComboBoxProps {
+ items: { name: string; description: string }[];
+ onInputValueChange: (inputValue: string) => void;
+ disabled?: boolean;
+ onEnter?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
+}
+
+const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
+ const [items, setItems] = React.useState(props.items);
+ const {
+ isOpen,
+ getToggleButtonProps,
+ getLabelProps,
+ getMenuProps,
+ getInputProps,
+ highlightedIndex,
+ getItemProps,
+ selectedItem,
+ setInputValue,
+ } = useCombobox({
+ onInputValueChange({ inputValue }) {
+ if (!inputValue) return;
+ props.onInputValueChange(inputValue);
+ setItems(
+ props.items.filter((item) =>
+ item.name.toLowerCase().startsWith(inputValue.toLowerCase())
+ )
+ );
+ },
+ items,
+ itemToString(item) {
+ return item ? item.name : "";
+ },
+ });
+
+ const divRef = React.useRef<HTMLDivElement>(null);
+ const ulRef = React.useRef<HTMLUListElement>(null);
+ const showAbove = () => {
+ return (divRef.current?.getBoundingClientRect().top || 0) > UlMaxHeight;
+ };
+
+ return (
+ <div className="flex px-2" ref={divRef} hidden={!isOpen}>
+ <MainTextInput
+ disabled={props.disabled}
+ placeholder="Ask anything:"
+ {...getInputProps({
+ onKeyDown: (event) => {
+ if (event.key === "Enter" && (!isOpen || items.length === 0)) {
+ // Prevent Downshift's default 'Enter' behavior.
+ (event.nativeEvent as any).preventDownshiftDefault = true;
+ if (props.onEnter) props.onEnter(event);
+ setInputValue("");
+ }
+ },
+ ref: ref as any,
+ })}
+ />
+ <Ul
+ {...getMenuProps({
+ ref: ulRef,
+ })}
+ showAbove={showAbove()}
+ ulHeightPixels={ulRef.current?.getBoundingClientRect().height || 0}
+ >
+ {isOpen &&
+ items.map((item, index) => (
+ <Li
+ key={`${item.name}${index}`}
+ {...getItemProps({ item, index })}
+ highlighted={highlightedIndex === index}
+ selected={selectedItem === item}
+ >
+ <span>
+ {item.name}: {item.description}
+ </span>
+ </Li>
+ ))}
+ </Ul>
+ </div>
+ );
+});
+
+export default ComboBox;
diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
index 71303c70..824bb086 100644
--- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
+++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
@@ -9,6 +9,10 @@ abstract class AbstractContinueGUIClientProtocol {
abstract onStateUpdate(state: any): void;
+ abstract onAvailableSlashCommands(
+ callback: (commands: { name: string; description: string }[]) => void
+ ): void;
+
abstract sendClear(): void;
abstract retryAtIndex(index: number): void;
diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts
index a8e28fc5..59397742 100644
--- a/extension/react-app/src/hooks/useContinueGUIProtocol.ts
+++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts
@@ -45,6 +45,16 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol {
});
}
+ onAvailableSlashCommands(
+ callback: (commands: { name: string; description: string }[]) => void
+ ) {
+ this.messenger.onMessageType("available_slash_commands", (data: any) => {
+ if (data.commands) {
+ callback(data.commands);
+ }
+ });
+ }
+
sendClear() {
this.messenger.send("clear_history", {});
}
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index cb7a5440..1569c178 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -12,7 +12,8 @@ import { History } from "../../../schema/History";
import { HistoryNode } from "../../../schema/HistoryNode";
import StepContainer from "../components/StepContainer";
import useContinueGUIProtocol from "../hooks/useWebsocket";
-import { Trash } from "@styled-icons/heroicons-outline";
+import { BookOpen, Trash } from "@styled-icons/heroicons-outline";
+import ComboBox from "../components/ComboBox";
let TopGUIDiv = styled.div`
display: grid;
grid-template-columns: 1fr;
@@ -42,128 +43,132 @@ interface GUIProps {
function GUI(props: GUIProps) {
const [waitingForSteps, setWaitingForSteps] = useState(false);
const [userInputQueue, setUserInputQueue] = useState<string[]>([]);
- const [history, setHistory] = useState<History | undefined>({
- timeline: [
- {
- step: {
- name: "Waiting for user input",
- cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py",
- description:
- "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py` and ```\nprint(sum(first, second))\n```\n- Testing\n- Testing 2\n- Testing 3",
- },
- observation: {
- title: "ERROR FOUND",
- error:
- "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
- },
- output: [
- {
- traceback: {
- frames: [
- {
- filepath:
- "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
- lineno: 7,
- function: "<module>",
- code: "print(sum(first, second))",
- },
- ],
- message: "unsupported operand type(s) for +: 'int' and 'str'",
- error_type:
- ' ^^^^^^^^^^^^^^^^^^\n File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum\n return a + b\n ~~^~~\nTypeError',
- full_traceback:
- "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
- },
- },
- null,
- ],
- },
- {
- step: {
- name: "EditCodeStep",
- range_in_files: [
- {
- filepath:
- "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
- range: {
- start: {
- line: 0,
- character: 0,
- },
- end: {
- line: 6,
- character: 25,
- },
- },
- },
- ],
- prompt:
- "I ran into this problem with my Python code:\n\n Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'\n\n Below are the files that might need to be fixed:\n\n {code}\n\n This is what the code should be in order to avoid the problem:\n",
- description:
- "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py` and\n```python\nprint(sum(first, second))\n```\n- Testing\n- Testing 2\n- Testing 3",
- },
- output: [
- null,
- {
- reversible: true,
- actions: [
- {
- reversible: true,
- filesystem: {},
- filepath:
- "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
- range: {
- start: {
- line: 0,
- character: 0,
- },
- end: {
- line: 6,
- character: 25,
- },
- },
- replacement:
- "\nfrom sum import sum\n\nfirst = 1\nsecond = 2\n\nprint(sum(first, second))",
- },
- ],
- },
- ],
- },
- {
- step: {
- name: "SolveTracebackStep",
- traceback: {
- frames: [
- {
- filepath:
- "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
- lineno: 7,
- function: "<module>",
- code: "print(sum(first, second))",
- },
- ],
- message: "unsupported operand type(s) for +: 'int' and 'str'",
- error_type:
- ' ^^^^^^^^^^^^^^^^^^\n File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum\n return a + b\n ~~^~~\nTypeError',
- full_traceback:
- "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
- },
- description: "Running step: SolveTracebackStep",
- },
- output: [null, null],
- },
- {
- step: {
- name: "RunCodeStep",
- cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py",
- description:
- "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`",
- },
- output: [null, null],
- },
- ],
- current_index: 3,
- } as any);
+ const [availableSlashCommands, setAvailableSlashCommands] = useState<
+ { name: string; description: string }[]
+ >([]);
+ const [history, setHistory] = useState<History | undefined>();
+ // {
+ // timeline: [
+ // {
+ // step: {
+ // name: "Waiting for user input",
+ // cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py",
+ // description:
+ // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py` and ```\nprint(sum(first, second))\n```\n- Testing\n- Testing 2\n- Testing 3",
+ // },
+ // observation: {
+ // title: "ERROR FOUND",
+ // error:
+ // "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
+ // },
+ // output: [
+ // {
+ // traceback: {
+ // frames: [
+ // {
+ // filepath:
+ // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
+ // lineno: 7,
+ // function: "<module>",
+ // code: "print(sum(first, second))",
+ // },
+ // ],
+ // message: "unsupported operand type(s) for +: 'int' and 'str'",
+ // error_type:
+ // ' ^^^^^^^^^^^^^^^^^^\n File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum\n return a + b\n ~~^~~\nTypeError',
+ // full_traceback:
+ // "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
+ // },
+ // },
+ // null,
+ // ],
+ // },
+ // {
+ // step: {
+ // name: "EditCodeStep",
+ // range_in_files: [
+ // {
+ // filepath:
+ // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
+ // range: {
+ // start: {
+ // line: 0,
+ // character: 0,
+ // },
+ // end: {
+ // line: 6,
+ // character: 25,
+ // },
+ // },
+ // },
+ // ],
+ // prompt:
+ // "I ran into this problem with my Python code:\n\n Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'\n\n Below are the files that might need to be fixed:\n\n {code}\n\n This is what the code should be in order to avoid the problem:\n",
+ // description:
+ // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py` and\n```python\nprint(sum(first, second))\n```\n- Testing\n- Testing 2\n- Testing 3",
+ // },
+ // output: [
+ // null,
+ // {
+ // reversible: true,
+ // actions: [
+ // {
+ // reversible: true,
+ // filesystem: {},
+ // filepath:
+ // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
+ // range: {
+ // start: {
+ // line: 0,
+ // character: 0,
+ // },
+ // end: {
+ // line: 6,
+ // character: 25,
+ // },
+ // },
+ // replacement:
+ // "\nfrom sum import sum\n\nfirst = 1\nsecond = 2\n\nprint(sum(first, second))",
+ // },
+ // ],
+ // },
+ // ],
+ // },
+ // {
+ // step: {
+ // name: "SolveTracebackStep",
+ // traceback: {
+ // frames: [
+ // {
+ // filepath:
+ // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py",
+ // lineno: 7,
+ // function: "<module>",
+ // code: "print(sum(first, second))",
+ // },
+ // ],
+ // message: "unsupported operand type(s) for +: 'int' and 'str'",
+ // error_type:
+ // ' ^^^^^^^^^^^^^^^^^^\n File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum\n return a + b\n ~~^~~\nTypeError',
+ // full_traceback:
+ // "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
+ // },
+ // description: "Running step: SolveTracebackStep",
+ // },
+ // output: [null, null],
+ // },
+ // {
+ // step: {
+ // name: "RunCodeStep",
+ // cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py",
+ // description:
+ // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`",
+ // },
+ // output: [null, null],
+ // },
+ // ],
+ // current_index: 3,
+ // } as any);
const topGuiDivRef = useRef<HTMLDivElement>(null);
const client = useContinueGUIProtocol();
@@ -197,13 +202,24 @@ function GUI(props: GUIProps) {
scrollToBottom();
});
+ client?.onAvailableSlashCommands((commands) => {
+ console.log("Received available slash commands: ", commands);
+ setAvailableSlashCommands(
+ commands.map((c) => {
+ return {
+ name: "/" + c.name,
+ description: c.description,
+ };
+ })
+ );
+ });
}, [client]);
useEffect(() => {
scrollToBottom();
}, [waitingForSteps]);
- const mainTextInputRef = useRef<HTMLTextAreaElement>(null);
+ const mainTextInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (mainTextInputRef.current) {
@@ -246,8 +262,6 @@ function GUI(props: GUIProps) {
return [...queue, input];
});
}
- mainTextInputRef.current.value = "";
- mainTextInputRef.current.style.height = "";
}
setWaitingForSteps(true);
@@ -270,7 +284,12 @@ function GUI(props: GUIProps) {
}}
>
<TopBar>
- <h3>Continue</h3>
+ <a href="https://continue.dev/docs" className="no-underline">
+ <HeaderButton style={{ padding: "3px" }}>
+ Continue Docs
+ <BookOpen size="1.6em" />
+ </HeaderButton>
+ </a>
<HeaderButton style={{ padding: "3px" }}>
Clear History
<Trash
@@ -323,7 +342,7 @@ function GUI(props: GUIProps) {
})}
</div>
- <MainTextInput
+ <ComboBox
disabled={
history
? history.timeline[history.current_index].step.name ===
@@ -331,22 +350,13 @@ function GUI(props: GUIProps) {
: false
}
ref={mainTextInputRef}
- onKeyDown={(e) => {
- if (e.key === "Enter") {
- onMainTextInput();
- e.stopPropagation();
- e.preventDefault();
- }
- }}
- rows={1}
- onChange={() => {
- const textarea = mainTextInputRef.current!;
- textarea.style.height = ""; /* Reset the height*/
- textarea.style.height = `${Math.min(
- textarea.scrollHeight - 15,
- 500
- )}px`;
+ onEnter={(e) => {
+ onMainTextInput();
+ e.stopPropagation();
+ e.preventDefault();
}}
+ onInputValueChange={() => {}}
+ items={availableSlashCommands}
/>
<ContinueButton onClick={onMainTextInput} />
</TopGUIDiv>
diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl
index 42f3d4a3..3d7639a9 100644
--- a/extension/scripts/continuedev-0.1.1-py3-none-any.whl
+++ b/extension/scripts/continuedev-0.1.1-py3-none-any.whl
Binary files differ