Compare commits

..

22 Commits

Author SHA1 Message Date
Mike Sawka 936537a03a put in some code to fix losing focus on the screen name (#679) 2024-06-14 17:24:36 -07:00
Red J Adaya 66310e8bfb fix pre tag bg color in markdown (#678) 2024-06-14 10:00:53 -07:00
Mike Sawka 2361154551 never log ephemeral commands to history (#676) 2024-06-07 17:58:21 -07:00
Red J Adaya 7888a61766 Fix regression where click event in the DOM is no longer being captured (#675) 2024-06-07 17:41:05 -07:00
Evan Simkowitz 29ad232ac6 Don't show ghost text if curLine is empty (#674) 2024-06-07 17:40:50 -07:00
Red J Adaya 067bef0cb5 Remove old aichat and some fixes (#672) 2024-06-06 20:09:57 -07:00
Mike Sawka 97deb2ef14 update ghost text color (#671) 2024-06-06 19:24:06 -07:00
Mike Sawka 69d58ef525 bigger rightsidebar by default. also remove ctrl:space keybinding (#670) 2024-06-06 18:45:28 -07:00
Red J Adaya d7173c970c aichat improvements (#667) 2024-06-06 17:31:52 -07:00
Mike Sawka 1234919229 enable right sidebar (#663) 2024-05-28 22:44:57 -07:00
Mike Sawka 652250ac3c bump electron to 30.0.8 (#662) 2024-05-28 21:01:18 -07:00
Mike Sawka 11b110d633 bump version to 0.7.6 (#661) 2024-05-28 20:55:40 -07:00
Red J Adaya 14b38b31c8 AI Chat Sidebar (#657) 2024-05-28 20:53:16 -07:00
SG Prakash 1ace947d1e added hide/show MenuBar feature (#660)
**Description:**
I recently installed waveterm and I just love it. I like to use my
terminal without titlebar, So I removed the titlebar of waveterm in my
ubuntu desktop but the menu bar is showing constantly. So I look into
the code and made the changes. This pull request adds a simple feature
which will hide/show the menubar using `ALT` key.

**Changes Made:**
1. When initializing electron's BrowserWindow, I added a parameter
`autoHideMenuBar: true`.

**Testing Done:**
Tested the feature by pressing `ALT` Key. It works perfectly.
2024-05-28 20:48:28 -07:00
Evan Simkowitz 13f4203437 Autocomplete using the Newton parser (#599)
I've reworked the autocomplete parser to more closely match Newton, the
Fig-compatible parser I prototyped earlier this year. I was able to move
a lot faster by reusing patterns that inshellisense proved out, such as
for templates and generators.

I also support some features that inshellisense doesn't, like proper
combining of Posix-compatible flags, handling of option argument
separators, and handling of cursors in insertValues.
2024-05-28 17:17:29 -07:00
orliesaurus cd15beba26 fix: yml formatting issues (#656)
looks like there were some issues to the yml format before that caused
the workflow to not run.
2024-05-21 16:02:30 -07:00
Mike Sawka aac9e27382 upgrade electron version (#658) 2024-05-16 12:48:37 -07:00
Ian Jennings d07068c09d TestDriver v2.12.12 (#655) 2024-05-08 11:10:17 -07:00
Cole Lashley 1307a77b56 ai chat selection fix (#639) 2024-05-06 20:02:13 -07:00
Ian Jennings 2772c76728 Upgrade TestDriver to v2.12.5 (#652) 2024-05-06 14:17:25 -07:00
Evan Simkowitz 4384525ca6 Remove "main" window concept from emain.ts (#651)
This continues the work to unblock multiple window support. `emain.ts`
now contains no more references to a "main" window, instead all event
handlers will dispatch to either the window that triggered the event,
the currently focused window, or the first window to be spun up.
2024-05-06 13:43:27 -07:00
Mike Sawka d6b82e3792 blockstore integration (#650)
* blockstore migration
* use a testdb for tests,
* cut down test sizes so they run a bit faster
* test init/cleanup code improvements
2024-05-06 10:24:34 -07:00
78 changed files with 19268 additions and 905 deletions
+3 -3
View File
@@ -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
+4 -4
View File
@@ -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"]
}
]
+14710
View File
File diff suppressed because it is too large Load Diff
+6 -4
View File
@@ -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",
@@ -42,14 +43,13 @@
"overlayscrollbars-react": "^0.5.5",
"papaparse": "^5.4.1",
"react": "^18.1.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.1.0",
"react-markdown": "^9.0.0",
"remark": "^15.0.1",
"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",
@@ -79,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",
+1
View File
@@ -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);
+1
View File
@@ -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
View File
@@ -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>
+1
View File
@@ -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";
+33 -2
View File
@@ -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>
+8 -2
View File
@@ -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>
) : (
+1
View File
@@ -19,3 +19,4 @@ export { TabIcon } from "./tabicon";
export { DatePicker } from "./datepicker";
export { TermStyleList } from "./termstyle";
export { CopyButton } from "./copybutton";
export { TypingIndicator } from "./typingindicator";
+54 -11
View File
@@ -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;
}
+63 -73
View File
@@ -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 };
-2
View File
@@ -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 () => {
+23 -1
View File
@@ -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
+2 -2
View File
@@ -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;
+89
View File
@@ -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