mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f1a6dfb249 | |||
| 6319b26924 | |||
| 41fe49a54c | |||
| e044487489 | |||
| 0fdcd27fee | |||
| d923de412a | |||
| 15485d7235 | |||
| 59aef86e77 | |||
| 5353f40a20 | |||
| f86f010a34 |
+4
-1
@@ -18,6 +18,7 @@
|
||||
"appId": "dev.commandline.waveterm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lexical/react": "^0.14.3",
|
||||
"@monaco-editor/react": "^4.5.1",
|
||||
"@table-nav/core": "^0.0.7",
|
||||
"@table-nav/react": "^0.0.7",
|
||||
@@ -31,6 +32,7 @@
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"electron-updater": "^6.1.8",
|
||||
"framer-motion": "^10.16.16",
|
||||
"lexical": "^0.14.3",
|
||||
"mobx": "6.12",
|
||||
"mobx-react": "^7.5.0",
|
||||
"monaco-editor": "^0.44.0",
|
||||
@@ -41,6 +43,7 @@
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-frame-component": "^5.2.6",
|
||||
"react-markdown": "^9.0.0",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
@@ -50,8 +53,8 @@
|
||||
"uuid": "^9.0.0",
|
||||
"winston": "^3.8.2",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-web-links": "^0.9.0",
|
||||
"xterm-addon-serialize": "^0.11.0",
|
||||
"xterm-addon-web-links": "^0.9.0",
|
||||
"xterm-addon-webgl": "^0.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
+1
-1
@@ -146,7 +146,7 @@ class App extends React.Component<{}, {}> {
|
||||
<If condition={GlobalModel.isDev && rightSidebarCollapsed && activeMainView == "session"}>
|
||||
<div className="right-sidebar-triggers">
|
||||
<Button className="secondary ghost right-sidebar-trigger" onClick={this.openRightSidebar}>
|
||||
<i className="fa-sharp fa-regular fa-lightbulb"></i>
|
||||
<i className="fa-sharp fa-solid fa-sidebar-flip"></i>
|
||||
</Button>
|
||||
</div>
|
||||
</If>
|
||||
|
||||
@@ -6,7 +6,7 @@ import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import { GlobalModel, GlobalCommandRunner, RemotesModel, getApi } from "@/models";
|
||||
import { GlobalModel, GlobalCommandRunner, RemotesModel } from "@/models";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "@/common/elements";
|
||||
import { commandRtnHandler, isBlank } from "@/util/util";
|
||||
import { getTermThemes } from "@/util/themeutil";
|
||||
@@ -70,7 +70,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
return;
|
||||
}
|
||||
const prtn = GlobalCommandRunner.setTheme(themeSource, false);
|
||||
getApi().setNativeThemeSource(themeSource);
|
||||
GlobalModel.getElectronApi().setNativeThemeSource(themeSource);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
prtn = GlobalCommandRunner.releaseCheckAutoOff(false);
|
||||
}
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
getApi().changeAutoUpdate(val);
|
||||
GlobalModel.getElectronApi().changeAutoUpdate(val);
|
||||
}
|
||||
|
||||
getFontSizes(): DropdownItem[] {
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { GlobalModel, getApi } from "@/models";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { Modal, LinkButton } from "@/elements";
|
||||
import * as util from "@/util/util";
|
||||
import * as appconst from "@/app/appconst";
|
||||
@@ -26,7 +26,7 @@ class AboutModal extends React.Component<{}, {}> {
|
||||
|
||||
@boundMethod
|
||||
updateApp(): void {
|
||||
getApi().installAppUpdate();
|
||||
GlobalModel.getElectronApi().installAppUpdate();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
|
||||
@@ -226,13 +226,12 @@
|
||||
}
|
||||
|
||||
.ts,
|
||||
.termopts,
|
||||
.renderer {
|
||||
.termopts {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.renderer .renderer-icon {
|
||||
margin-right: 0.5em;
|
||||
.renderer-icon {
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
|
||||
.metapart-mono {
|
||||
|
||||
@@ -291,10 +291,10 @@ class LineHeader extends React.Component<{ screen: LineContainerType; line: Line
|
||||
</div>
|
||||
<If condition={!isBlank(renderer) && renderer != "terminal"}>
|
||||
<div className="meta-divider">|</div>
|
||||
<div className="renderer">
|
||||
<div className="renderer-icon">
|
||||
<i className="fa-sharp fa-solid fa-fill renderer-icon" />
|
||||
{renderer}
|
||||
</div>
|
||||
<div className="renderer-name">{renderer}</div>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
@@ -665,6 +665,83 @@ class LineCmd extends React.Component<
|
||||
};
|
||||
};
|
||||
|
||||
@boundMethod
|
||||
handleContextMenu(e: React.MouseEvent) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
let { line, screen } = this.props;
|
||||
const containerType = screen.getContainerType();
|
||||
const isMainContainer = containerType == appconst.LineContainer_Main;
|
||||
const cmd = screen.getCmd(line);
|
||||
let menu: ContextMenuItem[] = [
|
||||
{ role: "copy", label: "Copy", type: "normal" },
|
||||
{ role: "paste", label: "Paste", type: "normal" },
|
||||
{ type: "separator" },
|
||||
{ label: "Copy Command", click: () => this.copyCommandStr() },
|
||||
];
|
||||
const isTerminal = isBlank(line.renderer) || line.renderer == "terminal";
|
||||
if (isTerminal) {
|
||||
menu.push({ label: "Copy Visible Output", click: () => this.copyOutput(false) });
|
||||
menu.push({ label: "Copy Full Output", click: () => this.copyOutput(true) });
|
||||
}
|
||||
if (isMainContainer) {
|
||||
menu.push({ type: "separator" });
|
||||
const isMinimized = line.linestate["wave:min"];
|
||||
if (isMinimized) {
|
||||
menu.push({
|
||||
label: "Show Block Output",
|
||||
click: () => GlobalCommandRunner.lineMinimize(line.lineid, false, true),
|
||||
});
|
||||
} else {
|
||||
menu.push({
|
||||
label: "Hide Block Output",
|
||||
click: () => GlobalCommandRunner.lineMinimize(line.lineid, true, true),
|
||||
});
|
||||
}
|
||||
if (cmd?.isRunning()) {
|
||||
menu.push({ type: "separator" });
|
||||
menu.push({
|
||||
label: "Send Signal",
|
||||
type: "submenu",
|
||||
submenu: [
|
||||
{ label: "SIGINT", click: () => GlobalCommandRunner.lineSignal(line.lineid, "SIGINT", true) },
|
||||
{ label: "SIGTERM", click: () => GlobalCommandRunner.lineSignal(line.lineid, "SIGTERM", true) },
|
||||
{ label: "SIGKILL", click: () => GlobalCommandRunner.lineSignal(line.lineid, "SIGKILL", true) },
|
||||
{ type: "separator" },
|
||||
{ label: "SIGUSR1", click: () => GlobalCommandRunner.lineSignal(line.lineid, "SIGUSR1", true) },
|
||||
{ label: "SIGUSR2", click: () => GlobalCommandRunner.lineSignal(line.lineid, "SIGUSR2", true) },
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
menu.push({ type: "separator" });
|
||||
menu.push({ label: "Restart Line", click: () => GlobalCommandRunner.lineRestart(line.lineid, true) });
|
||||
menu.push({ type: "separator" });
|
||||
menu.push({ label: "Delete Block", click: () => GlobalCommandRunner.lineDelete(line.lineid, true) });
|
||||
}
|
||||
GlobalModel.contextMenuModel.showContextMenu(menu, { x: e.clientX, y: e.clientY });
|
||||
}
|
||||
|
||||
copyCommandStr() {
|
||||
const { line, screen } = this.props;
|
||||
const cmd: Cmd = screen.getCmd(line);
|
||||
if (cmd != null) {
|
||||
navigator.clipboard.writeText(cmd.getCmdStr());
|
||||
}
|
||||
}
|
||||
|
||||
copyOutput(fullOutput: boolean) {
|
||||
const { line, screen } = this.props;
|
||||
let termWrap = screen.getTermWrap(line.lineid);
|
||||
if (termWrap == null) {
|
||||
return;
|
||||
}
|
||||
let outputStr = termWrap.getOutput(fullOutput);
|
||||
if (fullOutput != null) {
|
||||
navigator.clipboard.writeText(outputStr);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { screen, line, width, staticRender, visible } = this.props;
|
||||
const isVisible = visible.get();
|
||||
@@ -750,6 +827,7 @@ class LineCmd extends React.Component<
|
||||
data-lineid={line.lineid}
|
||||
data-linenum={line.linenum}
|
||||
data-screenid={line.screenid}
|
||||
onContextMenu={this.handleContextMenu}
|
||||
>
|
||||
<If condition={isSelected || cmdError}>
|
||||
<div key="mask" className={cn("line-mask", { "error-mask": cmdError })}></div>
|
||||
|
||||
@@ -22,6 +22,23 @@
|
||||
border-bottom: 1px solid var(--app-border-color);
|
||||
}
|
||||
|
||||
.rsb-modes {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 5px 10px;
|
||||
gap: 5px;
|
||||
border-bottom: 1px solid var(--app-border-color);
|
||||
|
||||
.icon-container {
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-container:hover {
|
||||
background-color: var(--app-selected-mask-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// Copyright 2023-2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import dayjs from "dayjs";
|
||||
import { If, For } from "tsx-control-statements/components";
|
||||
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { ResizableSidebar, Button } from "@/elements";
|
||||
import { WaveBookDisplay } from "./wavebook";
|
||||
|
||||
import "./right.less";
|
||||
|
||||
@@ -55,6 +57,14 @@ class KeybindDevPane extends React.Component<{}, {}> {
|
||||
|
||||
@mobxReact.observer
|
||||
class RightSideBar extends React.Component<RightSideBarProps, {}> {
|
||||
mode: OV<string> = mobx.observable.box(null, { name: "RightSideBar-mode" });
|
||||
|
||||
setMode(mode: string) {
|
||||
mobx.action(() => {
|
||||
this.mode.set(mode);
|
||||
})();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ResizableSidebar
|
||||
@@ -71,9 +81,31 @@ class RightSideBar extends React.Component<RightSideBarProps, {}> {
|
||||
<i className="fa-sharp fa-regular fa-xmark"></i>
|
||||
</Button>
|
||||
</div>
|
||||
<If condition={GlobalModel.isDev}>
|
||||
<div className="rsb-modes">
|
||||
<div className="flex-spacer" />
|
||||
<If condition={GlobalModel.isDev}>
|
||||
<div
|
||||
className="icon-container"
|
||||
title="Show Keybinding Debugger"
|
||||
onClick={() => this.setMode("keybind")}
|
||||
>
|
||||
<i className="fa-fw fa-sharp fa-keyboard fa-solid" />
|
||||
</div>
|
||||
</If>
|
||||
<div
|
||||
className="icon-container"
|
||||
title="Show Keybinding Debugger"
|
||||
onClick={() => this.setMode("wavebook")}
|
||||
>
|
||||
<i className="fa-sharp fa-solid fa-book-sparkles"></i>
|
||||
</div>
|
||||
</div>
|
||||
<If condition={this.mode.get() == "keybind"}>
|
||||
<KeybindDevPane></KeybindDevPane>
|
||||
</If>
|
||||
<If condition={this.mode.get() == "wavebook"}>
|
||||
<WaveBookDisplay></WaveBookDisplay>
|
||||
</If>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</ResizableSidebar>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { If, For } from "tsx-control-statements/components";
|
||||
import { GlobalModel } from "@/models";
|
||||
|
||||
import * as lexical from "lexical";
|
||||
import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
|
||||
import { LexicalComposer } from "@lexical/react/LexicalComposer";
|
||||
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
|
||||
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
||||
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
|
||||
import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
|
||||
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
|
||||
import { $convertFromMarkdownString, $convertToMarkdownString, TRANSFORMERS } from "@lexical/markdown";
|
||||
import { CodeNode, CodeHighlightNode } from "@lexical/code";
|
||||
import { LinkNode } from "@lexical/link";
|
||||
import { ListNode, ListItemNode } from "@lexical/list";
|
||||
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
|
||||
import { HorizontalRuleNode } from "@lexical/react/LexicalHorizontalRuleNode";
|
||||
import { KeybindManager } from "@/util/keyutil";
|
||||
|
||||
const theme = {
|
||||
// Theme styling goes here
|
||||
};
|
||||
|
||||
class WaveBookKeybindings extends React.Component<{}, {}> {
|
||||
componentDidMount(): void {
|
||||
const keybindManager = GlobalModel.keybindManager;
|
||||
keybindManager.registerKeybinding("pane", "wavebook", "generic:confirm", (waveEvent) => {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
componentWillUnmount(): void {
|
||||
const keybindManager = GlobalModel.keybindManager;
|
||||
keybindManager.unregisterDomain("wavebook");
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class WaveBookDisplay extends React.Component<{}, {}> {
|
||||
render() {
|
||||
return "playbooks";
|
||||
}
|
||||
}
|
||||
|
||||
export { WaveBookDisplay };
|
||||
+29
-6
@@ -470,6 +470,35 @@ electron.ipcMain.on("toggle-developer-tools", (event) => {
|
||||
event.returnValue = true;
|
||||
});
|
||||
|
||||
function convertMenuDefArrToMenu(menuDefArr: ElectronContextMenuItem[]): electron.Menu {
|
||||
const menuItems: electron.MenuItem[] = [];
|
||||
for (const menuDef of menuDefArr) {
|
||||
const menuItemTemplate: electron.MenuItemConstructorOptions = {
|
||||
role: menuDef.role as any,
|
||||
label: menuDef.label,
|
||||
type: menuDef.type,
|
||||
click: () => {
|
||||
MainWindow?.webContents.send("contextmenu-click", menuDef.id);
|
||||
},
|
||||
};
|
||||
if (menuDef.submenu != null) {
|
||||
menuItemTemplate.submenu = convertMenuDefArrToMenu(menuDef.submenu);
|
||||
}
|
||||
const menuItem = new electron.MenuItem(menuItemTemplate);
|
||||
menuItems.push(menuItem);
|
||||
}
|
||||
return electron.Menu.buildFromTemplate(menuItems);
|
||||
}
|
||||
|
||||
electron.ipcMain.on("contextmenu-show", (event, menuDefArr: ElectronContextMenuItem[], { x, y }) => {
|
||||
if (menuDefArr == null || menuDefArr.length == 0) {
|
||||
return;
|
||||
}
|
||||
const menu = convertMenuDefArrToMenu(menuDefArr);
|
||||
menu.popup({ x, y });
|
||||
event.returnValue = true;
|
||||
});
|
||||
|
||||
electron.ipcMain.on("hide-window", (event) => {
|
||||
if (MainWindow != null) {
|
||||
MainWindow.hide();
|
||||
@@ -676,12 +705,6 @@ function runWaveSrv() {
|
||||
return rtnPromise;
|
||||
}
|
||||
|
||||
electron.ipcMain.on("context-screen", (_, { screenId }, { x, y }) => {
|
||||
console.log("context-screen", screenId);
|
||||
const menu = getContextMenu();
|
||||
menu.popup({ x, y });
|
||||
});
|
||||
|
||||
electron.ipcMain.on("context-editmenu", (_, { x, y }, opts) => {
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
|
||||
@@ -27,8 +27,9 @@ contextBridge.exposeInMainWorld("api", {
|
||||
onAppUpdateStatus: (callback) => ipcRenderer.on("app-update-status", (_, val) => callback(val)),
|
||||
onZoomChanged: (callback) => ipcRenderer.on("zoom-changed", callback),
|
||||
onMenuItemAbout: (callback) => ipcRenderer.on("menu-item-about", callback),
|
||||
contextScreen: (screenOpts, position) => ipcRenderer.send("context-screen", screenOpts, position),
|
||||
contextEditMenu: (position, opts) => ipcRenderer.send("context-editmenu", position, opts),
|
||||
onWaveSrvStatusChange: (callback) => ipcRenderer.on("wavesrv-status-change", callback),
|
||||
onToggleDevUI: (callback) => ipcRenderer.on("toggle-devui", callback),
|
||||
showContextMenu: (menu, position) => ipcRenderer.send("contextmenu-show", menu, position),
|
||||
onContextMenuClick: (callback) => ipcRenderer.on("contextmenu-click", callback),
|
||||
});
|
||||
|
||||
@@ -9,7 +9,6 @@ import { App } from "@/app/app";
|
||||
import * as DOMPurify from "dompurify";
|
||||
import { loadFonts } from "@/util/fontutil";
|
||||
import * as textmeasure from "@/util/textmeasure";
|
||||
import { getApi } from "@/models";
|
||||
|
||||
// @ts-ignore
|
||||
let VERSION = __WAVETERM_VERSION__;
|
||||
|
||||
@@ -86,6 +86,10 @@ class CommandRunner {
|
||||
return GlobalModel.submitCommand("line", "restart", [lineArg], { nohist: "1" }, interactive);
|
||||
}
|
||||
|
||||
lineSignal(lineArg: string, signal: string, interactive: boolean): Promise<CommandRtnType> {
|
||||
return GlobalModel.submitCommand("signal", null, [lineArg, signal], { nohist: "1" }, interactive);
|
||||
}
|
||||
|
||||
lineSet(lineArg: string, opts: { renderer?: string }): Promise<CommandRtnType> {
|
||||
let kwargs = { nohist: "1" };
|
||||
if ("renderer" in opts) {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Model } from "./model";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
class ContextMenuModel {
|
||||
globalModel: Model;
|
||||
handlers: Map<string, () => void> = new Map(); // id -> handler
|
||||
|
||||
constructor(globalModel: Model) {
|
||||
this.globalModel = globalModel;
|
||||
this.globalModel.getElectronApi().onContextMenuClick(this.handleContextMenuClick.bind(this));
|
||||
}
|
||||
|
||||
handleContextMenuClick(e: any, id: string): void {
|
||||
let handler = this.handlers.get(id);
|
||||
if (handler) {
|
||||
handler();
|
||||
}
|
||||
}
|
||||
|
||||
_convertAndRegisterMenu(menu: ContextMenuItem[]): ElectronContextMenuItem[] {
|
||||
let electronMenuItems: ElectronContextMenuItem[] = [];
|
||||
for (let item of menu) {
|
||||
let electronItem: ElectronContextMenuItem = {
|
||||
role: item.role,
|
||||
type: item.type,
|
||||
label: item.label,
|
||||
id: uuidv4(),
|
||||
};
|
||||
if (item.click) {
|
||||
this.handlers.set(electronItem.id, item.click);
|
||||
}
|
||||
if (item.submenu) {
|
||||
electronItem.submenu = this._convertAndRegisterMenu(item.submenu);
|
||||
}
|
||||
electronMenuItems.push(electronItem);
|
||||
}
|
||||
return electronMenuItems;
|
||||
}
|
||||
|
||||
showContextMenu(menu: ContextMenuItem[], position: { x: number; y: number }): void {
|
||||
this.handlers.clear();
|
||||
const electronMenuItems = this._convertAndRegisterMenu(menu);
|
||||
this.globalModel.getElectronApi().showContextMenu(electronMenuItems, position);
|
||||
}
|
||||
}
|
||||
|
||||
export { ContextMenuModel };
|
||||
+1
-1
@@ -6,7 +6,7 @@ import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { isBlank } from "@/util/util";
|
||||
import * as appconst from "@/app/appconst";
|
||||
import { Model } from "./model";
|
||||
import type { Model } from "./model";
|
||||
import { GlobalCommandRunner } from "./global";
|
||||
import { app } from "electron";
|
||||
|
||||
|
||||
+8
-5
@@ -32,6 +32,7 @@ import { MainSidebarModel } from "./mainsidebar";
|
||||
import { RightSidebarModel } from "./rightsidebar";
|
||||
import { Screen } from "./screen";
|
||||
import { Cmd } from "./cmd";
|
||||
import { ContextMenuModel } from "./contextmenu";
|
||||
import { GlobalCommandRunner } from "./global";
|
||||
import { clearMonoFontCache, getMonoFontSize } from "@/util/textmeasure";
|
||||
import type { TermWrap } from "@/plugins/terminal/term";
|
||||
@@ -121,6 +122,7 @@ class Model {
|
||||
modalsModel: ModalsModel;
|
||||
mainSidebarModel: MainSidebarModel;
|
||||
rightSidebarModel: RightSidebarModel;
|
||||
contextMenuModel: ContextMenuModel;
|
||||
isDarkTheme: OV<boolean> = mobx.observable.box(getApi().getShouldUseDarkColors(), {
|
||||
name: "isDarkTheme",
|
||||
});
|
||||
@@ -177,6 +179,7 @@ class Model {
|
||||
this.modalsModel = new ModalsModel();
|
||||
this.mainSidebarModel = new MainSidebarModel(this);
|
||||
this.rightSidebarModel = new RightSidebarModel(this);
|
||||
this.contextMenuModel = new ContextMenuModel(this);
|
||||
const isWaveSrvRunning = getApi().getWaveSrvStatus();
|
||||
this.waveSrvRunning = mobx.observable.box(isWaveSrvRunning, {
|
||||
name: "model-wavesrv-running",
|
||||
@@ -378,7 +381,7 @@ class Model {
|
||||
refocus() {
|
||||
// givefocus() give back focus to cmd or input
|
||||
const activeScreen = this.getActiveScreen();
|
||||
if (screen == null) {
|
||||
if (activeScreen == null) {
|
||||
return;
|
||||
}
|
||||
activeScreen.giveFocus();
|
||||
@@ -707,10 +710,6 @@ class Model {
|
||||
GlobalCommandRunner.setTermUsedRows(context, height);
|
||||
}
|
||||
|
||||
contextScreen(e: any, screenId: string) {
|
||||
getApi().contextScreen({ screenId: screenId }, { x: e.x, y: e.y });
|
||||
}
|
||||
|
||||
contextEditMenu(e: any, opts: ContextMenuOpts) {
|
||||
getApi().contextEditMenu({ x: e.x, y: e.y }, opts);
|
||||
}
|
||||
@@ -1801,6 +1800,10 @@ class Model {
|
||||
this.appUpdateStatus.set(status);
|
||||
})();
|
||||
}
|
||||
|
||||
getElectronApi(): ElectronApi {
|
||||
return getApi();
|
||||
}
|
||||
}
|
||||
|
||||
export { Model, getApi };
|
||||
|
||||
@@ -129,4 +129,56 @@ class PacketDataBuffer extends PtyDataBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
export { PtyDataBuffer, PacketDataBuffer };
|
||||
class JsonLinesDataBuffer extends PtyDataBuffer {
|
||||
parsePos: number;
|
||||
callback: (any) => void;
|
||||
|
||||
constructor(callback: (any) => void) {
|
||||
super();
|
||||
this.parsePos = 0;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
super.reset();
|
||||
this.parsePos = 0;
|
||||
}
|
||||
|
||||
processLine(line: string) {
|
||||
if (line.length == 0) {
|
||||
return;
|
||||
}
|
||||
let jsonVal: any = null;
|
||||
try {
|
||||
jsonVal = JSON.parse(line.trim());
|
||||
} catch (e) {
|
||||
console.log("invalid json", line, e);
|
||||
return;
|
||||
}
|
||||
if (jsonVal != null) {
|
||||
this.callback(jsonVal);
|
||||
}
|
||||
}
|
||||
|
||||
parseData() {
|
||||
for (let i = this.parsePos; i < this.dataSize; i++) {
|
||||
let ch = this.rawData[i];
|
||||
if (ch == NewLineCharCode) {
|
||||
// line does *not* include the newline
|
||||
let line = new TextDecoder().decode(
|
||||
new Uint8Array(this.rawData.buffer, this.parsePos, i - this.parsePos)
|
||||
);
|
||||
this.parsePos = i + 1;
|
||||
this.processLine(line);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
receiveData(pos: number, data: Uint8Array, reason?: string): void {
|
||||
super.receiveData(pos, data, reason);
|
||||
this.parseData();
|
||||
}
|
||||
}
|
||||
|
||||
export { PtyDataBuffer, PacketDataBuffer, JsonLinesDataBuffer };
|
||||
|
||||
@@ -9,6 +9,7 @@ import { CSVRenderer } from "./csv/csv";
|
||||
import { OpenAIRenderer, OpenAIRendererModel } from "./openai/openai";
|
||||
import { SimplePdfRenderer } from "./pdf/pdf";
|
||||
import { SimpleMediaRenderer } from "./media/media";
|
||||
import { WaveAppRenderer, WaveAppRendererModel } from "./waveapp/waveapp";
|
||||
import { isBlank } from "@/util/util";
|
||||
import { sprintf } from "sprintf-js";
|
||||
|
||||
@@ -100,6 +101,18 @@ const PluginConfigs: RendererPluginType[] = [
|
||||
mimeTypes: ["video/*", "audio/*"],
|
||||
simpleComponent: SimpleMediaRenderer,
|
||||
},
|
||||
{
|
||||
name: "waveapp",
|
||||
rendererType: "full",
|
||||
heightType: "pixels",
|
||||
dataType: "model",
|
||||
collapseType: "remove",
|
||||
hidePrompt: true,
|
||||
globalCss: null,
|
||||
mimeTypes: ["application/x-waveapp"],
|
||||
fullComponent: WaveAppRenderer,
|
||||
modelCtor: () => new WaveAppRendererModel(),
|
||||
},
|
||||
];
|
||||
|
||||
class PluginModelClass {
|
||||
|
||||
@@ -287,6 +287,39 @@ class TermWrap {
|
||||
return usedRows;
|
||||
}
|
||||
|
||||
// gets the text output of the terminal (respects line wrapping)
|
||||
// if fullOutput is true, returns all output, otherwise only the visible output
|
||||
getOutput(fullOutput: boolean): string {
|
||||
let activeBuf = this.terminal?.buffer?.active;
|
||||
if (activeBuf == null) {
|
||||
return null;
|
||||
}
|
||||
const totalLines = activeBuf.length;
|
||||
let output = [];
|
||||
let emptyStart = -1;
|
||||
let startLine = fullOutput ? 0 : activeBuf.viewportY;
|
||||
for (let i = startLine; i < totalLines; i++) {
|
||||
const line = activeBuf.getLine(i);
|
||||
const lineStr = line?.translateToString(true) ?? "";
|
||||
if (lineStr == "") {
|
||||
if (emptyStart == -1) {
|
||||
emptyStart = output.length;
|
||||
}
|
||||
} else {
|
||||
emptyStart = -1;
|
||||
}
|
||||
if (line?.isWrapped) {
|
||||
output[output.length - 1] += lineStr;
|
||||
} else {
|
||||
output.push(lineStr);
|
||||
}
|
||||
}
|
||||
if (emptyStart != -1) {
|
||||
output = output.slice(0, emptyStart);
|
||||
}
|
||||
return output.join("\n");
|
||||
}
|
||||
|
||||
updateUsedRows(forceFull: boolean, reason: string) {
|
||||
if (this.terminal == null) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# WaveApp Plugin
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user