summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--continuedev/src/continuedev/core/autopilot.py47
-rw-r--r--continuedev/src/continuedev/core/main.py9
-rw-r--r--continuedev/src/continuedev/models/generate_json_schema.py4
-rw-r--r--continuedev/src/continuedev/server/gui.py14
-rw-r--r--extension/media/Lexend/Lexend-VariableFont_wght.ttfbin0 -> 176220 bytes
-rw-r--r--extension/media/Lexend/OFL.txt93
-rw-r--r--extension/media/Lexend/README.txt71
-rw-r--r--extension/media/Lexend/static/Lexend-Black.ttfbin0 -> 78496 bytes
-rw-r--r--extension/media/Lexend/static/Lexend-Bold.ttfbin0 -> 78632 bytes
-rw-r--r--extension/media/Lexend/static/Lexend-ExtraBold.ttfbin0 -> 78716 bytes
-rw-r--r--extension/media/Lexend/static/Lexend-ExtraLight.ttfbin0 -> 78508 bytes
-rw-r--r--extension/media/Lexend/static/Lexend-Light.ttfbin0 -> 78668 bytes
-rw-r--r--extension/media/Lexend/static/Lexend-Medium.ttfbin0 -> 78696 bytes
-rw-r--r--extension/media/Lexend/static/Lexend-Regular.ttfbin0 -> 78360 bytes
-rw-r--r--extension/media/Lexend/static/Lexend-SemiBold.ttfbin0 -> 78744 bytes
-rw-r--r--extension/media/Lexend/static/Lexend-Thin.ttfbin0 -> 78380 bytes
-rw-r--r--extension/react-app/package-lock.json59
-rw-r--r--extension/react-app/package.json1
-rw-r--r--extension/react-app/src/App.tsx39
-rw-r--r--extension/react-app/src/components/CodeBlock.tsx4
-rw-r--r--extension/react-app/src/components/ComboBox.tsx191
-rw-r--r--extension/react-app/src/components/ContinueButton.tsx2
-rw-r--r--extension/react-app/src/components/HeaderButtonWithText.tsx44
-rw-r--r--extension/react-app/src/components/Loader.tsx40
-rw-r--r--extension/react-app/src/components/PillButton.tsx144
-rw-r--r--extension/react-app/src/components/StepContainer.tsx13
-rw-r--r--extension/react-app/src/components/TextDialog.tsx2
-rw-r--r--extension/react-app/src/components/UserInputContainer.tsx6
-rw-r--r--extension/react-app/src/components/index.ts22
-rw-r--r--extension/react-app/src/hooks/ContinueGUIClientProtocol.ts4
-rw-r--r--extension/react-app/src/hooks/useContinueGUIProtocol.ts8
-rw-r--r--extension/react-app/src/index.css9
-rw-r--r--extension/react-app/src/main.tsx6
-rw-r--r--extension/react-app/src/tabs/gui.tsx22
-rw-r--r--extension/schema/FullState.d.ts133
-rw-r--r--extension/src/debugPanel.ts4
-rw-r--r--schema/json/FullState.json304
37 files changed, 1088 insertions, 207 deletions
diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py
index b1c4f471..acdc1f0d 100644
--- a/continuedev/src/continuedev/core/autopilot.py
+++ b/continuedev/src/continuedev/core/autopilot.py
@@ -12,7 +12,7 @@ from .observation import Observation, InternalErrorObservation
from ..server.ide_protocol import AbstractIdeProtocolServer
from ..libs.util.queue import AsyncSubscriptionQueue
from ..models.main import ContinueBaseModel
-from .main import Context, ContinueCustomException, Policy, History, FullState, Step, HistoryNode
+from .main import Context, ContinueCustomException, HighlightedRangeContext, Policy, History, FullState, Step, HistoryNode
from ..steps.core.core import ReversibleStep, ManualEditStep, UserInputStep
from ..libs.util.telemetry import capture_event
from .sdk import ContinueSDK
@@ -140,11 +140,24 @@ class Autopilot(ContinueBaseModel):
tb_step.step_name, {"output": output, **tb_step.params})
await self._run_singular_step(step)
- _highlighted_ranges: List[RangeInFileWithContents] = []
+ _highlighted_ranges: List[HighlightedRangeContext] = []
_adding_highlighted_code: bool = False
+ def _make_sure_is_editing_range(self):
+ """If none of the highlighted ranges are currently being edited, the first should be selected"""
+ if len(self._highlighted_ranges) == 0:
+ return
+ if not any(map(lambda x: x.editing, self._highlighted_ranges)):
+ self._highlighted_ranges[0].editing = True
+
async def handle_highlighted_code(self, range_in_files: List[RangeInFileWithContents]):
- if not self._adding_highlighted_code:
+ if not self._adding_highlighted_code and len(self._highlighted_ranges) > 0:
+ return
+
+ # If un-highlighting, then remove the range
+ if len(self._highlighted_ranges) == 1 and len(range_in_files) == 1 and range_in_files[0].range.start == range_in_files[0].range.end:
+ self._highlighted_ranges = []
+ await self.update_subscribers()
return
# Filter out rifs from ~/.continue/diffs folder
@@ -160,20 +173,25 @@ class Autopilot(ContinueBaseModel):
for i, rif in enumerate(self._highlighted_ranges):
found_overlap = False
for new_rif in range_in_files:
- if rif.filepath == new_rif.filepath and rif.range.overlaps_with(new_rif.range):
+ if rif.range.filepath == new_rif.filepath and rif.range.range.overlaps_with(new_rif.range):
found_overlap = True
break
# Also don't allow multiple ranges in same file with same content. This is useless to the model, and avoids
# the bug where cmd+f causes repeated highlights
- if rif.filepath == new_rif.filepath and rif.contents == new_rif.contents:
+ if rif.range.filepath == new_rif.filepath and rif.range.contents == new_rif.contents:
found_overlap = True
break
if not found_overlap:
new_ranges.append(rif)
- self._highlighted_ranges = new_ranges + range_in_files
+ self._highlighted_ranges = new_ranges + [HighlightedRangeContext(
+ range=rif, editing=False, pinned=False
+ ) for rif in range_in_files]
+
+ self._make_sure_is_editing_range()
+
await self.update_subscribers()
_step_depth: int = 0
@@ -193,12 +211,25 @@ class Autopilot(ContinueBaseModel):
if i not in indices:
kept_ranges.append(rif)
self._highlighted_ranges = kept_ranges
+
+ self._make_sure_is_editing_range()
+
await self.update_subscribers()
async def toggle_adding_highlighted_code(self):
self._adding_highlighted_code = not self._adding_highlighted_code
await self.update_subscribers()
+ async def set_editing_at_indices(self, indices: List[int]):
+ for i in range(len(self._highlighted_ranges)):
+ self._highlighted_ranges[i].editing = i in indices
+ await self.update_subscribers()
+
+ async def set_pinned_at_indices(self, indices: List[int]):
+ for i in range(len(self._highlighted_ranges)):
+ self._highlighted_ranges[i].pinned = i in indices
+ await self.update_subscribers()
+
async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]:
# Allow config to set disallowed steps
if step.__class__.__name__ in self.continue_sdk.config.disallowed_steps:
@@ -359,6 +390,10 @@ class Autopilot(ContinueBaseModel):
if len(self._main_user_input_queue) > 1:
return
+ # Remove context unless pinned
+ self._highlighted_ranges = [
+ hr for hr in self._highlighted_ranges if hr.pinned]
+
# await self._request_halt()
# Just run the step that takes user input, and
# then up to the policy to decide how to deal with it.
diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py
index 28fd964e..62cc4936 100644
--- a/continuedev/src/continuedev/core/main.py
+++ b/continuedev/src/continuedev/core/main.py
@@ -199,13 +199,20 @@ class SlashCommandDescription(ContinueBaseModel):
description: str
+class HighlightedRangeContext(ContinueBaseModel):
+ """Context for a highlighted range"""
+ range: RangeInFileWithContents
+ editing: bool
+ pinned: bool
+
+
class FullState(ContinueBaseModel):
"""A full state of the program, including the history"""
history: History
active: bool
user_input_queue: List[str]
default_model: str
- highlighted_ranges: List[RangeInFileWithContents]
+ highlighted_ranges: List[HighlightedRangeContext]
slash_commands: List[SlashCommandDescription]
adding_highlighted_code: bool
diff --git a/continuedev/src/continuedev/models/generate_json_schema.py b/continuedev/src/continuedev/models/generate_json_schema.py
index 080787a5..6cebf429 100644
--- a/continuedev/src/continuedev/models/generate_json_schema.py
+++ b/continuedev/src/continuedev/models/generate_json_schema.py
@@ -1,7 +1,7 @@
from .main import *
from .filesystem import RangeInFile, FileEdit
from .filesystem_edit import FileEditWithFullContents
-from ..core.main import History, HistoryNode
+from ..core.main import History, HistoryNode, FullState
from pydantic import schema_json_of
import os
@@ -12,7 +12,7 @@ MODELS_TO_GENERATE = [
] + [
FileEditWithFullContents
] + [
- History, HistoryNode
+ History, HistoryNode, FullState
]
RENAMES = {
diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py
index fa573b37..8e9b1fb9 100644
--- a/continuedev/src/continuedev/server/gui.py
+++ b/continuedev/src/continuedev/server/gui.py
@@ -87,6 +87,10 @@ class GUIProtocolServer(AbstractGUIProtocolServer):
self.on_delete_context_at_indices(data["indices"])
elif message_type == "toggle_adding_highlighted_code":
self.on_toggle_adding_highlighted_code()
+ elif message_type == "set_editing_at_indices":
+ self.on_set_editing_at_indices(data["indices"])
+ elif message_type == "set_pinned_at_indices":
+ self.on_set_pinned_at_indices(data["indices"])
except Exception as e:
print(e)
@@ -135,6 +139,16 @@ class GUIProtocolServer(AbstractGUIProtocolServer):
self.session.autopilot.toggle_adding_highlighted_code()
)
+ def on_set_editing_at_indices(self, indices: List[int]):
+ asyncio.create_task(
+ self.session.autopilot.set_editing_at_indices(indices)
+ )
+
+ def on_set_pinned_at_indices(self, indices: List[int]):
+ asyncio.create_task(
+ self.session.autopilot.set_pinned_at_indices(indices)
+ )
+
@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(websocket_session)):
diff --git a/extension/media/Lexend/Lexend-VariableFont_wght.ttf b/extension/media/Lexend/Lexend-VariableFont_wght.ttf
new file mode 100644
index 00000000..b294dc84
--- /dev/null
+++ b/extension/media/Lexend/Lexend-VariableFont_wght.ttf
Binary files differ
diff --git a/extension/media/Lexend/OFL.txt b/extension/media/Lexend/OFL.txt
new file mode 100644
index 00000000..6b679248
--- /dev/null
+++ b/extension/media/Lexend/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2018 The Lexend Project Authors (https://github.com/googlefonts/lexend), with Reserved Font Name “RevReading Lexend”.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/extension/media/Lexend/README.txt b/extension/media/Lexend/README.txt
new file mode 100644
index 00000000..f2966dfe
--- /dev/null
+++ b/extension/media/Lexend/README.txt
@@ -0,0 +1,71 @@
+Lexend Variable Font
+====================
+
+This download contains Lexend as both a variable font and static fonts.
+
+Lexend is a variable font with this axis:
+ wght
+
+This means all the styles are contained in a single file:
+ Lexend-VariableFont_wght.ttf
+
+If your app fully supports variable fonts, you can now pick intermediate styles
+that aren’t available as static fonts. Not all apps support variable fonts, and
+in those cases you can use the static font files for Lexend:
+ static/Lexend-Thin.ttf
+ static/Lexend-ExtraLight.ttf
+ static/Lexend-Light.ttf
+ static/Lexend-Regular.ttf
+ static/Lexend-Medium.ttf
+ static/Lexend-SemiBold.ttf
+ static/Lexend-Bold.ttf
+ static/Lexend-ExtraBold.ttf
+ static/Lexend-Black.ttf
+
+Get started
+-----------
+
+1. Install the font files you want to use
+
+2. Use your app's font picker to view the font family and all the
+available styles
+
+Learn more about variable fonts
+-------------------------------
+
+ https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
+ https://variablefonts.typenetwork.com
+ https://medium.com/variable-fonts
+
+In desktop apps
+
+ https://theblog.adobe.com/can-variable-fonts-illustrator-cc
+ https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
+
+Online
+
+ https://developers.google.com/fonts/docs/getting_started
+ https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
+ https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
+
+Installing fonts
+
+ MacOS: https://support.apple.com/en-us/HT201749
+ Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
+ Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
+
+Android Apps
+
+ https://developers.google.com/fonts/docs/android
+ https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
+
+License
+-------
+Please read the full license text (OFL.txt) to understand the permissions,
+restrictions and requirements for usage, redistribution, and modification.
+
+You can use them in your products & projects – print or digital,
+commercial or otherwise.
+
+This isn't legal advice, please consider consulting a lawyer and see the full
+license for all details.
diff --git a/extension/media/Lexend/static/Lexend-Black.ttf b/extension/media/Lexend/static/Lexend-Black.ttf
new file mode 100644
index 00000000..2fea087c
--- /dev/null
+++ b/extension/media/Lexend/static/Lexend-Black.ttf
Binary files differ
diff --git a/extension/media/Lexend/static/Lexend-Bold.ttf b/extension/media/Lexend/static/Lexend-Bold.ttf
new file mode 100644
index 00000000..95884f6e
--- /dev/null
+++ b/extension/media/Lexend/static/Lexend-Bold.ttf
Binary files differ
diff --git a/extension/media/Lexend/static/Lexend-ExtraBold.ttf b/extension/media/Lexend/static/Lexend-ExtraBold.ttf
new file mode 100644
index 00000000..02f84ed3
--- /dev/null
+++ b/extension/media/Lexend/static/Lexend-ExtraBold.ttf
Binary files differ
diff --git a/extension/media/Lexend/static/Lexend-ExtraLight.ttf b/extension/media/Lexend/static/Lexend-ExtraLight.ttf
new file mode 100644
index 00000000..20e7068d
--- /dev/null
+++ b/extension/media/Lexend/static/Lexend-ExtraLight.ttf
Binary files differ
diff --git a/extension/media/Lexend/static/Lexend-Light.ttf b/extension/media/Lexend/static/Lexend-Light.ttf
new file mode 100644
index 00000000..fb6d097c
--- /dev/null
+++ b/extension/media/Lexend/static/Lexend-Light.ttf
Binary files differ
diff --git a/extension/media/Lexend/static/Lexend-Medium.ttf b/extension/media/Lexend/static/Lexend-Medium.ttf
new file mode 100644
index 00000000..d91a8673
--- /dev/null
+++ b/extension/media/Lexend/static/Lexend-Medium.ttf
Binary files differ
diff --git a/extension/media/Lexend/static/Lexend-Regular.ttf b/extension/media/Lexend/static/Lexend-Regular.ttf
new file mode 100644
index 00000000..b423d3ab
--- /dev/null
+++ b/extension/media/Lexend/static/Lexend-Regular.ttf
Binary files differ
diff --git a/extension/media/Lexend/static/Lexend-SemiBold.ttf b/extension/media/Lexend/static/Lexend-SemiBold.ttf
new file mode 100644
index 00000000..9dcb8214
--- /dev/null
+++ b/extension/media/Lexend/static/Lexend-SemiBold.ttf
Binary files differ
diff --git a/extension/media/Lexend/static/Lexend-Thin.ttf b/extension/media/Lexend/static/Lexend-Thin.ttf
new file mode 100644
index 00000000..0d7df881
--- /dev/null
+++ b/extension/media/Lexend/static/Lexend-Thin.ttf
Binary files differ
diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json
index fb13dffd..7316581d 100644
--- a/extension/react-app/package-lock.json
+++ b/extension/react-app/package-lock.json
@@ -20,6 +20,7 @@
"react-redux": "^8.0.5",
"react-switch": "^7.0.0",
"react-syntax-highlighter": "^15.5.0",
+ "react-tooltip": "^5.18.0",
"styled-components": "^5.3.6",
"vscode-webview": "^1.0.1-beta.1"
},
@@ -597,6 +598,19 @@
"node": ">=12"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz",
+ "integrity": "sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g=="
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.4.tgz",
+ "integrity": "sha512-21hhDEPOiWkGp0Ys4Wi6Neriah7HweToKra626CIK712B5m9qkdz54OP9gVldUg+URnBTpv/j/bi/skmGdstXQ==",
+ "dependencies": {
+ "@floating-ui/core": "^1.3.1"
+ }
+ },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
@@ -1310,6 +1324,11 @@
"node": ">= 6"
}
},
+ "node_modules/classnames": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
+ "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
+ },
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -2967,6 +2986,19 @@
"react": ">= 0.14.0"
}
},
+ "node_modules/react-tooltip": {
+ "version": "5.18.0",
+ "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.18.0.tgz",
+ "integrity": "sha512-qjDK/skUJJ27sc9lTWeNxp2rLzmenBTskSsRiDOCPnupGSz2GhL5IZxDizK/sOsk0hn5iSCywt+3jKxUJ3Y4Sw==",
+ "dependencies": {
+ "@floating-ui/dom": "^1.0.0",
+ "classnames": "^2.3.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.14.0",
+ "react-dom": ">=16.14.0"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -3886,6 +3918,19 @@
"dev": true,
"optional": true
},
+ "@floating-ui/core": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz",
+ "integrity": "sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g=="
+ },
+ "@floating-ui/dom": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.4.tgz",
+ "integrity": "sha512-21hhDEPOiWkGp0Ys4Wi6Neriah7HweToKra626CIK712B5m9qkdz54OP9gVldUg+URnBTpv/j/bi/skmGdstXQ==",
+ "requires": {
+ "@floating-ui/core": "^1.3.1"
+ }
+ },
"@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
@@ -4350,6 +4395,11 @@
}
}
},
+ "classnames": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
+ "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
+ },
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -5411,6 +5461,15 @@
"refractor": "^3.6.0"
}
},
+ "react-tooltip": {
+ "version": "5.18.0",
+ "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.18.0.tgz",
+ "integrity": "sha512-qjDK/skUJJ27sc9lTWeNxp2rLzmenBTskSsRiDOCPnupGSz2GhL5IZxDizK/sOsk0hn5iSCywt+3jKxUJ3Y4Sw==",
+ "requires": {
+ "@floating-ui/dom": "^1.0.0",
+ "classnames": "^2.3.0"
+ }
+ },
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
diff --git a/extension/react-app/package.json b/extension/react-app/package.json
index 12701906..4bedb813 100644
--- a/extension/react-app/package.json
+++ b/extension/react-app/package.json
@@ -21,6 +21,7 @@
"react-redux": "^8.0.5",
"react-switch": "^7.0.0",
"react-syntax-highlighter": "^15.5.0",
+ "react-tooltip": "^5.18.0",
"styled-components": "^5.3.6",
"vscode-webview": "^1.0.1-beta.1"
},
diff --git a/extension/react-app/src/App.tsx b/extension/react-app/src/App.tsx
index a51541d0..8785f88f 100644
--- a/extension/react-app/src/App.tsx
+++ b/extension/react-app/src/App.tsx
@@ -1,28 +1,33 @@
import DebugPanel from "./components/DebugPanel";
import MainTab from "./tabs/main";
-import { Provider } from "react-redux";
-import store from "./redux/store";
import WelcomeTab from "./tabs/welcome";
import ChatTab from "./tabs/chat";
import GUI from "./tabs/gui";
+import { createContext } from "react";
+import useContinueGUIProtocol from "./hooks/useWebsocket";
+import ContinueGUIClientProtocol from "./hooks/useContinueGUIProtocol";
+
+export const GUIClientContext = createContext<
+ ContinueGUIClientProtocol | undefined
+>(undefined);
function App() {
+ const client = useContinueGUIProtocol();
+
return (
- <>
- <Provider store={store}>
- <DebugPanel
- tabs={[
- {
- element: <GUI />,
- title: "GUI",
- },
- // { element: <MainTab />, title: "Debug Panel" },
- // { element: <WelcomeTab />, title: "Welcome" },
- // { element: <ChatTab />, title: "Chat" },
- ]}
- ></DebugPanel>
- </Provider>
- </>
+ <GUIClientContext.Provider value={client}>
+ <DebugPanel
+ tabs={[
+ {
+ element: <GUI />,
+ title: "GUI",
+ },
+ // { element: <MainTab />, title: "Debug Panel" },
+ // { element: <WelcomeTab />, title: "Welcome" },
+ // { element: <ChatTab />, title: "Chat" },
+ ]}
+ />
+ </GUIClientContext.Provider>
);
}
diff --git a/extension/react-app/src/components/CodeBlock.tsx b/extension/react-app/src/components/CodeBlock.tsx
index 17f5626b..fe9b3a95 100644
--- a/extension/react-app/src/components/CodeBlock.tsx
+++ b/extension/react-app/src/components/CodeBlock.tsx
@@ -52,9 +52,9 @@ function CopyButton(props: { textToCopy: string; visible: boolean }) {
}}
>
{clicked ? (
- <CheckCircle color="#00ff00" size="1.4em" />
+ <CheckCircle color="#00ff00" size="1.5em" />
) : (
- <Clipboard color={hovered ? "#00ff00" : "white"} size="1.4em" />
+ <Clipboard color={hovered ? "#00ff00" : "white"} size="1.5em" />
)}
</StyledCopyButton>
</>
diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx
index 81b148b9..7ee5dc24 100644
--- a/extension/react-app/src/components/ComboBox.tsx
+++ b/extension/react-app/src/components/ComboBox.tsx
@@ -4,6 +4,7 @@ import styled from "styled-components";
import {
buttonColor,
defaultBorderRadius,
+ lightGray,
secondaryDark,
vscBackground,
} from ".";
@@ -16,11 +17,32 @@ import {
LockClosed,
LockOpen,
Plus,
+ DocumentPlus,
} from "@styled-icons/heroicons-outline";
+import { HighlightedRangeContext } from "../../../schema/FullState";
// #region styled components
const mainInputFontSize = 16;
+const EmptyPillDiv = styled.div`
+ padding: 8px;
+ border-radius: ${defaultBorderRadius};
+ border: 1px dashed ${lightGray};
+ color: ${lightGray};
+ background-color: ${vscBackground};
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+ text-align: center;
+ cursor: pointer;
+ font-size: 13px;
+
+ &:hover {
+ background-color: ${lightGray};
+ color: ${vscBackground};
+ }
+`;
+
const ContextDropdown = styled.div`
position: absolute;
padding: 4px;
@@ -41,17 +63,19 @@ const MainTextInput = styled.textarea`
padding: 8px;
font-size: ${mainInputFontSize}px;
+ font-family: inherit;
+ border: 1px solid transparent;
border-radius: ${defaultBorderRadius};
- border: 1px solid white;
margin: 8px auto;
+ height: auto;
width: 100%;
- background-color: ${vscBackground};
+ background-color: ${secondaryDark};
color: white;
z-index: 1;
&:focus {
+ outline: 1px solid #ff000066;
border: 1px solid transparent;
- outline: 1px solid orange;
}
`;
@@ -69,7 +93,6 @@ const Ul = styled.ul<{
background: ${vscBackground};
background-color: ${secondaryDark};
color: white;
- font-family: "Fira Code", monospace;
max-height: ${UlMaxHeight}px;
overflow: scroll;
padding: 0;
@@ -102,7 +125,7 @@ interface ComboBoxProps {
onInputValueChange: (inputValue: string) => void;
disabled?: boolean;
onEnter: (e: React.KeyboardEvent<HTMLInputElement>) => void;
- highlightedCodeSections: (RangeInFile & { contents: string })[];
+ highlightedCodeSections: HighlightedRangeContext[];
deleteContextItems: (indices: number[]) => void;
onTogglePin: () => void;
onToggleAddContext: () => void;
@@ -119,16 +142,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
React.useState(false);
const [pinned, setPinned] = useState(false);
const [highlightedCodeSections, setHighlightedCodeSections] = React.useState(
- props.highlightedCodeSections || [
- {
- filepath: "test.ts",
- range: {
- start: { line: 0, character: 0 },
- end: { line: 0, character: 0 },
- },
- contents: "import * as a from 'a';",
- },
- ]
+ props.highlightedCodeSections || []
);
useEffect(() => {
@@ -169,6 +183,71 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
return (
<>
+ <div className="px-2 flex gap-2 items-center flex-wrap mt-2">
+ {highlightedCodeSections.length > 1 && (
+ <>
+ <HeaderButtonWithText
+ text="Clear Context"
+ onClick={() => {
+ props.deleteContextItems(
+ highlightedCodeSections.map((_, idx) => idx)
+ );
+ }}
+ >
+ <Trash size="1.6em" />
+ </HeaderButtonWithText>
+ </>
+ )}
+ {highlightedCodeSections.map((section, idx) => (
+ <PillButton
+ editing={section.editing}
+ pinned={section.pinned}
+ index={idx}
+ key={`${section.filepath}${idx}`}
+ title={`${section.range.filepath} (${
+ section.range.range.start.line + 1
+ }-${section.range.range.end.line + 1})`}
+ onDelete={() => {
+ if (props.deleteContextItems) {
+ props.deleteContextItems([idx]);
+ }
+ setHighlightedCodeSections((prev) => {
+ const newSections = [...prev];
+ newSections.splice(idx, 1);
+ return newSections;
+ });
+ }}
+ onHover={(val: boolean) => {
+ if (val) {
+ setHoveringButton(val);
+ } else {
+ setTimeout(() => {
+ setHoveringButton(val);
+ }, 100);
+ }
+ }}
+ />
+ ))}
+ {props.highlightedCodeSections.length > 0 &&
+ (props.addingHighlightedCode ? (
+ <EmptyPillDiv
+ onClick={() => {
+ props.onToggleAddContext();
+ }}
+ >
+ Highlight to Add Context
+ </EmptyPillDiv>
+ ) : (
+ <HeaderButtonWithText
+ text="Add to Context"
+ onClick={() => {
+ props.onToggleAddContext();
+ }}
+ >
+ <DocumentPlus width="1.6em"></DocumentPlus>
+ </HeaderButtonWithText>
+ ))}
+ </div>
<div className="flex px-2" ref={divRef} hidden={!isOpen}>
<MainTextInput
disabled={props.disabled}
@@ -260,80 +339,10 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
))}
</Ul>
</div>
- <div className="px-2 flex gap-2 items-center flex-wrap">
- {highlightedCodeSections.length === 0 && (
- <HeaderButtonWithText
- text={
- props.addingHighlightedCode ? "Adding Context" : "Add Context"
- }
- onClick={() => {
- props.onToggleAddContext();
- }}
- inverted={props.addingHighlightedCode}
- >
- <Plus size="1.6em" />
- </HeaderButtonWithText>
- )}
- {highlightedCodeSections.length > 0 && (
- <>
- <HeaderButtonWithText
- text="Clear Context"
- onClick={() => {
- props.deleteContextItems(
- highlightedCodeSections.map((_, idx) => idx)
- );
- }}
- >
- <Trash size="1.6em" />
- </HeaderButtonWithText>
- <HeaderButtonWithText
- text={pinned ? "Unpin Context" : "Pin Context"}
- inverted={pinned}
- onClick={() => {
- setPinned((prev) => !prev);
- props.onTogglePin();
- }}
- >
- {pinned ? (
- <LockClosed size="1.6em"></LockClosed>
- ) : (
- <LockOpen size="1.6em"></LockOpen>
- )}
- </HeaderButtonWithText>
- </>
- )}
- {highlightedCodeSections.map((section, idx) => (
- <PillButton
- title={`${section.filepath} (${section.range.start.line + 1}-${
- section.range.end.line + 1
- })`}
- onDelete={() => {
- if (props.deleteContextItems) {
- props.deleteContextItems([idx]);
- }
- setHighlightedCodeSections((prev) => {
- const newSections = [...prev];
- newSections.splice(idx, 1);
- return newSections;
- });
- }}
- onHover={(val: boolean) => {
- if (val) {
- setHoveringButton(val);
- } else {
- setTimeout(() => {
- setHoveringButton(val);
- }, 100);
- }
- }}
- />
- ))}
-
- <span className="text-trueGray-400 ml-auto mr-4 text-xs text-right">
- Highlight code to include as context. Currently open file included by
- default. {highlightedCodeSections.length === 0 && ""}
- </span>
- </div>
+ {/* <span className="text-trueGray-400 ml-auto m-auto text-xs text-right">
+ Highlight code to include as context. Currently open file included by
+ default. {highlightedCodeSections.length === 0 && ""}
+ </span> */}
<ContextDropdown
onMouseEnter={() => {
setHoveringContextDropdown(true);
@@ -345,9 +354,9 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
>
{highlightedCodeSections.map((section, idx) => (
<>
- <p>{section.filepath}</p>
+ <p>{section.range.filepath}</p>
<CodeBlock showCopy={false} key={idx}>
- {section.contents}
+ {section.range.contents}
</CodeBlock>
</>
))}
diff --git a/extension/react-app/src/components/ContinueButton.tsx b/extension/react-app/src/components/ContinueButton.tsx
index 5295799a..462f2b46 100644
--- a/extension/react-app/src/components/ContinueButton.tsx
+++ b/extension/react-app/src/components/ContinueButton.tsx
@@ -18,7 +18,7 @@ let StyledButton = styled(Button)`
&:hover {
transition-delay: 0.5s;
- transition-property: background;
+ transition-property: "background";
background: linear-gradient(
45deg,
#be1a55 14.44%,
diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx
index 72a653c5..de8e3c98 100644
--- a/extension/react-app/src/components/HeaderButtonWithText.tsx
+++ b/extension/react-app/src/components/HeaderButtonWithText.tsx
@@ -1,6 +1,7 @@
import React, { useState } from "react";
-
-import { HeaderButton } from ".";
+import { Tooltip } from "react-tooltip";
+import styled from "styled-components";
+import { HeaderButton, StyledTooltip, defaultBorderRadius } from ".";
interface HeaderButtonWithTextProps {
text: string;
@@ -13,25 +14,28 @@ interface HeaderButtonWithTextProps {
const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => {
const [hover, setHover] = useState(false);
- const paddingLeft = (props.disabled ? (props.active ? "3px" : "1px"): (hover ? "4px" : "1px"));
return (
- <HeaderButton
- inverted={props.inverted}
- disabled={props.disabled}
- style={{ padding: (props.active ? "3px" : "1px"), paddingLeft, borderRadius: (props.active ? "50%" : undefined) }}
- onMouseEnter={() => {
- if (!props.disabled) {
- setHover(true);
- }
- }}
- onMouseLeave={() => {
- setHover(false);
- }}
- onClick={props.onClick}
- >
- <span hidden={!hover}>{props.text}</span>
- {props.children}
- </HeaderButton>
+ <>
+ <HeaderButton
+ data-tooltip-id={`header_button_${props.text}`}
+ inverted={props.inverted}
+ disabled={props.disabled}
+ onMouseEnter={() => {
+ if (!props.disabled) {
+ setHover(true);
+ }
+ }}
+ onMouseLeave={() => {
+ setHover(false);
+ }}
+ onClick={props.onClick}
+ >
+ {props.children}
+ </HeaderButton>
+ <StyledTooltip id={`header_button_${props.text}`} place="bottom">
+ {props.text}
+ </StyledTooltip>
+ </>
);
};
diff --git a/extension/react-app/src/components/Loader.tsx b/extension/react-app/src/components/Loader.tsx
new file mode 100644
index 00000000..90eff793
--- /dev/null
+++ b/extension/react-app/src/components/Loader.tsx
@@ -0,0 +1,40 @@
+import { Play } from "@styled-icons/heroicons-outline";
+import { useSelector } from "react-redux";
+import styled from "styled-components";
+import { RootStore } from "../redux/store";
+
+const DEFAULT_SIZE = "28px";
+
+const FlashingDiv = styled.div`
+ margin: auto;
+ width: ${DEFAULT_SIZE};
+ animation: flash 1.2s infinite ease-in-out;
+ @keyframes flash {
+ 0% {
+ opacity: 0.4;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0.4;
+ }
+ }
+`;
+
+function Loader(props: { size?: string }) {
+ const vscMediaUrl = useSelector(
+ (state: RootStore) => state.config.vscMediaUrl
+ );
+ return (
+ <FlashingDiv>
+ {vscMediaUrl ? (
+ <img src={`${vscMediaUrl}/play_button.png`} width="22px" />
+ ) : (
+ <Play width={props.size || DEFAULT_SIZE} />
+ )}
+ </FlashingDiv>
+ );
+}
+
+export default Loader;
diff --git a/extension/react-app/src/components/PillButton.tsx b/extension/react-app/src/components/PillButton.tsx
index 5a02c6b2..a384832e 100644
--- a/extension/react-app/src/components/PillButton.tsx
+++ b/extension/react-app/src/components/PillButton.tsx
@@ -1,54 +1,136 @@
-import { useState } from "react";
+import { useContext, useState } from "react";
import styled from "styled-components";
-import { defaultBorderRadius } from ".";
-import { XMark } from "@styled-icons/heroicons-outline";
+import {
+ StyledTooltip,
+ defaultBorderRadius,
+ lightGray,
+ secondaryDark,
+} from ".";
+import { Trash, PaintBrush, MapPin } from "@styled-icons/heroicons-outline";
+import { GUIClientContext } from "../App";
const Button = styled.button`
border: none;
color: white;
- background-color: transparent;
- border: 1px solid white;
+ background-color: ${secondaryDark};
border-radius: ${defaultBorderRadius};
- padding: 3px 6px;
+ padding: 8px;
+ overflow: hidden;
+
+ cursor: pointer;
+`;
+
+const GridDiv = styled.div`
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ display: grid;
+ grid-gap: 0;
+ grid-template-columns: 1fr 1fr;
+ align-items: center;
+ border-radius: ${defaultBorderRadius};
+ overflow: hidden;
+
+ background-color: ${secondaryDark};
+`;
+
+const ButtonDiv = styled.div<{ backgroundColor: string }>`
+ background-color: ${secondaryDark};
+ padding: 3px;
+ height: 100%;
+ display: flex;
+ align-items: center;
&:hover {
- background-color: white;
- color: black;
+ background-color: ${(props) => props.backgroundColor};
}
-
- cursor: pointer;
`;
interface PillButtonProps {
onHover?: (arg0: boolean) => void;
onDelete?: () => void;
title: string;
+ index: number;
+ editing: boolean;
+ pinned: boolean;
}
const PillButton = (props: PillButtonProps) => {
const [isHovered, setIsHovered] = useState(false);
+ const client = useContext(GUIClientContext);
+
return (
- <Button
- onMouseEnter={() => {
- setIsHovered(true);
- if (props.onHover) {
- props.onHover(true);
- }
- }}
- onMouseLeave={() => {
- setIsHovered(false);
- if (props.onHover) {
- props.onHover(false);
- }
- }}
- onClick={() => {
- if (props.onDelete) {
- props.onDelete();
- }
- }}
- >
- {props.title}
- </Button>
+ <>
+ <Button
+ style={{
+ position: "relative",
+ borderColor: props.editing
+ ? "#8800aa"
+ : props.pinned
+ ? "#ffff0099"
+ : "transparent",
+ borderWidth: "1px",
+ borderStyle: "solid",
+ }}
+ onMouseEnter={() => {
+ setIsHovered(true);
+ if (props.onHover) {
+ props.onHover(true);
+ }
+ }}
+ onMouseLeave={() => {
+ setIsHovered(false);
+ if (props.onHover) {
+ props.onHover(false);
+ }
+ }}
+ >
+ {isHovered && (
+ <GridDiv>
+ <ButtonDiv
+ data-tooltip-id={`edit-${props.index}`}
+ backgroundColor={"#8800aa55"}
+ onClick={() => {
+ client?.setEditingAtIndices([props.index]);
+ }}
+ >
+ <PaintBrush style={{ margin: "auto" }} width="1.6em"></PaintBrush>
+ </ButtonDiv>
+
+ {/* <ButtonDiv
+ data-tooltip-id={`pin-${props.index}`}
+ backgroundColor={"#ffff0055"}
+ onClick={() => {
+ client?.setPinnedAtIndices([props.index]);
+ }}
+ >
+ <MapPin style={{ margin: "auto" }} width="1.6em"></MapPin>
+ </ButtonDiv> */}
+ <StyledTooltip id={`pin-${props.index}`}>
+ Edit this range
+ </StyledTooltip>
+ <ButtonDiv
+ data-tooltip-id={`delete-${props.index}`}
+ backgroundColor={"#cc000055"}
+ onClick={() => {
+ if (props.onDelete) {
+ props.onDelete();
+ }
+ }}
+ >
+ <Trash style={{ margin: "auto" }} width="1.6em"></Trash>
+ </ButtonDiv>
+ </GridDiv>
+ )}
+ {props.title}
+ </Button>
+ <StyledTooltip id={`edit-${props.index}`}>
+ {props.editing ? "Editing this range" : "Edit this range"}
+ </StyledTooltip>
+ <StyledTooltip id={`delete-${props.index}`}>Delete</StyledTooltip>
+ </>
);
};
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx
index 2aed2e72..91d7b8ef 100644
--- a/extension/react-app/src/components/StepContainer.tsx
+++ b/extension/react-app/src/components/StepContainer.tsx
@@ -13,7 +13,7 @@ import {
ArrowPath,
XMark,
} from "@styled-icons/heroicons-outline";
-import { Stop } from "@styled-icons/heroicons-solid";
+import { StopCircle } from "@styled-icons/heroicons-solid";
import { HistoryNode } from "../../../schema/HistoryNode";
import ReactMarkdown from "react-markdown";
import HeaderButtonWithText from "./HeaderButtonWithText";
@@ -67,7 +67,6 @@ const HeaderDiv = styled.div<{ error: boolean; loading: boolean }>`
const ContentDiv = styled.div<{ isUserInput: boolean }>`
padding: 8px;
- padding-left: 16px;
background-color: ${(props) =>
props.isUserInput ? secondaryDark : vscBackground};
font-size: 13px;
@@ -167,7 +166,7 @@ function StepContainer(props: StepContainerProps) {
? "#f00"
: props.historyNode.active
? undefined
- : "white"
+ : "transparent"
}
className="overflow-hidden cursor-pointer"
onClick={(e) => {
@@ -182,7 +181,7 @@ function StepContainer(props: StepContainerProps) {
loading={props.historyNode.active as boolean | false}
error={props.historyNode.observation?.error ? true : false}
>
- <h4 className="m-2">
+ <div className="m-2">
{!isUserInput &&
(props.open ? (
<ChevronDown size="1.4em" />
@@ -191,7 +190,7 @@ function StepContainer(props: StepContainerProps) {
))}
{props.historyNode.observation?.title ||
(props.historyNode.step.name as any)}
- </h4>
+ </div>
{/* <HeaderButton
onClick={(e) => {
e.stopPropagation();
@@ -203,16 +202,14 @@ function StepContainer(props: StepContainerProps) {
<>
<HeaderButtonWithText
- disabled={props.historyNode.active as boolean}
onClick={(e) => {
e.stopPropagation();
props.onDelete();
}}
text={props.historyNode.active ? "Stop" : "Delete"}
- active={props.historyNode.active}
>
{props.historyNode.active ? (
- <Stop size="1.2em" onClick={props.onDelete} />
+ <StopCircle size="1.6em" onClick={props.onDelete} />
) : (
<XMark size="1.6em" onClick={props.onDelete} />
)}
diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx
index a564f884..ea5727f0 100644
--- a/extension/react-app/src/components/TextDialog.tsx
+++ b/extension/react-app/src/components/TextDialog.tsx
@@ -8,6 +8,7 @@ const ScreenCover = styled.div`
width: 100%;
height: 100%;
background-color: rgba(168, 168, 168, 0.5);
+ z-index: 100;
`;
const DialogContainer = styled.div`
@@ -35,7 +36,6 @@ const TextArea = styled.textarea`
border-radius: 8px;
padding: 8px;
outline: 1px solid black;
- font-family: Arial, Helvetica, sans-serif;
resize: none;
&:focus {
diff --git a/extension/react-app/src/components/UserInputContainer.tsx b/extension/react-app/src/components/UserInputContainer.tsx
index 28437d35..f51f0cb5 100644
--- a/extension/react-app/src/components/UserInputContainer.tsx
+++ b/extension/react-app/src/components/UserInputContainer.tsx
@@ -15,12 +15,10 @@ interface UserInputContainerProps {
}
const StyledDiv = styled.div`
- background-color: rgb(45 45 45);
+ background-color: ${secondaryDark};
padding: 8px;
padding-left: 16px;
padding-right: 16px;
- border-bottom: 1px solid white;
- border-top: 1px solid white;
font-size: 13px;
display: flex;
align-items: center;
@@ -29,7 +27,7 @@ const StyledDiv = styled.div`
const UserInputContainer = (props: UserInputContainerProps) => {
return (
<StyledDiv>
- <b>{props.children}</b>
+ {props.children}
<div style={{ marginLeft: "auto" }}>
<HeaderButtonWithText
onClick={(e) => {
diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts
index db1925ed..9ae0f097 100644
--- a/extension/react-app/src/components/index.ts
+++ b/extension/react-app/src/components/index.ts
@@ -1,7 +1,9 @@
+import { Tooltip } from "react-tooltip";
import styled, { keyframes } from "styled-components";
export const defaultBorderRadius = "5px";
-export const secondaryDark = "rgb(42 42 42)";
+export const lightGray = "rgb(100 100 100)";
+export const secondaryDark = "rgb(45 45 45)";
export const vscBackground = "rgb(30 30 30)";
export const vscBackgroundTransparent = "#1e1e1ede";
export const buttonColor = "rgb(113 28 59)";
@@ -26,6 +28,16 @@ export const Button = styled.button`
}
`;
+export const StyledTooltip = styled(Tooltip)`
+ font-size: 12px;
+ background-color: rgb(60 60 60);
+ border-radius: ${defaultBorderRadius};
+ padding: 6px;
+ padding-left: 12px;
+ padding-right: 12px;
+ z-index: 100;
+`;
+
export const TextArea = styled.textarea`
width: 100%;
border-radius: ${defaultBorderRadius};
@@ -128,19 +140,17 @@ export const HeaderButton = styled.button<{ inverted: boolean | undefined }>`
background-color: ${({ inverted }) => (inverted ? "white" : "transparent")};
color: ${({ inverted }) => (inverted ? "black" : "white")};
- border: 1px solid white;
+ border: none;
border-radius: ${defaultBorderRadius};
cursor: pointer;
&:hover {
background-color: ${({ inverted }) =>
- typeof inverted === "undefined" || inverted ? "white" : "transparent"};
- color: ${({ inverted }) =>
- typeof inverted === "undefined" || inverted ? "black" : "white"};
+ typeof inverted === "undefined" || inverted ? lightGray : "transparent"};
}
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
- padding: 1px;
+ padding: 2px;
`;
diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
index f123bb2b..a179c2bf 100644
--- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
+++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
@@ -23,6 +23,10 @@ abstract class AbstractContinueGUIClientProtocol {
abstract deleteContextAtIndices(indices: number[]): void;
+ abstract setEditingAtIndices(indices: number[]): void;
+
+ abstract setPinnedAtIndices(indices: number[]): void;
+
abstract toggleAddingHighlightedCode(): void;
}
diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts
index 49f200ae..2060dd7f 100644
--- a/extension/react-app/src/hooks/useContinueGUIProtocol.ts
+++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts
@@ -75,6 +75,14 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol {
this.messenger.send("delete_context_at_indices", { indices });
}
+ setEditingAtIndices(indices: number[]) {
+ this.messenger.send("set_editing_at_indices", { indices });
+ }
+
+ setPinnedAtIndices(indices: number[]) {
+ this.messenger.send("set_pinned_at_indices", { indices });
+ }
+
toggleAddingHighlightedCode(): void {
this.messenger.send("toggle_adding_highlighted_code", {});
}
diff --git a/extension/react-app/src/index.css b/extension/react-app/src/index.css
index 6dc514ec..682551f8 100644
--- a/extension/react-app/src/index.css
+++ b/extension/react-app/src/index.css
@@ -10,19 +10,12 @@
--def-border-radius: 5px;
}
-@font-face {
- font-family: "Mona Sans";
- src: url("assets/Mona-Sans.woff2") format("woff2 supports variations"),
- url("assets/Mona-Sans.woff2") format("woff2-variations");
- font-weight: 200 900;
- font-stretch: 75% 85%;
-}
-
html,
body,
#root {
height: 100%;
background-color: var(--vsc-background);
+ font-family: "Lexend", sans-serif;
}
body {
diff --git a/extension/react-app/src/main.tsx b/extension/react-app/src/main.tsx
index 0b02575c..a76bced6 100644
--- a/extension/react-app/src/main.tsx
+++ b/extension/react-app/src/main.tsx
@@ -1,6 +1,8 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
+import { Provider } from "react-redux";
+import store from "./redux/store";
import "./index.css";
import posthog from "posthog-js";
@@ -17,7 +19,9 @@ posthog.init("phc_JS6XFROuNbhJtVCEdTSYk6gl5ArRrTNMpCcguAXlSPs", {
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<PostHogProvider client={posthog}>
- <App />
+ <Provider store={store}>
+ <App />
+ </Provider>
</PostHogProvider>
</React.StrictMode>
);
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index e5320c6a..3cce30de 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -1,11 +1,13 @@
import styled from "styled-components";
-import { defaultBorderRadius, Loader } from "../components";
+import { defaultBorderRadius } from "../components";
+import Loader from "../components/Loader";
import ContinueButton from "../components/ContinueButton";
-import { useCallback, useEffect, useRef, useState } from "react";
+import { FullState, HighlightedRangeContext } from "../../../schema/FullState";
+import { useCallback, useEffect, useRef, useState, useContext } from "react";
import { History } from "../../../schema/History";
import { HistoryNode } from "../../../schema/HistoryNode";
import StepContainer from "../components/StepContainer";
-import useContinueGUIProtocol from "../hooks/useWebsocket";
+import { GUIClientContext } from "../App";
import {
BookOpen,
ChatBubbleOvalLeftEllipsis,
@@ -52,6 +54,7 @@ interface GUIProps {
}
function GUI(props: GUIProps) {
+ const client = useContext(GUIClientContext);
const posthog = usePostHog();
const vscMachineId = useSelector(
(state: RootStore) => state.config.vscMachineId
@@ -70,7 +73,9 @@ function GUI(props: GUIProps) {
const [usingFastModel, setUsingFastModel] = useState(false);
const [waitingForSteps, setWaitingForSteps] = useState(false);
const [userInputQueue, setUserInputQueue] = useState<string[]>([]);
- const [highlightedRanges, setHighlightedRanges] = useState([]);
+ const [highlightedRanges, setHighlightedRanges] = useState<
+ HighlightedRangeContext[]
+ >([]);
const [addingHighlightedCode, setAddingHighlightedCode] = useState(false);
const [availableSlashCommands, setAvailableSlashCommands] = useState<
{ name: string; description: string }[]
@@ -112,7 +117,6 @@ function GUI(props: GUIProps) {
const [feedbackDialogMessage, setFeedbackDialogMessage] = useState("");
const topGuiDivRef = useRef<HTMLDivElement>(null);
- const client = useContinueGUIProtocol();
const [scrollTimeout, setScrollTimeout] = useState<NodeJS.Timeout | null>(
null
@@ -148,7 +152,7 @@ function GUI(props: GUIProps) {
}, []);
useEffect(() => {
- client?.onStateUpdate((state) => {
+ client?.onStateUpdate((state: FullState) => {
// Scroll only if user is at very bottom of the window.
setUsingFastModel(state.default_model === "gpt-3.5-turbo");
const shouldScrollToBottom =
@@ -289,7 +293,7 @@ function GUI(props: GUIProps) {
>
{typeof client === "undefined" && (
<>
- <Loader></Loader>
+ <Loader />
<p style={{ textAlign: "center" }}>Loading Continue server...</p>
</>
)}
@@ -316,7 +320,8 @@ function GUI(props: GUIProps) {
setStepsOpen(nextStepsOpen);
}}
onToggleAll={() => {
- setStepsOpen((prev) => prev.map((_, index) => !prev[index]));
+ const shouldOpen = !stepsOpen[index];
+ setStepsOpen((prev) => prev.map(() => shouldOpen));
}}
key={index}
onUserInput={(input: string) => {
@@ -381,6 +386,7 @@ function GUI(props: GUIProps) {
borderRadius: defaultBorderRadius,
padding: "16px",
margin: "16px",
+ zIndex: 100,
}}
hidden={!showDataSharingInfo}
>
diff --git a/extension/schema/FullState.d.ts b/extension/schema/FullState.d.ts
new file mode 100644
index 00000000..981e772e
--- /dev/null
+++ b/extension/schema/FullState.d.ts
@@ -0,0 +1,133 @@
+/* eslint-disable */
+/**
+ * This file was automatically generated by json-schema-to-typescript.
+ * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
+ * and run json-schema-to-typescript to regenerate this file.
+ */
+
+export type FullState = FullState1;
+export type Name = string;
+export type Hide = boolean;
+export type Description = string;
+export type SystemMessage = string;
+export type Role = "assistant" | "user" | "system" | "function";
+export type Content = string;
+export type Name1 = string;
+export type Summary = string;
+export type Name2 = string;
+export type Arguments = string;
+export type ChatContext = ChatMessage[];
+export type ManageOwnChatContext = boolean;
+export type Depth = number;
+export type Deleted = boolean;
+export type Active = boolean;
+export type Timeline = HistoryNode[];
+export type CurrentIndex = number;
+export type Active1 = boolean;
+export type UserInputQueue = string[];
+export type DefaultModel = string;
+export type Filepath = string;
+export type Line = number;
+export type Character = number;
+export type Contents = string;
+export type Editing = boolean;
+export type Pinned = boolean;
+export type HighlightedRanges = HighlightedRangeContext[];
+export type Name3 = string;
+export type Description1 = string;
+export type SlashCommands = SlashCommandDescription[];
+export type AddingHighlightedCode = boolean;
+
+/**
+ * A full state of the program, including the history
+ */
+export interface FullState1 {
+ history: History;
+ active: Active1;
+ user_input_queue: UserInputQueue;
+ default_model: DefaultModel;
+ highlighted_ranges: HighlightedRanges;
+ slash_commands: SlashCommands;
+ adding_highlighted_code: AddingHighlightedCode;
+ [k: string]: unknown;
+}
+/**
+ * A history of steps taken and their results
+ */
+export interface History {
+ timeline: Timeline;
+ current_index: CurrentIndex;
+ [k: string]: unknown;
+}
+/**
+ * A point in history, a list of which make up History
+ */
+export interface HistoryNode {
+ step: Step;
+ observation?: Observation;
+ depth: Depth;
+ deleted?: Deleted;
+ active?: Active;
+ [k: string]: unknown;
+}
+export interface Step {
+ name?: Name;
+ hide?: Hide;
+ description?: Description;
+ system_message?: SystemMessage;
+ chat_context?: ChatContext;
+ manage_own_chat_context?: ManageOwnChatContext;
+ [k: string]: unknown;
+}
+export interface ChatMessage {
+ role: Role;
+ content?: Content;
+ name?: Name1;
+ summary: Summary;
+ function_call?: FunctionCall;
+ [k: string]: unknown;
+}
+export interface FunctionCall {
+ name: Name2;
+ arguments: Arguments;
+ [k: string]: unknown;
+}
+export interface Observation {
+ [k: string]: unknown;
+}
+/**
+ * Context for a highlighted range
+ */
+export interface HighlightedRangeContext {
+ range: RangeInFileWithContents;
+ editing: Editing;
+ pinned: Pinned;
+ [k: string]: unknown;
+}
+/**
+ * A range in a file with the contents of the range.
+ */
+export interface RangeInFileWithContents {
+ filepath: Filepath;
+ range: Range;
+ contents: Contents;
+ [k: string]: unknown;
+}
+/**
+ * A range in a file. 0-indexed.
+ */
+export interface Range {
+ start: Position;
+ end: Position;
+ [k: string]: unknown;
+}
+export interface Position {
+ line: Line;
+ character: Character;
+ [k: string]: unknown;
+}
+export interface SlashCommandDescription {
+ name: Name3;
+ description: Description1;
+ [k: string]: unknown;
+}
diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts
index b88c86f3..487bbedf 100644
--- a/extension/src/debugPanel.ts
+++ b/extension/src/debugPanel.ts
@@ -341,6 +341,10 @@ export function setupDebugPanel(
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>const vscode = acquireVsCodeApi();</script>
<link href="${styleMainUri}" rel="stylesheet">
+
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Lexend:wght@300&display=swap" rel="stylesheet">
<title>Continue</title>
</head>
diff --git a/schema/json/FullState.json b/schema/json/FullState.json
new file mode 100644
index 00000000..af0f25e1
--- /dev/null
+++ b/schema/json/FullState.json
@@ -0,0 +1,304 @@
+{
+ "title": "FullState",
+ "$ref": "#/definitions/src__continuedev__core__main__FullState",
+ "definitions": {
+ "FunctionCall": {
+ "title": "FunctionCall",
+ "type": "object",
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string"
+ },
+ "arguments": {
+ "title": "Arguments",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "arguments"
+ ]
+ },
+ "ChatMessage": {
+ "title": "ChatMessage",
+ "type": "object",
+ "properties": {
+ "role": {
+ "title": "Role",
+ "enum": [
+ "assistant",
+ "user",
+ "system",
+ "function"
+ ],
+ "type": "string"
+ },
+ "content": {
+ "title": "Content",
+ "type": "string"
+ },
+ "name": {
+ "title": "Name",
+ "type": "string"
+ },
+ "summary": {
+ "title": "Summary",
+ "type": "string"
+ },
+ "function_call": {
+ "$ref": "#/definitions/FunctionCall"
+ }
+ },
+ "required": [
+ "role",
+ "summary"
+ ]
+ },
+ "Step": {
+ "title": "Step",
+ "type": "object",
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string"
+ },
+ "hide": {
+ "title": "Hide",
+ "default": false,
+ "type": "boolean"
+ },
+ "description": {
+ "title": "Description",
+ "type": "string"
+ },
+ "system_message": {
+ "title": "System Message",
+ "type": "string"
+ },
+ "chat_context": {
+ "title": "Chat Context",
+ "default": [],
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ChatMessage"
+ }
+ },
+ "manage_own_chat_context": {
+ "title": "Manage Own Chat Context",
+ "default": false,
+ "type": "boolean"
+ }
+ }
+ },
+ "Observation": {
+ "title": "Observation",
+ "type": "object",
+ "properties": {}
+ },
+ "HistoryNode": {
+ "title": "HistoryNode",
+ "description": "A point in history, a list of which make up History",
+ "type": "object",
+ "properties": {
+ "step": {
+ "$ref": "#/definitions/Step"
+ },
+ "observation": {
+ "$ref": "#/definitions/Observation"
+ },
+ "depth": {
+ "title": "Depth",
+ "type": "integer"
+ },
+ "deleted": {
+ "title": "Deleted",
+ "default": false,
+ "type": "boolean"
+ },
+ "active": {
+ "title": "Active",
+ "default": true,
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "step",
+ "depth"
+ ]
+ },
+ "History": {
+ "title": "History",
+ "description": "A history of steps taken and their results",
+ "type": "object",
+ "properties": {
+ "timeline": {
+ "title": "Timeline",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/HistoryNode"
+ }
+ },
+ "current_index": {
+ "title": "Current Index",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "timeline",
+ "current_index"
+ ]
+ },
+ "Position": {
+ "title": "Position",
+ "type": "object",
+ "properties": {
+ "line": {
+ "title": "Line",
+ "type": "integer"
+ },
+ "character": {
+ "title": "Character",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "line",
+ "character"
+ ]
+ },
+ "Range": {
+ "title": "Range",
+ "description": "A range in a file. 0-indexed.",
+ "type": "object",
+ "properties": {
+ "start": {
+ "$ref": "#/definitions/Position"
+ },
+ "end": {
+ "$ref": "#/definitions/Position"
+ }
+ },
+ "required": [
+ "start",
+ "end"
+ ]
+ },
+ "RangeInFileWithContents": {
+ "title": "RangeInFileWithContents",
+ "description": "A range in a file with the contents of the range.",
+ "type": "object",
+ "properties": {
+ "filepath": {
+ "title": "Filepath",
+ "type": "string"
+ },
+ "range": {
+ "$ref": "#/definitions/Range"
+ },
+ "contents": {
+ "title": "Contents",
+ "type": "string"
+ }
+ },
+ "required": [
+ "filepath",
+ "range",
+ "contents"
+ ]
+ },
+ "HighlightedRangeContext": {
+ "title": "HighlightedRangeContext",
+ "description": "Context for a highlighted range",
+ "type": "object",
+ "properties": {
+ "range": {
+ "$ref": "#/definitions/RangeInFileWithContents"
+ },
+ "editing": {
+ "title": "Editing",
+ "type": "boolean"
+ },
+ "pinned": {
+ "title": "Pinned",
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "range",
+ "editing",
+ "pinned"
+ ]
+ },
+ "SlashCommandDescription": {
+ "title": "SlashCommandDescription",
+ "type": "object",
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string"
+ },
+ "description": {
+ "title": "Description",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "description"
+ ]
+ },
+ "src__continuedev__core__main__FullState": {
+ "title": "FullState",
+ "description": "A full state of the program, including the history",
+ "type": "object",
+ "properties": {
+ "history": {
+ "$ref": "#/definitions/History"
+ },
+ "active": {
+ "title": "Active",
+ "type": "boolean"
+ },
+ "user_input_queue": {
+ "title": "User Input Queue",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "default_model": {
+ "title": "Default Model",
+ "type": "string"
+ },
+ "highlighted_ranges": {
+ "title": "Highlighted Ranges",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/HighlightedRangeContext"
+ }
+ },
+ "slash_commands": {
+ "title": "Slash Commands",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/SlashCommandDescription"
+ }
+ },
+ "adding_highlighted_code": {
+ "title": "Adding Highlighted Code",
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "history",
+ "active",
+ "user_input_queue",
+ "default_model",
+ "highlighted_ranges",
+ "slash_commands",
+ "adding_highlighted_code"
+ ]
+ }
+ }
+} \ No newline at end of file