mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
More Prompt Updates / Line Action Implementation (#357)
* termfontsize-sm (for meta1). add some small margin. remove rootIndicator. switch cwd/remote to red if root * break LineHeader out into its own react component * actual set our --term colors as the *theme* for xtermjs * move line-actions into its own div (absolute position). hover/background colors * add duration, format renderer, spacing, remove meta-wrap * simplify cmdtext output. simpler multi-line rendering. * adjust position/height of line actions for better interaction with long lines * fix horiz scrolling * remove unused * quick fix for text lines
This commit is contained in:
+3
-3
@@ -17,12 +17,12 @@ body {
|
||||
|
||||
body {
|
||||
&.is-dev .sidebar {
|
||||
background-color: var(--sidebar-dev-bg-color);
|
||||
background-color: var(--app-panel-bg-color-dev);
|
||||
}
|
||||
}
|
||||
|
||||
body .sidebar {
|
||||
background-color: var(--sidebar-bg-color);
|
||||
background-color: var(--app-panel-bg-color);
|
||||
}
|
||||
|
||||
textarea {
|
||||
@@ -117,7 +117,7 @@ svg.icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hideScrollbarUntillHover {
|
||||
.scrollbar-hide-until-hover {
|
||||
overflow: scroll;
|
||||
|
||||
&::-webkit-scrollbar-thumb,
|
||||
|
||||
@@ -7,7 +7,7 @@ import cn from "classnames";
|
||||
|
||||
class TabIcon extends React.Component<{ icon: string; color: string }> {
|
||||
render() {
|
||||
let { icon, color, className } = this.props;
|
||||
let { icon, color } = this.props;
|
||||
let iconClass = "";
|
||||
if (icon === "default" || icon === "square") {
|
||||
iconClass = "fa-solid fa-square fa-fw";
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.term-prompt {
|
||||
font-weight: normal;
|
||||
font-size: var(--termfontsize);
|
||||
line-height: var(--termlineheight);
|
||||
// we are explicitly *not* setting font size properties here so prompt will inherit from caller
|
||||
font-family: var(--termfontfamily);
|
||||
font-weight: normal;
|
||||
color: var(--term-gray);
|
||||
|
||||
i {
|
||||
@@ -62,11 +61,7 @@
|
||||
color: var(--term-bright-green);
|
||||
}
|
||||
|
||||
.term-prompt-end-user {
|
||||
color: var(--term-bright-green);
|
||||
}
|
||||
|
||||
.term-prompt-end-root {
|
||||
&.term-prompt-isroot .term-prompt-cwd {
|
||||
color: var(--term-bright-red);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,11 +94,7 @@ class Prompt extends React.Component<{ rptr: RemotePtrType; festate: Record<stri
|
||||
if (remote && remote.remotecanonicalname) {
|
||||
remoteTitle = "connected to " + remote.remotecanonicalname;
|
||||
}
|
||||
let cwdElem = (
|
||||
<span title="current directory" className="term-prompt-cwd">
|
||||
{cwd}
|
||||
</span>
|
||||
);
|
||||
let cwdElem = <span className="term-prompt-cwd">{cwd}</span>;
|
||||
let remoteElem = null;
|
||||
if (remoteStr != "local") {
|
||||
remoteElem = (
|
||||
@@ -107,12 +103,6 @@ class Prompt extends React.Component<{ rptr: RemotePtrType; festate: Record<stri
|
||||
</span>
|
||||
);
|
||||
}
|
||||
let rootIndicatorElem = null;
|
||||
if (isRoot) {
|
||||
rootIndicatorElem = <span className="term-prompt-end-root">#</span>;
|
||||
} else {
|
||||
rootIndicatorElem = <span className="term-prompt-end-user">$</span>;
|
||||
}
|
||||
let branchElem = null;
|
||||
let pythonElem = null;
|
||||
let condaElem = null;
|
||||
@@ -142,9 +132,14 @@ class Prompt extends React.Component<{ rptr: RemotePtrType; festate: Record<stri
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className={cn("term-prompt", { "term-prompt-color": this.props.color })}>
|
||||
{remoteElem} {condaElem} {pythonElem}
|
||||
{cwdElem} {branchElem} {rootIndicatorElem}
|
||||
<span
|
||||
className={cn(
|
||||
"term-prompt",
|
||||
{ "term-prompt-color": this.props.color },
|
||||
{ "term-prompt-isroot": isRoot }
|
||||
)}
|
||||
>
|
||||
{remoteElem} {cwdElem} {branchElem} {condaElem} {pythonElem}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* The file contains barebones of styling to appy themes to Prompt.
|
||||
* @TODO: Find a way to change the theme system-wide. atm, we are captruing colors in main.less
|
||||
*/
|
||||
|
||||
const themes = [
|
||||
{
|
||||
id: "default",
|
||||
terminal: { foreground: "#eceeec", background: "black" },
|
||||
},
|
||||
];
|
||||
|
||||
const getTheme = (_id = "default") => themes.find(({ id }) => id === _id);
|
||||
|
||||
export { getTheme };
|
||||
+95
-147
@@ -2,30 +2,13 @@
|
||||
margin: 0;
|
||||
padding: calc(var(--termpad) * 2) var(--termpad) calc(var(--termpad) * 2 + 1px) calc(var(--termpad) * 3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
font-weight: normal;
|
||||
font-family: var(--termfontfamily);
|
||||
|
||||
&.line-cmd {
|
||||
flex-direction: column;
|
||||
scroll-margin-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.line-text {
|
||||
// old formatting for text lines (requires override)
|
||||
flex-direction: row;
|
||||
padding-top: 5px;
|
||||
|
||||
.line-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
scroll-margin-bottom: 20px;
|
||||
|
||||
&.line-invalid {
|
||||
color: var(--term-text-white);
|
||||
@@ -118,7 +101,6 @@
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
margin-left: 0.5em;
|
||||
margin-right: 0.5em;
|
||||
vertical-align: text-top;
|
||||
fill: var(--primary-background-color);
|
||||
}
|
||||
@@ -152,152 +134,118 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.line-header {
|
||||
.line-header + div:not(.zero-height) {
|
||||
margin-top: calc(var(--termpad) + 2px);
|
||||
}
|
||||
|
||||
&:hover .line-actions {
|
||||
background-color: var(--app-panel-bg-color);
|
||||
.line-icon {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.line-actions {
|
||||
position: absolute;
|
||||
right: calc(var(--termpad) * 2);
|
||||
top: 0;
|
||||
font-size: 14px;
|
||||
color: var(--line-actions-inactive-color);
|
||||
border-radius: 4px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
line-height: 1.2;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.line-icon {
|
||||
visibility: hidden;
|
||||
|
||||
&.active {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--line-actions-active-color);
|
||||
}
|
||||
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.line-icon + .line-icon:not(.line-icon-shrink-left) {
|
||||
margin-left: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.line-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
font-weight: normal;
|
||||
font-family: var(--termfontfamily);
|
||||
font-size: var(--termfontsize);
|
||||
line-height: var(--termlineheight);
|
||||
|
||||
&.is-expanded {
|
||||
height: auto;
|
||||
.meta + .meta {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.line-icon {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
cursor: pointer;
|
||||
padding: 3px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.line-icon-show {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.line-icon + .line-icon {p
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.line-icon.active {
|
||||
visibility: visible;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover .line-icon {
|
||||
visibility: visible;
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.meta.meta-line1 {
|
||||
color: var(--term-gray);
|
||||
}
|
||||
|
||||
.meta.meta-line2 {
|
||||
margin-left: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.line-header + div:not(.zero-height) {
|
||||
margin-top: var(--termpad);
|
||||
}
|
||||
|
||||
.meta-wrap {
|
||||
flex: 1 1 0px;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.user {
|
||||
color: var(--line-meta-user-color);
|
||||
font-weight: bold;
|
||||
margin-top: 1px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.ts,
|
||||
.termopts,
|
||||
.renderer {
|
||||
.meta {
|
||||
display: flex;
|
||||
}
|
||||
flex-direction: row;
|
||||
|
||||
.renderer {
|
||||
margin-left: 3px;
|
||||
svg {
|
||||
width: 1em;
|
||||
.user {
|
||||
color: var(--line-meta-user-color);
|
||||
font-weight: bold;
|
||||
margin-top: 1px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
&.meta-line1 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: var(--termfontsize-sm);
|
||||
line-height: var(--termlineheight-sm);
|
||||
color: var(--term-gray);
|
||||
}
|
||||
|
||||
.meta-divider {
|
||||
margin-left: var(--termpad);
|
||||
margin-right: var(--termpad);
|
||||
}
|
||||
|
||||
.ts,
|
||||
.termopts,
|
||||
.renderer {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.renderer .renderer-icon {
|
||||
margin-right: 0.5em;
|
||||
fill: var(--line-svg-fill-color);
|
||||
}
|
||||
|
||||
.metapart-mono {
|
||||
margin-left: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.settings {
|
||||
display: none;
|
||||
margin-left: 0.5em;
|
||||
cursor: pointer;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
line-height: 1em;
|
||||
svg {
|
||||
fill: var(--line-svg-fill-color);
|
||||
&:hover {
|
||||
fill: var(--line-svg-hover-fill-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.termopts {
|
||||
display: none;
|
||||
.resize-button {
|
||||
cursor: pointer;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.metapart-mono {
|
||||
margin-left: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cmdtext {
|
||||
overflow: hidden;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.cmdtext-overflow {
|
||||
flex-shrink: 0;
|
||||
padding-right: 2px;
|
||||
color: var(--term-text-white);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.meta-cmdtext {
|
||||
color: var(--term-bright-white);
|
||||
}
|
||||
}
|
||||
|
||||
.cmdtext-expanded-wrapper {
|
||||
padding-left: 6px;
|
||||
overflow-y: auto;
|
||||
max-height: 60px;
|
||||
border-left: 2px solid #333;
|
||||
margin-left: 3px;
|
||||
|
||||
.cmdtext-expanded {
|
||||
overflow: auto;
|
||||
max-height: calc(var(--termlineheight) * 3.3);
|
||||
white-space: pre;
|
||||
color: var(--term-bright-white);
|
||||
font-weight: bold;
|
||||
width: calc(100% - var(--termpad) * 2);
|
||||
|
||||
color: var(--term-text-white);
|
||||
padding-bottom: 5px;
|
||||
&.is-multiline {
|
||||
border-left: 2px solid #333;
|
||||
margin-left: 3px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+234
-273
File diff suppressed because it is too large
Load Diff
@@ -28,12 +28,16 @@
|
||||
font-size: var(--termfontsize);
|
||||
}
|
||||
|
||||
.lines-spacer + .line-sep-labeled {
|
||||
margin-top: var(--termpad);
|
||||
}
|
||||
|
||||
.line-sep-labeled::before,
|
||||
.line-sep-labeled::after {
|
||||
content: "";
|
||||
height: 1px;
|
||||
flex-grow: 1;
|
||||
background-color: var(--app-text-color);
|
||||
background-color: var(--term-gray);
|
||||
}
|
||||
|
||||
.line-sep-labeled::before {
|
||||
|
||||
+6
-2
@@ -9,6 +9,8 @@
|
||||
--termfontsize: 12px;
|
||||
--termlineheight: 15px;
|
||||
--termpad: 7px; // padding value (scaled to termfontsize)
|
||||
--termfontsize-sm: 10px;
|
||||
--termlineheight-sm: 13px;
|
||||
|
||||
// other fonts
|
||||
--fixed-font: "Martian Mono", sans-serif;
|
||||
@@ -43,6 +45,8 @@
|
||||
--app-border-color: rgb(51, 51, 51);
|
||||
--app-maincontent-bg-color: #333;
|
||||
--app-border-radius: 10px;
|
||||
--app-panel-bg-color: rgba(21, 23, 21, 1);
|
||||
--app-panel-bg-color-dev: rgb(21, 23, 48);
|
||||
|
||||
// global generic colors
|
||||
--app-black: rgb(0, 0, 0);
|
||||
@@ -150,8 +154,6 @@
|
||||
--hotkey-text-color: rgb(195, 200, 194);
|
||||
|
||||
// sidebar colors
|
||||
--sidebar-bg-color: rgba(21, 23, 21, 1);
|
||||
--sidebar-dev-bg-color: rgb(21, 23, 48);
|
||||
--sidebar-settings-color: rgb(255, 255, 255);
|
||||
--sidebar-separator-color: var(--app-border-color);
|
||||
--sidebar-highlight-color: rgba(241, 246, 243, 0.08);
|
||||
@@ -186,6 +188,8 @@
|
||||
--line-status-success-fill: rgb(88, 193, 66);
|
||||
--line-status-error-fill: #cc0000;
|
||||
--line-status-warning-fill: #ffa500;
|
||||
--line-actions-inactive-color: rgba(255, 255, 255, 0.5);
|
||||
--line-actions-active-color: rgba(255, 255, 255, 1);
|
||||
|
||||
// view colors
|
||||
// todo: bookmarks is a view, colors must be unified with --view* colors
|
||||
|
||||
@@ -324,7 +324,7 @@ class MainSideBar extends React.Component<MainSideBarProps, {}> {
|
||||
]}
|
||||
/>
|
||||
<div
|
||||
className="middle hideScrollbarUntillHover"
|
||||
className="middle scrollbar-hide-until-hover"
|
||||
id="sidebar-middle"
|
||||
style={{
|
||||
maxHeight: `calc(100vh - ${this.middleHeightSubtractor.get()}px)`,
|
||||
|
||||
@@ -182,7 +182,7 @@ class ScreenTabs extends React.Component<
|
||||
{/* Inner container ensures that hovering over the scrollbar doesn't trigger the hover effect on the tabs. This prevents weird flickering of the icons when the mouse is moved over the scrollbar. */}
|
||||
<div
|
||||
key="container-inner"
|
||||
className="screen-tabs-container-inner no-highlight-scrollbar hideScrollbarUntillHover"
|
||||
className="screen-tabs-container-inner no-highlight-scrollbar scrollbar-hide-until-hover"
|
||||
>
|
||||
<Reorder.Group
|
||||
className="screen-tabs"
|
||||
|
||||
@@ -37,6 +37,10 @@ class Cmd {
|
||||
return this.data.get().restartts;
|
||||
}
|
||||
|
||||
getDurationMs(): number {
|
||||
return this.data.get().durationms;
|
||||
}
|
||||
|
||||
getAsWebCmd(lineid: string): WebCmd {
|
||||
let cmd = this.data.get();
|
||||
let remote = this.model.getRemote(this.remote.remoteid);
|
||||
|
||||
+5
-1
@@ -353,12 +353,16 @@ class Model {
|
||||
if (fontSize > appconst.MaxFontSize) {
|
||||
fontSize = appconst.MaxFontSize;
|
||||
}
|
||||
const fontSizeSm = fontSize - 2;
|
||||
const monoFontSize = getMonoFontSize(fontSize);
|
||||
const monoFontSizeSm = getMonoFontSize(fontSizeSm);
|
||||
mobx.action(() => {
|
||||
this.bumpRenderVersion();
|
||||
this.setStyleVar("--termfontsize", fontSize + "px");
|
||||
this.setStyleVar("--termlineheight", monoFontSize.height + "px");
|
||||
this.setStyleVar("--termpad", Math.floor(monoFontSize.height / 2) + "px");
|
||||
this.setStyleVar("--termpad", monoFontSize.pad + "px");
|
||||
this.setStyleVar("--termfontsize-sm", fontSizeSm + "px");
|
||||
this.setStyleVar("--termlineheight-sm", monoFontSizeSm.height + "px");
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import * as mobx from "mobx";
|
||||
import { Terminal } from "xterm";
|
||||
import type { ITheme } from "xterm";
|
||||
//TODO: replace with `@xterm/addon-web-links` when it's available as stable
|
||||
import { WebLinksAddon } from "xterm-addon-web-links";
|
||||
import { sprintf } from "sprintf-js";
|
||||
@@ -10,7 +11,6 @@ import { boundMethod } from "autobind-decorator";
|
||||
import { windowWidthToCols, windowHeightToRows } from "@/util/textmeasure";
|
||||
import { boundInt } from "@/util/util";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { getTheme } from "@/common/themes/themes";
|
||||
|
||||
type DataUpdate = {
|
||||
data: Uint8Array;
|
||||
@@ -36,6 +36,30 @@ type TermWrapOpts = {
|
||||
onUpdateContentHeight: (termContext: RendererContext, height: number) => void;
|
||||
};
|
||||
|
||||
function getThemeFromCSSVars(): ITheme {
|
||||
let theme: ITheme = {};
|
||||
let rootStyle = getComputedStyle(document.documentElement);
|
||||
theme.foreground = rootStyle.getPropertyValue("--term-white");
|
||||
theme.background = rootStyle.getPropertyValue("--term-black");
|
||||
theme.black = rootStyle.getPropertyValue("--term-black");
|
||||
theme.red = rootStyle.getPropertyValue("--term-red");
|
||||
theme.green = rootStyle.getPropertyValue("--term-green");
|
||||
theme.yellow = rootStyle.getPropertyValue("--term-yellow");
|
||||
theme.blue = rootStyle.getPropertyValue("--term-blue");
|
||||
theme.magenta = rootStyle.getPropertyValue("--term-magenta");
|
||||
theme.cyan = rootStyle.getPropertyValue("--term-cyan");
|
||||
theme.white = rootStyle.getPropertyValue("--term-white");
|
||||
theme.brightBlack = rootStyle.getPropertyValue("--term-bright-black");
|
||||
theme.brightRed = rootStyle.getPropertyValue("--term-bright-red");
|
||||
theme.brightGreen = rootStyle.getPropertyValue("--term-bright-green");
|
||||
theme.brightYellow = rootStyle.getPropertyValue("--term-bright-yellow");
|
||||
theme.brightBlue = rootStyle.getPropertyValue("--term-bright-blue");
|
||||
theme.brightMagenta = rootStyle.getPropertyValue("--term-bright-magenta");
|
||||
theme.brightCyan = rootStyle.getPropertyValue("--term-bright-cyan");
|
||||
theme.brightWhite = rootStyle.getPropertyValue("--term-bright-white");
|
||||
return theme;
|
||||
}
|
||||
|
||||
// cmd-instance
|
||||
class TermWrap {
|
||||
terminal: any;
|
||||
@@ -86,13 +110,13 @@ class TermWrap {
|
||||
let cols = windowWidthToCols(opts.winSize.width, opts.fontSize);
|
||||
this.termSize = { rows: opts.termOpts.rows, cols: cols };
|
||||
}
|
||||
const { terminal } = getTheme();
|
||||
let theme = getThemeFromCSSVars();
|
||||
this.terminal = new Terminal({
|
||||
rows: this.termSize.rows,
|
||||
cols: this.termSize.cols,
|
||||
fontSize: opts.fontSize,
|
||||
fontFamily: opts.fontFamily,
|
||||
theme: { foreground: terminal.foreground, background: terminal.background },
|
||||
theme: theme,
|
||||
});
|
||||
this.terminal.loadAddon(
|
||||
new WebLinksAddon((e, uri) => {
|
||||
|
||||
@@ -268,6 +268,26 @@ function getDateStr(d: Date): string {
|
||||
return dowStr + " " + yearStr + "-" + monthStr + "-" + dayStr;
|
||||
}
|
||||
|
||||
function formatDuration(ms: number): string {
|
||||
if (ms < 1000) {
|
||||
return ms + "ms";
|
||||
}
|
||||
if (ms < 10000) {
|
||||
return (ms / 1000).toFixed(2) + "s";
|
||||
}
|
||||
if (ms < 100000) {
|
||||
return (ms / 1000).toFixed(1) + "s";
|
||||
}
|
||||
if (ms < 60 * 60 * 1000) {
|
||||
let mins = Math.floor(ms / 60000);
|
||||
let secs = Math.floor((ms % 60000) / 1000);
|
||||
return mins + "m" + secs + "s";
|
||||
}
|
||||
let hours = Math.floor(ms / (60 * 60 * 1000));
|
||||
let mins = Math.floor((ms % (60 * 60 * 1000)) / 60000);
|
||||
return hours + "h" + mins + "m";
|
||||
}
|
||||
|
||||
function getRemoteConnVal(r: RemoteType): number {
|
||||
switch (r.status) {
|
||||
case "connected":
|
||||
@@ -400,4 +420,5 @@ export {
|
||||
getRemoteName,
|
||||
ces,
|
||||
fireAndForget,
|
||||
formatDuration,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user