Compare commits

..

2 Commits

Author SHA1 Message Date
sawka b271c55054 bump to v0.6.2 2024-02-10 19:41:23 -03:00
sawka 592e023441 backport zdotdir fix to v0.6.x release 2024-02-10 19:34:56 -03:00
180 changed files with 10132 additions and 12105 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
name: "Build Helper"
on: workflow_dispatch
env:
WAVETERM_VERSION: 0.6.1
WAVETERM_VERSION: 0.6.2
GO_VERSION: "1.21.5"
NODE_VERSION: "21.5.0"
jobs:
+1 -3
View File
@@ -10,7 +10,7 @@
# Wave Terminal
A open-source, cross-platform, AI-integrated, modern terminal for seamless workflows.
A open-source, cross-platform, modern terminal for seamless workflows.
Wave isn't just another terminal emulator; it's a rethink on how terminals are built. Wave combines command line with the power of the open web to help veteran CLI users and new developers alike.
@@ -18,7 +18,6 @@ Wave isn't just another terminal emulator; it's a rethink on how terminals are b
* Persistent sessions that can restore state across network disconnections and reboots
* Searchable contextual command history across all remote sessions (saved locally)
* Workspaces, tabs, and command blocks to keep you organized
* AI Integration with ChatGPT (or ChatGPT compatible APIs) to help write commands and get answers inline
## Installation
@@ -36,7 +35,6 @@ brew install --cask wave
* Homepage — https://www.waveterm.dev
* Download Page — https://www.waveterm.dev/download
* Documentation — https://docs.waveterm.dev/
* Blog — https://blog.waveterm.dev/
* Quick Start Guide — https://docs.waveterm.dev/quickstart/
* Discord Community — https://discord.gg/XfvZ334gwU
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
# assumes we have Wave-darwin-x64-[version].zip and Wave-darwin-arm64-[version].zip in current directory
VERSION=0.6.1
VERSION=0.6.2
rm -rf temp
rm -rf builds
mkdir temp
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "waveterm",
"author": "Command Line Inc",
"productName": "Wave",
"version": "0.6.1",
"version": "0.6.2",
"main": "dist/emain.js",
"license": "Apache-2.0",
"dependencies": {
-158
View File
@@ -664,161 +664,3 @@ a.a-block {
}
}
}
.settings-field {
display: flex;
flex-direction: row;
align-items: center;
&.settings-field.sub-field {
.settings-label {
font-weight: normal;
text-align: right;
padding-right: 20px;
}
}
&.settings-error {
color: @term-red;
margin-top: 20px;
padding: 10px;
border-radius: 5px;
background-color: #200707;
border: 1px solid @term-red;
font-weight: bold;
.error-dismiss {
padding: 2px;
cursor: pointer;
}
}
.settings-label {
font-weight: bold;
width: 12em;
display: flex;
flex-direction: row;
align-items: center;
.info-message {
margin-left: 5px;
}
}
.settings-input {
display: flex;
flex-direction: row;
align-items: center;
color: @term-white;
&.settings-clickable {
cursor: pointer;
}
&.inline-edit.edit-active {
input.input {
padding: 0;
height: 20px;
}
.button {
height: 20px;
}
}
input {
padding: 4px;
border-radius: 3px;
}
.control {
.icon {
width: 1.5em;
height: 1.5em;
margin: 0;
}
}
.tab-color-icon.color-default path {
fill: @tab-green;
}
.tab-color-icon.color-green path {
fill: @tab-green;
}
.tab-color-icon.color-orange path {
fill: @tab-orange;
}
.tab-color-icon.color-red path {
fill: @tab-red;
}
.tab-color-icon.color-yellow path {
fill: @tab-yellow;
}
.tab-color-icon.color-blue path {
fill: @tab-blue;
}
.tab-color-icon.color-mint path {
fill: @tab-mint;
}
.tab-color-icon.color-cyan path {
fill: @tab-cyan;
}
.tab-color-icon.color-white path {
fill: @tab-white;
}
.tab-color-icon.color-violet path {
fill: @tab-violet;
}
.tab-color-icon.color-pink path {
fill: @tab-pink;
}
.tab-colors,
.tab-icons {
display: flex;
flex-direction: row;
align-items: center;
.tab-color-sep,
.tab-icon-sep {
padding-left: 10px;
padding-right: 10px;
font-weight: bold;
}
.tab-color-icon,
.tab-icon-icon {
width: 1.1em;
vertical-align: middle;
}
.tab-color-name,
.tab-icon-name {
margin-left: 1em;
}
.tab-color-select,
.tab-icon-select {
cursor: pointer;
margin: 5px;
&:hover {
outline: 2px solid white;
}
}
}
.action-text {
margin-left: 20px;
color: @term-red;
}
.settings-share-link {
width: 160px;
}
}
&:not(:first-child) {
margin-top: 10px;
}
}
+6 -3
View File
@@ -7,9 +7,10 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator";
import { If } from "tsx-control-statements/components";
import dayjs from "dayjs";
import type { ContextMenuOpts } from "../types/types";
import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel } from "@/models";
import { isBlank } from "@/util/util";
import { GlobalModel } from "../model/model";
import { isBlank } from "../util/util";
import { WorkspaceView } from "./workspace/workspaceview";
import { PluginsView } from "./pluginsview/pluginsview";
import { BookmarksView } from "./bookmarks/bookmarks";
@@ -24,12 +25,14 @@ import "./app.less";
dayjs.extend(localizedFormat);
type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer
class App extends React.Component<{}, {}> {
dcWait: OV<boolean> = mobx.observable.box(false, { name: "dcWait" });
mainContentRef: React.RefObject<HTMLDivElement> = React.createRef();
constructor(props: {}) {
constructor(props: any) {
super(props);
if (GlobalModel.isDev) document.body.className = "is-dev";
}
-41
View File
@@ -8,7 +8,6 @@ export const SESSION_SETTINGS = "sessionSettings";
export const LINE_SETTINGS = "lineSettings";
export const CLIENT_SETTINGS = "clientSettings";
export const TAB_SWITCHER = "tabSwitcher";
export const USER_INPUT = "userInput";
export const LineContainer_Main = "main";
export const LineContainer_History = "history";
@@ -17,43 +16,3 @@ export const LineContainer_Sidebar = "sidebar";
export const ConfirmKey_HideShellPrompt = "hideshellprompt";
export const NoStrPos = -1;
export const RemotePtyRows = 8; // also in main.tsx
export const RemotePtyCols = 80;
export const ProdServerEndpoint = "http://127.0.0.1:1619";
export const ProdServerWsEndpoint = "ws://127.0.0.1:1623";
export const DevServerEndpoint = "http://127.0.0.1:8090";
export const DevServerWsEndpoint = "ws://127.0.0.1:8091";
export const DefaultTermFontSize = 12;
export const MinFontSize = 8;
export const MaxFontSize = 24;
export const InputChunkSize = 500;
export const RemoteColors = ["red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"];
export const TabColors = ["red", "orange", "yellow", "green", "mint", "cyan", "blue", "violet", "pink", "white"];
export const TabIcons = [
"sparkle",
"fire",
"ghost",
"cloud",
"compass",
"crown",
"droplet",
"graduation-cap",
"heart",
"file",
];
// @ts-ignore
export const VERSION = __WAVETERM_VERSION__;
// @ts-ignore
export const BUILD = __WAVETERM_BUILD__;
/**
* Levels for the screen status indicator
*/
export enum StatusIndicatorLevel {
None = 0,
Output = 1,
Success = 2,
Error = 3,
}
+1 -1
View File
@@ -1,4 +1,4 @@
@import "@/common/themes/themes.less";
@import "../../app/common/themes/themes.less";
.bookmarks-view {
background-color: @background-session;
+9 -20
View File
@@ -7,27 +7,20 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator";
import { If, For } from "tsx-control-statements/components";
import cn from "classnames";
import { GlobalModel } from "@/models";
import { CmdStrCode, Markdown } from "@/common/elements";
import type { BookmarkType } from "../../types/types";
import { GlobalModel } from "../../model/model";
import { CmdStrCode, Markdown } from "../common/common";
import { ReactComponent as XmarkIcon } from "@/assets/icons/line/xmark.svg";
import { ReactComponent as CopyIcon } from "@/assets/icons/favourites/copy.svg";
import { ReactComponent as PenIcon } from "@/assets/icons/favourites/pen.svg";
import { ReactComponent as TrashIcon } from "@/assets/icons/favourites/trash.svg";
import { ReactComponent as FavoritesIcon } from "@/assets/icons/favourites.svg";
import { ReactComponent as XmarkIcon } from "../assets/icons/line/xmark.svg";
import { ReactComponent as CopyIcon } from "../assets/icons/favourites/copy.svg";
import { ReactComponent as PenIcon } from "../assets/icons/favourites/pen.svg";
import { ReactComponent as TrashIcon } from "../assets/icons/favourites/trash.svg";
import { ReactComponent as FavoritesIcon } from "../assets/icons/favourites.svg";
import "./bookmarks.less";
type BookmarkProps = {
bookmark: BookmarkType;
};
@mobxReact.observer
class Bookmark extends React.Component<BookmarkProps, {}> {
constructor(props: BookmarkProps) {
super(props);
}
class Bookmark extends React.Component<{ bookmark: BookmarkType }, {}> {
@boundMethod
handleDeleteClick(): void {
let { bookmark } = this.props;
@@ -186,10 +179,6 @@ class Bookmark extends React.Component<BookmarkProps, {}> {
@mobxReact.observer
class BookmarksView extends React.Component<{}, {}> {
constructor(props: {}) {
super(props);
}
@boundMethod
closeView(): void {
GlobalModel.bookmarksModel.closeView();
+1 -1
View File
@@ -1,4 +1,4 @@
@import "@/common/themes/themes.less";
@import "../../app/common/themes/themes.less";
.clientsettings-view {
background-color: @background-session;
+28 -56
View File
@@ -6,18 +6,23 @@ import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator";
import cn from "classnames";
import { GlobalModel, GlobalCommandRunner, RemotesModel } from "@/models";
import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "@/common/elements";
import { commandRtnHandler, isBlank } from "@/util/util";
import * as appconst from "@/app/appconst";
import { GlobalModel, GlobalCommandRunner, MinFontSize, MaxFontSize, RemotesModel } from "../../model/model";
import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "../common/common";
import { CommandRtnType, ClientDataType } from "../../types/types";
import { commandRtnHandler, isBlank } from "../../util/util";
import "./clientsettings.less";
type OV<V> = mobx.IObservableValue<V>;
// @ts-ignore
const VERSION = __WAVETERM_VERSION__;
// @ts-ignore
const BUILD = __WAVETERM_BUILD__;
@mobxReact.observer
class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hoveredItemId: string }> {
fontSizeDropdownActive: OV<boolean> = mobx.observable.box(false, {
name: "clientSettings-fontSizeDropdownActive",
});
fontSizeDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "clientSettings-fontSizeDropdownActive" });
errorMessage: OV<string> = mobx.observable.box(null, { name: "ClientSettings-errorMessage" });
@boundMethod
@@ -29,12 +34,12 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
@boundMethod
handleChangeFontSize(fontSize: string): void {
const newFontSize = Number(fontSize);
let newFontSize = Number(fontSize);
this.fontSizeDropdownActive.set(false);
if (GlobalModel.termFontSize.get() == newFontSize) {
return;
}
const prtn = GlobalCommandRunner.setTermFontSize(newFontSize, false);
let prtn = GlobalCommandRunner.setTermFontSize(newFontSize, false);
commandRtnHandler(prtn, this.errorMessage);
}
@@ -67,29 +72,29 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
commandRtnHandler(prtn, this.errorMessage);
}
getFontSizes(): DropdownItem[] {
const availableFontSizes: DropdownItem[] = [];
for (let s = appconst.MinFontSize; s <= appconst.MaxFontSize; s++) {
availableFontSizes.push({ label: s + "px", value: String(s) });
getFontSizes(): any {
let availableFontSizes: { label: string; value: number }[] = [];
for (let s = MinFontSize; s <= MaxFontSize; s++) {
availableFontSizes.push({ label: s + "px", value: s });
}
return availableFontSizes;
}
@boundMethod
inlineUpdateOpenAIModel(newModel: string): void {
const prtn = GlobalCommandRunner.setClientOpenAISettings({ model: newModel });
let prtn = GlobalCommandRunner.setClientOpenAISettings({ model: newModel });
commandRtnHandler(prtn, this.errorMessage);
}
@boundMethod
inlineUpdateOpenAIToken(newToken: string): void {
const prtn = GlobalCommandRunner.setClientOpenAISettings({ apitoken: newToken });
let prtn = GlobalCommandRunner.setClientOpenAISettings({ apitoken: newToken });
commandRtnHandler(prtn, this.errorMessage);
}
@boundMethod
inlineUpdateOpenAIMaxTokens(newMaxTokensStr: string): void {
const prtn = GlobalCommandRunner.setClientOpenAISettings({ maxtokens: newMaxTokensStr });
let prtn = GlobalCommandRunner.setClientOpenAISettings({ maxtokens: newMaxTokensStr });
commandRtnHandler(prtn, this.errorMessage);
}
@@ -105,41 +110,19 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
GlobalModel.clientSettingsViewModel.closeView();
}
@boundMethod
handleChangeShortcut(newShortcut: string): void {
const prtn = GlobalCommandRunner.setGlobalShortcut(newShortcut);
commandRtnHandler(prtn, this.errorMessage);
}
getFKeys(): DropdownItem[] {
const opts: DropdownItem[] = [];
opts.push({ label: "Disabled", value: "" });
const platform = GlobalModel.getPlatform();
for (let i = 1; i <= 12; i++) {
const shortcut = (platform == "darwin" ? "Cmd" : "Alt") + "+F" + String(i);
opts.push({ label: shortcut, value: shortcut });
}
return opts;
}
getCurrentShortcut(): string {
const clientData = GlobalModel.clientData.get();
return clientData?.clientopts?.globalshortcut ?? "";
}
render() {
const isHidden = GlobalModel.activeMainView.get() != "clientsettings";
let isHidden = GlobalModel.activeMainView.get() != "clientsettings";
if (isHidden) {
return null;
}
const cdata: ClientDataType = GlobalModel.clientData.get();
const openAIOpts = cdata.openaiopts ?? {};
const apiTokenStr = isBlank(openAIOpts.apitoken) ? "(not set)" : "********";
const maxTokensStr = String(
let cdata: ClientDataType = GlobalModel.clientData.get();
let openAIOpts = cdata.openaiopts ?? {};
let apiTokenStr = isBlank(openAIOpts.apitoken) ? "(not set)" : "********";
let maxTokensStr = String(
openAIOpts.maxtokens == null || openAIOpts.maxtokens == 0 ? 1000 : openAIOpts.maxtokens
);
const curFontSize = GlobalModel.termFontSize.get();
let curFontSize = GlobalModel.termFontSize.get();
return (
<div className={cn("view clientsettings-view")}>
@@ -168,7 +151,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
<div className="settings-field">
<div className="settings-label">Client Version</div>
<div className="settings-input">
{appconst.VERSION} {appconst.BUILD}
{VERSION} {BUILD}
</div>
</div>
<div className="settings-field">
@@ -229,17 +212,6 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
/>
</div>
</div>
<div className="settings-field">
<div className="settings-label">Global Hotkey</div>
<div className="settings-input">
<Dropdown
className="hotkey-dropdown"
options={this.getFKeys()}
defaultValue={this.getCurrentShortcut()}
onChange={this.handleChangeShortcut}
/>
</div>
</div>
<SettingsError errorMessage={this.errorMessage} />
</div>
</div>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-123
View File
@@ -1,123 +0,0 @@
@import "@/common/themes/themes.less";
.wave-button {
background: none;
color: inherit;
border: none;
font: inherit;
cursor: pointer;
outline: inherit;
display: flex;
padding: 6px 16px;
align-items: center;
gap: 4px;
border-radius: 6px;
height: auto;
&:hover {
color: @term-white;
}
i {
fill: rgba(255, 255, 255, 0.12);
}
&.primary {
color: @term-green;
background: none;
i {
fill: @term-green;
}
&.solid {
color: @term-bright-white;
background: @term-green;
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
0px 0px 0.5px 0px rgba(255, 255, 255, 0.8) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.6) inset;
i {
fill: @term-white;
}
}
&.outlined {
border: 1px solid @term-green;
}
&.ghost {
// Styles for .ghost are already defined above
}
&:hover {
color: @term-bright-white;
}
}
&.secondary {
color: @term-white;
background: none;
&.solid {
background: rgba(255, 255, 255, 0.09);
box-shadow: none;
}
&.outlined {
border: 1px solid rgba(255, 255, 255, 0.09);
}
&.ghost {
padding: 6px 10px;
i {
fill: @term-green;
}
}
}
&.color-yellow {
&.solid {
border-color: @warning-yellow;
background-color: mix(@warning-yellow, @term-white, 50%);
box-shadow: none;
}
&.outlined {
color: @warning-yellow;
border-color: @warning-yellow;
&:hover {
color: @term-white;
border-color: @term-white;
}
}
&.ghost {
}
}
&.color-red {
&.solid {
border-color: @term-red;
background-color: mix(@term-red, @term-white, 50%);
box-shadow: none;
}
&.outlined {
color: @term-red;
border-color: @term-red;
}
&.ghost {
}
}
&.disabled {
opacity: 0.5;
}
&.link-button {
cursor: pointer;
}
}
-63
View File
@@ -1,63 +0,0 @@
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
import { boundMethod } from "autobind-decorator";
import cn from "classnames";
import "./button.less";
type ButtonVariantType = "outlined" | "solid" | "ghost";
type ButtonThemeType = "primary" | "secondary";
interface ButtonProps {
theme?: ButtonThemeType;
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
variant?: ButtonVariantType;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
color?: string;
style?: React.CSSProperties;
autoFocus?: boolean;
className?: string;
}
class Button extends React.Component<ButtonProps> {
static defaultProps = {
theme: "primary",
variant: "solid",
color: "",
style: {},
};
@boundMethod
handleClick() {
if (this.props.onClick && !this.props.disabled) {
this.props.onClick();
}
}
render() {
const { leftIcon, rightIcon, theme, children, disabled, variant, color, style, autoFocus, className } =
this.props;
return (
<button
className={cn("wave-button", theme, variant, color, { disabled: disabled }, className)}
onClick={this.handleClick}
disabled={disabled}
style={style}
autoFocus={autoFocus}
>
{leftIcon && <span className="icon-left">{leftIcon}</span>}
{children}
{rightIcon && <span className="icon-right">{rightIcon}</span>}
</button>
);
}
}
export { Button };
export type { ButtonProps };
-68
View File
@@ -1,68 +0,0 @@
@import "@/common/themes/themes.less";
.checkbox {
display: flex;
input[type="checkbox"] {
height: 0;
width: 0;
}
input[type="checkbox"] + label {
position: relative;
display: flex;
align-items: center;
color: @term-bright-white;
transition: color 250ms cubic-bezier(0.4, 0, 0.23, 1);
}
input[type="checkbox"] + label > span {
display: flex;
justify-content: center;
align-items: center;
margin-right: 10px;
width: 20px;
height: 20px;
background: transparent;
border: 2px solid #9e9e9e;
border-radius: 2px;
cursor: pointer;
transition: all 250ms cubic-bezier(0.4, 0, 0.23, 1);
}
input[type="checkbox"] + label:hover > span,
input[type="checkbox"]:focus + label > span {
background: rgba(255, 255, 255, 0.1);
}
input[type="checkbox"]:checked + label > ins {
height: 100%;
}
input[type="checkbox"]:checked + label > span {
border: 10px solid @term-green;
}
input[type="checkbox"]:checked + label > span:before {
content: "";
position: absolute;
top: -2px;
left: 3px;
width: 7px;
height: 12px;
border-right: 2px solid #fff;
border-bottom: 2px solid #fff;
transform: rotate(45deg);
transform-origin: 0% 100%;
animation: checkbox-check 500ms cubic-bezier(0.4, 0, 0.23, 1);
}
@keyframes checkbox-check {
0% {
opacity: 0;
}
33% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
}
-70
View File
@@ -1,70 +0,0 @@
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
import * as mobx from "mobx";
import cn from "classnames";
import "./checkbox.less";
class Checkbox extends React.Component<
{
checked?: boolean;
defaultChecked?: boolean;
onChange: (value: boolean) => void;
label: React.ReactNode;
className?: string;
id?: string;
},
{ checkedInternal: boolean }
> {
generatedId;
static idCounter = 0;
constructor(props) {
super(props);
this.state = {
checkedInternal: this.props.checked ?? Boolean(this.props.defaultChecked),
};
this.generatedId = `checkbox-${Checkbox.idCounter++}`;
}
componentDidUpdate(prevProps) {
if (this.props.checked !== undefined && this.props.checked !== prevProps.checked) {
this.setState({ checkedInternal: this.props.checked });
}
}
handleChange = (e) => {
const newChecked = e.target.checked;
if (this.props.checked === undefined) {
this.setState({ checkedInternal: newChecked });
}
this.props.onChange(newChecked);
};
render() {
const { label, className, id } = this.props;
const { checkedInternal } = this.state;
const checkboxId = id || this.generatedId;
return (
<div className={cn("checkbox", className)}>
<input
type="checkbox"
id={checkboxId}
checked={checkedInternal}
onChange={this.handleChange}
aria-checked={checkedInternal}
role="checkbox"
/>
<label htmlFor={checkboxId}>
<span></span>
{label}
</label>
</div>
);
}
}
export { Checkbox };
-102
View File
@@ -1,102 +0,0 @@
@import "@/common/themes/themes.less";
.cmdstr-code {
position: relative;
display: flex;
flex-direction: row;
padding: 0px 10px 0px 0;
&.is-large {
.use-button {
height: 28px;
width: 28px;
}
.code-div code {
}
}
&.limit-height .code-div {
max-height: 58px;
}
&.limit-height.is-large .code-div {
max-height: 68px;
}
.use-button {
flex-grow: 0;
padding: 3px;
border-radius: 3px 0 0 3px;
height: 22px;
width: 22px;
display: flex;
align-items: center;
justify-content: center;
align-self: flex-start;
cursor: pointer;
}
.code-div {
background-color: @term-black;
display: flex;
flex-direction: row;
min-width: 100px;
overflow: auto;
border-left: 1px solid #777;
code {
flex-shrink: 0;
min-width: 100px;
color: @term-white;
white-space: pre;
padding: 2px 8px 2px 8px;
background-color: @term-black;
font-size: 1em;
font-family: @fixed-font;
}
}
.copy-control {
width: 0;
position: relative;
display: block;
visibility: hidden;
.inner-copy {
position: absolute;
bottom: -1px;
right: -20px;
padding: 2px;
padding-left: 4px;
cursor: pointer;
width: 20px;
&:hover {
color: @term-white;
}
}
}
&:hover .copy-control {
visibility: visible !important;
}
}
.copied-indicator {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: @term-white;
opacity: 0;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
pointer-events: none;
animation-name: fade-in-out;
animation-duration: 0.3s;
}
-66
View File
@@ -1,66 +0,0 @@
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
import { boundMethod } from "autobind-decorator";
import cn from "classnames";
import { If } from "tsx-control-statements/components";
import { ReactComponent as CheckIcon } from "@/assets/icons/line/check.svg";
import { ReactComponent as CopyIcon } from "@/assets/icons/history/copy.svg";
import "./cmdstrcode.less";
class CmdStrCode extends React.Component<
{
cmdstr: string;
onUse: () => void;
onCopy: () => void;
isCopied: boolean;
fontSize: "normal" | "large";
limitHeight: boolean;
},
{}
> {
@boundMethod
handleUse(e: any) {
e.stopPropagation();
if (this.props.onUse != null) {
this.props.onUse();
}
}
@boundMethod
handleCopy(e: any) {
e.stopPropagation();
if (this.props.onCopy != null) {
this.props.onCopy();
}
}
render() {
let { isCopied, cmdstr, fontSize, limitHeight } = this.props;
return (
<div className={cn("cmdstr-code", { "is-large": fontSize == "large" }, { "limit-height": limitHeight })}>
<If condition={isCopied}>
<div key="copied" className="copied-indicator">
<div>copied</div>
</div>
</If>
<div key="use" className="use-button hoverEffect" title="Use Command" onClick={this.handleUse}>
<CheckIcon className="icon" />
</div>
<div key="code" className="code-div">
<code>{cmdstr}</code>
</div>
<div key="copy" className="copy-control hoverEffect">
<div className="inner-copy" onClick={this.handleCopy} title="copy">
<CopyIcon className="icon" />
</div>
</div>
</div>
);
}
}
export { CmdStrCode };
-10
View File
@@ -1,10 +0,0 @@
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
function renderCmdText(text: string): any {
return <span>&#x2318;{text}</span>;
}
export { renderCmdText };

Some files were not shown because too many files have changed in this diff Show More