mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 936537a03a | |||
| 66310e8bfb | |||
| 2361154551 | |||
| 7888a61766 | |||
| 29ad232ac6 | |||
| 067bef0cb5 | |||
| 97deb2ef14 | |||
| 69d58ef525 | |||
| d7173c970c | |||
| 1234919229 | |||
| 652250ac3c | |||
| 11b110d633 | |||
| 14b38b31c8 | |||
| 1ace947d1e | |||
| 13f4203437 | |||
| cd15beba26 | |||
| aac9e27382 | |||
| d07068c09d | |||
| 1307a77b56 | |||
| 2772c76728 | |||
| 4384525ca6 | |||
| d6b82e3792 |
@@ -22,8 +22,8 @@ jobs:
|
||||
- uses: dashcamio/testdriver@main
|
||||
id: testdriver
|
||||
with:
|
||||
version: v2.10.2
|
||||
prerun: |
|
||||
version: v2.12.12
|
||||
prerun: |
|
||||
rm ~/Desktop/WITH-LOVE-FROM-AMERICA.txt
|
||||
cd ~/actions-runner/_work/testdriver/testdriver/
|
||||
brew install go
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
scripthaus run electron 1>/dev/null 2>&1 &
|
||||
echo "Electron Done"
|
||||
exit
|
||||
prompt: |
|
||||
prompt: |
|
||||
1. wait 10 seconds
|
||||
1. click "Get Started"
|
||||
1. validate that overlapping text does not appear in the application
|
||||
|
||||
@@ -285,10 +285,6 @@
|
||||
"keys": ["Ctrl:r"],
|
||||
"commandStr": ["/history"]
|
||||
},
|
||||
{
|
||||
"command": "cmdinput:openAIChat",
|
||||
"keys": ["Ctrl:Space"]
|
||||
},
|
||||
{
|
||||
"command": "history:closeHistory",
|
||||
"keys": ["Ctrl:g", "Ctrl:c"]
|
||||
@@ -332,5 +328,9 @@
|
||||
{
|
||||
"command": "codeedit:togglePreview",
|
||||
"keys": ["Cmd:p"]
|
||||
},
|
||||
{
|
||||
"command": "rightsidebar:toggle",
|
||||
"keys": ["Cmd:Shift:Space"]
|
||||
}
|
||||
]
|
||||
Generated
+14710
File diff suppressed because it is too large
Load Diff
+6
-2
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"productName": "Wave",
|
||||
"description": "An Open-Source, AI-Native, Terminal Built for Seamless Workflows",
|
||||
"version": "0.7.5",
|
||||
"version": "0.7.6",
|
||||
"main": "dist/emain.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -24,6 +24,7 @@
|
||||
"@table-nav/react": "^0.0.7",
|
||||
"@tanstack/match-sorter-utils": "^8.8.4",
|
||||
"@tanstack/react-table": "^8.10.3",
|
||||
"@withfig/autocomplete": "^2.652.3",
|
||||
"autobind-decorator": "^2.4.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"clsx": "^2.1.1",
|
||||
@@ -48,6 +49,7 @@
|
||||
"remark-gfm": "^4.0.0",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"throttle-debounce": "^5.0.0",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"tsx-control-statements": "^5.1.1",
|
||||
"uuid": "^9.0.0",
|
||||
"winston": "^3.8.2",
|
||||
@@ -77,13 +79,15 @@
|
||||
"@types/semver": "^7.5.6",
|
||||
"@types/sprintf-js": "^1.1.3",
|
||||
"@types/throttle-debounce": "^5.0.1",
|
||||
"@types/tinycolor2": "^1",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"@types/webpack-env": "^1.18.3",
|
||||
"@withfig/autocomplete-types": "^1.30.0",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-plugin-jsx-control-statements": "^4.1.2",
|
||||
"copy-webpack-plugin": "^12.0.0",
|
||||
"css-loader": "^7.1.0",
|
||||
"electron": "^30.0.1",
|
||||
"electron": "^30.0.8",
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-builder-squirrel-windows": "^24.13.3",
|
||||
"file-loader": "^6.2.0",
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
--cmdinput-button-bg-color: var(--tab-green);
|
||||
--cmdinput-disabled-bg-color: var(--app-text-bg-disabled-color);
|
||||
--cmdinput-history-bg-color: var(--app-bg-color);
|
||||
--cmdinput-ghost-text-color: rgb(145, 150, 144);
|
||||
|
||||
/* screen view color */
|
||||
--screen-view-text-caption-color: rgb(139, 145, 138);
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
--modal-header-bottom-border-color: rgba(0, 0, 0, 0.3);
|
||||
|
||||
/* cmd input */
|
||||
--cmdinput-ghost-text-color: rgb(116, 116, 116);
|
||||
|
||||
/* scroll colors */
|
||||
--scrollbar-background-color: var(--app-bg-color);
|
||||
|
||||
+16
-9
@@ -31,12 +31,20 @@ class App extends React.Component<{}, {}> {
|
||||
dcWait: OV<boolean> = mobx.observable.box(false, { name: "dcWait" });
|
||||
mainContentRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||
termThemesLoaded: OV<boolean> = mobx.observable.box(false, { name: "termThemesLoaded" });
|
||||
chatFocusTimeoutId: NodeJS.Timeout = null;
|
||||
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
if (GlobalModel.isDev) document.body.classList.add("is-dev");
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.chatFocusTimeoutId) {
|
||||
clearTimeout(this.chatFocusTimeoutId);
|
||||
this.chatFocusTimeoutId = null;
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleContextMenu(e: any) {
|
||||
let isInNonTermInput = false;
|
||||
@@ -68,16 +76,15 @@ class App extends React.Component<{}, {}> {
|
||||
|
||||
@boundMethod
|
||||
openMainSidebar() {
|
||||
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
||||
const width = mainSidebarModel.getWidth(true);
|
||||
mainSidebarModel.saveState(width, false);
|
||||
GlobalModel.mainSidebarModel.setCollapsed(false);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
openRightSidebar() {
|
||||
const rightSidebarModel = GlobalModel.rightSidebarModel;
|
||||
const width = rightSidebarModel.getWidth(true);
|
||||
rightSidebarModel.saveState(width, false);
|
||||
GlobalModel.rightSidebarModel.setCollapsed(false);
|
||||
this.chatFocusTimeoutId = setTimeout(() => {
|
||||
GlobalModel.inputModel.setChatSidebarFocus();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
@@ -159,13 +166,13 @@ class App extends React.Component<{}, {}> {
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
<If condition={GlobalModel.isDev && rightSidebarCollapsed && activeMainView == "session"}>
|
||||
<div className="right-sidebar-triggers">
|
||||
<If condition={rightSidebarCollapsed && activeMainView == "session"}>
|
||||
<div className="right-sidebar-triggers" title="Open Wave AI (Cmd-Shift-Space)">
|
||||
<Button
|
||||
className="secondary ghost right-sidebar-trigger"
|
||||
onClick={this.openRightSidebar}
|
||||
>
|
||||
<i className="fa-sharp fa-solid fa-sidebar-flip"></i>
|
||||
<i className="fa-sharp fa-regular fa-sparkles"></i>
|
||||
</Button>
|
||||
</div>
|
||||
</If>
|
||||
|
||||
@@ -72,3 +72,4 @@ export const ErrorCode_InvalidCwd = "ERRCWD";
|
||||
export const InputAuxView_History = "history";
|
||||
export const InputAuxView_Info = "info";
|
||||
export const InputAuxView_AIChat = "aichat";
|
||||
export const InputAuxView_Suggestions = "suggestions";
|
||||
|
||||
@@ -11,10 +11,10 @@ import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "@/commo
|
||||
import { commandRtnHandler, isBlank } from "@/util/util";
|
||||
import { getTermThemes } from "@/util/themeutil";
|
||||
import * as appconst from "@/app/appconst";
|
||||
import { MainView } from "@/common/elements/mainview";
|
||||
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
|
||||
|
||||
import "./clientsettings.less";
|
||||
import { MainView } from "../common/elements/mainview";
|
||||
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
|
||||
|
||||
class ClientSettingsKeybindings extends React.Component<{}, {}> {
|
||||
componentDidMount() {
|
||||
@@ -110,6 +110,19 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
GlobalModel.getElectronApi().changeAutoUpdate(val);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeAutocompleteEnabled(val: boolean): void {
|
||||
const prtn: Promise<CommandRtnType> = GlobalCommandRunner.setAutocompleteEnabled(val);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeAutocompleteDebuggingEnabled(val: boolean): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.autocompleteModel.loggingEnabled = val;
|
||||
})();
|
||||
}
|
||||
|
||||
getFontSizes(): DropdownItem[] {
|
||||
const availableFontSizes: DropdownItem[] = [];
|
||||
for (let s = appconst.MinFontSize; s <= appconst.MaxFontSize; s++) {
|
||||
@@ -447,6 +460,24 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Command Autocomplete</div>
|
||||
<div className="settings-input">
|
||||
<Toggle
|
||||
checked={cdata.clientopts.autocompleteenabled ?? false}
|
||||
onChange={this.handleChangeAutocompleteEnabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Command Autocomplete Debugging</div>
|
||||
<div className="settings-input">
|
||||
<Toggle
|
||||
checked={GlobalModel.autocompleteModel.loggingEnabled}
|
||||
onChange={this.handleChangeAutocompleteDebuggingEnabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
</MainView>
|
||||
|
||||
@@ -3,11 +3,13 @@ import { Button } from "./button";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
import "./copybutton.less";
|
||||
|
||||
type CopyButtonProps = {
|
||||
title: string;
|
||||
className?: string;
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
};
|
||||
|
||||
@@ -34,10 +36,14 @@ class CopyButton extends React.Component<CopyButtonProps, {}> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { title, onClick } = this.props;
|
||||
const { title, className } = this.props;
|
||||
const isCopied = this.isCopied.get();
|
||||
return (
|
||||
<Button onClick={this.handleOnClick} className="copy-button secondary ghost" title={title}>
|
||||
<Button
|
||||
onClick={this.handleOnClick}
|
||||
className={clsx("copy-button secondary ghost", className)}
|
||||
title={title}
|
||||
>
|
||||
{isCopied ? (
|
||||
<i className="fa-sharp fa-solid fa-check"></i>
|
||||
) : (
|
||||
|
||||
@@ -19,3 +19,4 @@ export { TabIcon } from "./tabicon";
|
||||
export { DatePicker } from "./datepicker";
|
||||
export { TermStyleList } from "./termstyle";
|
||||
export { CopyButton } from "./copybutton";
|
||||
export { TypingIndicator } from "./typingindicator";
|
||||
|
||||
@@ -48,28 +48,67 @@
|
||||
|
||||
pre {
|
||||
background-color: var(--markdown-bg-color);
|
||||
margin: 4px 10px 4px 10px;
|
||||
padding: 0.7em;
|
||||
margin: 4px 10px;
|
||||
padding: 0.4em 0.7em;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
|
||||
code {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
line-height: normal;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
pre.selected {
|
||||
outline: 2px solid var(--markdown-outline-color);
|
||||
.codeblock-actions {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
border-radius: 4px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
background-color: var(--line-actions-bg-color);
|
||||
backdrop-filter: blur(8px);
|
||||
|
||||
i {
|
||||
color: var(--line-actions-inactive-color);
|
||||
|
||||
&:first-child {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--line-actions-active-color);
|
||||
}
|
||||
|
||||
&.fa-check {
|
||||
color: var(--app-success-color);
|
||||
}
|
||||
|
||||
&.fa-square-terminal {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .codeblock-actions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
color: var(--app-text-color);
|
||||
background-color: var(--markdown-bg-color);
|
||||
font-family: var(--termfontfamily);
|
||||
border-radius: 4px;
|
||||
background-color: var(--markdown-bg-color);
|
||||
padding: 0.15em 0.5em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
pre.selected {
|
||||
outline: 2px solid var(--markdown-outline-color);
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -101,6 +140,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.markdown.content {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown > *:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { CopyButton } from "@/elements";
|
||||
import { clsx } from "clsx";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import * as mobx from "mobx";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
|
||||
import "./markdown.less";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
|
||||
function LinkRenderer(props: any): any {
|
||||
function Link(props: any): JSX.Element {
|
||||
let newUrl = "https://extern?" + encodeURIComponent(props.href);
|
||||
return (
|
||||
<a href={newUrl} target="_blank" rel={"noopener"}>
|
||||
@@ -21,96 +21,86 @@ function LinkRenderer(props: any): any {
|
||||
);
|
||||
}
|
||||
|
||||
function HeaderRenderer(props: any, hnum: number): any {
|
||||
function Header(props: any, hnum: number): JSX.Element {
|
||||
return <div className={clsx("title", "is-" + hnum)}>{props.children}</div>;
|
||||
}
|
||||
|
||||
function CodeRenderer(props: any): any {
|
||||
function Code(props: any): JSX.Element {
|
||||
return <code>{props.children}</code>;
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class CodeBlockMarkdown extends React.Component<
|
||||
{ children: React.ReactNode; codeSelectSelectedIndex?: number; uuid: string },
|
||||
{}
|
||||
> {
|
||||
blockIndex: number;
|
||||
blockRef: React.RefObject<HTMLPreElement>;
|
||||
const CodeBlock = mobxReact.observer(
|
||||
(props: { children: React.ReactNode; onClickExecute?: (cmd: string) => void }): JSX.Element => {
|
||||
const copied: OV<boolean> = mobx.observable.box(false, { name: "copied" });
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.blockRef = React.createRef();
|
||||
this.blockIndex = GlobalModel.inputModel.addCodeBlockToCodeSelect(this.blockRef, this.props.uuid);
|
||||
}
|
||||
|
||||
render() {
|
||||
let clickHandler: (e: React.MouseEvent<HTMLElement>, blockIndex: number) => void;
|
||||
let inputModel = GlobalModel.inputModel;
|
||||
clickHandler = (e: React.MouseEvent<HTMLElement>, blockIndex: number) => {
|
||||
inputModel.setCodeSelectSelectedCodeBlock(blockIndex);
|
||||
const getTextContent = (children: any) => {
|
||||
if (typeof children === "string") {
|
||||
return children;
|
||||
} else if (Array.isArray(children)) {
|
||||
return children.map(getTextContent).join("");
|
||||
} else if (children.props && children.props.children) {
|
||||
return getTextContent(children.props.children);
|
||||
}
|
||||
return "";
|
||||
};
|
||||
let selected = this.blockIndex == this.props.codeSelectSelectedIndex;
|
||||
|
||||
const handleCopy = async (e: React.MouseEvent) => {
|
||||
let textToCopy = getTextContent(props.children);
|
||||
textToCopy = textToCopy.replace(/\n$/, ""); // remove trailing newline
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
copied.set(true);
|
||||
setTimeout(() => copied.set(false), 2000); // Reset copied state after 2 seconds
|
||||
};
|
||||
|
||||
const handleExecute = (e: React.MouseEvent) => {
|
||||
let textToCopy = getTextContent(props.children);
|
||||
textToCopy = textToCopy.replace(/\n$/, ""); // remove trailing newline
|
||||
if (props.onClickExecute) {
|
||||
props.onClickExecute(textToCopy);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<pre
|
||||
ref={this.blockRef}
|
||||
className={clsx({ selected: selected })}
|
||||
onClick={(event) => clickHandler(event, this.blockIndex)}
|
||||
>
|
||||
{this.props.children}
|
||||
<pre className="codeblock">
|
||||
{props.children}
|
||||
<div className="codeblock-actions">
|
||||
<CopyButton className="copy-button" onClick={handleCopy} title="Copy" />
|
||||
<If condition={props.onClickExecute}>
|
||||
<i className="fa-regular fa-square-terminal" onClick={handleExecute}></i>
|
||||
</If>
|
||||
</div>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@mobxReact.observer
|
||||
class Markdown extends React.Component<
|
||||
{ text: string; style?: any; extraClassName?: string; codeSelect?: boolean },
|
||||
{
|
||||
text: string;
|
||||
style?: any;
|
||||
className?: string;
|
||||
onClickExecute?: (cmd: string) => void;
|
||||
},
|
||||
{}
|
||||
> {
|
||||
curUuid: string;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.curUuid = uuidv4();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
CodeBlockRenderer(props: any, codeSelect: boolean, codeSelectIndex: number, curUuid: string): any {
|
||||
if (codeSelect) {
|
||||
return (
|
||||
<CodeBlockMarkdown codeSelectSelectedIndex={codeSelectIndex} uuid={curUuid}>
|
||||
{props.children}
|
||||
</CodeBlockMarkdown>
|
||||
);
|
||||
} else {
|
||||
const clickHandler = (e: React.MouseEvent<HTMLElement>) => {
|
||||
let blockText = (e.target as HTMLElement).innerText;
|
||||
if (blockText) {
|
||||
blockText = blockText.replace(/\n$/, ""); // remove trailing newline
|
||||
navigator.clipboard.writeText(blockText);
|
||||
}
|
||||
};
|
||||
return <pre onClick={(event) => clickHandler(event)}>{props.children}</pre>;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let text = this.props.text;
|
||||
let codeSelect = this.props.codeSelect;
|
||||
let curCodeSelectIndex = GlobalModel.inputModel.getCodeSelectSelectedIndex();
|
||||
let { text, className, onClickExecute } = this.props;
|
||||
let markdownComponents = {
|
||||
a: LinkRenderer,
|
||||
h1: (props) => HeaderRenderer(props, 1),
|
||||
h2: (props) => HeaderRenderer(props, 2),
|
||||
h3: (props) => HeaderRenderer(props, 3),
|
||||
h4: (props) => HeaderRenderer(props, 4),
|
||||
h5: (props) => HeaderRenderer(props, 5),
|
||||
h6: (props) => HeaderRenderer(props, 6),
|
||||
code: (props) => CodeRenderer(props),
|
||||
pre: (props) => this.CodeBlockRenderer(props, codeSelect, curCodeSelectIndex, this.curUuid),
|
||||
a: Link,
|
||||
h1: (props) => <Header {...props} hnum={1} />,
|
||||
h2: (props) => <Header {...props} hnum={2} />,
|
||||
h3: (props) => <Header {...props} hnum={3} />,
|
||||
h4: (props) => <Header {...props} hnum={4} />,
|
||||
h5: (props) => <Header {...props} hnum={5} />,
|
||||
h6: (props) => <Header {...props} hnum={6} />,
|
||||
code: Code,
|
||||
pre: (props) => <CodeBlock {...props} onClickExecute={onClickExecute} />,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx("markdown content", this.props.extraClassName)} style={this.props.style}>
|
||||
<div className={clsx("markdown content", className)} style={this.props.style}>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||
{text}
|
||||
</ReactMarkdown>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
@dot-width: 11px;
|
||||
@dot-color: var(--app-success-color);
|
||||
@speed: 1.5s;
|
||||
|
||||
.typing {
|
||||
position: relative;
|
||||
height: @dot-width;
|
||||
|
||||
span {
|
||||
content: "";
|
||||
animation: blink @speed infinite;
|
||||
animation-fill-mode: both;
|
||||
height: @dot-width;
|
||||
width: @dot-width;
|
||||
background: @dot-color;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: 50%;
|
||||
|
||||
&:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
margin-left: @dot-width * 1.5;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
margin-left: @dot-width * 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
20% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
import "./typingindicator.less";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
const TypingIndicator: React.FC<Props> = (props: { className?: string }) => {
|
||||
return (
|
||||
<div className={clsx("typing", props.className)}>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { TypingIndicator };
|
||||
@@ -135,14 +135,12 @@ export const StatusIndicator: React.FC<StatusIndicatorProps> = (props) => {
|
||||
*/
|
||||
React.useEffect(() => {
|
||||
if (runningCommands && !timeoutState) {
|
||||
console.log("show spinner");
|
||||
setTimeoutState(
|
||||
setTimeout(() => {
|
||||
setSpinnerVisible(true);
|
||||
}, 100)
|
||||
);
|
||||
} else if (!runningCommands) {
|
||||
console.log("clear spinner");
|
||||
clearSpinnerTimeout();
|
||||
}
|
||||
return () => {
|
||||
|
||||
@@ -114,6 +114,25 @@ class LineActions extends React.Component<{ screen: LineContainerType; line: Lin
|
||||
GlobalCommandRunner.lineRestart(line.lineid, true);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
clickChat(e: any) {
|
||||
e.stopPropagation();
|
||||
const { line, screen } = this.props;
|
||||
const termWrap = screen.getTermWrap(line.lineid);
|
||||
const cmd = screen.getCmd(line);
|
||||
if (termWrap && cmd) {
|
||||
GlobalModel.sidebarchatModel.setCmdAndOutput(
|
||||
screen.getCmd(line)?.getCmdStr(),
|
||||
termWrap?.getOutput(false),
|
||||
// multiply by 2 to get a rough estimate of the number of rows within the chatbox
|
||||
screen.getUsedRows(lineutil.getRendererContext(line), line, cmd, 300) * 2,
|
||||
cmdShouldMarkError(cmd)
|
||||
);
|
||||
GlobalModel.inputModel.setChatSidebarFocus();
|
||||
GlobalModel.sidebarchatModel.resetSelectedCodeBlockIndex();
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
clickMinimize() {
|
||||
const { line } = this.props;
|
||||
@@ -154,10 +173,14 @@ class LineActions extends React.Component<{ screen: LineContainerType; line: Lin
|
||||
const { line, screen } = this.props;
|
||||
const isMinimized = line.linestate["wave:min"];
|
||||
const containerType = screen.getContainerType();
|
||||
// console.log("******************", screen.getTermWrap(line.lineid));
|
||||
return (
|
||||
<div className="line-actions">
|
||||
<Choose>
|
||||
<When condition={containerType == appconst.LineContainer_Main}>
|
||||
<div key="chat" title="Restart Command" className="line-icon" onClick={this.clickChat}>
|
||||
<i className="fa-sharp fa-regular fa-sparkles fa-fw" />
|
||||
</div>
|
||||
<div key="restart" title="Restart Command" className="line-icon" onClick={this.clickRestart}>
|
||||
<i className="fa-sharp fa-regular fa-arrows-rotate fa-fw" />
|
||||
</div>
|
||||
@@ -514,7 +537,6 @@ class LineCmd extends React.Component<
|
||||
const lastHeight = this.lastHeight;
|
||||
this.lastHeight = curHeight;
|
||||
this.props.onHeightChange(line.linenum, curHeight, lastHeight);
|
||||
// console.log("line height change: ", line.linenum, lastHeight, "=>", curHeight);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
|
||||
@@ -23,10 +23,10 @@ let MagicLayout = {
|
||||
MainSidebarDefaultWidth: 240,
|
||||
|
||||
RightSidebarMinWidth: 0,
|
||||
RightSidebarMaxWidth: 300,
|
||||
RightSidebarMaxWidth: 700,
|
||||
RightSidebarSnapThreshold: 90,
|
||||
RightSidebarDragResistance: 50,
|
||||
RightSidebarDefaultWidth: 240,
|
||||
RightSidebarDefaultWidth: 400,
|
||||
};
|
||||
|
||||
let m = MagicLayout;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
.sidebarchat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
> .scrollable {
|
||||
flex-flow: column nowrap;
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
overflow-y: auto;
|
||||
|
||||
.chat-window {
|
||||
display: flex;
|
||||
// margin-bottom: 5px;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
// This is the filler that will push the chat messages to the bottom until the chat window is full
|
||||
.filler {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
> * {
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-msg {
|
||||
padding: calc(var(--termpad) * 2);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
.chat-msg-header {
|
||||
display: flex;
|
||||
margin-bottom: 2px;
|
||||
|
||||
i {
|
||||
margin-top: 2px;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-msg-assistant {
|
||||
color: var(--app-text-color);
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-msg-error {
|
||||
color: var(--cmdinput-text-error);
|
||||
font-family: var(--markdown-font);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.typing-indicator {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebarchat-input-wrapper {
|
||||
padding: 16px 15px 7px;
|
||||
flex: 0 0 auto;
|
||||
border-top: 1px solid var(--app-border-color);
|
||||
|
||||
.chat-textarea {
|
||||
color: var(--app-text-primary-color);
|
||||
background-color: var(--cmdinput-textarea-bg);
|
||||
resize: none;
|
||||
width: 100%;
|
||||
border: transparent;
|
||||
outline: none;
|
||||
overflow: auto;
|
||||
overflow-wrap: anywhere;
|
||||
font-family: var(--termfontfamily);
|
||||
font-weight: normal;
|
||||
line-height: var(--termlineheight);
|
||||
height: 21px;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user